Skip to content

Commit 64b00d8

Browse files
BertBert
authored andcommitted
GH-598: before trying to harden the bulky test
1 parent 69357b9 commit 64b00d8

File tree

7 files changed

+541
-286
lines changed

7 files changed

+541
-286
lines changed
Lines changed: 381 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,381 @@
1+
// Copyright (c) 2025, MASQ (https://masq.ai) and/or its affiliates. All rights reserved.
2+
3+
use masq_lib::logger::Logger;
4+
use std::collections::HashMap;
5+
use std::fmt::{Display, Formatter};
6+
use web3::types::Address;
7+
8+
#[derive(Default)]
9+
pub struct AccountingMsgsDebugStats {
10+
report_routing_service_provided_processed: AccountingMsgStats,
11+
report_exit_service_provided_processed: AccountingMsgStats,
12+
report_services_consumed_processed: AccountingMsgStats,
13+
}
14+
15+
impl AccountingMsgsDebugStats {
16+
pub fn manage_debug_log(
17+
&mut self,
18+
logger: &Logger,
19+
msg_type: AccountingMsgType,
20+
log_window_size: u16,
21+
new_postings: Vec<NewPosting>,
22+
) {
23+
if logger.debug_enabled() {
24+
if let Some(loggable_stats) = self.manage_log(msg_type, new_postings, log_window_size) {
25+
debug!(logger, "{}", loggable_stats);
26+
}
27+
}
28+
}
29+
30+
fn manage_log(
31+
&mut self,
32+
msg_type: AccountingMsgType,
33+
new_postings: Vec<NewPosting>,
34+
log_window_size: u16,
35+
) -> Option<LoggableStats> {
36+
self.record(new_postings, msg_type);
37+
self.request_log_instruction(log_window_size, msg_type)
38+
}
39+
40+
fn record(&mut self, new_postings: Vec<NewPosting>, msg_type: AccountingMsgType) {
41+
match msg_type {
42+
AccountingMsgType::RoutingServiceProvided => {
43+
self.report_routing_service_provided_processed
44+
.handle_new_postings(new_postings);
45+
}
46+
AccountingMsgType::ExitServiceProvided => {
47+
self.report_exit_service_provided_processed
48+
.handle_new_postings(new_postings);
49+
}
50+
AccountingMsgType::ServicesConsumed => {
51+
self.report_services_consumed_processed
52+
.handle_new_postings(new_postings);
53+
}
54+
}
55+
}
56+
57+
fn request_log_instruction(
58+
&mut self,
59+
gap_size: u16,
60+
msg_type: AccountingMsgType,
61+
) -> Option<LoggableStats> {
62+
match msg_type {
63+
AccountingMsgType::RoutingServiceProvided => self
64+
.report_routing_service_provided_processed
65+
.loggable_stats(gap_size),
66+
AccountingMsgType::ExitServiceProvided => self
67+
.report_exit_service_provided_processed
68+
.loggable_stats(gap_size),
69+
AccountingMsgType::ServicesConsumed => self
70+
.report_services_consumed_processed
71+
.loggable_stats(gap_size),
72+
}
73+
}
74+
}
75+
76+
#[derive(Debug, PartialEq, Clone, Copy)]
77+
pub struct NewPosting {
78+
address: Address,
79+
amount_wei: u128,
80+
}
81+
82+
impl NewPosting {
83+
pub fn new(address: Address, amount_wei: u128) -> Self {
84+
Self {
85+
address,
86+
amount_wei,
87+
}
88+
}
89+
}
90+
91+
#[derive(Debug, PartialEq)]
92+
pub struct LoggableStats {
93+
msg_type: AccountingMsgType,
94+
accounting_msg_stats: HashMap<Address, u128>,
95+
log_window_in_pcs_of_msgs: u16,
96+
}
97+
98+
impl Display for LoggableStats {
99+
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
100+
todo!()
101+
}
102+
}
103+
104+
#[derive(Default)]
105+
struct AccountingMsgStats {
106+
stats: HashMap<Address, u128>,
107+
msg_count_since_last_logged: usize,
108+
}
109+
110+
impl AccountingMsgStats {
111+
fn loggable_stats(&mut self, log_window_size: u16) -> Option<LoggableStats> {
112+
if self.msg_count_since_last_logged == log_window_size as usize {
113+
self.msg_count_since_last_logged = 0;
114+
115+
Some(LoggableStats {
116+
msg_type: AccountingMsgType::RoutingServiceProvided,
117+
accounting_msg_stats: self.stats.drain().collect(),
118+
log_window_in_pcs_of_msgs: log_window_size,
119+
})
120+
} else {
121+
None
122+
}
123+
}
124+
125+
fn handle_new_postings(&mut self, new_postings: Vec<NewPosting>) {
126+
for new_posting in &new_postings {
127+
*self.stats.entry(new_posting.address).or_default() += new_posting.amount_wei;
128+
}
129+
self.msg_count_since_last_logged += 1;
130+
}
131+
}
132+
133+
#[derive(Debug, PartialEq, Clone, Copy)]
134+
pub enum AccountingMsgType {
135+
RoutingServiceProvided,
136+
ExitServiceProvided,
137+
ServicesConsumed,
138+
}
139+
140+
pub struct NewPostingsDebugContainer {
141+
debug_enabled: bool,
142+
vec: Vec<NewPosting>,
143+
}
144+
145+
impl NewPostingsDebugContainer {
146+
pub fn new(logger: &Logger) -> Self {
147+
Self {
148+
debug_enabled: logger.debug_enabled(),
149+
vec: vec![],
150+
}
151+
}
152+
153+
pub fn add(mut self, address: Address, sum_wei: u128) -> Self {
154+
if self.debug_enabled {
155+
self.vec.push(NewPosting::new(address, sum_wei));
156+
}
157+
self
158+
}
159+
}
160+
161+
impl Into<Vec<NewPosting>> for NewPostingsDebugContainer {
162+
fn into(self) -> Vec<NewPosting> {
163+
self.vec
164+
}
165+
}
166+
167+
#[cfg(test)]
168+
mod tests {
169+
use super::{
170+
AccountingMsgType, AccountingMsgsDebugStats, LoggableStats, NewPosting,
171+
NewPostingsDebugContainer,
172+
};
173+
use crate::blockchain::test_utils::make_address;
174+
use itertools::Itertools;
175+
use log::Level;
176+
use masq_lib::logger::Logger;
177+
use masq_lib::test_utils::logging::{init_test_logging, TestLogHandler};
178+
use std::collections::HashMap;
179+
use web3::types::Address;
180+
181+
#[test]
182+
fn test_loggable_count_works_for_routing_service_provided() {
183+
test_manage_debug_log(
184+
AccountingMsgType::RoutingServiceProvided,
185+
6,
186+
|subject| {
187+
subject
188+
.report_routing_service_provided_processed
189+
.stats
190+
.clone()
191+
},
192+
|subject| {
193+
subject
194+
.report_routing_service_provided_processed
195+
.msg_count_since_last_logged
196+
},
197+
);
198+
}
199+
200+
#[test]
201+
fn test_loggable_count_works_for_exit_service_provided() {
202+
test_manage_debug_log(
203+
AccountingMsgType::ExitServiceProvided,
204+
3,
205+
|subject| subject.report_exit_service_provided_processed.stats.clone(),
206+
|subject| {
207+
subject
208+
.report_exit_service_provided_processed
209+
.msg_count_since_last_logged
210+
},
211+
);
212+
}
213+
214+
#[test]
215+
fn test_loggable_count_works_for_services_consumed() {
216+
test_manage_debug_log(
217+
AccountingMsgType::ServicesConsumed,
218+
8,
219+
|subject| subject.report_services_consumed_processed.stats.clone(),
220+
|subject| {
221+
subject
222+
.report_services_consumed_processed
223+
.msg_count_since_last_logged
224+
},
225+
);
226+
}
227+
228+
fn test_manage_debug_log(
229+
msg_type: AccountingMsgType,
230+
gap_size: u16,
231+
fetch_stats: fn(&AccountingMsgsDebugStats) -> HashMap<Address, u128>,
232+
fetch_msg_count_processed: fn(&AccountingMsgsDebugStats) -> usize,
233+
) {
234+
let initial_new_postings_feeds = (0..gap_size - 1)
235+
.map(|idx| NewPosting::new(make_address(idx as u32), (idx as u128 + 1) * 1234567))
236+
.collect::<Vec<_>>();
237+
let mut subject = AccountingMsgsDebugStats::default();
238+
239+
let initial_state_total_count = fetch_stats(&subject);
240+
let initial_msg_count_processed = fetch_msg_count_processed(&subject);
241+
assert_eq!(initial_state_total_count, hashmap!());
242+
assert_eq!(initial_msg_count_processed, 0);
243+
244+
let first_log_instruction_opt = initial_new_postings_feeds
245+
.iter()
246+
.fold(None, |_, new_posting| {
247+
subject.manage_log(msg_type, vec![*new_posting], gap_size)
248+
});
249+
let first_expected_stats = Vec::from_iter(
250+
initial_new_postings_feeds
251+
.iter()
252+
.map(|new_posting| (new_posting.address, new_posting.amount_wei)),
253+
);
254+
let first_actual_stats = fetch_stats(&subject);
255+
let first_msg_count_processed = fetch_msg_count_processed(&subject);
256+
assert_eq!(first_log_instruction_opt, None);
257+
assert_eq!(
258+
first_actual_stats.into_iter().sorted().collect_vec(),
259+
first_expected_stats
260+
);
261+
assert_eq!(first_msg_count_processed, gap_size as usize - 1);
262+
263+
let second_new_posting = initial_new_postings_feeds.first().unwrap().clone();
264+
let second_log_instruction_opt =
265+
subject.manage_log(msg_type, vec![second_new_posting], gap_size);
266+
let second_actual_stats = fetch_stats(&subject);
267+
let second_msg_count_processed = fetch_msg_count_processed(&subject);
268+
let mut second_expected_stats = first_expected_stats.clone();
269+
second_expected_stats.get_mut(0).unwrap().1 += second_new_posting.amount_wei;
270+
let loggable_stats = second_log_instruction_opt.unwrap();
271+
assert_eq!(
272+
loggable_stats
273+
.accounting_msg_stats
274+
.into_iter()
275+
.sorted()
276+
.collect_vec(),
277+
second_expected_stats,
278+
);
279+
assert_eq!(loggable_stats.log_window_in_pcs_of_msgs, gap_size,);
280+
assert_eq!(second_actual_stats, hashmap!());
281+
assert_eq!(second_msg_count_processed, 0);
282+
283+
let third_new_posting = initial_new_postings_feeds.last().unwrap().clone();
284+
let third_log_instruction = subject.manage_log(msg_type, vec![third_new_posting], gap_size);
285+
let third_actual_stats = fetch_stats(&subject);
286+
let third_msg_count_processed = fetch_msg_count_processed(&subject);
287+
assert_eq!(third_log_instruction, None);
288+
assert_eq!(
289+
third_actual_stats,
290+
hashmap!(third_new_posting.address => third_new_posting.amount_wei)
291+
);
292+
assert_eq!(third_msg_count_processed, 1);
293+
}
294+
295+
#[test]
296+
fn new_posting_debug_container_for_debug_enabled() {
297+
let mut logger = Logger::new("test");
298+
logger.set_level_for_test(Level::Debug);
299+
let container = NewPostingsDebugContainer::new(&logger);
300+
let new_posting_1 = NewPosting::new(make_address(1), 1234567);
301+
let new_posting_2 = NewPosting::new(make_address(2), 7654321);
302+
303+
let container = container.add(new_posting_1.address, new_posting_1.amount_wei);
304+
let container = container.add(new_posting_1.address, new_posting_1.amount_wei);
305+
let container = container.add(new_posting_2.address, new_posting_2.amount_wei);
306+
307+
let stats: Vec<NewPosting> = container.into();
308+
assert_eq!(stats, vec![new_posting_1, new_posting_1, new_posting_2]);
309+
}
310+
311+
#[test]
312+
fn new_posting_debug_container_for_debug_not_enabled() {
313+
let mut logger = Logger::new("test");
314+
logger.set_level_for_test(Level::Info);
315+
let container = NewPostingsDebugContainer::new(&logger);
316+
let new_posting_1 = NewPosting::new(make_address(1), 1234567);
317+
let new_posting_2 = NewPosting::new(make_address(2), 7654321);
318+
319+
let container = container.add(new_posting_1.address, new_posting_1.amount_wei);
320+
let container = container.add(new_posting_1.address, new_posting_1.amount_wei);
321+
let container = container.add(new_posting_2.address, new_posting_2.amount_wei);
322+
323+
let stats: Vec<NewPosting> = container.into();
324+
assert_eq!(stats, vec![]);
325+
}
326+
327+
#[test]
328+
fn accounts_stats_are_logged_only_if_debug_enabled() {
329+
init_test_logging();
330+
let test_name = "accounts_stats_are_logged_only_if_debug_enabled";
331+
let mut logger = Logger::new(test_name);
332+
logger.set_level_for_test(Level::Debug);
333+
let mut subject = AccountingMsgsDebugStats::default();
334+
let new_posting_1 = NewPosting::new(make_address(1), 1234567);
335+
let new_posting_2 = NewPosting::new(make_address(2), 7654321);
336+
337+
subject.manage_debug_log(
338+
&logger,
339+
AccountingMsgType::ServicesConsumed,
340+
1,
341+
vec![new_posting_1, new_posting_2],
342+
);
343+
344+
TestLogHandler::new()
345+
.exists_log_containing(&format!("DEBUG: {test_name}: Account debits in last"));
346+
}
347+
348+
#[test]
349+
fn accounts_stats_are_not_logged_if_debug_is_not_enabled() {
350+
init_test_logging();
351+
let test_name = "accounts_stats_are_not_logged_if_debug_is_not_enabled";
352+
let mut logger = Logger::new("test");
353+
logger.set_level_for_test(Level::Info);
354+
let mut subject = AccountingMsgsDebugStats::default();
355+
let new_posting_1 = NewPosting::new(make_address(1), 1234567);
356+
let new_posting_2 = NewPosting::new(make_address(2), 7654321);
357+
358+
subject.manage_debug_log(
359+
&logger,
360+
AccountingMsgType::ServicesConsumed,
361+
1,
362+
vec![new_posting_1, new_posting_2],
363+
);
364+
365+
TestLogHandler::new().exists_no_log_containing(&format!("DEBUG: {test_name}:"));
366+
}
367+
368+
#[test]
369+
fn display_loggable_stats() {
370+
let loggable_stats = LoggableStats {
371+
msg_type: AccountingMsgType::RoutingServiceProvided,
372+
accounting_msg_stats: hashmap!(make_address(1) => 1234567, make_address(2) => 7654321),
373+
log_window_in_pcs_of_msgs: 15,
374+
};
375+
let expected_display = "\
376+
Account debits in last 15 RoutingServiceProvided messages (wei):\n\
377+
0x0000000000000000000000000000000000000001: 1234567,\
378+
0x0000000000000000000000000000000000000002: 7654321";
379+
assert_eq!(format!("{}", loggable_stats), expected_display);
380+
}
381+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// Copyright (c) 2025, MASQ (https://masq.ai) and/or its affiliates. All rights reserved.
2+
3+
pub mod accounting_msgs_debug;
4+
pub mod msg_id_generator;
5+
6+
use crate::accountant::logging_utils::accounting_msgs_debug::AccountingMsgsDebugStats;
7+
8+
pub struct LoggingUtils {
9+
pub accounting_msgs_stats: AccountingMsgsDebugStats,
10+
pub msg_id_generator: Box<dyn msg_id_generator::MessageIdGenerator>,
11+
}
12+
13+
impl Default for LoggingUtils {
14+
fn default() -> Self {
15+
todo!()
16+
}
17+
}

0 commit comments

Comments
 (0)