Skip to content

Commit fe09165

Browse files
committed
GH-469: Moved base branch to GH-784
1 parent a5e8cf2 commit fe09165

File tree

5 files changed

+265
-0
lines changed

5 files changed

+265
-0
lines changed

Diff for: masq/src/command_factory.rs

+4
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ use crate::commands::crash_command::CrashCommand;
1010
use crate::commands::descriptor_command::DescriptorCommand;
1111
use crate::commands::financials_command::FinancialsCommand;
1212
use crate::commands::generate_wallets_command::GenerateWalletsCommand;
13+
use crate::commands::neighborhood_info_command::NeighborhoodInfoCommand;
1314
use crate::commands::recover_wallets_command::RecoverWalletsCommand;
1415
use crate::commands::scan_command::ScanCommand;
1516
use crate::commands::set_configuration_command::SetConfigurationCommand;
@@ -60,6 +61,9 @@ impl CommandFactory for CommandFactoryReal {
6061
Ok(command) => Box::new(command),
6162
Err(msg) => return Err(CommandSyntax(msg)),
6263
},
64+
65+
"neighborhood-info" => Box::new(NeighborhoodInfoCommand::new()),
66+
6367
"recover-wallets" => match RecoverWalletsCommand::new(pieces) {
6468
Ok(command) => Box::new(command),
6569
Err(msg) => return Err(CommandSyntax(msg)),

Diff for: masq/src/commands/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,4 @@ pub mod setup_command;
1616
pub mod shutdown_command;
1717
pub mod start_command;
1818
pub mod wallet_addresses_command;
19+
pub mod neighborhood_info_command;

Diff for: masq/src/commands/neighborhood_info_command.rs

+170
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
use clap::{App, SubCommand};
2+
use masq_lib::constants::NODE_NOT_RUNNING_ERROR;
3+
use masq_lib::messages::{UiCollectNeighborhoodInfoRequest, UiCollectNeighborhoodInfoResponse};
4+
use masq_lib::short_writeln;
5+
use crate::command_context::CommandContext;
6+
use crate::commands::commands_common::{Command, CommandError, STANDARD_COMMAND_TIMEOUT_MILLIS, transaction};
7+
use crate::commands::commands_common::CommandError::Payload;
8+
9+
10+
#[derive(Debug)]
11+
12+
pub struct NeighborhoodInfoCommand {}
13+
14+
const NEIGHBORHOOD_INFO_SUBCOMMAND_ABOUT: &str =
15+
"Example about for Neighborhood Info Command.";
16+
17+
pub fn neighborhood_info_subcommand() -> App<'static, 'static> {
18+
SubCommand::with_name("neighborhood-info").about(NEIGHBORHOOD_INFO_SUBCOMMAND_ABOUT)
19+
}
20+
21+
impl Command for NeighborhoodInfoCommand {
22+
fn execute(&self, context: &mut dyn CommandContext) -> Result<(), CommandError> {
23+
let input = UiCollectNeighborhoodInfoRequest {};
24+
let output: Result<UiCollectNeighborhoodInfoResponse, CommandError> =
25+
transaction(input, context, STANDARD_COMMAND_TIMEOUT_MILLIS);
26+
27+
match output {
28+
Ok(response) => {
29+
short_writeln!(context.stdout(), "NeighborhoodInfo Command msg -- TODO {:?}", response);
30+
Ok(())
31+
}
32+
33+
Err(Payload(code, message)) if code == NODE_NOT_RUNNING_ERROR => {
34+
short_writeln!(
35+
context.stderr(),
36+
"MASQNode is not running; therefore neighborhood information cannot be displayed."
37+
);
38+
Err(Payload(code, message))
39+
}
40+
Err(e) => {
41+
short_writeln!(context.stderr(), "Neighborhood information retrieval failed: {:?}", e);
42+
Err(e)
43+
}
44+
}
45+
}
46+
}
47+
48+
impl Default for NeighborhoodInfoCommand {
49+
fn default() -> Self {
50+
Self::new()
51+
}
52+
}
53+
54+
impl NeighborhoodInfoCommand {
55+
pub fn new() -> Self {
56+
Self {}
57+
}
58+
}
59+
60+
61+
#[cfg(test)]
62+
mod tests {
63+
use std::collections::HashMap;
64+
use std::sync::{Arc, Mutex};
65+
use masq_lib::constants::NODE_NOT_RUNNING_ERROR;
66+
use masq_lib::messages::{NodeInfo, ToMessageBody, UiCollectNeighborhoodInfoRequest, UiCollectNeighborhoodInfoResponse};
67+
use crate::command_context::ContextError;
68+
use crate::command_context::ContextError::ConnectionDropped;
69+
use crate::command_factory::{CommandFactory, CommandFactoryReal};
70+
use crate::commands::commands_common::{Command, CommandError, STANDARD_COMMAND_TIMEOUT_MILLIS};
71+
use crate::commands::commands_common::CommandError::ConnectionProblem;
72+
use crate::commands::neighborhood_info_command::{NEIGHBORHOOD_INFO_SUBCOMMAND_ABOUT, NeighborhoodInfoCommand};
73+
use crate::test_utils::mocks::CommandContextMock;
74+
75+
#[test]
76+
fn constants_have_correct_values() {
77+
assert_eq!(
78+
NEIGHBORHOOD_INFO_SUBCOMMAND_ABOUT,
79+
"Example about for Neighborhood Info Command."
80+
);
81+
}
82+
83+
#[test]
84+
fn testing_command_factory() {
85+
let factory = CommandFactoryReal::new();
86+
let expect_result = HashMap::from([
87+
(
88+
"public_key_1".to_string(),
89+
NodeInfo {
90+
version: 252,
91+
country_code: "UK".to_string(),
92+
exit_service: true,
93+
unreachable_hosts: vec!["facebook.com".to_string(), "x.com".to_string()],
94+
},
95+
),
96+
(
97+
"public_key_2".to_string(),
98+
NodeInfo {
99+
version: 5,
100+
country_code: "CZ".to_string(),
101+
exit_service: false,
102+
unreachable_hosts: vec!["facebook.com".to_string(), "x.com".to_string()],
103+
},
104+
),
105+
]);
106+
let mut context = CommandContextMock::new().transact_result(Ok(UiCollectNeighborhoodInfoResponse {
107+
neighborhood_database: expect_result,
108+
}.tmb(0)));
109+
let subject = factory.make(&["neighborhood-info".to_string()]).unwrap();
110+
111+
let result = subject.execute(&mut context);
112+
113+
assert_eq!(result, Ok(()));
114+
}
115+
116+
#[test]
117+
fn doesnt_work_if_node_is_not_running() {
118+
let mut context = CommandContextMock::new().transact_result(Err(
119+
ContextError::PayloadError(NODE_NOT_RUNNING_ERROR, "irrelevant".to_string()),
120+
));
121+
let stdout_arc = context.stdout_arc();
122+
let stderr_arc = context.stderr_arc();
123+
let subject = NeighborhoodInfoCommand::new();
124+
125+
let result = subject.execute(&mut context);
126+
127+
assert_eq!(
128+
result,
129+
Err(CommandError::Payload(
130+
NODE_NOT_RUNNING_ERROR,
131+
"irrelevant".to_string()
132+
))
133+
);
134+
assert_eq!(
135+
stderr_arc.lock().unwrap().get_string(),
136+
"MASQNode is not running; therefore neighborhood information cannot be displayed.\n"
137+
);
138+
assert_eq!(stdout_arc.lock().unwrap().get_string(), String::new());
139+
}
140+
141+
142+
#[test]
143+
fn descriptor_command_bad_path() {
144+
let transact_params_arc = Arc::new(Mutex::new(vec![]));
145+
let mut context = CommandContextMock::new()
146+
.transact_params(&transact_params_arc)
147+
.transact_result(Err(ConnectionDropped("Booga".to_string())));
148+
let stdout_arc = context.stdout_arc();
149+
let stderr_arc = context.stderr_arc();
150+
let subject = NeighborhoodInfoCommand::new();
151+
152+
let result = subject.execute(&mut context);
153+
154+
assert_eq!(result, Err(ConnectionProblem("Booga".to_string())));
155+
let transact_params = transact_params_arc.lock().unwrap();
156+
assert_eq!(
157+
*transact_params,
158+
vec![(
159+
UiCollectNeighborhoodInfoRequest {}.tmb(0),
160+
STANDARD_COMMAND_TIMEOUT_MILLIS
161+
)]
162+
);
163+
assert_eq!(stdout_arc.lock().unwrap().get_string(), String::new());
164+
assert_eq!(
165+
stderr_arc.lock().unwrap().get_string(),
166+
"Neighborhood information retrieval failed: ConnectionProblem(\"Booga\")\n"
167+
);
168+
}
169+
170+
}

Diff for: masq/src/schema.rs

+2
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ use crate::commands::wallet_addresses_command::wallet_addresses_subcommand;
2020
use clap::{App, AppSettings, Arg};
2121
use lazy_static::lazy_static;
2222
use masq_lib::constants::{DEFAULT_UI_PORT, HIGHEST_USABLE_PORT, LOWEST_USABLE_INSECURE_PORT};
23+
use crate::commands::neighborhood_info_command::neighborhood_info_subcommand;
2324

2425
lazy_static! {
2526
static ref UI_PORT_HELP: String = format!(
@@ -69,6 +70,7 @@ pub fn app() -> App<'static, 'static> {
6970
.subcommand(descriptor_subcommand())
7071
.subcommand(financials_subcommand())
7172
.subcommand(generate_wallets_subcommand())
73+
.subcommand(neighborhood_info_subcommand())
7274
.subcommand(recover_wallets_subcommand())
7375
.subcommand(scan_subcommand())
7476
.subcommand(set_configuration_subcommand())

Diff for: masq_lib/src/messages.rs

+88
Original file line numberDiff line numberDiff line change
@@ -629,6 +629,31 @@ pub struct CustomQueries {
629629
pub receivable_opt: Option<RangeQuery<i64>>,
630630
}
631631

632+
///////////+------------------ GH-469
633+
//
634+
// Still thinking about these names
635+
//
636+
637+
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
638+
pub struct NodeInfo {
639+
pub version: u32,
640+
pub country_code: String,
641+
pub exit_service: bool,
642+
pub unreachable_hosts: Vec<String>,
643+
}
644+
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
645+
pub struct UiCollectNeighborhoodInfoRequest {}
646+
conversation_message!(UiCollectNeighborhoodInfoRequest, "neighborhoodInfo");
647+
648+
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
649+
pub struct UiCollectNeighborhoodInfoResponse {
650+
pub neighborhood_database: HashMap<String, NodeInfo>,
651+
}
652+
conversation_message!(UiCollectNeighborhoodInfoResponse, "neighborhoodInfo");
653+
654+
655+
///////////+------------------ GH-469
656+
632657
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
633658
pub struct RangeQuery<T> {
634659
#[serde(rename = "minAgeS")]
@@ -903,6 +928,69 @@ mod tests {
903928
);
904929
}
905930

931+
932+
// ---------- GH-469
933+
#[test]
934+
fn can_deserialize_ui_collect_country_codes_response() {
935+
let json = r#"
936+
{
937+
"neighborhood_database": {
938+
"public_key_1": {
939+
"version": 252,
940+
"country_code": "UK",
941+
"exit_service": true,
942+
"unreachable_hosts": ["facebook.com", "x.com"]
943+
},
944+
"public_key_2": {
945+
"version": 5,
946+
"country_code": "CZ",
947+
"exit_service": false,
948+
"unreachable_hosts": ["facebook.com", "x.com"]
949+
}
950+
}
951+
}
952+
"#;
953+
let message_body = MessageBody {
954+
opcode: "countryCodes".to_string(),
955+
path: Conversation(1234),
956+
payload: Ok(json.to_string()),
957+
};
958+
959+
let result: Result<(UiCollectNeighborhoodInfoResponse, u64), UiMessageError> =
960+
UiCollectNeighborhoodInfoResponse::fmb(message_body);
961+
962+
let expect_result = HashMap::from([
963+
(
964+
"public_key_1".to_string(),
965+
NodeInfo {
966+
version: 252,
967+
country_code: "UK".to_string(),
968+
exit_service: true,
969+
unreachable_hosts: vec!["facebook.com".to_string(), "x.com".to_string()],
970+
},
971+
),
972+
(
973+
"public_key_2".to_string(),
974+
NodeInfo {
975+
version: 5,
976+
country_code: "CZ".to_string(),
977+
exit_service: false,
978+
unreachable_hosts: vec!["facebook.com".to_string(), "x.com".to_string()],
979+
},
980+
),
981+
]);
982+
assert_eq!(
983+
result,
984+
Ok((
985+
UiCollectNeighborhoodInfoResponse {
986+
neighborhood_database: expect_result,
987+
},
988+
1234
989+
))
990+
);
991+
}
992+
993+
906994
#[test]
907995
fn ui_descriptor_methods_were_correctly_generated() {
908996
let subject = UiDescriptorResponse {

0 commit comments

Comments
 (0)