diff --git a/.github/workflows/e2e_tests.yml b/.github/workflows/e2e_tests.yml index 5e1bac239..e01016cba 100644 --- a/.github/workflows/e2e_tests.yml +++ b/.github/workflows/e2e_tests.yml @@ -28,6 +28,7 @@ jobs: uses: Swatinem/rust-cache@v2 with: cache-on-failure: true + cache-all-crates: true # due to candid-extractor - name: Install dfx uses: dfinity/setup-dfx@main @@ -45,8 +46,43 @@ jobs: run: | cargo build -p meroctl -p merod -p e2e-tests + - name: Prepare e2e-tests config + id: prepare_e2e_tests_config + run: | + # Generate 4 unique random numbers + random_numbers=() + while [ ${#random_numbers[@]} -lt 3 ]; do + num=$((RANDOM%37001 + 3000)) + if [[ ! " ${random_numbers[@]} " =~ " ${num} " ]]; then + random_numbers+=($num) + fi + done + + # Export random numbers to environment variables + SWARM_PORT="${random_numbers[0]}" + SERVER_PORT="${random_numbers[1]}" + ICP_PORT="${random_numbers[2]}" + + echo "SWARM_PORT=$SWARM_PORT" >> $GITHUB_OUTPUT + echo "SERVER_PORT=$SERVER_PORT" >> $GITHUB_OUTPUT + echo "ICP_PORT=$ICP_PORT" >> $GITHUB_OUTPUT + + # Update JSON file with jq + jq --arg swarmPort "$SWARM_PORT" \ + --arg serverPort "$SERVER_PORT" \ + --arg icpPort "${random_numbers[2]}" \ + '.network.startSwarmPort = ($swarmPort | tonumber) | + .network.startServerPort = ($serverPort | tonumber) | + .protocolSandboxes[1].config.rpcUrl = "http://127.0.0.1:\($icpPort)" + ' e2e-tests/config/config.json > updated_config.json + + mv updated_config.json e2e-tests/config/config.json + - name: Deploy ICP local devnet + env: + ICP_PORT: ${{ steps.prepare_e2e_tests_config.outputs.ICP_PORT }} run: | + echo "ICP_PORT=$ICP_PORT" cargo install candid-extractor cd ./contracts/icp/context-config ./deploy_devnet.sh @@ -60,39 +96,41 @@ jobs: export SWARM_HOST=$(ifconfig | grep 'inet ' | grep -v '127.0.0.1' | awk '{print $2}' | head -n 1) echo "Running e2e tests, check job summary for details" - echo "# E2E tests 🏗️" >> $GITHUB_STEP_SUMMARY ./target/debug/e2e-tests \ --input-dir ./e2e-tests/config \ --output-dir ./e2e-tests/corpus \ --merod-binary ./target/debug/merod \ --meroctl-binary ./target/debug/meroctl - - name: Update pull request comment + - name: Get PR number + id: pr_number if: success() || failure() + env: + GH_TOKEN: ${{ github.token }} + GH_REF: ${{ github.ref }} + shell: bash + run: | + echo "PR_NUMBER=$(gh pr list \ + --repo ${{ github.repository }} \ + --state open \ + --head "${GH_REF#refs/heads/}" \ + --base master \ + --json number \ + -q '.[0].number')" >> $GITHUB_OUTPUT + + - name: Update pull request comment + if: (success() || failure()) && steps.pr_number.outputs.PR_NUMBER != '' uses: thollander/actions-comment-pull-request@v3 with: file-path: ./e2e-tests/corpus/report.md + pr-number: ${{ steps.pr_number.outputs.PR_NUMBER }} + comment-tag: e2e-tests-report + mode: recreate - - name: Show node logs + - name: Upload artifacts if: success() || failure() - run: | - LOGS_DIR=./e2e-tests/corpus/logs - if [ ! -d "$LOGS_DIR" ]; then - echo "Directory $LOGS_DIR does not exist." - exit 1 - fi - - echo "# Node logs 📋" >> $GITHUB_STEP_SUMMARY - - for FOLDER in "$LOGS_DIR"/*; do - if [ -d "$FOLDER" ]; then - - RUN_LOG="$FOLDER/run.log" - if [ -f "$RUN_LOG" ]; then - echo "## Node logs: $(basename $FOLDER) 📋" >> $GITHUB_STEP_SUMMARY - cat "$RUN_LOG" >> $GITHUB_STEP_SUMMARY - else - echo "## No run.log found in $FOLDER ⚠️" >> $GITHUB_STEP_SUMMARY - fi - fi - done + uses: actions/upload-artifact@v4 + with: + name: e2e-tests-corpus + path: e2e-tests/corpus/ + retention-days: 2 diff --git a/contracts/icp/context-config/deploy_devnet.sh b/contracts/icp/context-config/deploy_devnet.sh index 668f9ebbd..737d1789e 100755 --- a/contracts/icp/context-config/deploy_devnet.sh +++ b/contracts/icp/context-config/deploy_devnet.sh @@ -80,7 +80,7 @@ RECIPIENT_PRINCIPAL=$(dfx identity get-principal) dfx identity use default # Start dfx with clean state -dfx start --clean --background +dfx start --clean --background --host 127.0.0.1:"${ICP_PORT:-4943}" # Create initial identity if needed dfx identity new --storage-mode=plaintext minting || true diff --git a/e2e-tests/config/scenarios/kv-store/test.json b/e2e-tests/config/scenarios/kv-store/test.json index dd684e19d..667e7e2fe 100644 --- a/e2e-tests/config/scenarios/kv-store/test.json +++ b/e2e-tests/config/scenarios/kv-store/test.json @@ -10,7 +10,7 @@ "contextCreate": null }, { - "jsonRpcCall": { + "call": { "methodName": "set", "argsJson": { "key": "foo", "value": "bar" }, "expectedResultJson": null, @@ -18,7 +18,7 @@ } }, { - "jsonRpcCall": { + "call": { "methodName": "get", "argsJson": { "key": "foo" }, "expectedResultJson": "bar", @@ -29,7 +29,7 @@ "contextInviteJoin": null }, { - "jsonRpcCall": { + "call": { "methodName": "get", "argsJson": { "key": "foo" }, "expectedResultJson": "bar", diff --git a/e2e-tests/src/driver.rs b/e2e-tests/src/driver.rs index 42e40d6da..d8864cfe6 100644 --- a/e2e-tests/src/driver.rs +++ b/e2e-tests/src/driver.rs @@ -1,7 +1,9 @@ +use core::fmt::Write; +use core::time::Duration; +use std::collections::hash_map::Entry; use std::collections::HashMap; use std::env; use std::path::PathBuf; -use std::time::Duration; use camino::Utf8PathBuf; use eyre::{bail, OptionExt, Result as EyreResult}; @@ -59,46 +61,48 @@ impl<'a> TestContext<'a> { pub struct Driver { environment: TestEnvironment, config: Config, - meroctl: Meroctl, - merods: HashMap, +} + +pub struct Mero { + ctl: Meroctl, + ds: HashMap, } impl Driver { - pub fn new(environment: TestEnvironment, config: Config) -> Self { - let meroctl = Meroctl::new(&environment); + pub const fn new(environment: TestEnvironment, config: Config) -> Self { Self { environment, config, - meroctl, - merods: HashMap::new(), } } - pub async fn run(&mut self) -> EyreResult<()> { + pub async fn run(&self) -> EyreResult<()> { self.environment.init().await?; - let mut sandbox_environments: Vec = Default::default(); - for protocol_sandbox in self.config.protocol_sandboxes.iter() { + let mut sandbox_environments: Vec = Vec::default(); + for protocol_sandbox in &self.config.protocol_sandboxes { match protocol_sandbox { ProtocolSandboxConfig::Near(config) => { let near = NearSandboxEnvironment::init(config.clone()).await?; sandbox_environments.push(ProtocolSandboxEnvironment::Near(near)); } ProtocolSandboxConfig::Icp(config) => { - let icp = IcpSandboxEnvironment::init(config.clone()).await?; + let icp = IcpSandboxEnvironment::init(config.clone())?; sandbox_environments.push(ProtocolSandboxEnvironment::Icp(icp)); } } } let mut report = TestRunReport::new(); - for sandbox in sandbox_environments.iter() { + for sandbox in &sandbox_environments { self.environment .output_writer .write_header(&format!("Running protocol {}", sandbox.name()), 1); - self.boot_merods(sandbox).await?; - report = self.run_scenarios(report, sandbox.name()).await?; - self.stop_merods().await; + + let mero = self.setup_mero(sandbox).await?; + report = self.run_scenarios(&mero, report, sandbox.name()).await?; + self.stop_merods(&mero.ds).await; + self.environment .output_writer .write_header(&format!("Finished protocol {}", sandbox.name()), 1); @@ -117,19 +121,21 @@ impl Driver { self.environment .output_writer - .write_string(format!("Report file: {:?}", report_file)); + .write_string(format!("Report file: {report_file:?}")); report.result() } - async fn boot_merods(&mut self, sandbox: &ProtocolSandboxEnvironment) -> EyreResult<()> { + async fn setup_mero(&self, sandbox: &ProtocolSandboxEnvironment) -> EyreResult { self.environment .output_writer .write_header("Starting merod nodes", 2); + let mut merods = HashMap::new(); + for i in 0..self.config.network.node_count { let node_name = format!("node{}", i + 1); - if !self.merods.contains_key(&node_name) { + if let Entry::Vacant(e) = merods.entry(node_name.clone()) { let mut args = vec![format!( "discovery.rendezvous.namespace=\"calimero/e2e-tests/{}\"", self.environment.test_id @@ -140,12 +146,16 @@ impl Driver { let mut config_args = vec![]; config_args.extend(args.iter().map(|arg| &**arg)); - let merod = Merod::new(node_name.clone(), &self.environment); + let merod = Merod::new( + node_name.clone(), + self.environment.nodes_dir.join(sandbox.name()), + &self.environment.logs_dir.join(sandbox.name()), + self.environment.merod_binary.clone(), + self.environment.output_writer, + ); - let swarm_host = match env::var(&self.config.network.swarm_host_env) { - Ok(host) => host, - Err(_) => "0.0.0.0".to_owned(), - }; + let swarm_host = env::var(&self.config.network.swarm_host_env) + .unwrap_or_else(|_| "0.0.0.0".to_owned()); merod .init( @@ -158,28 +168,34 @@ impl Driver { merod.run().await?; - drop(self.merods.insert(node_name, merod)); + let _ = e.insert(merod); } } // TODO: Implement health check? sleep(Duration::from_secs(10)).await; - Ok(()) + Ok(Mero { + ctl: Meroctl::new( + self.environment.nodes_dir.join(sandbox.name()), + self.environment.meroctl_binary.clone(), + self.environment.output_writer, + ), + ds: merods, + }) } - async fn stop_merods(&mut self) { - for (_, merod) in self.merods.iter() { + async fn stop_merods(&self, merods: &HashMap) { + for (_, merod) in merods { if let Err(err) = merod.stop().await { - eprintln!("Error stopping merod: {:?}", err); + eprintln!("Error stopping merod: {err:?}"); } } - - self.merods.clear(); } async fn run_scenarios( &self, + mero: &Mero, mut report: TestRunReport, protocol_name: String, ) -> EyreResult { @@ -193,10 +209,11 @@ impl Driver { if test_file_path.exists() { let scenario_report = self .run_scenario( + mero, path.file_name() - .ok_or_eyre("failed")? + .ok_or_eyre("failed to get scenario file name")? .to_str() - .ok_or_eyre("failed")?, + .ok_or_eyre("failed to convert scenario file name")?, test_file_path, ) .await?; @@ -205,7 +222,7 @@ impl Driver { report .scenario_matrix .entry(scenario_report.scenario_name.clone()) - .or_insert_with(HashMap::new) + .or_default() .insert(protocol_name.clone(), scenario_report), ); } @@ -217,6 +234,7 @@ impl Driver { async fn run_scenario( &self, + mero: &Mero, scenarion_name: &str, file_path: PathBuf, ) -> EyreResult { @@ -228,33 +246,37 @@ impl Driver { self.environment .output_writer - .write_string(format!("Source file: {:?}", file_path)); + .write_string(format!("Source file: {file_path:?}")); self.environment .output_writer .write_string(format!("Steps count: {}", scenario.steps.len())); - let (inviter, invitees) = match self.pick_inviter_node() { - Some((inviter, invitees)) => (inviter, invitees), - None => bail!("Not enough nodes to run the test"), + let Some((inviter, invitees)) = self.pick_inviter_node(&mero.ds) else { + bail!("Not enough nodes to run the test") }; self.environment .output_writer - .write_string(format!("Picked inviter: {}", inviter)); + .write_string(format!("Picked inviter: {inviter}")); self.environment .output_writer - .write_string(format!("Picked invitees: {:?}", invitees)); + .write_string(format!("Picked invitees: {invitees:?}")); - let mut ctx = TestContext::new( - inviter, - invitees, - &self.meroctl, - self.environment.output_writer, - ); + let mut ctx = + TestContext::new(inviter, invitees, &mero.ctl, self.environment.output_writer); let mut report = TestScenarioReport::new(scenarion_name.to_owned()); - for step in scenario.steps.iter() { + let mut scenario_failed = false; + for (i, step) in scenario.steps.iter().enumerate() { + if scenario_failed { + report.steps.push(TestStepReport { + step_name: format!("{}. {}", i, step.display_name()), + result: None, + }); + continue; + } + self.environment .output_writer .write_header("Running test step", 3); @@ -262,18 +284,26 @@ impl Driver { self.environment.output_writer.write_json(&step)?; let result = step.run_assert(&mut ctx).await; + + if result.is_err() { + scenario_failed = true; + self.environment + .output_writer + .write_string(format!("Error: {result:?}")); + } + report.steps.push(TestStepReport { - step_name: step.display_name(), - result, + step_name: format!("{}. {}", i, step.display_name()), + result: Some(result), }); } Ok(report) } - fn pick_inviter_node(&self) -> Option<(String, Vec)> { - let mut node_names: Vec = self.merods.keys().cloned().collect(); - if node_names.len() < 1 { + fn pick_inviter_node(&self, merods: &HashMap) -> Option<(String, Vec)> { + let mut node_names: Vec = merods.keys().cloned().collect(); + if node_names.is_empty() { None } else { let mut rng = rand::thread_rng(); @@ -291,7 +321,7 @@ struct TestRunReport { impl TestRunReport { fn new() -> Self { Self { - scenario_matrix: Default::default(), + scenario_matrix: HashMap::default(), } } @@ -301,7 +331,7 @@ impl TestRunReport { for (_, scenarios) in &self.scenario_matrix { for (_, scenario) in scenarios { for step in &scenario.steps { - if let Err(e) = &step.result { + if let Some(Err(e)) = &step.result { errors.push(e.to_string()); } } @@ -316,18 +346,19 @@ impl TestRunReport { } async fn store_to_file(&self, folder: Utf8PathBuf) -> EyreResult { - let markdown = self.to_markdown(); + let markdown = self.to_markdown()?; let report_file = folder.join("report.md"); write(&report_file, markdown).await?; Ok(report_file) } - fn to_markdown(&self) -> String { + fn to_markdown(&self) -> EyreResult { let mut markdown = String::new(); + writeln!(&mut markdown, "## E2E tests report")?; + for (scenario, protocols) in &self.scenario_matrix { - markdown.push_str(&format!("## Scenario: {}\n", scenario)); - markdown.push_str("| Protocol/Step |"); + writeln!(&mut markdown, "### Scenario: {scenario}")?; // Collecting all step names let mut step_names = vec![]; @@ -339,38 +370,44 @@ impl TestRunReport { } } - // Adding step names to the first row of the table + // Write table header + write!(&mut markdown, "| Protocol/Step |")?; for step_name in &step_names { - markdown.push_str(&format!(" {} |", step_name)); + write!(&mut markdown, " {step_name} |")?; } - markdown.push_str("\n| :--- |"); + writeln!(&mut markdown)?; + + // Write table header separator + write!(&mut markdown, "| :--- |")?; for _ in &step_names { - markdown.push_str(" :---: |"); + write!(&mut markdown, " :---: |")?; } - markdown.push_str("\n"); + writeln!(&mut markdown)?; - // Adding protocol rows + // Write table rows for (protocol, report) in protocols { - markdown.push_str(&format!("| {} |", protocol)); + write!(&mut markdown, "| {protocol} |")?; for step_name in &step_names { let result = report .steps .iter() .find(|step| &step.step_name == step_name) - .map_or(":interrobang:", |step| { - if step.result.is_ok() { - ":white_check_mark:" - } else { - ":x:" - } + .map_or(":bug:", |step| { + step.result + .as_ref() + .map_or(":fast_forward:", |result| match result { + Ok(()) => ":white_check_mark:", + Err(_) => ":x:", + }) }); - markdown.push_str(&format!(" {} |", result)); + write!(&mut markdown, " {result} |")?; } - markdown.push_str("\n"); + writeln!(&mut markdown)?; } - markdown.push_str("\n"); + writeln!(&mut markdown)?; } - markdown + + Ok(markdown) } } @@ -383,12 +420,12 @@ impl TestScenarioReport { fn new(scenario_name: String) -> Self { Self { scenario_name, - steps: Default::default(), + steps: Vec::default(), } } } struct TestStepReport { step_name: String, - result: EyreResult<()>, + result: Option>, } diff --git a/e2e-tests/src/main.rs b/e2e-tests/src/main.rs index 063e2a8f3..306620781 100644 --- a/e2e-tests/src/main.rs +++ b/e2e-tests/src/main.rs @@ -74,20 +74,20 @@ pub struct TestEnvironment { pub output_writer: OutputWriter, } -impl Into for Args { - fn into(self) -> TestEnvironment { +impl From for TestEnvironment { + fn from(val: Args) -> Self { let mut rng = rand::thread_rng(); - TestEnvironment { + Self { test_id: rng.gen::(), - merod_binary: self.merod_binary, - meroctl_binary: self.meroctl_binary, - input_dir: self.input_dir.clone(), - output_dir: self.output_dir.clone(), - nodes_dir: self.output_dir.join("nodes"), - logs_dir: self.output_dir.join("logs"), - icp_dir: self.output_dir.join("icp"), - output_writer: OutputWriter::new(self.output_format), + merod_binary: val.merod_binary, + meroctl_binary: val.meroctl_binary, + input_dir: val.input_dir.clone(), + output_dir: val.output_dir.clone(), + nodes_dir: val.output_dir.join("nodes"), + logs_dir: val.output_dir.join("logs"), + icp_dir: val.output_dir.join("icp"), + output_writer: OutputWriter::new(val.output_format), } } } @@ -118,7 +118,7 @@ async fn main() -> EyreResult<()> { let config_content = read_to_string(config_path).await?; let config: Config = serde_json::from_str(&config_content)?; - let mut driver = Driver::new(args.into(), config); + let driver = Driver::new(args.into(), config); driver.run().await } diff --git a/e2e-tests/src/meroctl.rs b/e2e-tests/src/meroctl.rs index f6d25440f..315101bf0 100644 --- a/e2e-tests/src/meroctl.rs +++ b/e2e-tests/src/meroctl.rs @@ -5,20 +5,23 @@ use eyre::{bail, eyre, OptionExt, Result as EyreResult}; use tokio::process::Command; use crate::output::OutputWriter; -use crate::TestEnvironment; pub struct Meroctl { - nodes_dir: Utf8PathBuf, + home_dir: Utf8PathBuf, binary: Utf8PathBuf, output_writer: OutputWriter, } impl Meroctl { - pub fn new(environment: &TestEnvironment) -> Self { + pub const fn new( + home_dir: Utf8PathBuf, + binary: Utf8PathBuf, + output_writer: OutputWriter, + ) -> Self { Self { - nodes_dir: environment.nodes_dir.clone(), - binary: environment.meroctl_binary.clone(), - output_writer: environment.output_writer, + home_dir, + binary, + output_writer, } } @@ -142,7 +145,7 @@ impl Meroctl { async fn run_cmd(&self, node_name: &str, args: &[&str]) -> EyreResult { let mut root_args = vec![ "--home", - self.nodes_dir.as_str(), + self.home_dir.as_str(), "--node-name", node_name, "--output-format", @@ -171,9 +174,8 @@ impl Meroctl { mut json: serde_json::Value, key: &str, ) -> EyreResult { - let json = match json.as_object_mut() { - Some(json) => json, - None => bail!("'{}' is not a JSON object", json), + let Some(json) = json.as_object_mut() else { + bail!("'{}' is not a JSON object", json) }; json.remove(key) @@ -181,9 +183,8 @@ impl Meroctl { } fn get_string_from_object(&self, json: &serde_json::Value, key: &str) -> EyreResult { - let json = match json.as_object() { - Some(json) => json, - None => bail!("'{}' is not a JSON object", json), + let Some(json) = json.as_object() else { + bail!("'{}' is not a JSON object", json) }; let json = json diff --git a/e2e-tests/src/merod.rs b/e2e-tests/src/merod.rs index a0da0a1bc..5c6993729 100644 --- a/e2e-tests/src/merod.rs +++ b/e2e-tests/src/merod.rs @@ -1,4 +1,4 @@ -use std::cell::RefCell; +use core::cell::RefCell; use std::process::Stdio; use camino::Utf8PathBuf; @@ -8,26 +8,31 @@ use tokio::io::copy; use tokio::process::{Child, Command}; use crate::output::OutputWriter; -use crate::TestEnvironment; pub struct Merod { pub name: String, process: RefCell>, - nodes_dir: Utf8PathBuf, + home_dir: Utf8PathBuf, log_dir: Utf8PathBuf, binary: Utf8PathBuf, output_writer: OutputWriter, } impl Merod { - pub fn new(name: String, environment: &TestEnvironment) -> Self { + pub fn new( + name: String, + home_dir: Utf8PathBuf, + logs_dir: &Utf8PathBuf, + binary: Utf8PathBuf, + output_writer: OutputWriter, + ) -> Self { Self { process: RefCell::new(None), - nodes_dir: environment.nodes_dir.clone(), - log_dir: environment.logs_dir.join(&name), - binary: environment.merod_binary.clone(), + home_dir, + log_dir: logs_dir.join(&name), + binary, name, - output_writer: environment.output_writer, + output_writer, } } @@ -38,7 +43,7 @@ impl Merod { server_port: u32, args: &[&str], ) -> EyreResult<()> { - create_dir_all(&self.nodes_dir.join(&self.name)).await?; + create_dir_all(&self.home_dir.join(&self.name)).await?; create_dir_all(&self.log_dir).await?; let mut child = self @@ -96,7 +101,7 @@ impl Merod { } async fn run_cmd(&self, args: &[&str], log_suffix: &str) -> EyreResult { - let mut root_args = vec!["--home", self.nodes_dir.as_str(), "--node-name", &self.name]; + let mut root_args = vec!["--home", self.home_dir.as_str(), "--node-name", &self.name]; root_args.extend(args); @@ -105,7 +110,7 @@ impl Merod { self.output_writer .write_string(format!("Command: '{:} {:}'", &self.binary, args_str)); - let log_file = self.log_dir.join(format!("{}.log", log_suffix)); + let log_file = self.log_dir.join(format!("{log_suffix}.log")); let mut log_file = File::create(&log_file).await?; let mut child = Command::new(&self.binary) .args(root_args) @@ -115,7 +120,7 @@ impl Merod { if let Some(mut stdout) = child.stdout.take() { drop(tokio::spawn(async move { if let Err(err) = copy(&mut stdout, &mut log_file).await { - eprintln!("Error copying stdout: {:?}", err); + eprintln!("Error copying stdout: {err:?}"); } })); } diff --git a/e2e-tests/src/output.rs b/e2e-tests/src/output.rs index 446bd527f..e75dd2940 100644 --- a/e2e-tests/src/output.rs +++ b/e2e-tests/src/output.rs @@ -15,22 +15,22 @@ pub enum OutputFormat { } impl OutputWriter { - pub fn new(format: OutputFormat) -> Self { + pub const fn new(format: OutputFormat) -> Self { Self { format } } - pub fn write_str(&self, line: &str) { + pub fn write_str(self, line: &str) { match self.format { - OutputFormat::Markdown => println!("{} ", line), - OutputFormat::PlainText => println!("{}", line), + OutputFormat::Markdown => println!("{line} "), + OutputFormat::PlainText => println!("{line}"), } } - pub fn write_string(&self, line: String) { + pub fn write_string(self, line: String) { self.write_str(&line); } - pub fn write_header(&self, header: &str, level: usize) { + pub fn write_header(self, header: &str, level: usize) { match self.format { OutputFormat::Markdown => println!("{} {} ", "#".repeat(level), header), OutputFormat::PlainText => { @@ -39,12 +39,12 @@ impl OutputWriter { "-".repeat(level * 5), header, "-".repeat(level * 5), - ) + ); } } } - pub fn write_json(&self, json: &T) -> EyreResult<()> + pub fn write_json(self, json: &T) -> EyreResult<()> where T: ?Sized + Serialize, { diff --git a/e2e-tests/src/protocol.rs b/e2e-tests/src/protocol.rs index a03676800..d4bf49b82 100644 --- a/e2e-tests/src/protocol.rs +++ b/e2e-tests/src/protocol.rs @@ -13,15 +13,15 @@ pub enum ProtocolSandboxEnvironment { impl ProtocolSandboxEnvironment { pub async fn node_args(&self, node_name: &str) -> EyreResult> { match self { - ProtocolSandboxEnvironment::Near(env) => env.node_args(node_name).await, - ProtocolSandboxEnvironment::Icp(env) => env.node_args(), + Self::Near(env) => env.node_args(node_name).await, + Self::Icp(env) => Ok(env.node_args()), } } pub fn name(&self) -> String { match self { - ProtocolSandboxEnvironment::Near(_) => "near".to_string(), - ProtocolSandboxEnvironment::Icp(_) => "icp".to_string(), + Self::Near(_) => "near".to_owned(), + Self::Icp(_) => "icp".to_owned(), } } } diff --git a/e2e-tests/src/protocol/icp.rs b/e2e-tests/src/protocol/icp.rs index da39f650c..ccf098c76 100644 --- a/e2e-tests/src/protocol/icp.rs +++ b/e2e-tests/src/protocol/icp.rs @@ -1,5 +1,5 @@ +use core::time::Duration; use std::net::TcpStream; -use std::time::Duration; use eyre::{bail, OptionExt, Result as EyreResult}; use serde::{Deserialize, Serialize}; @@ -20,7 +20,7 @@ pub struct IcpSandboxEnvironment { } impl IcpSandboxEnvironment { - pub async fn init(config: IcpProtocolConfig) -> EyreResult { + pub fn init(config: IcpProtocolConfig) -> EyreResult { let rpc_url = Url::parse(&config.rpc_url)?; let rpc_host = rpc_url .host_str() @@ -30,7 +30,7 @@ impl IcpSandboxEnvironment { .ok_or_eyre("failed to get icp rpc port from config")?; if let Err(err) = TcpStream::connect_timeout( - &format!("{}:{}", rpc_host, rpc_port).parse()?, + &format!("{rpc_host}:{rpc_port}").parse()?, Duration::from_secs(3), ) { bail!( @@ -40,11 +40,11 @@ impl IcpSandboxEnvironment { ); } - return Ok(Self { config }); + Ok(Self { config }) } - pub fn node_args(&self) -> EyreResult> { - return Ok(vec![ + pub fn node_args(&self) -> Vec { + vec![ format!("context.config.new.protocol=\"{}\"", "icp"), format!("context.config.new.network=\"{}\"", "local"), format!( @@ -68,6 +68,6 @@ impl IcpSandboxEnvironment { "context.config.signer.self.icp.local.secret_key=\"{}\"", self.config.secret_key ), - ]); + ] } } diff --git a/e2e-tests/src/protocol/near.rs b/e2e-tests/src/protocol/near.rs index 79ae607a1..023c20740 100644 --- a/e2e-tests/src/protocol/near.rs +++ b/e2e-tests/src/protocol/near.rs @@ -56,7 +56,7 @@ impl NearSandboxEnvironment { .into_result()?; let near_secret_key = near_account.secret_key(); - return Ok(vec![ + Ok(vec![ format!( "context.config.new.contract_id=\"{}\"", self.contract.as_account().id() @@ -78,6 +78,6 @@ impl NearSandboxEnvironment { "context.config.signer.self.near.testnet.secret_key=\"{}\"", near_secret_key ), - ]); + ]) } } diff --git a/e2e-tests/src/steps.rs b/e2e-tests/src/steps.rs index ff60869db..a527809a9 100644 --- a/e2e-tests/src/steps.rs +++ b/e2e-tests/src/steps.rs @@ -2,7 +2,7 @@ use application_install::ApplicationInstallStep; use context_create::ContextCreateStep; use context_invite_join::ContextInviteJoinStep; use eyre::Result as EyreResult; -use jsonrpc_call::JsonRpcCallStep; +use jsonrpc_call::CallStep; use serde::{Deserialize, Serialize}; use crate::driver::{Test, TestContext}; @@ -24,25 +24,25 @@ pub enum TestStep { ApplicationInstall(ApplicationInstallStep), ContextCreate(ContextCreateStep), ContextInviteJoin(ContextInviteJoinStep), - JsonRpcCall(JsonRpcCallStep), + Call(CallStep), } impl Test for TestStep { fn display_name(&self) -> String { match self { - TestStep::ApplicationInstall(step) => step.display_name(), - TestStep::ContextCreate(step) => step.display_name(), - TestStep::ContextInviteJoin(step) => step.display_name(), - TestStep::JsonRpcCall(step) => step.display_name(), + Self::ApplicationInstall(step) => step.display_name(), + Self::ContextCreate(step) => step.display_name(), + Self::ContextInviteJoin(step) => step.display_name(), + Self::Call(step) => step.display_name(), } } async fn run_assert(&self, ctx: &mut TestContext<'_>) -> EyreResult<()> { match self { - TestStep::ApplicationInstall(step) => step.run_assert(ctx).await, - TestStep::ContextCreate(step) => step.run_assert(ctx).await, - TestStep::ContextInviteJoin(step) => step.run_assert(ctx).await, - TestStep::JsonRpcCall(step) => step.run_assert(ctx).await, + Self::ApplicationInstall(step) => step.run_assert(ctx).await, + Self::ContextCreate(step) => step.run_assert(ctx).await, + Self::ContextInviteJoin(step) => step.run_assert(ctx).await, + Self::Call(step) => step.run_assert(ctx).await, } } } diff --git a/e2e-tests/src/steps/application_install.rs b/e2e-tests/src/steps/application_install.rs index 11558b141..464f76db0 100644 --- a/e2e-tests/src/steps/application_install.rs +++ b/e2e-tests/src/steps/application_install.rs @@ -27,12 +27,12 @@ pub enum ApplicationInstallTarget { impl Test for ApplicationInstallStep { fn display_name(&self) -> String { - "ApplicationInstall".to_string() + format!("app install ({:?})", self.target) } async fn run_assert(&self, ctx: &mut TestContext<'_>) -> EyreResult<()> { - if let ApplicationInstallTarget::AllMembers = self.target { - for invitee in ctx.invitees.iter() { + if matches!(self.target, ApplicationInstallTarget::AllMembers) { + for invitee in &ctx.invitees { let application_id = self.application.install(ctx.meroctl, invitee).await?; if let Some(existing_app_id) = &ctx.application_id { if existing_app_id != &application_id { @@ -45,9 +45,8 @@ impl Test for ApplicationInstallStep { } ctx.application_id = Some(application_id); - ctx.output_writer.write_string(format!( - "Report: Installed application on '{}' node", - invitee + ctx.output_writer.write_str(&format!( + "Report: Installed application on '{invitee}' node" )); } } @@ -76,9 +75,7 @@ impl Test for ApplicationInstallStep { impl ApplicationSource { async fn install(&self, meroctl: &Meroctl, node_name: &str) -> EyreResult { match self { - ApplicationSource::LocalFile(path) => { - meroctl.application_install(node_name, path).await - } + Self::LocalFile(path) => meroctl.application_install(node_name, path).await, } } } diff --git a/e2e-tests/src/steps/context_create.rs b/e2e-tests/src/steps/context_create.rs index 2a73f062d..897e551ff 100644 --- a/e2e-tests/src/steps/context_create.rs +++ b/e2e-tests/src/steps/context_create.rs @@ -9,7 +9,7 @@ pub struct ContextCreateStep; impl Test for ContextCreateStep { fn display_name(&self) -> String { - "ContextCreate".to_string() + "ctx create".to_owned() } async fn run_assert(&self, ctx: &mut TestContext<'_>) -> EyreResult<()> { @@ -19,7 +19,7 @@ impl Test for ContextCreateStep { let (context_id, member_public_key) = ctx .meroctl - .context_create(&ctx.inviter, &application_id) + .context_create(&ctx.inviter, application_id) .await?; ctx.context_id = Some(context_id); diff --git a/e2e-tests/src/steps/context_invite_join.rs b/e2e-tests/src/steps/context_invite_join.rs index f24b26329..e61a16335 100644 --- a/e2e-tests/src/steps/context_invite_join.rs +++ b/e2e-tests/src/steps/context_invite_join.rs @@ -1,4 +1,4 @@ -use std::time::Duration; +use core::time::Duration; use eyre::{bail, Result as EyreResult}; use serde::{Deserialize, Serialize}; @@ -12,7 +12,7 @@ pub struct ContextInviteJoinStep; impl Test for ContextInviteJoinStep { fn display_name(&self) -> String { - "ContextInviteJoin".to_string() + "ctx invite-join".to_owned() } async fn run_assert(&self, ctx: &mut TestContext<'_>) -> EyreResult<()> { @@ -24,7 +24,7 @@ impl Test for ContextInviteJoinStep { bail!("Inviter public key is required for InviteJoinContextStep"); }; - for invitee in ctx.invitees.iter() { + for invitee in &ctx.invitees { let (invitee_public_key, invitee_private_key) = ctx.meroctl.identity_generate(invitee).await?; @@ -68,7 +68,7 @@ impl Test for ContextInviteJoinStep { sleep(Duration::from_secs(40)).await; ctx.output_writer - .write_string(format!("Report: Node '{}' joined the context", invitee)); + .write_string(format!("Report: Node '{invitee}' joined the context")); } Ok(()) diff --git a/e2e-tests/src/steps/jsonrpc_call.rs b/e2e-tests/src/steps/jsonrpc_call.rs index 1b7aa0c7d..4c532a021 100644 --- a/e2e-tests/src/steps/jsonrpc_call.rs +++ b/e2e-tests/src/steps/jsonrpc_call.rs @@ -7,23 +7,23 @@ use crate::driver::{Test, TestContext}; #[derive(Clone, Debug, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] -pub struct JsonRpcCallStep { +pub struct CallStep { pub method_name: String, pub args_json: serde_json::Value, pub expected_result_json: Option, - pub target: JsonRpcCallTarget, + pub target: CallTarget, } #[derive(Clone, Debug, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] -pub enum JsonRpcCallTarget { +pub enum CallTarget { Inviter, AllMembers, } -impl Test for JsonRpcCallStep { +impl Test for CallStep { fn display_name(&self) -> String { - "JsonRpcCall".to_string() + format!("call ({}, {:?})", self.method_name, self.target) } async fn run_assert(&self, ctx: &mut TestContext<'_>) -> EyreResult<()> { @@ -38,7 +38,7 @@ impl Test for JsonRpcCallStep { bail!("Inviter public key is required for JsonRpcExecuteStep"); } - if let JsonRpcCallTarget::AllMembers = self.target { + if matches!(self.target, CallTarget::AllMembers) { for invitee in &ctx.invitees { if let Some(invitee_public_key) = ctx.invitees_public_keys.get(invitee) { drop(public_keys.insert(invitee.clone(), invitee_public_key.clone())); @@ -51,7 +51,7 @@ impl Test for JsonRpcCallStep { } } - for (node, public_key) in public_keys.iter() { + for (node, public_key) in &public_keys { let response = ctx .meroctl .json_rpc_execute( @@ -80,7 +80,7 @@ impl Test for JsonRpcCallStep { } ctx.output_writer - .write_string(format!("Report: Call on '{}' node passed assertion", node)); + .write_string(format!("Report: Call on '{node}' node passed assertion")); } Ok(())