From 6770055b654de0e9d082807c7bddcb718a52de00 Mon Sep 17 00:00:00 2001 From: Syther007 Date: Fri, 18 Oct 2024 23:25:19 +1300 Subject: [PATCH] GH-469: Added NeighborhoodInfoCommand formatting --- .../src/commands/neighborhood_info_command.rs | 125 +++++++++++++++++- masq_lib/src/messages.rs | 4 +- 2 files changed, 124 insertions(+), 5 deletions(-) diff --git a/masq/src/commands/neighborhood_info_command.rs b/masq/src/commands/neighborhood_info_command.rs index 8918f4506..9e0daafd0 100644 --- a/masq/src/commands/neighborhood_info_command.rs +++ b/masq/src/commands/neighborhood_info_command.rs @@ -24,12 +24,13 @@ impl Command for NeighborhoodInfoCommand { let output: Result = transaction(input, context, STANDARD_COMMAND_TIMEOUT_MILLIS); + // TODO GH-469: Add formatting here & create test... match output { Ok(response) => { - short_writeln!(context.stdout(), "NeighborhoodInfo Command msg -- TODO {:?}", response); + process_command_response(response, context); + // short_writeln!(context.stdout(), "NeighborhoodInfo Command msg -- TODO {:?}", response); Ok(()) } - Err(Payload(code, message)) if code == NODE_NOT_RUNNING_ERROR => { short_writeln!( context.stderr(), @@ -58,6 +59,65 @@ impl NeighborhoodInfoCommand { } + +fn process_command_response(response: UiCollectNeighborhoodInfoResponse, context: &mut dyn CommandContext) { + fn wrap_text(text: &str, width: usize) -> Vec { + let mut result = Vec::new(); + let mut start = 0; + + while start < text.len() { + let end = (start + width).min(text.len()); + let line = &text[start..end]; + result.push(line.to_string()); + start = end; + } + + result + } + + + short_writeln!(context.stdout(),"{:<15} {:<10} {:<15} {:<15}","Public Key", "Version", "Country Code", "Exit Service"); + short_writeln!(context.stdout(),"{}", "-".repeat(55)); + + for (node, info) in &response.neighborhood_database { + let country_code = info.country_code_opt.as_deref().unwrap_or("N/A"); + let exit_service = if info.exit_service { "Yes" } else { "No" }; + short_writeln!(context.stdout(), + "{:<15} {:<10} {:<15} {:<15}", + node, + info.version, + country_code, + exit_service + ); + } + + short_writeln!(context.stdout(),""); + short_writeln!(context.stdout(),"{:<15} {:<10}","Public Key", "Unreachable Hosts"); + short_writeln!(context.stdout(),"{}", "-".repeat(33)); + + for (node, info) in &response.neighborhood_database { + let unreachable_hosts = if info.unreachable_hosts.is_empty() { + "None".to_string() + } else { + info.unreachable_hosts.join(", ") + }; + + let wrapped_hosts = wrap_text(&unreachable_hosts, 75); + + for (i, line) in wrapped_hosts.iter().enumerate() { + if i == 0 { + short_writeln!(context.stdout(), "{:<15} {:<75}", node, line); + } else { + short_writeln!(context.stdout(), "{:<15} {:<75}", "", line); + } + } + } +} + + + + + #[cfg(test)] mod tests { use std::collections::HashMap; @@ -99,7 +159,7 @@ mod tests { version: 5, country_code_opt: Some("CZ".to_string()), exit_service: false, - unreachable_hosts: vec!["facebook.com".to_string(), "x.com".to_string()], + unreachable_hosts: vec!["example.com".to_string()], }, ), ]); @@ -167,4 +227,63 @@ mod tests { ); } + + #[test] + fn testing_command_format() { + let factory = CommandFactoryReal::new(); + let expect_result = HashMap::from([ + ( + "public_key_1".to_string(), + NodeInfo { + version: 252, + country_code_opt: Some("UK".to_string()), + exit_service: true, + unreachable_hosts: vec!["facebook.com".to_string(), "x.com".to_string()], + }, + ), + ( + "public_key_2".to_string(), + NodeInfo { + version: 5, + country_code_opt: Some("CZ".to_string()), + exit_service: false, + unreachable_hosts: vec!["example.com".to_string(), "x.com".to_string(), "youtube.com".to_string(), "google.com".to_string(), "masq.ai".to_string(), "fish.org".to_string(), "someverrrrrrrrrrylooooooooooooongurl.org".to_string(), "akc.org".to_string(), "notahost.com".to_string()], + }, + ), + ( + "public_key_3".to_string(), + NodeInfo { + version: 65, + country_code_opt: None, + exit_service: true, + unreachable_hosts: vec![], + }, + ) + ]); + let mut context = CommandContextMock::new().transact_result(Ok(UiCollectNeighborhoodInfoResponse { + neighborhood_database: expect_result, + }.tmb(0))); + let stdout_arc = context.stdout_arc(); + let subject = factory.make(&["neighborhood-info".to_string()]).unwrap(); + + let command_result = subject.execute(&mut context); + let stdout_string= stdout_arc.lock().unwrap().get_string(); + + eprintln!("{}", stdout_string); + + assert_eq!(command_result, Ok(())); + let lines: Vec<&str> = stdout_string.lines().collect(); + assert!(lines.contains(&"Public Key Version Country Code Exit Service ")); + assert!(lines.contains(&"-------------------------------------------------------")); + assert!(lines.contains(&"public_key_1 252 UK Yes ")); + assert!(lines.contains(&"public_key_2 5 CZ No ")); + assert!(lines.contains(&"public_key_3 65 N/A Yes ")); + assert!(lines.contains(&"Public Key Unreachable Hosts")); + assert!(lines.contains(&"---------------------------------")); + assert!(lines.contains(&"public_key_1 facebook.com, x.com ")); + assert!(lines.contains(&"public_key_2 example.com, x.com, youtube.com, google.com, masq.ai, fish.org, someverrrrr")); + assert!(lines.contains(&" rrrrrylooooooooooooongurl.org, akc.org, notahost.com ")); + assert!(lines.contains(&"public_key_3 None ")); + } + } \ No newline at end of file diff --git a/masq_lib/src/messages.rs b/masq_lib/src/messages.rs index f81a40cc3..f440837f8 100644 --- a/masq_lib/src/messages.rs +++ b/masq_lib/src/messages.rs @@ -969,7 +969,7 @@ mod tests { "public_key_1".to_string(), NodeInfo { version: 252, - country_code: "UK".to_string(), + country_code_opt: Some("UK".to_string()), exit_service: true, unreachable_hosts: vec!["facebook.com".to_string(), "x.com".to_string()], }, @@ -978,7 +978,7 @@ mod tests { "public_key_2".to_string(), NodeInfo { version: 5, - country_code: "CZ".to_string(), + country_code_opt: Some("CZ".to_string()), exit_service: false, unreachable_hosts: vec!["example.com".to_string()], },