Skip to content

Commit

Permalink
GH-469: Moved base branch to GH-784
Browse files Browse the repository at this point in the history
  • Loading branch information
Syther007 committed Sep 24, 2024
1 parent a5e8cf2 commit fe09165
Show file tree
Hide file tree
Showing 5 changed files with 265 additions and 0 deletions.
4 changes: 4 additions & 0 deletions masq/src/command_factory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use crate::commands::crash_command::CrashCommand;
use crate::commands::descriptor_command::DescriptorCommand;
use crate::commands::financials_command::FinancialsCommand;
use crate::commands::generate_wallets_command::GenerateWalletsCommand;
use crate::commands::neighborhood_info_command::NeighborhoodInfoCommand;
use crate::commands::recover_wallets_command::RecoverWalletsCommand;
use crate::commands::scan_command::ScanCommand;
use crate::commands::set_configuration_command::SetConfigurationCommand;
Expand Down Expand Up @@ -60,6 +61,9 @@ impl CommandFactory for CommandFactoryReal {
Ok(command) => Box::new(command),
Err(msg) => return Err(CommandSyntax(msg)),
},

"neighborhood-info" => Box::new(NeighborhoodInfoCommand::new()),

"recover-wallets" => match RecoverWalletsCommand::new(pieces) {
Ok(command) => Box::new(command),
Err(msg) => return Err(CommandSyntax(msg)),
Expand Down
1 change: 1 addition & 0 deletions masq/src/commands/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,4 @@ pub mod setup_command;
pub mod shutdown_command;
pub mod start_command;
pub mod wallet_addresses_command;
pub mod neighborhood_info_command;
170 changes: 170 additions & 0 deletions masq/src/commands/neighborhood_info_command.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
use clap::{App, SubCommand};
use masq_lib::constants::NODE_NOT_RUNNING_ERROR;
use masq_lib::messages::{UiCollectNeighborhoodInfoRequest, UiCollectNeighborhoodInfoResponse};
use masq_lib::short_writeln;
use crate::command_context::CommandContext;
use crate::commands::commands_common::{Command, CommandError, STANDARD_COMMAND_TIMEOUT_MILLIS, transaction};
use crate::commands::commands_common::CommandError::Payload;


#[derive(Debug)]

pub struct NeighborhoodInfoCommand {}

const NEIGHBORHOOD_INFO_SUBCOMMAND_ABOUT: &str =
"Example about for Neighborhood Info Command.";

pub fn neighborhood_info_subcommand() -> App<'static, 'static> {
SubCommand::with_name("neighborhood-info").about(NEIGHBORHOOD_INFO_SUBCOMMAND_ABOUT)
}

impl Command for NeighborhoodInfoCommand {
fn execute(&self, context: &mut dyn CommandContext) -> Result<(), CommandError> {
let input = UiCollectNeighborhoodInfoRequest {};
let output: Result<UiCollectNeighborhoodInfoResponse, CommandError> =
transaction(input, context, STANDARD_COMMAND_TIMEOUT_MILLIS);

match output {
Ok(response) => {
short_writeln!(context.stdout(), "NeighborhoodInfo Command msg -- TODO {:?}", response);
Ok(())
}

Err(Payload(code, message)) if code == NODE_NOT_RUNNING_ERROR => {
short_writeln!(
context.stderr(),
"MASQNode is not running; therefore neighborhood information cannot be displayed."
);
Err(Payload(code, message))
}
Err(e) => {
short_writeln!(context.stderr(), "Neighborhood information retrieval failed: {:?}", e);
Err(e)
}
}
}
}

impl Default for NeighborhoodInfoCommand {
fn default() -> Self {
Self::new()
}
}

impl NeighborhoodInfoCommand {
pub fn new() -> Self {
Self {}
}
}


#[cfg(test)]
mod tests {
use std::collections::HashMap;
use std::sync::{Arc, Mutex};
use masq_lib::constants::NODE_NOT_RUNNING_ERROR;
use masq_lib::messages::{NodeInfo, ToMessageBody, UiCollectNeighborhoodInfoRequest, UiCollectNeighborhoodInfoResponse};
use crate::command_context::ContextError;
use crate::command_context::ContextError::ConnectionDropped;
use crate::command_factory::{CommandFactory, CommandFactoryReal};
use crate::commands::commands_common::{Command, CommandError, STANDARD_COMMAND_TIMEOUT_MILLIS};
use crate::commands::commands_common::CommandError::ConnectionProblem;
use crate::commands::neighborhood_info_command::{NEIGHBORHOOD_INFO_SUBCOMMAND_ABOUT, NeighborhoodInfoCommand};
use crate::test_utils::mocks::CommandContextMock;

#[test]
fn constants_have_correct_values() {
assert_eq!(
NEIGHBORHOOD_INFO_SUBCOMMAND_ABOUT,
"Example about for Neighborhood Info Command."
);
}

#[test]
fn testing_command_factory() {
let factory = CommandFactoryReal::new();
let expect_result = HashMap::from([
(
"public_key_1".to_string(),
NodeInfo {
version: 252,
country_code: "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: "CZ".to_string(),
exit_service: false,
unreachable_hosts: vec!["facebook.com".to_string(), "x.com".to_string()],
},
),
]);
let mut context = CommandContextMock::new().transact_result(Ok(UiCollectNeighborhoodInfoResponse {
neighborhood_database: expect_result,
}.tmb(0)));
let subject = factory.make(&["neighborhood-info".to_string()]).unwrap();

let result = subject.execute(&mut context);

assert_eq!(result, Ok(()));
}

#[test]
fn doesnt_work_if_node_is_not_running() {
let mut context = CommandContextMock::new().transact_result(Err(
ContextError::PayloadError(NODE_NOT_RUNNING_ERROR, "irrelevant".to_string()),
));
let stdout_arc = context.stdout_arc();
let stderr_arc = context.stderr_arc();
let subject = NeighborhoodInfoCommand::new();

let result = subject.execute(&mut context);

assert_eq!(
result,
Err(CommandError::Payload(
NODE_NOT_RUNNING_ERROR,
"irrelevant".to_string()
))
);
assert_eq!(
stderr_arc.lock().unwrap().get_string(),
"MASQNode is not running; therefore neighborhood information cannot be displayed.\n"
);
assert_eq!(stdout_arc.lock().unwrap().get_string(), String::new());
}


#[test]
fn descriptor_command_bad_path() {
let transact_params_arc = Arc::new(Mutex::new(vec![]));
let mut context = CommandContextMock::new()
.transact_params(&transact_params_arc)
.transact_result(Err(ConnectionDropped("Booga".to_string())));
let stdout_arc = context.stdout_arc();
let stderr_arc = context.stderr_arc();
let subject = NeighborhoodInfoCommand::new();

let result = subject.execute(&mut context);

assert_eq!(result, Err(ConnectionProblem("Booga".to_string())));
let transact_params = transact_params_arc.lock().unwrap();
assert_eq!(
*transact_params,
vec![(
UiCollectNeighborhoodInfoRequest {}.tmb(0),
STANDARD_COMMAND_TIMEOUT_MILLIS
)]
);
assert_eq!(stdout_arc.lock().unwrap().get_string(), String::new());
assert_eq!(
stderr_arc.lock().unwrap().get_string(),
"Neighborhood information retrieval failed: ConnectionProblem(\"Booga\")\n"
);
}

}
2 changes: 2 additions & 0 deletions masq/src/schema.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ use crate::commands::wallet_addresses_command::wallet_addresses_subcommand;
use clap::{App, AppSettings, Arg};
use lazy_static::lazy_static;
use masq_lib::constants::{DEFAULT_UI_PORT, HIGHEST_USABLE_PORT, LOWEST_USABLE_INSECURE_PORT};
use crate::commands::neighborhood_info_command::neighborhood_info_subcommand;

lazy_static! {
static ref UI_PORT_HELP: String = format!(
Expand Down Expand Up @@ -69,6 +70,7 @@ pub fn app() -> App<'static, 'static> {
.subcommand(descriptor_subcommand())
.subcommand(financials_subcommand())
.subcommand(generate_wallets_subcommand())
.subcommand(neighborhood_info_subcommand())
.subcommand(recover_wallets_subcommand())
.subcommand(scan_subcommand())
.subcommand(set_configuration_subcommand())
Expand Down
88 changes: 88 additions & 0 deletions masq_lib/src/messages.rs
Original file line number Diff line number Diff line change
Expand Up @@ -629,6 +629,31 @@ pub struct CustomQueries {
pub receivable_opt: Option<RangeQuery<i64>>,
}

///////////+------------------ GH-469
//
// Still thinking about these names
//

#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub struct NodeInfo {
pub version: u32,
pub country_code: String,
pub exit_service: bool,
pub unreachable_hosts: Vec<String>,
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub struct UiCollectNeighborhoodInfoRequest {}
conversation_message!(UiCollectNeighborhoodInfoRequest, "neighborhoodInfo");

#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub struct UiCollectNeighborhoodInfoResponse {
pub neighborhood_database: HashMap<String, NodeInfo>,
}
conversation_message!(UiCollectNeighborhoodInfoResponse, "neighborhoodInfo");


///////////+------------------ GH-469

#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub struct RangeQuery<T> {
#[serde(rename = "minAgeS")]
Expand Down Expand Up @@ -903,6 +928,69 @@ mod tests {
);
}


// ---------- GH-469
#[test]
fn can_deserialize_ui_collect_country_codes_response() {
let json = r#"
{
"neighborhood_database": {
"public_key_1": {
"version": 252,
"country_code": "UK",
"exit_service": true,
"unreachable_hosts": ["facebook.com", "x.com"]
},
"public_key_2": {
"version": 5,
"country_code": "CZ",
"exit_service": false,
"unreachable_hosts": ["facebook.com", "x.com"]
}
}
}
"#;
let message_body = MessageBody {
opcode: "countryCodes".to_string(),
path: Conversation(1234),
payload: Ok(json.to_string()),
};

let result: Result<(UiCollectNeighborhoodInfoResponse, u64), UiMessageError> =
UiCollectNeighborhoodInfoResponse::fmb(message_body);

let expect_result = HashMap::from([
(
"public_key_1".to_string(),
NodeInfo {
version: 252,
country_code: "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: "CZ".to_string(),
exit_service: false,
unreachable_hosts: vec!["facebook.com".to_string(), "x.com".to_string()],
},
),
]);
assert_eq!(
result,
Ok((
UiCollectNeighborhoodInfoResponse {
neighborhood_database: expect_result,
},
1234
))
);
}


#[test]
fn ui_descriptor_methods_were_correctly_generated() {
let subject = UiDescriptorResponse {
Expand Down

0 comments on commit fe09165

Please sign in to comment.