diff --git a/Cargo.lock b/Cargo.lock index 6a3fd2db..905e5540 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -108,21 +108,6 @@ dependencies = [ "serde_json", ] -[[package]] -name = "assert_fs" -version = "1.0.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f070617a68e5c2ed5d06ee8dd620ee18fb72b99f6c094bed34cf8ab07c875b48" -dependencies = [ - "anstyle", - "doc-comment", - "globwalk", - "predicates 3.0.4", - "predicates-core", - "predicates-tree", - "tempfile", -] - [[package]] name = "async-channel" version = "1.9.0" @@ -269,7 +254,7 @@ checksum = "5fd55a5ba1179988837d24ab4c7cc8ed6efdeff578ede0416b4225a5fca35bd0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn", ] [[package]] @@ -331,7 +316,7 @@ checksum = "c980ee35e870bd1a4d2c8294d4c04d0499e67bca1e4b5cefcc693c2fa00caea9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn", ] [[package]] @@ -828,16 +813,6 @@ dependencies = [ "tracing", ] -[[package]] -name = "bstr" -version = "1.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c48f0051a4b4c5e0b6d365cd04af53aeaa209e3cc15ec2cdb69e73cc87fbd0dc" -dependencies = [ - "memchr", - "serde", -] - [[package]] name = "bumpalo" version = "3.14.0" @@ -926,7 +901,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.48", + "syn", ] [[package]] @@ -1145,12 +1120,6 @@ version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" -[[package]] -name = "difflib" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6184e33543162437515c2e2b48714794e37845ec9851711914eec9d308f6ebe8" - [[package]] name = "digest" version = "0.10.7" @@ -1183,24 +1152,12 @@ dependencies = [ "winapi", ] -[[package]] -name = "doc-comment" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" - [[package]] name = "dotenv" version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77c90badedccf4105eca100756a0b1289e191f6fcbdadd3cee1d2f614f97da8f" -[[package]] -name = "downcast" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1435fa1053d8b2fbbe9be7e97eca7f33d37b28409959813daefc1446a14247f1" - [[package]] name = "dyn-clone" version = "1.0.16" @@ -1357,15 +1314,6 @@ dependencies = [ "miniz_oxide", ] -[[package]] -name = "float-cmp" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4" -dependencies = [ - "num-traits", -] - [[package]] name = "fnv" version = "1.0.7" @@ -1381,12 +1329,6 @@ dependencies = [ "percent-encoding", ] -[[package]] -name = "fragile" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c2141d6d6c8512188a7891b4b01590a45f6dac67afb4f255c4124dbb86d4eaa" - [[package]] name = "fs_extra" version = "1.3.0" @@ -1450,7 +1392,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn", ] [[package]] @@ -1506,30 +1448,6 @@ version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" -[[package]] -name = "globset" -version = "0.4.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57da3b9b5b85bd66f31093f8c408b90a74431672542466497dcbdfdc02034be1" -dependencies = [ - "aho-corasick", - "bstr", - "log", - "regex-automata", - "regex-syntax 0.8.2", -] - -[[package]] -name = "globwalk" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93e3af942408868f6934a7b85134a3230832b9977cf66125df2f9edcfce4ddcc" -dependencies = [ - "bitflags 1.3.2", - "ignore", - "walkdir", -] - [[package]] name = "gloo-timers" version = "0.2.6" @@ -1735,22 +1653,6 @@ dependencies = [ "unicode-normalization", ] -[[package]] -name = "ignore" -version = "0.4.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b46810df39e66e925525d6e38ce1e7f6e1d208f72dc39757880fcb66e2c58af1" -dependencies = [ - "crossbeam-deque", - "globset", - "log", - "memchr", - "regex-automata", - "same-file", - "walkdir", - "winapi-util", -] - [[package]] name = "indenter" version = "0.3.3" @@ -1869,15 +1771,6 @@ dependencies = [ "either", ] -[[package]] -name = "itertools" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" -dependencies = [ - "either", -] - [[package]] name = "itoa" version = "1.0.10" @@ -1913,7 +1806,7 @@ dependencies = [ "diff", "ena", "is-terminal", - "itertools 0.10.5", + "itertools", "lalrpop-util", "petgraph", "pico-args", @@ -2059,33 +1952,6 @@ dependencies = [ "windows-sys 0.48.0", ] -[[package]] -name = "mockall" -version = "0.11.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c84490118f2ee2d74570d114f3d0493cbf02790df303d2707606c3e14e07c96" -dependencies = [ - "cfg-if", - "downcast", - "fragile", - "lazy_static", - "mockall_derive", - "predicates 2.1.5", - "predicates-tree", -] - -[[package]] -name = "mockall_derive" -version = "0.11.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22ce75669015c4f47b289fd4d4f56e894e4c96003ffdf3ac51313126f94c6cbb" -dependencies = [ - "cfg-if", - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "new_debug_unreachable" version = "1.0.4" @@ -2101,12 +1967,6 @@ dependencies = [ "unicode-segmentation", ] -[[package]] -name = "normalize-line-endings" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be" - [[package]] name = "num-integer" version = "0.1.45" @@ -2264,7 +2124,7 @@ checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn", ] [[package]] @@ -2350,48 +2210,6 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" -[[package]] -name = "predicates" -version = "2.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59230a63c37f3e18569bdb90e4a89cbf5bf8b06fea0b84e65ea10cc4df47addd" -dependencies = [ - "difflib", - "float-cmp", - "itertools 0.10.5", - "normalize-line-endings", - "predicates-core", - "regex", -] - -[[package]] -name = "predicates" -version = "3.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6dfc28575c2e3f19cb3c73b93af36460ae898d426eba6fc15b9bd2a5220758a0" -dependencies = [ - "anstyle", - "difflib", - "itertools 0.11.0", - "predicates-core", -] - -[[package]] -name = "predicates-core" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b794032607612e7abeb4db69adb4e33590fa6cf1149e95fd7cb00e634b92f174" - -[[package]] -name = "predicates-tree" -version = "1.0.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "368ba315fb8c5052ab692e68a0eefec6ec57b23a36959c14496f0b0df2c0cecf" -dependencies = [ - "predicates-core", - "termtree", -] - [[package]] name = "proc-macro2" version = "1.0.76" @@ -2681,15 +2499,6 @@ version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c" -[[package]] -name = "same-file" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" -dependencies = [ - "winapi-util", -] - [[package]] name = "schannel" version = "0.1.23" @@ -2761,7 +2570,7 @@ checksum = "46fe8f8603d81ba86327b23a2e9cdf49e1255fb94a4c5f297f6ee0547178ea2c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn", ] [[package]] @@ -2900,9 +2709,7 @@ checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970" name = "sn-testnet-deploy" version = "0.1.0" dependencies = [ - "assert_fs", "async-recursion", - "async-trait", "aws-config", "aws-sdk-s3", "chrono", @@ -2917,8 +2724,6 @@ dependencies = [ "indicatif", "inquire", "log", - "mockall", - "predicates 2.1.5", "rand", "rayon", "regex", @@ -2990,17 +2795,6 @@ version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" -[[package]] -name = "syn" -version = "1.0.109" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - [[package]] name = "syn" version = "2.0.48" @@ -3077,12 +2871,6 @@ dependencies = [ "winapi-util", ] -[[package]] -name = "termtree" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3369f5ac52d5eb6ab48c6b4ffdc8efbcad6b89c765749064ba298f2c68a16a76" - [[package]] name = "thiserror" version = "1.0.56" @@ -3100,7 +2888,7 @@ checksum = "fa0faa943b50f3db30a20aa7e265dbc66076993efed8463e8de414e5d06d3471" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn", ] [[package]] @@ -3192,7 +2980,7 @@ checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn", ] [[package]] @@ -3278,7 +3066,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn", ] [[package]] @@ -3450,16 +3238,6 @@ version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f3c4517f54858c779bbcbf228f4fca63d121bf85fbecb2dc578cdf4a39395690" -[[package]] -name = "walkdir" -version = "2.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee" -dependencies = [ - "same-file", - "winapi-util", -] - [[package]] name = "want" version = "0.3.1" @@ -3496,7 +3274,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.48", + "syn", "wasm-bindgen-shared", ] @@ -3530,7 +3308,7 @@ checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn", "wasm-bindgen-backend", "wasm-bindgen-shared", ] diff --git a/Cargo.toml b/Cargo.toml index 3eadaf1b..0a84f025 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,7 +10,6 @@ name="testnet-deploy" [dependencies] async-recursion = "1.0.4" -async-trait = "0.1" aws-config = "0.56.0" aws-sdk-s3 = "0.29.0" chrono = "0.4.31" @@ -38,7 +37,4 @@ tokio = { version = "1.26", features = ["full"] } tokio-stream = "0.1.14" [dev-dependencies] -assert_fs = "~1.0" httpmock = "0.6" -mockall = "0.11.3" -predicates = "2.0" diff --git a/src/ansible.rs b/src/ansible.rs index a9c6e345..78c7aace 100644 --- a/src/ansible.rs +++ b/src/ansible.rs @@ -3,16 +3,13 @@ // // This SAFE Network Software is licensed under the BSD-3-Clause license. // Please see the LICENSE file for more details. -use crate::error::{Error, Result}; -use crate::CloudProvider; -use crate::{is_binary_on_path, run_external_command}; +use crate::{ + error::{Error, Result}, + is_binary_on_path, run_external_command, CloudProvider, +}; use log::debug; -#[cfg(test)] -use mockall::automock; use serde::Deserialize; -use std::collections::HashMap; -use std::net::IpAddr; -use std::path::PathBuf; +use std::{collections::HashMap, net::IpAddr, path::PathBuf}; /// Ansible has multiple 'binaries', e.g., `ansible-playbook`, `ansible-inventory` etc. that are /// wrappers around the main `ansible` program. It would be a bit cumbersome to create a different @@ -46,22 +43,6 @@ impl AnsibleBinary { } } -/// Provides an interface for running Ansible. -/// -/// This trait exists for unit testing: it enables testing behaviour without actually calling the -/// Ansible process. -#[cfg_attr(test, automock)] -pub trait AnsibleRunnerInterface { - fn inventory_list(&self, inventory_path: PathBuf) -> Result>; - fn run_playbook( - &self, - playbook_path: PathBuf, - inventory_path: PathBuf, - user: String, - extra_vars_document: Option, - ) -> Result<()>; -} - pub struct AnsibleRunner { pub provider: CloudProvider, pub working_directory_path: PathBuf, @@ -69,22 +50,6 @@ pub struct AnsibleRunner { pub vault_password_file_path: PathBuf, } -impl AnsibleRunner { - pub fn new( - working_directory_path: PathBuf, - provider: CloudProvider, - ssh_sk_path: PathBuf, - vault_password_file_path: PathBuf, - ) -> AnsibleRunner { - AnsibleRunner { - provider, - working_directory_path, - ssh_sk_path, - vault_password_file_path, - } - } -} - // The following three structs are utilities that are used to parse the output of the // `ansible-inventory` command. #[derive(Debug, Deserialize)] @@ -100,11 +65,25 @@ struct Output { _meta: Meta, } -impl AnsibleRunnerInterface for AnsibleRunner { +impl AnsibleRunner { + pub fn new( + working_directory_path: PathBuf, + provider: CloudProvider, + ssh_sk_path: PathBuf, + vault_password_file_path: PathBuf, + ) -> AnsibleRunner { + AnsibleRunner { + provider, + working_directory_path, + ssh_sk_path, + vault_password_file_path, + } + } + // This function is used to list the inventory of the ansible runner. // It takes a PathBuf as an argument which represents the inventory path. // It returns a Result containing a vector of tuples. Each tuple contains a string representing the name and the ansible host. - fn inventory_list(&self, inventory_path: PathBuf) -> Result> { + pub fn inventory_list(&self, inventory_path: PathBuf) -> Result> { // Run the external command and store the output. let output = run_external_command( AnsibleBinary::AnsibleInventory.get_binary_path()?, @@ -145,7 +124,7 @@ impl AnsibleRunnerInterface for AnsibleRunner { Ok(inventory) } - fn run_playbook( + pub fn run_playbook( &self, playbook_path: PathBuf, inventory_path: PathBuf, diff --git a/src/digital_ocean.rs b/src/digital_ocean.rs index f6408f92..0f094595 100644 --- a/src/digital_ocean.rs +++ b/src/digital_ocean.rs @@ -5,13 +5,9 @@ // Please see the LICENSE file for more details. use crate::error::{Error, Result}; -use async_trait::async_trait; use log::debug; -#[cfg(test)] -use mockall::automock; use reqwest::Client; -use std::net::Ipv4Addr; -use std::str::FromStr; +use std::{net::Ipv4Addr, str::FromStr}; pub const DIGITAL_OCEAN_API_BASE_URL: &str = "https://api.digitalocean.com"; pub const DIGITAL_OCEAN_API_PAGE_SIZE: usize = 200; @@ -22,25 +18,14 @@ pub struct Droplet { pub ip_address: Ipv4Addr, } -/// Provides an interface for using the SSH client. -/// -/// This trait exists for unit testing: it enables testing behaviour without actually calling the -/// ssh process. -#[cfg_attr(test, automock)] -#[async_trait] -pub trait DigitalOceanClientInterface { - async fn list_droplets(&self) -> Result>; -} - pub struct DigitalOceanClient { pub base_url: String, pub access_token: String, pub page_size: usize, } -#[async_trait] -impl DigitalOceanClientInterface for DigitalOceanClient { - async fn list_droplets(&self) -> Result> { +impl DigitalOceanClient { + pub async fn list_droplets(&self) -> Result> { let client = Client::new(); let mut has_next_page = true; let mut page = 1; diff --git a/src/lib.rs b/src/lib.rs index 58de65b3..a3193bdc 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -18,16 +18,13 @@ pub mod setup; pub mod ssh; pub mod terraform; -#[cfg(test)] -mod tests; - use crate::{ - ansible::{AnsibleRunner, AnsibleRunnerInterface}, + ansible::AnsibleRunner, error::{Error, Result}, - rpc_client::{RpcClient, RpcClientInterface}, - s3::{S3Repository, S3RepositoryInterface}, - ssh::{SshClient, SshClientInterface}, - terraform::{TerraformRunner, TerraformRunnerInterface}, + rpc_client::RpcClient, + s3::S3Repository, + ssh::SshClient, + terraform::TerraformRunner, }; use flate2::read::GzDecoder; use indicatif::{ProgressBar, ProgressStyle}; @@ -310,13 +307,13 @@ impl TestnetDeployBuilder { } let testnet = TestnetDeploy::new( - Box::new(terraform_runner), - Box::new(ansible_runner), - Box::new(rpc_client), - Box::new(SshClient::new(ssh_secret_key_path)), + terraform_runner, + ansible_runner, + rpc_client, + SshClient::new(ssh_secret_key_path), working_directory_path, provider.clone(), - Box::new(S3Repository {}), + S3Repository {}, ); Ok(testnet) @@ -324,25 +321,25 @@ impl TestnetDeployBuilder { } pub struct TestnetDeploy { - pub terraform_runner: Box, - pub ansible_runner: Box, - pub rpc_client: Box, - pub ssh_client: Box, + pub terraform_runner: TerraformRunner, + pub ansible_runner: AnsibleRunner, + pub rpc_client: RpcClient, + pub ssh_client: SshClient, pub working_directory_path: PathBuf, pub cloud_provider: CloudProvider, - pub s3_repository: Box, + pub s3_repository: S3Repository, pub inventory_file_path: PathBuf, } impl TestnetDeploy { pub fn new( - terraform_runner: Box, - ansible_runner: Box, - rpc_client: Box, - ssh_client: Box, + terraform_runner: TerraformRunner, + ansible_runner: AnsibleRunner, + rpc_client: RpcClient, + ssh_client: SshClient, working_directory_path: PathBuf, cloud_provider: CloudProvider, - s3_repository: Box, + s3_repository: S3Repository, ) -> TestnetDeploy { let inventory_file_path = working_directory_path .join("ansible") @@ -382,7 +379,7 @@ impl TestnetDeploy { println!("Downloading the rpc client for safenode..."); let archive_name = "safenode_rpc_client-latest-x86_64-unknown-linux-musl.tar.gz"; get_and_extract_archive_from_s3( - &*self.s3_repository, + &self.s3_repository, "sn-node-rpc-client", archive_name, &self.working_directory_path, @@ -525,8 +522,7 @@ impl TestnetDeploy { .par_iter() .filter_map(|(vm_name, ip_address)| { let ip_address = *ip_address; - let ssh_client_clone = self.ssh_client.clone_box(); - match ssh_client_clone.run_script( + match self.ssh_client.run_script( ip_address, "safe", PathBuf::from("scripts").join("get_peer_multiaddr.sh"), @@ -569,7 +565,7 @@ impl TestnetDeploy { do_clean( name, self.working_directory_path.clone(), - &*self.terraform_runner, + &self.terraform_runner, vec![ "build".to_string(), "genesis".to_string(), @@ -583,7 +579,7 @@ impl TestnetDeploy { /// Shared Helpers /// pub async fn get_and_extract_archive_from_s3( - s3_repository: &dyn S3RepositoryInterface, + s3_repository: &S3Repository, bucket_name: &str, archive_bucket_path: &str, dest_path: &Path, @@ -686,7 +682,7 @@ pub fn is_binary_on_path(binary_name: &str) -> bool { pub fn do_clean( name: &str, working_directory_path: PathBuf, - terraform_runner: &dyn TerraformRunnerInterface, + terraform_runner: &TerraformRunner, inventory_types: Vec, ) -> Result<()> { terraform_runner.init()?; diff --git a/src/logs.rs b/src/logs.rs index 89d391e2..379ffe85 100644 --- a/src/logs.rs +++ b/src/logs.rs @@ -4,9 +4,12 @@ // This SAFE Network Software is licensed under the BSD-3-Clause license. // Please see the LICENSE file for more details. -use crate::error::{Error, Result}; -use crate::s3::{S3Repository, S3RepositoryInterface}; -use crate::{get_progress_bar, run_external_command, TestnetDeploy}; +use crate::{ + error::{Error, Result}, + get_progress_bar, run_external_command, + s3::S3Repository, + TestnetDeploy, +}; use fs_extra::dir::{copy, remove, CopyOptions}; use log::debug; use rayon::iter::{IntoParallelIterator, IntoParallelRefIterator, ParallelIterator}; @@ -136,11 +139,13 @@ impl TestnetDeploy { println!("Running ripgrep with command: {rg_cmd}"); let progress_bar = get_progress_bar(all_node_inventory.len() as u64)?; - let ssh_client = self.ssh_client.clone_box(); let _failed_inventory = all_node_inventory .par_iter() .filter_map(|(vm_name, ip_address)| { - let op = match ssh_client.run_command(ip_address, "safe", &rg_cmd, true) { + let op = match self + .ssh_client + .run_command(ip_address, "safe", &rg_cmd, true) + { Ok(output) => match Self::store_rg_output(&output, &log_abs_dest, vm_name) { Ok(_) => None, Err(err) => { diff --git a/src/logstash.rs b/src/logstash.rs index c7535e9b..ff6ed0e0 100644 --- a/src/logstash.rs +++ b/src/logstash.rs @@ -4,18 +4,20 @@ // This SAFE Network Software is licensed under the BSD-3-Clause license. // Please see the LICENSE file for more details. -use crate::ansible::{AnsibleRunner, AnsibleRunnerInterface}; -use crate::digital_ocean::{ - DigitalOceanClient, DigitalOceanClientInterface, DIGITAL_OCEAN_API_BASE_URL, - DIGITAL_OCEAN_API_PAGE_SIZE, +use crate::{ + ansible::AnsibleRunner, + digital_ocean::{DigitalOceanClient, DIGITAL_OCEAN_API_BASE_URL, DIGITAL_OCEAN_API_PAGE_SIZE}, + do_clean, + error::{Error, Result}, + ssh::SshClient, + terraform::TerraformRunner, + CloudProvider, }; -use crate::error::{Error, Result}; -use crate::ssh::{SshClient, SshClientInterface}; -use crate::terraform::{TerraformRunner, TerraformRunnerInterface}; -use crate::{do_clean, CloudProvider}; use log::debug; -use std::net::{IpAddr, SocketAddr}; -use std::path::PathBuf; +use std::{ + net::{IpAddr, SocketAddr}, + path::PathBuf, +}; pub const LOGSTASH_PORT: u16 = 5044; @@ -136,10 +138,10 @@ impl LogstashDeployBuilder { ); let logstash = LogstashDeploy::new( - Box::new(terraform_runner), - Box::new(ansible_runner), - Box::new(SshClient::new(ssh_secret_key_path)), - Box::new(digital_ocean_client), + terraform_runner, + ansible_runner, + SshClient::new(ssh_secret_key_path), + digital_ocean_client, working_directory_path, provider.clone(), ); @@ -149,10 +151,10 @@ impl LogstashDeployBuilder { } pub struct LogstashDeploy { - pub terraform_runner: Box, - pub ansible_runner: Box, - pub ssh_client: Box, - pub digital_ocean_client: Box, + pub terraform_runner: TerraformRunner, + pub ansible_runner: AnsibleRunner, + pub ssh_client: SshClient, + pub digital_ocean_client: DigitalOceanClient, pub working_directory_path: PathBuf, pub cloud_provider: CloudProvider, pub inventory_file_path: PathBuf, @@ -160,10 +162,10 @@ pub struct LogstashDeploy { impl LogstashDeploy { pub fn new( - terraform_runner: Box, - ansible_runner: Box, - ssh_client: Box, - digital_ocean_client: Box, + terraform_runner: TerraformRunner, + ansible_runner: AnsibleRunner, + ssh_client: SshClient, + digital_ocean_client: DigitalOceanClient, working_directory_path: PathBuf, cloud_provider: CloudProvider, ) -> LogstashDeploy { @@ -249,7 +251,7 @@ impl LogstashDeploy { do_clean( name, self.working_directory_path.clone(), - &*self.terraform_runner, + &self.terraform_runner, vec!["logstash".to_string()], ) } diff --git a/src/main.rs b/src/main.rs index a8e19c4c..0acd5317 100644 --- a/src/main.rs +++ b/src/main.rs @@ -8,14 +8,11 @@ use clap::{Parser, Subcommand}; use color_eyre::{eyre::eyre, Help, Result}; use dotenv::dotenv; use rand::Rng; -use sn_testnet_deploy::deploy::DeployCmd; -use sn_testnet_deploy::error::Error; -use sn_testnet_deploy::logstash::LogstashDeployBuilder; -use sn_testnet_deploy::manage_test_data::TestDataClientBuilder; -use sn_testnet_deploy::setup::setup_dotenv_file; use sn_testnet_deploy::{ - get_data_directory, get_wallet_directory, notify_slack, CloudProvider, DeploymentInventory, - SnCodebaseType, TestnetDeployBuilder, + deploy::DeployCmd, error::Error, get_data_directory, get_wallet_directory, + logstash::LogstashDeployBuilder, manage_test_data::TestDataClientBuilder, notify_slack, + setup::setup_dotenv_file, CloudProvider, DeploymentInventory, SnCodebaseType, + TestnetDeployBuilder, }; pub fn parse_provider(val: &str) -> Result { diff --git a/src/manage_test_data.rs b/src/manage_test_data.rs index 1c7c5817..2b4cb3e4 100644 --- a/src/manage_test_data.rs +++ b/src/manage_test_data.rs @@ -4,29 +4,31 @@ // This SAFE Network Software is licensed under the BSD-3-Clause license. // Please see the LICENSE file for more details. -use crate::error::{Error, Result}; -use crate::s3::{S3Repository, S3RepositoryInterface}; -use crate::safe::{ - SafeBinaryRepository, SafeBinaryRepositoryInterface, SafeClient, SafeClientInterface, +use crate::{ + error::{Error, Result}, + extract_archive, get_and_extract_archive_from_s3, + s3::S3Repository, + safe::{SafeBinaryRepository, SafeClient}, + DeploymentInventory, SnCodebaseType, }; -use crate::{extract_archive, DeploymentInventory}; -use crate::{get_and_extract_archive_from_s3, SnCodebaseType}; use rand::Rng; use sha2::{Digest, Sha256}; -use std::collections::HashMap; -use std::fs::File; -use std::io::{Read, Write}; -use std::net::SocketAddr; -use std::os::unix::fs::PermissionsExt; -use std::path::{Path, PathBuf}; +use std::{ + collections::HashMap, + fs::File, + io::{Read, Write}, + net::SocketAddr, + os::unix::fs::PermissionsExt, + path::{Path, PathBuf}, +}; const BASE_URL: &str = "https://github.com/maidsafe/safe_network/releases/download"; pub struct TestDataClient { pub working_directory_path: PathBuf, - pub s3_repository: Box, - pub safe_client: Box, - pub safe_binary_repository: Box, + pub s3_repository: S3Repository, + pub safe_client: SafeClient, + pub safe_binary_repository: SafeBinaryRepository, } #[derive(Default)] @@ -61,9 +63,9 @@ impl TestDataClientBuilder { }; let test_data_client = TestDataClient::new( working_directory_path.clone(), - Box::new(S3Repository {}), - Box::new(SafeClient::new(safe_binary_path, working_directory_path)), - Box::new(SafeBinaryRepository {}), + S3Repository {}, + SafeClient::new(safe_binary_path, working_directory_path), + SafeBinaryRepository {}, ); Ok(test_data_client) } @@ -72,9 +74,9 @@ impl TestDataClientBuilder { impl TestDataClient { pub fn new( working_directory_path: PathBuf, - s3_repository: Box, - safe_client: Box, - safe_binary_repository: Box, + s3_repository: S3Repository, + safe_client: SafeClient, + safe_binary_repository: SafeBinaryRepository, ) -> TestDataClient { TestDataClient { working_directory_path, @@ -91,7 +93,7 @@ impl TestDataClient { repo_owner, branch, .. } => { Self::download_and_extract_safe_client_from_s3( - &*self.s3_repository, + &self.s3_repository, &inventory.name, &self.working_directory_path, repo_owner, @@ -101,7 +103,7 @@ impl TestDataClient { } crate::SnCodebaseType::Versioned { safe_version, .. } => { Self::download_and_extract_safe_client_from_url( - &*self.safe_binary_repository, + &self.safe_binary_repository, safe_version, &self.working_directory_path, ) @@ -186,7 +188,7 @@ impl TestDataClient { match sn_codebase_type { SnCodebaseType::Main { .. } => { Self::download_and_extract_safe_client_from_url( - &*self.safe_binary_repository, + &self.safe_binary_repository, "latest", &self.working_directory_path, ) @@ -196,7 +198,7 @@ impl TestDataClient { repo_owner, branch, .. } => { Self::download_and_extract_safe_client_from_s3( - &*self.s3_repository, + &self.s3_repository, name, &self.working_directory_path, repo_owner, @@ -209,7 +211,7 @@ impl TestDataClient { safenode_version: _, } => { Self::download_and_extract_safe_client_from_url( - &*self.safe_binary_repository, + &self.safe_binary_repository, safe_version, &self.working_directory_path, ) @@ -223,7 +225,7 @@ impl TestDataClient { std::fs::create_dir_all(test_data_dir_path)?; } get_and_extract_archive_from_s3( - &*self.s3_repository, + &self.s3_repository, "sn-testnet", "test-data.tar.gz", test_data_dir_path, @@ -265,7 +267,7 @@ impl TestDataClient { } async fn download_and_extract_safe_client_from_s3( - s3_repository: &dyn S3RepositoryInterface, + s3_repository: &S3Repository, name: &str, working_directory_path: &Path, repo_owner: &str, @@ -289,7 +291,7 @@ impl TestDataClient { } async fn download_and_extract_safe_client_from_url( - safe_binary_repository: &dyn SafeBinaryRepositoryInterface, + safe_binary_repository: &SafeBinaryRepository, version: &str, working_directory_path: &Path, ) -> Result<()> { diff --git a/src/rpc_client.rs b/src/rpc_client.rs index b42001e9..a75db501 100644 --- a/src/rpc_client.rs +++ b/src/rpc_client.rs @@ -4,12 +4,8 @@ // This SAFE Network Software is licensed under the BSD-3-Clause license. // Please see the LICENSE file for more details. -use crate::error::Result; -use crate::run_external_command; -#[cfg(test)] -use mockall::automock; -use std::net::SocketAddr; -use std::path::PathBuf; +use crate::{error::Result, run_external_command}; +use std::{net::SocketAddr, path::PathBuf}; pub struct NodeInfo { pub endpoint: String, @@ -20,14 +16,6 @@ pub struct NodeInfo { pub last_restart: u32, } -/// This trait exists for unit testing. -/// -/// It allows us to return dummy node info during a test, without making a real RPC call. -#[cfg_attr(test, automock)] -pub trait RpcClientInterface { - fn get_info(&self, rpc_address: SocketAddr) -> Result; -} - pub struct RpcClient { pub binary_path: PathBuf, pub working_directory_path: PathBuf, @@ -40,10 +28,8 @@ impl RpcClient { working_directory_path, } } -} -impl RpcClientInterface for RpcClient { - fn get_info(&self, rpc_address: SocketAddr) -> Result { + pub fn get_info(&self, rpc_address: SocketAddr) -> Result { let output = run_external_command( self.binary_path.clone(), self.working_directory_path.clone(), diff --git a/src/s3.rs b/src/s3.rs index 214ec9cd..4c8d879f 100644 --- a/src/s3.rs +++ b/src/s3.rs @@ -6,42 +6,15 @@ use crate::error::{Error, Result}; use async_recursion::async_recursion; -use async_trait::async_trait; use aws_sdk_s3::{error::ProvideErrorMetadata, Client}; -#[cfg(test)] -use mockall::automock; use std::path::{Path, PathBuf}; use tokio::io::AsyncWriteExt; use tokio_stream::StreamExt; -/// Provides an interface for using S3. -/// -/// This trait exists for unit testing: it enables testing behaviour without actually calling the -/// ssh process. -#[cfg_attr(test, automock)] -#[async_trait] -pub trait S3RepositoryInterface { - async fn download_object( - &self, - bucket_name: &str, - object_key: &str, - dest_path: &Path, - ) -> Result<()>; - async fn download_folder( - &self, - bucket_name: &str, - folder_path: &str, - dest_path: &Path, - ) -> Result<()>; - async fn delete_folder(&self, bucket_name: &str, folder_path: &str) -> Result<()>; - async fn folder_exists(&self, bucket_name: &str, folder_path: &str) -> Result; -} - pub struct S3Repository {} -#[async_trait] -impl S3RepositoryInterface for S3Repository { - async fn download_object( +impl S3Repository { + pub async fn download_object( &self, bucket_name: &str, object_key: &str, @@ -54,7 +27,7 @@ impl S3RepositoryInterface for S3Repository { Ok(()) } - async fn download_folder( + pub async fn download_folder( &self, bucket_name: &str, folder_path: &str, @@ -68,7 +41,7 @@ impl S3RepositoryInterface for S3Repository { Ok(()) } - async fn delete_folder(&self, bucket_name: &str, folder_path: &str) -> Result<()> { + pub async fn delete_folder(&self, bucket_name: &str, folder_path: &str) -> Result<()> { let conf = aws_config::from_env().region("eu-west-2").load().await; let client = Client::new(&conf); self.list_and_delete(&client, bucket_name, folder_path) @@ -76,7 +49,7 @@ impl S3RepositoryInterface for S3Repository { Ok(()) } - async fn folder_exists(&self, bucket_name: &str, folder_path: &str) -> Result { + pub async fn folder_exists(&self, bucket_name: &str, folder_path: &str) -> Result { let conf = aws_config::from_env().region("eu-west-2").load().await; let client = Client::new(&conf); @@ -98,9 +71,7 @@ impl S3RepositoryInterface for S3Repository { })?; Ok(!output.contents().unwrap_or_default().is_empty()) } -} -impl S3Repository { #[async_recursion] async fn list_and_retrieve( &self, diff --git a/src/safe.rs b/src/safe.rs index 93e85175..d86b6f78 100644 --- a/src/safe.rs +++ b/src/safe.rs @@ -4,32 +4,22 @@ // This SAFE Network Software is licensed under the BSD-3-Clause license. // Please see the LICENSE file for more details. -use crate::error::{Error, Result}; -use crate::run_external_command; -use async_trait::async_trait; -#[cfg(test)] -use mockall::automock; +use crate::{ + error::{Error, Result}, + run_external_command, +}; use regex::Regex; -use std::net::SocketAddr; -use std::path::{Path, PathBuf}; -use tokio::fs::File as TokioFile; -use tokio::io::AsyncWriteExt; - -/// Provides an interface for using the `safe` client. -/// -/// This trait exists for unit testing: it enables testing behaviour without actually calling the -/// safe process. -#[cfg_attr(test, automock)] -pub trait SafeClientInterface { - fn wallet_get_faucet(&self, peer_multiaddr: &str, faucet_addr: SocketAddr) -> Result<()>; - fn download_files(&self, peer_multiaddr: &str) -> Result<()>; - fn upload_file(&self, peer_multiaddr: &str, path: &Path) -> Result; -} +use std::{ + net::SocketAddr, + path::{Path, PathBuf}, +}; +use tokio::{fs::File as TokioFile, io::AsyncWriteExt}; pub struct SafeClient { pub binary_path: PathBuf, pub working_directory_path: PathBuf, } + impl SafeClient { pub fn new(binary_path: PathBuf, working_directory_path: PathBuf) -> SafeClient { SafeClient { @@ -37,10 +27,8 @@ impl SafeClient { working_directory_path, } } -} -impl SafeClientInterface for SafeClient { - fn download_files(&self, peer_multiaddr: &str) -> Result<()> { + pub fn download_files(&self, peer_multiaddr: &str) -> Result<()> { run_external_command( self.binary_path.clone(), self.working_directory_path.clone(), @@ -56,7 +44,7 @@ impl SafeClientInterface for SafeClient { Ok(()) } - fn upload_file(&self, peer_multiaddr: &str, path: &Path) -> Result { + pub fn upload_file(&self, peer_multiaddr: &str, path: &Path) -> Result { let output = run_external_command( self.binary_path.clone(), self.working_directory_path.clone(), @@ -83,7 +71,7 @@ impl SafeClientInterface for SafeClient { )) } - fn wallet_get_faucet(&self, peer_multiaddr: &str, faucet_addr: SocketAddr) -> Result<()> { + pub fn wallet_get_faucet(&self, peer_multiaddr: &str, faucet_addr: SocketAddr) -> Result<()> { run_external_command( self.binary_path.clone(), self.working_directory_path.clone(), @@ -101,21 +89,10 @@ impl SafeClientInterface for SafeClient { } } -/// Provides an interface for downloading release binaries for safe or safenode. -/// -/// This trait exists for unit testing: it enables testing behaviour without actually calling the -/// safe process. -#[cfg_attr(test, automock)] -#[async_trait] -pub trait SafeBinaryRepositoryInterface { - async fn download(&self, binary_archive_url: &str, dest_path: &Path) -> Result<()>; -} - pub struct SafeBinaryRepository; -#[async_trait] -impl SafeBinaryRepositoryInterface for SafeBinaryRepository { - async fn download(&self, binary_archive_url: &str, dest_path: &Path) -> Result<()> { +impl SafeBinaryRepository { + pub async fn download(&self, binary_archive_url: &str, dest_path: &Path) -> Result<()> { let response = reqwest::get(binary_archive_url).await?; if !response.status().is_success() { diff --git a/src/ssh.rs b/src/ssh.rs index b48bf3c1..8455013a 100644 --- a/src/ssh.rs +++ b/src/ssh.rs @@ -4,38 +4,12 @@ // This SAFE Network Software is licensed under the BSD-3-Clause license. // Please see the LICENSE file for more details. -use crate::error::{Error, Result}; -use crate::run_external_command; +use crate::{ + error::{Error, Result}, + run_external_command, +}; use log::debug; -#[cfg(test)] -use mockall::automock; -use std::net::IpAddr; -use std::path::PathBuf; - -/// Provides an interface for using the SSH client. -/// -/// This trait exists for unit testing: it enables testing behaviour without actually calling the -/// ssh process. -#[cfg_attr(test, automock)] -pub trait SshClientInterface: Send + Sync { - fn get_private_key_path(&self) -> PathBuf; - fn wait_for_ssh_availability(&self, ip_address: &IpAddr, user: &str) -> Result<()>; - fn run_command( - &self, - ip_address: &IpAddr, - user: &str, - command: &str, - suppress_output: bool, - ) -> Result>; - fn run_script( - &self, - ip_address: IpAddr, - user: &str, - script: PathBuf, - suppress_output: bool, - ) -> Result>; - fn clone_box(&self) -> Box; -} +use std::{net::IpAddr, path::PathBuf}; #[derive(Clone)] pub struct SshClient { @@ -45,13 +19,12 @@ impl SshClient { pub fn new(private_key_path: PathBuf) -> SshClient { SshClient { private_key_path } } -} -impl SshClientInterface for SshClient { - fn get_private_key_path(&self) -> PathBuf { + + pub fn get_private_key_path(&self) -> PathBuf { self.private_key_path.clone() } - fn wait_for_ssh_availability(&self, ip_address: &IpAddr, user: &str) -> Result<()> { + pub fn wait_for_ssh_availability(&self, ip_address: &IpAddr, user: &str) -> Result<()> { println!("Checking for SSH availability at {ip_address}..."); let mut retries = 0; let max_retries = 10; @@ -91,7 +64,7 @@ impl SshClientInterface for SshClient { Err(Error::SshUnavailable) } - fn run_command( + pub fn run_command( &self, ip_address: &IpAddr, user: &str, @@ -129,7 +102,7 @@ impl SshClientInterface for SshClient { Ok(output) } - fn run_script( + pub fn run_script( &self, ip_address: IpAddr, user: &str, @@ -195,8 +168,4 @@ impl SshClientInterface for SshClient { })?; Ok(output) } - - fn clone_box(&self) -> Box { - Box::new(self.clone()) - } } diff --git a/src/terraform.rs b/src/terraform.rs index aa5d5307..fa4c9d3b 100644 --- a/src/terraform.rs +++ b/src/terraform.rs @@ -4,30 +4,12 @@ // This SAFE Network Software is licensed under the BSD-3-Clause license. // Please see the LICENSE file for more details. -use crate::error::{Error, Result}; -use crate::CloudProvider; -use crate::{is_binary_on_path, run_external_command}; -#[cfg(test)] -use mockall::automock; +use crate::{ + error::{Error, Result}, + is_binary_on_path, run_external_command, CloudProvider, +}; use std::path::PathBuf; -/// Provides an interface which corresponds to Terraform commands. -/// -/// To keep things simple, each subcommand will be its own function. -/// -/// This trait exists for unit testing: it enables testing behaviour without actually calling the -/// Terraform process. -#[cfg_attr(test, automock)] -pub trait TerraformRunnerInterface { - fn apply(&self, vars: Vec<(String, String)>) -> Result<()>; - fn destroy(&self) -> Result<()>; - fn init(&self) -> Result<()>; - fn workspace_delete(&self, name: &str) -> Result<()>; - fn workspace_list(&self) -> Result>; - fn workspace_new(&self, name: &str) -> Result<()>; - fn workspace_select(&self, name: &str) -> Result<()>; -} - pub struct TerraformRunner { pub binary_path: PathBuf, pub provider: CloudProvider, @@ -35,8 +17,30 @@ pub struct TerraformRunner { pub state_bucket_name: String, } -impl TerraformRunnerInterface for TerraformRunner { - fn apply(&self, vars: Vec<(String, String)>) -> Result<()> { +impl TerraformRunner { + pub fn new( + binary_path: PathBuf, + working_directory: PathBuf, + provider: CloudProvider, + state_bucket_name: &str, + ) -> Result { + if !binary_path.exists() { + // Try the path as a single binary name. + let bin_name = binary_path.to_string_lossy().to_string(); + if !is_binary_on_path(&binary_path.to_string_lossy()) { + return Err(Error::ToolBinaryNotFound(bin_name)); + } + } + let runner = TerraformRunner { + binary_path, + working_directory_path: working_directory, + provider, + state_bucket_name: state_bucket_name.to_string(), + }; + Ok(runner) + } + + pub fn apply(&self, vars: Vec<(String, String)>) -> Result<()> { let mut args = vec!["apply".to_string(), "-auto-approve".to_string()]; for var in vars.iter() { args.push("-var".to_string()); @@ -52,7 +56,7 @@ impl TerraformRunnerInterface for TerraformRunner { Ok(()) } - fn destroy(&self) -> Result<()> { + pub fn destroy(&self) -> Result<()> { run_external_command( self.binary_path.clone(), self.working_directory_path.clone(), @@ -63,7 +67,7 @@ impl TerraformRunnerInterface for TerraformRunner { Ok(()) } - fn init(&self) -> Result<()> { + pub fn init(&self) -> Result<()> { let args = vec![ "init".to_string(), "-backend-config".to_string(), @@ -79,7 +83,7 @@ impl TerraformRunnerInterface for TerraformRunner { Ok(()) } - fn workspace_delete(&self, name: &str) -> Result<()> { + pub fn workspace_delete(&self, name: &str) -> Result<()> { run_external_command( self.binary_path.clone(), self.working_directory_path.clone(), @@ -94,7 +98,7 @@ impl TerraformRunnerInterface for TerraformRunner { Ok(()) } - fn workspace_list(&self) -> Result> { + pub fn workspace_list(&self) -> Result> { let output = run_external_command( self.binary_path.clone(), self.working_directory_path.clone(), @@ -110,7 +114,7 @@ impl TerraformRunnerInterface for TerraformRunner { Ok(workspaces) } - fn workspace_new(&self, name: &str) -> Result<()> { + pub fn workspace_new(&self, name: &str) -> Result<()> { run_external_command( self.binary_path.clone(), self.working_directory_path.clone(), @@ -121,7 +125,7 @@ impl TerraformRunnerInterface for TerraformRunner { Ok(()) } - fn workspace_select(&self, name: &str) -> Result<()> { + pub fn workspace_select(&self, name: &str) -> Result<()> { run_external_command( self.binary_path.clone(), self.working_directory_path.clone(), @@ -136,27 +140,3 @@ impl TerraformRunnerInterface for TerraformRunner { Ok(()) } } - -impl TerraformRunner { - pub fn new( - binary_path: PathBuf, - working_directory: PathBuf, - provider: CloudProvider, - state_bucket_name: &str, - ) -> Result { - if !binary_path.exists() { - // Try the path as a single binary name. - let bin_name = binary_path.to_string_lossy().to_string(); - if !is_binary_on_path(&binary_path.to_string_lossy()) { - return Err(Error::ToolBinaryNotFound(bin_name)); - } - } - let runner = TerraformRunner { - binary_path, - working_directory_path: working_directory, - provider, - state_bucket_name: state_bucket_name.to_string(), - }; - Ok(runner) - } -} diff --git a/src/tests/clean.rs b/src/tests/clean.rs deleted file mode 100644 index 1c39aea1..00000000 --- a/src/tests/clean.rs +++ /dev/null @@ -1,191 +0,0 @@ -// Copyright (c) 2023, MaidSafe. -// All rights reserved. -// -// This SAFE Network Software is licensed under the BSD-3-Clause license. -// Please see the LICENSE file for more details. - -use super::super::{CloudProvider, TestnetDeploy}; -use super::setup::*; -use crate::ansible::MockAnsibleRunnerInterface; -use crate::rpc_client::MockRpcClientInterface; -use crate::s3::MockS3RepositoryInterface; -use crate::ssh::MockSshClientInterface; -use crate::terraform::MockTerraformRunnerInterface; -use assert_fs::prelude::*; -use color_eyre::{eyre::eyre, Result}; -use mockall::predicate::*; -use mockall::Sequence; - -#[tokio::test] -async fn should_run_terraform_destroy_and_delete_workspace_and_delete_inventory_files() -> Result<()> -{ - let (tmp_dir, working_dir) = setup_working_directory()?; - let s3_repository = setup_deploy_s3_repository("alpha", &working_dir)?; - let mut terraform_runner = setup_default_terraform_runner("alpha"); - let mut seq = Sequence::new(); - terraform_runner.expect_init().times(1).returning(|| Ok(())); - terraform_runner - .expect_workspace_list() - .times(1) - .returning(|| { - Ok(vec![ - "alpha".to_string(), - "default".to_string(), - "dev".to_string(), - ]) - }); - terraform_runner - .expect_workspace_select() - .times(1) - .in_sequence(&mut seq) - .with(eq("alpha".to_string())) - .returning(|_| Ok(())); - terraform_runner - .expect_destroy() - .times(1) - .returning(|| Ok(())); - terraform_runner - .expect_workspace_select() - .times(1) - .in_sequence(&mut seq) - .with(eq("dev".to_string())) - .returning(|_| Ok(())); - terraform_runner - .expect_workspace_delete() - .times(1) - .with(eq("alpha".to_string())) - .returning(|_| Ok(())); - - let testnet = TestnetDeploy::new( - Box::new(terraform_runner), - Box::new(MockAnsibleRunnerInterface::new()), - Box::new(MockRpcClientInterface::new()), - Box::new(MockSshClientInterface::new()), - working_dir.to_path_buf(), - CloudProvider::DigitalOcean, - Box::new(s3_repository), - ); - - // Calling init will create the Ansible inventory files, which we want to be removed by - // the clean operation. - testnet.init("alpha").await?; - testnet.clean("alpha").await?; - - let inventory_types = ["build", "genesis", "node"]; - for inventory_type in inventory_types.iter() { - let inventory_file = working_dir.child(format!( - "ansible/inventory/.{}_{}_inventory_digital_ocean.yml", - "alpha", inventory_type - )); - inventory_file.assert(predicates::path::missing()); - } - - drop(tmp_dir); - Ok(()) -} - -#[tokio::test] -async fn should_return_an_error_when_invalid_name_is_supplied() -> Result<()> { - let (tmp_dir, working_dir) = setup_working_directory()?; - let mut s3_repository = MockS3RepositoryInterface::new(); - s3_repository.expect_download_object().times(0); - let mut terraform_runner = MockTerraformRunnerInterface::new(); - terraform_runner.expect_init().times(1).returning(|| Ok(())); - terraform_runner - .expect_workspace_list() - .times(1) - .returning(|| { - Ok(vec![ - "alpha".to_string(), - "default".to_string(), - "dev".to_string(), - ]) - }); - - let testnet = TestnetDeploy::new( - Box::new(terraform_runner), - Box::new(MockAnsibleRunnerInterface::new()), - Box::new(MockRpcClientInterface::new()), - Box::new(MockSshClientInterface::new()), - working_dir.to_path_buf(), - CloudProvider::DigitalOcean, - Box::new(s3_repository), - ); - - let result = testnet.clean("beta").await; - match result { - Ok(()) => { - drop(tmp_dir); - Err(eyre!("deploy should have returned an error")) - } - Err(e) => { - assert_eq!(e.to_string(), "The 'beta' environment does not exist"); - drop(tmp_dir); - Ok(()) - } - } -} - -#[tokio::test] -async fn should_not_error_when_inventory_does_not_exist() -> Result<()> { - let (tmp_dir, working_dir) = setup_working_directory()?; - let mut seq = Sequence::new(); - let mut terraform_runner = MockTerraformRunnerInterface::new(); - terraform_runner.expect_init().times(1).returning(|| Ok(())); - terraform_runner - .expect_workspace_list() - .times(1) - .returning(|| { - Ok(vec![ - "alpha".to_string(), - "default".to_string(), - "dev".to_string(), - ]) - }); - terraform_runner - .expect_workspace_select() - .times(1) - .in_sequence(&mut seq) - .with(eq("alpha".to_string())) - .returning(|_| Ok(())); - terraform_runner - .expect_destroy() - .times(1) - .returning(|| Ok(())); - terraform_runner - .expect_workspace_select() - .times(1) - .in_sequence(&mut seq) - .with(eq("dev".to_string())) - .returning(|_| Ok(())); - terraform_runner - .expect_workspace_delete() - .times(1) - .with(eq("alpha".to_string())) - .returning(|_| Ok(())); - - let testnet = TestnetDeploy::new( - Box::new(terraform_runner), - Box::new(MockAnsibleRunnerInterface::new()), - Box::new(MockRpcClientInterface::new()), - Box::new(MockSshClientInterface::new()), - working_dir.to_path_buf(), - CloudProvider::DigitalOcean, - Box::new(MockS3RepositoryInterface::new()), - ); - - // Do not call the `init` command, which will be the case in the remote GHA workflow - // environment. In this case, the process should still complete without an error. It should not - // attempt to remove inventory files that don't exist. - let result = testnet.clean("alpha").await; - match result { - Ok(()) => { - drop(tmp_dir); - Ok(()) - } - Err(_) => { - drop(tmp_dir); - Err(eyre!("clean should run without error")) - } - } -} diff --git a/src/tests/get_genesis_multiaddr.rs b/src/tests/get_genesis_multiaddr.rs deleted file mode 100644 index 759a88bc..00000000 --- a/src/tests/get_genesis_multiaddr.rs +++ /dev/null @@ -1,73 +0,0 @@ -// Copyright (c) 2023, MaidSafe. -// All rights reserved. -// -// This SAFE Network Software is licensed under the BSD-3-Clause license. -// Please see the LICENSE file for more details. - -use super::super::{CloudProvider, TestnetDeploy}; -use super::setup::*; -use crate::ansible::MockAnsibleRunnerInterface; -use crate::rpc_client::{MockRpcClientInterface, NodeInfo}; -use crate::s3::MockS3RepositoryInterface; -use crate::ssh::MockSshClientInterface; -use crate::terraform::MockTerraformRunnerInterface; -use color_eyre::Result; -use mockall::predicate::*; -use std::net::{IpAddr, Ipv4Addr, SocketAddr}; -use std::path::PathBuf; - -#[tokio::test] -async fn should_return_the_genesis_multiaddr() -> Result<()> { - let (tmp_dir, working_dir) = setup_working_directory()?; - let mut s3_repository = MockS3RepositoryInterface::new(); - s3_repository.expect_download_object().times(0); - let mut ansible_runner = MockAnsibleRunnerInterface::new(); - ansible_runner - .expect_inventory_list() - .times(1) - .with(eq( - PathBuf::from("inventory").join(".beta_genesis_inventory_digital_ocean.yml") - )) - .returning(|_| { - Ok(vec![( - "beta-genesis".to_string(), - IpAddr::V4(Ipv4Addr::new(10, 0, 0, 10)), - )]) - }); - - let addr: SocketAddr = "10.0.0.10:12001".parse()?; - let mut rpc_client = MockRpcClientInterface::new(); - rpc_client - .expect_get_info() - .times(1) - .with(eq(addr)) - .returning(|_| Ok(NodeInfo { - endpoint: "https://10.0.0.1:12001".to_string(), - peer_id: "12D3KooWLvmkUDQRthtZv9CrzozRLk9ZVEHXgmx6UxVMiho5aded".to_string(), - logs_dir: PathBuf::from("/home/safe/.local/share/safe/node/12D3KooWLvmkUDQRthtZv9CrzozRLk9ZVEHXgmx6UxVMiho5aded/logs"), - pid: 4067, - safenode_version: "0.88.16".to_string(), - last_restart: 187 - })); - - let testnet = TestnetDeploy::new( - Box::new(MockTerraformRunnerInterface::new()), - Box::new(ansible_runner), - Box::new(rpc_client), - Box::new(MockSshClientInterface::new()), - working_dir.to_path_buf(), - CloudProvider::DigitalOcean, - Box::new(s3_repository), - ); - - let (multiaddr, genesis_ip) = testnet.get_genesis_multiaddr("beta").await?; - - assert_eq!( - multiaddr, - "/ip4/10.0.0.10/tcp/12000/p2p/12D3KooWLvmkUDQRthtZv9CrzozRLk9ZVEHXgmx6UxVMiho5aded" - ); - assert_eq!(genesis_ip, IpAddr::V4(Ipv4Addr::new(10, 0, 0, 10))); - - drop(tmp_dir); - Ok(()) -} diff --git a/src/tests/init.rs b/src/tests/init.rs deleted file mode 100644 index 19ca8bac..00000000 --- a/src/tests/init.rs +++ /dev/null @@ -1,313 +0,0 @@ -// Copyright (c) 2023, MaidSafe. -// All rights reserved. -// -// This SAFE Network Software is licensed under the BSD-3-Clause license. -// Please see the LICENSE file for more details. - -use super::super::{CloudProvider, TestnetDeploy}; -use super::setup::*; -use super::RPC_CLIENT_BIN_NAME; -use crate::ansible::MockAnsibleRunnerInterface; -use crate::rpc_client::MockRpcClientInterface; -use crate::s3::MockS3RepositoryInterface; -use crate::ssh::MockSshClientInterface; -use crate::terraform::MockTerraformRunnerInterface; -use assert_fs::prelude::*; -use color_eyre::{eyre::eyre, Result}; -use mockall::predicate::*; -use mockall::Sequence; -use std::os::unix::fs::PermissionsExt; - -#[tokio::test] -async fn should_create_a_new_workspace() -> Result<()> { - let (tmp_dir, working_dir) = setup_working_directory()?; - let mut terraform_runner = MockTerraformRunnerInterface::new(); - terraform_runner.expect_init().times(1).returning(|| Ok(())); - terraform_runner - .expect_workspace_list() - .times(1) - .returning(|| Ok(vec!["default".to_string(), "dev".to_string()])); - terraform_runner - .expect_workspace_new() - .times(1) - .with(eq("beta".to_string())) - .returning(|_| Ok(())); - let s3_repository = setup_deploy_s3_repository("beta", &working_dir)?; - let testnet = TestnetDeploy::new( - Box::new(terraform_runner), - Box::new(MockAnsibleRunnerInterface::new()), - Box::new(MockRpcClientInterface::new()), - Box::new(MockSshClientInterface::new()), - working_dir.to_path_buf(), - CloudProvider::DigitalOcean, - Box::new(s3_repository), - ); - testnet.init("beta").await?; - drop(tmp_dir); - Ok(()) -} - -#[tokio::test] -async fn should_not_create_a_new_workspace_when_one_with_the_same_name_exists() -> Result<()> { - let (tmp_dir, working_dir) = setup_working_directory()?; - let mut terraform_runner = MockTerraformRunnerInterface::new(); - terraform_runner.expect_init().times(1).returning(|| Ok(())); - terraform_runner - .expect_workspace_list() - .times(1) - .returning(|| { - Ok(vec![ - "alpha".to_string(), - "default".to_string(), - "dev".to_string(), - ]) - }); - terraform_runner - .expect_workspace_new() - .times(0) - .with(eq("alpha".to_string())) - .returning(|_| Ok(())); - - let s3_repository = setup_deploy_s3_repository("alpha", &working_dir)?; - let testnet = TestnetDeploy::new( - Box::new(terraform_runner), - Box::new(MockAnsibleRunnerInterface::new()), - Box::new(MockRpcClientInterface::new()), - Box::new(MockSshClientInterface::new()), - working_dir.to_path_buf(), - CloudProvider::DigitalOcean, - Box::new(s3_repository), - ); - testnet.init("alpha").await?; - drop(tmp_dir); - Ok(()) -} - -#[tokio::test] -async fn should_download_and_extract_the_rpc_client() -> Result<()> { - let (tmp_dir, working_dir) = setup_working_directory()?; - let downloaded_safe_archive = - working_dir.child("safenode_rpc_client-latest-x86_64-unknown-linux-musl.tar.gz"); - - let extracted_rpc_client_bin = working_dir.child(RPC_CLIENT_BIN_NAME); - let s3_repository = setup_deploy_s3_repository("alpha", &working_dir)?; - let terraform_runner = setup_default_terraform_runner("alpha"); - let testnet = TestnetDeploy::new( - Box::new(terraform_runner), - Box::new(MockAnsibleRunnerInterface::new()), - Box::new(MockRpcClientInterface::new()), - Box::new(MockSshClientInterface::new()), - working_dir.to_path_buf(), - CloudProvider::DigitalOcean, - Box::new(s3_repository), - ); - - testnet.init("alpha").await?; - - downloaded_safe_archive.assert(predicates::path::missing()); - extracted_rpc_client_bin.assert(predicates::path::is_file()); - - let metadata = std::fs::metadata(extracted_rpc_client_bin.path())?; - let permissions = metadata.permissions(); - assert!(permissions.mode() & 0o100 > 0, "File is not executable"); - drop(tmp_dir); - Ok(()) -} - -#[tokio::test] -async fn should_not_download_the_rpc_client_if_it_already_exists() -> Result<()> { - let (tmp_dir, working_dir) = setup_working_directory()?; - let fake_rpc_client_bin = working_dir.child(RPC_CLIENT_BIN_NAME); - fake_rpc_client_bin.write_binary(b"fake code")?; - - let mut s3_repository = MockS3RepositoryInterface::new(); - s3_repository - .expect_folder_exists() - .with(eq("sn-testnet"), eq("testnet-logs/alpha".to_string())) - .times(1) - .returning(|_, _| Ok(false)); - s3_repository.expect_download_object().times(0); - - let terraform_runner = setup_default_terraform_runner("alpha"); - let testnet = TestnetDeploy::new( - Box::new(terraform_runner), - Box::new(MockAnsibleRunnerInterface::new()), - Box::new(MockRpcClientInterface::new()), - Box::new(MockSshClientInterface::new()), - working_dir.to_path_buf(), - CloudProvider::DigitalOcean, - Box::new(s3_repository), - ); - testnet.init("alpha").await?; - - drop(tmp_dir); - Ok(()) -} - -#[tokio::test] -async fn should_generate_ansible_inventory_for_digital_ocean_for_the_new_testnet() -> Result<()> { - let (tmp_dir, working_dir) = setup_working_directory()?; - let s3_repository = setup_deploy_s3_repository("alpha", &working_dir)?; - let terraform_runner = setup_default_terraform_runner("alpha"); - - let testnet = TestnetDeploy::new( - Box::new(terraform_runner), - Box::new(MockAnsibleRunnerInterface::new()), - Box::new(MockRpcClientInterface::new()), - Box::new(MockSshClientInterface::new()), - working_dir.to_path_buf(), - CloudProvider::DigitalOcean, - Box::new(s3_repository), - ); - - testnet.init("alpha").await?; - - let inventory_files = ["build", "genesis", "node"]; - for inventory_type in inventory_files.iter() { - let inventory_file = working_dir.child(format!( - "ansible/inventory/.{}_{}_inventory_digital_ocean.yml", - "alpha", inventory_type - )); - inventory_file.assert(predicates::path::is_file()); - - let contents = std::fs::read_to_string(inventory_file.path())?; - assert!(contents.contains("alpha")); - assert!(contents.contains(inventory_type)); - } - drop(tmp_dir); - Ok(()) -} - -#[tokio::test] -async fn should_not_overwrite_generated_inventory() -> Result<()> { - let (tmp_dir, working_dir) = setup_working_directory()?; - let mut terraform_runner = MockTerraformRunnerInterface::new(); - let mut seq = Sequence::new(); - - let saved_archive_path = working_dir - .to_path_buf() - .join("safenode_rpc_client-latest-x86_64-unknown-linux-musl.tar.gz"); - let rpc_client_archive_path = - create_fake_bin_archive(&working_dir, "rpc_client.tar.gz", RPC_CLIENT_BIN_NAME)?; - let mut s3_repository = MockS3RepositoryInterface::new(); - s3_repository - .expect_download_object() - .with( - eq("sn-node-rpc-client"), - eq("safenode_rpc_client-latest-x86_64-unknown-linux-musl.tar.gz"), - eq(saved_archive_path), - ) - .times(1) - .returning(move |_bucket_name, _object_path, archive_path| { - std::fs::copy(&rpc_client_archive_path, archive_path)?; - Ok(()) - }); - s3_repository - .expect_folder_exists() - .with(eq("sn-testnet"), eq("testnet-logs/alpha".to_string())) - .times(2) - .returning(|_, _| Ok(false)); - - terraform_runner.expect_init().times(2).returning(|| Ok(())); - terraform_runner - .expect_workspace_list() - .times(1) - .in_sequence(&mut seq) - .returning(|| Ok(vec!["default".to_string(), "dev".to_string()])); - terraform_runner - .expect_workspace_new() - .times(1) - .with(eq("alpha".to_string())) - .returning(|_| Ok(())); - terraform_runner - .expect_workspace_list() - .times(1) - .in_sequence(&mut seq) - .returning(|| { - Ok(vec![ - "alpha".to_string(), - "default".to_string(), - "dev".to_string(), - ]) - }); - - let testnet = TestnetDeploy::new( - Box::new(terraform_runner), - Box::new(MockAnsibleRunnerInterface::new()), - Box::new(MockRpcClientInterface::new()), - Box::new(MockSshClientInterface::new()), - working_dir.to_path_buf(), - CloudProvider::DigitalOcean, - Box::new(s3_repository), - ); - - testnet.init("alpha").await?; - testnet.init("alpha").await?; // this should be idempotent - - let inventory_files = ["build", "genesis", "node"]; - for inventory_type in inventory_files.iter() { - let inventory_file = working_dir.child(format!( - "ansible/inventory/.{}_{}_inventory_digital_ocean.yml", - "alpha", inventory_type - )); - inventory_file.assert(predicates::path::is_file()); - - let contents = std::fs::read_to_string(inventory_file.path())?; - assert!(contents.contains("alpha")); - assert!(contents.contains(inventory_type)); - } - drop(tmp_dir); - Ok(()) -} - -#[tokio::test] -async fn should_return_an_error_if_logs_already_exist_for_environment() -> Result<()> { - let (tmp_dir, working_dir) = setup_working_directory()?; - let mut terraform_runner = MockTerraformRunnerInterface::new(); - terraform_runner.expect_init().times(0).returning(|| Ok(())); - terraform_runner - .expect_workspace_list() - .times(0) - .returning(|| { - Ok(vec![ - "alpha".to_string(), - "default".to_string(), - "dev".to_string(), - ]) - }); - terraform_runner - .expect_workspace_new() - .times(0) - .with(eq("alpha".to_string())) - .returning(|_| Ok(())); - - let mut s3_repository = MockS3RepositoryInterface::new(); - s3_repository - .expect_folder_exists() - .with(eq("sn-testnet"), eq("testnet-logs/alpha".to_string())) - .times(1) - .returning(|_, _| Ok(true)); - - let testnet = TestnetDeploy::new( - Box::new(terraform_runner), - Box::new(MockAnsibleRunnerInterface::new()), - Box::new(MockRpcClientInterface::new()), - Box::new(MockSshClientInterface::new()), - working_dir.to_path_buf(), - CloudProvider::DigitalOcean, - Box::new(s3_repository), - ); - - let result = testnet.init("alpha").await; - match result { - Ok(()) => { - drop(tmp_dir); - Err(eyre!("init should have returned an error")) - } - Err(e) => { - assert_eq!(e.to_string(), "Logs for a 'alpha' testnet already exist"); - drop(tmp_dir); - Ok(()) - } - } -} diff --git a/src/tests/logstash/clean.rs b/src/tests/logstash/clean.rs deleted file mode 100644 index 69554793..00000000 --- a/src/tests/logstash/clean.rs +++ /dev/null @@ -1,182 +0,0 @@ -// Copyright (c) 2023, MaidSafe. -// All rights reserved. -// -// This SAFE Network Software is licensed under the BSD-3-Clause license. -// Please see the LICENSE file for more details. - -use super::super::setup::*; -use crate::ansible::MockAnsibleRunnerInterface; -use crate::digital_ocean::MockDigitalOceanClientInterface; -use crate::logstash::LogstashDeploy; -use crate::ssh::MockSshClientInterface; -use crate::terraform::MockTerraformRunnerInterface; -use crate::CloudProvider; -use assert_fs::prelude::*; -use color_eyre::{eyre::eyre, Result}; -use mockall::predicate::*; -use mockall::Sequence; - -#[tokio::test] -async fn should_run_terraform_destroy_and_delete_workspace_and_delete_inventory_files() -> Result<()> -{ - let (tmp_dir, working_dir) = setup_working_directory()?; - let mut terraform_runner = setup_default_terraform_runner("alpha"); - let mut seq = Sequence::new(); - terraform_runner.expect_init().times(1).returning(|| Ok(())); - terraform_runner - .expect_workspace_list() - .times(1) - .returning(|| { - Ok(vec![ - "alpha".to_string(), - "default".to_string(), - "dev".to_string(), - ]) - }); - terraform_runner - .expect_workspace_select() - .times(1) - .in_sequence(&mut seq) - .with(eq("alpha".to_string())) - .returning(|_| Ok(())); - terraform_runner - .expect_destroy() - .times(1) - .returning(|| Ok(())); - terraform_runner - .expect_workspace_select() - .times(1) - .in_sequence(&mut seq) - .with(eq("dev".to_string())) - .returning(|_| Ok(())); - terraform_runner - .expect_workspace_delete() - .times(1) - .with(eq("alpha".to_string())) - .returning(|_| Ok(())); - - let logstash = LogstashDeploy::new( - Box::new(terraform_runner), - Box::new(MockAnsibleRunnerInterface::new()), - Box::new(MockSshClientInterface::new()), - Box::new(MockDigitalOceanClientInterface::new()), - working_dir.to_path_buf(), - CloudProvider::DigitalOcean, - ); - - // Calling init will create the Ansible inventory files, which we want to be removed by - // the clean operation. - logstash.init("alpha").await?; - logstash.clean("alpha").await?; - - let inventory_file = working_dir.child(format!( - "ansible/inventory/.{}_logstash_inventory_digital_ocean.yml", - "alpha" - )); - inventory_file.assert(predicates::path::missing()); - - drop(tmp_dir); - Ok(()) -} - -#[tokio::test] -async fn should_return_an_error_when_invalid_name_is_supplied() -> Result<()> { - let (tmp_dir, working_dir) = setup_working_directory()?; - let mut terraform_runner = MockTerraformRunnerInterface::new(); - terraform_runner.expect_init().times(1).returning(|| Ok(())); - terraform_runner - .expect_workspace_list() - .times(1) - .returning(|| { - Ok(vec![ - "alpha".to_string(), - "default".to_string(), - "dev".to_string(), - ]) - }); - - let logstash = LogstashDeploy::new( - Box::new(terraform_runner), - Box::new(MockAnsibleRunnerInterface::new()), - Box::new(MockSshClientInterface::new()), - Box::new(MockDigitalOceanClientInterface::new()), - working_dir.to_path_buf(), - CloudProvider::DigitalOcean, - ); - - let result = logstash.clean("beta").await; - match result { - Ok(()) => { - drop(tmp_dir); - Err(eyre!("deploy should have returned an error")) - } - Err(e) => { - assert_eq!(e.to_string(), "The 'beta' environment does not exist"); - drop(tmp_dir); - Ok(()) - } - } -} - -#[tokio::test] -async fn should_not_error_when_inventory_does_not_exist() -> Result<()> { - let (tmp_dir, working_dir) = setup_working_directory()?; - let mut seq = Sequence::new(); - let mut terraform_runner = MockTerraformRunnerInterface::new(); - terraform_runner.expect_init().times(1).returning(|| Ok(())); - terraform_runner - .expect_workspace_list() - .times(1) - .returning(|| { - Ok(vec![ - "alpha".to_string(), - "default".to_string(), - "dev".to_string(), - ]) - }); - terraform_runner - .expect_workspace_select() - .times(1) - .in_sequence(&mut seq) - .with(eq("alpha".to_string())) - .returning(|_| Ok(())); - terraform_runner - .expect_destroy() - .times(1) - .returning(|| Ok(())); - terraform_runner - .expect_workspace_select() - .times(1) - .in_sequence(&mut seq) - .with(eq("dev".to_string())) - .returning(|_| Ok(())); - terraform_runner - .expect_workspace_delete() - .times(1) - .with(eq("alpha".to_string())) - .returning(|_| Ok(())); - - let logstash = LogstashDeploy::new( - Box::new(terraform_runner), - Box::new(MockAnsibleRunnerInterface::new()), - Box::new(MockSshClientInterface::new()), - Box::new(MockDigitalOceanClientInterface::new()), - working_dir.to_path_buf(), - CloudProvider::DigitalOcean, - ); - - // Do not call the `init` command, which will be the case in the remote GHA workflow - // environment. In this case, the process should still complete without an error. It should not - // attempt to remove inventory files that don't exist. - let result = logstash.clean("alpha").await; - match result { - Ok(()) => { - drop(tmp_dir); - Ok(()) - } - Err(_) => { - drop(tmp_dir); - Err(eyre!("clean should run without error")) - } - } -} diff --git a/src/tests/logstash/deploy.rs b/src/tests/logstash/deploy.rs deleted file mode 100644 index 18da4288..00000000 --- a/src/tests/logstash/deploy.rs +++ /dev/null @@ -1,98 +0,0 @@ -// Copyright (c) 2023, MaidSafe. -// All rights reserved. -// -// This SAFE Network Software is licensed under the BSD-3-Clause license. -// Please see the LICENSE file for more details. - -use super::super::setup::*; -use crate::ansible::MockAnsibleRunnerInterface; -use crate::digital_ocean::MockDigitalOceanClientInterface; -use crate::logstash::LogstashDeploy; -use crate::ssh::MockSshClientInterface; -use crate::terraform::MockTerraformRunnerInterface; -use crate::CloudProvider; -use color_eyre::Result; -use mockall::predicate::*; -use std::net::{IpAddr, Ipv4Addr}; -use std::path::PathBuf; - -#[tokio::test] -async fn should_run_terraform_apply() -> Result<()> { - let (tmp_dir, working_dir) = setup_working_directory()?; - let mut terraform_runner = MockTerraformRunnerInterface::new(); - terraform_runner - .expect_workspace_select() - .times(1) - .with(eq("beta".to_string())) - .returning(|_| Ok(())); - terraform_runner - .expect_apply() - .times(1) - .with(eq(vec![("node_count".to_string(), "2".to_string())])) - .returning(|_| Ok(())); - - let logstash = LogstashDeploy::new( - Box::new(terraform_runner), - Box::new(MockAnsibleRunnerInterface::new()), - Box::new(MockSshClientInterface::new()), - Box::new(MockDigitalOceanClientInterface::new()), - working_dir.to_path_buf(), - CloudProvider::DigitalOcean, - ); - - logstash.create_infra("beta", 2).await?; - - drop(tmp_dir); - Ok(()) -} - -#[tokio::test] -async fn should_run_ansible_to_provision_the_logstash_nodes() -> Result<()> { - let (tmp_dir, working_dir) = setup_working_directory()?; - let mut ansible_runner = MockAnsibleRunnerInterface::new(); - ansible_runner - .expect_inventory_list() - .times(1) - .with(eq( - PathBuf::from("inventory").join(".beta_logstash_inventory_digital_ocean.yml") - )) - .returning(|_| { - Ok(vec![( - "beta-logstash-1".to_string(), - IpAddr::V4(Ipv4Addr::new(10, 0, 0, 10)), - )]) - }); - ansible_runner - .expect_run_playbook() - .times(1) - .with( - eq(PathBuf::from("logstash.yml")), - eq(PathBuf::from("inventory").join(".beta_logstash_inventory_digital_ocean.yml")), - eq("root".to_string()), - eq(Some( - "{ \"provider\": \"digital-ocean\", \"stack_name\": \"beta\", \"logstash_host_ip_address\": \"10.0.0.10\" }".to_string(), - )), - ) - .returning(|_, _, _, _| Ok(())); - - let mut ssh_runner = MockSshClientInterface::new(); - ssh_runner - .expect_wait_for_ssh_availability() - .times(1) - .with(eq(IpAddr::V4(Ipv4Addr::new(10, 0, 0, 10))), eq("root")) - .returning(|_, _| Ok(())); - - let logstash = LogstashDeploy::new( - Box::new(MockTerraformRunnerInterface::new()), - Box::new(ansible_runner), - Box::new(ssh_runner), - Box::new(MockDigitalOceanClientInterface::new()), - working_dir.to_path_buf(), - CloudProvider::DigitalOcean, - ); - - logstash.provision("beta").await?; - - drop(tmp_dir); - Ok(()) -} diff --git a/src/tests/logstash/get_stack_hosts.rs b/src/tests/logstash/get_stack_hosts.rs deleted file mode 100644 index b277f38b..00000000 --- a/src/tests/logstash/get_stack_hosts.rs +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright (c) 2023, MaidSafe. -// All rights reserved. -// -// This SAFE Network Software is licensed under the BSD-3-Clause license. -// Please see the LICENSE file for more details. - -use super::super::setup::*; -use crate::ansible::MockAnsibleRunnerInterface; -use crate::digital_ocean::{Droplet, MockDigitalOceanClientInterface}; -use crate::logstash::LogstashDeploy; -use crate::ssh::MockSshClientInterface; -use crate::terraform::MockTerraformRunnerInterface; -use crate::CloudProvider; -use color_eyre::Result; -use std::net::{IpAddr, Ipv4Addr}; -use std::str::FromStr; - -#[tokio::test] -async fn should_return_the_correct_hosts_for_the_stack() -> Result<()> { - let (tmp_dir, working_dir) = setup_working_directory()?; - let mut digital_ocean_client_mock = MockDigitalOceanClientInterface::new(); - digital_ocean_client_mock - .expect_list_droplets() - .times(1) - .returning(|| { - Ok(vec![ - Droplet { - id: 2000, - name: "logstash-main-1".to_string(), - ip_address: Ipv4Addr::from_str("10.0.0.1")?, - }, - Droplet { - id: 2001, - name: "logstash-main-2".to_string(), - ip_address: Ipv4Addr::from_str("10.0.0.2")?, - }, - Droplet { - id: 2002, - name: "logstash-test-1".to_string(), - ip_address: Ipv4Addr::from_str("10.0.0.3")?, - }, - Droplet { - id: 2003, - name: "logstash-test-2".to_string(), - ip_address: Ipv4Addr::from_str("10.0.0.4")?, - }, - ]) - }); - - let logstash = LogstashDeploy::new( - Box::new(MockTerraformRunnerInterface::new()), - Box::new(MockAnsibleRunnerInterface::new()), - Box::new(MockSshClientInterface::new()), - Box::new(digital_ocean_client_mock), - working_dir.to_path_buf(), - CloudProvider::DigitalOcean, - ); - - let stack_hosts = logstash.get_stack_hosts("main").await?; - - assert_eq!(2, stack_hosts.len()); - assert_eq!(stack_hosts[0].ip(), IpAddr::from_str("10.0.0.1")?); - assert_eq!(stack_hosts[0].port(), 5044); - assert_eq!(stack_hosts[1].ip(), IpAddr::from_str("10.0.0.2")?); - assert_eq!(stack_hosts[1].port(), 5044); - - drop(tmp_dir); - Ok(()) -} diff --git a/src/tests/logstash/init.rs b/src/tests/logstash/init.rs deleted file mode 100644 index 7213af49..00000000 --- a/src/tests/logstash/init.rs +++ /dev/null @@ -1,166 +0,0 @@ -// Copyright (c) 2023, MaidSafe. -// All rights reserved. -// -// This SAFE Network Software is licensed under the BSD-3-Clause license. -// Please see the LICENSE file for more details. - -use super::super::setup::*; -use crate::ansible::MockAnsibleRunnerInterface; -use crate::digital_ocean::MockDigitalOceanClientInterface; -use crate::logstash::LogstashDeploy; -use crate::ssh::MockSshClientInterface; -use crate::terraform::MockTerraformRunnerInterface; -use crate::CloudProvider; -use assert_fs::prelude::*; -use color_eyre::Result; -use mockall::predicate::*; -use mockall::Sequence; - -#[tokio::test] -async fn should_create_a_new_workspace() -> Result<()> { - let (tmp_dir, working_dir) = setup_working_directory()?; - let mut terraform_runner = MockTerraformRunnerInterface::new(); - terraform_runner.expect_init().times(1).returning(|| Ok(())); - terraform_runner - .expect_workspace_list() - .times(1) - .returning(|| Ok(vec!["default".to_string(), "dev".to_string()])); - terraform_runner - .expect_workspace_new() - .times(1) - .with(eq("beta".to_string())) - .returning(|_| Ok(())); - - let logstash = LogstashDeploy::new( - Box::new(terraform_runner), - Box::new(MockAnsibleRunnerInterface::new()), - Box::new(MockSshClientInterface::new()), - Box::new(MockDigitalOceanClientInterface::new()), - working_dir.to_path_buf(), - CloudProvider::DigitalOcean, - ); - - logstash.init("beta").await?; - - drop(tmp_dir); - Ok(()) -} - -#[tokio::test] -async fn should_not_create_a_new_workspace_when_one_with_the_same_name_exists() -> Result<()> { - let (tmp_dir, working_dir) = setup_working_directory()?; - let mut terraform_runner = MockTerraformRunnerInterface::new(); - terraform_runner.expect_init().times(1).returning(|| Ok(())); - terraform_runner - .expect_workspace_list() - .times(1) - .returning(|| { - Ok(vec![ - "alpha".to_string(), - "default".to_string(), - "dev".to_string(), - ]) - }); - terraform_runner - .expect_workspace_new() - .times(0) - .with(eq("alpha".to_string())) - .returning(|_| Ok(())); - - let logstash = LogstashDeploy::new( - Box::new(terraform_runner), - Box::new(MockAnsibleRunnerInterface::new()), - Box::new(MockSshClientInterface::new()), - Box::new(MockDigitalOceanClientInterface::new()), - working_dir.to_path_buf(), - CloudProvider::DigitalOcean, - ); - - logstash.init("alpha").await?; - - drop(tmp_dir); - Ok(()) -} - -#[tokio::test] -async fn should_generate_ansible_inventory_for_digital_ocean_for_the_new_logstash() -> Result<()> { - let (tmp_dir, working_dir) = setup_working_directory()?; - let terraform_runner = setup_default_terraform_runner("alpha"); - - let logstash = LogstashDeploy::new( - Box::new(terraform_runner), - Box::new(MockAnsibleRunnerInterface::new()), - Box::new(MockSshClientInterface::new()), - Box::new(MockDigitalOceanClientInterface::new()), - working_dir.to_path_buf(), - CloudProvider::DigitalOcean, - ); - - logstash.init("alpha").await?; - - let inventory_file = working_dir.child(format!( - "ansible/inventory/.{}_logstash_inventory_digital_ocean.yml", - "alpha" - )); - inventory_file.assert(predicates::path::is_file()); - - let contents = std::fs::read_to_string(inventory_file.path())?; - assert!(contents.contains("alpha")); - assert!(contents.contains("logstash")); - drop(tmp_dir); - Ok(()) -} - -#[tokio::test] -async fn should_not_overwrite_generated_inventory() -> Result<()> { - let (tmp_dir, working_dir) = setup_working_directory()?; - let mut terraform_runner = MockTerraformRunnerInterface::new(); - let mut seq = Sequence::new(); - - terraform_runner.expect_init().times(2).returning(|| Ok(())); - terraform_runner - .expect_workspace_list() - .times(1) - .in_sequence(&mut seq) - .returning(|| Ok(vec!["default".to_string(), "dev".to_string()])); - terraform_runner - .expect_workspace_new() - .times(1) - .with(eq("alpha".to_string())) - .returning(|_| Ok(())); - terraform_runner - .expect_workspace_list() - .times(1) - .in_sequence(&mut seq) - .returning(|| { - Ok(vec![ - "alpha".to_string(), - "default".to_string(), - "dev".to_string(), - ]) - }); - - let logstash = LogstashDeploy::new( - Box::new(terraform_runner), - Box::new(MockAnsibleRunnerInterface::new()), - Box::new(MockSshClientInterface::new()), - Box::new(MockDigitalOceanClientInterface::new()), - working_dir.to_path_buf(), - CloudProvider::DigitalOcean, - ); - - logstash.init("alpha").await?; - logstash.init("alpha").await?; // this should be idempotent - - let inventory_file = working_dir.child(format!( - "ansible/inventory/.{}_logstash_inventory_digital_ocean.yml", - "alpha" - )); - inventory_file.assert(predicates::path::is_file()); - - let contents = std::fs::read_to_string(inventory_file.path())?; - assert!(contents.contains("alpha")); - assert!(contents.contains("logstash")); - drop(tmp_dir); - Ok(()) -} diff --git a/src/tests/logstash/mod.rs b/src/tests/logstash/mod.rs deleted file mode 100644 index 77623994..00000000 --- a/src/tests/logstash/mod.rs +++ /dev/null @@ -1,10 +0,0 @@ -// Copyright (c) 2023, MaidSafe. -// All rights reserved. -// -// This SAFE Network Software is licensed under the BSD-3-Clause license. -// Please see the LICENSE file for more details. - -mod clean; -mod deploy; -mod get_stack_hosts; -mod init; diff --git a/src/tests/mod.rs b/src/tests/mod.rs deleted file mode 100644 index 9f3697df..00000000 --- a/src/tests/mod.rs +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright (c) 2023, MaidSafe. -// All rights reserved. -// -// This SAFE Network Software is licensed under the BSD-3-Clause license. -// Please see the LICENSE file for more details. - -mod clean; -mod get_genesis_multiaddr; -mod init; -mod logstash; -mod setup; -mod upload_test_data; - -const RPC_CLIENT_BIN_NAME: &str = "safenode_rpc_client"; diff --git a/src/tests/setup.rs b/src/tests/setup.rs deleted file mode 100644 index cd86d515..00000000 --- a/src/tests/setup.rs +++ /dev/null @@ -1,234 +0,0 @@ -// Copyright (c) 2023, MaidSafe. -// All rights reserved. -// -// This SAFE Network Software is licensed under the BSD-3-Clause license. -// Please see the LICENSE file for more details. - -use super::*; -use crate::s3::MockS3RepositoryInterface; -use crate::safe::MockSafeBinaryRepositoryInterface; -use crate::terraform::MockTerraformRunnerInterface; -use assert_fs::fixture::ChildPath; -use assert_fs::prelude::*; -use assert_fs::TempDir; -use color_eyre::Result; -use flate2::write::GzEncoder; -use flate2::Compression; -use mockall::predicate::*; -use std::fs::File; - -pub fn setup_working_directory() -> Result<(TempDir, ChildPath)> { - let tmp_dir = assert_fs::TempDir::new()?; - let working_dir = tmp_dir.child("work"); - working_dir.create_dir_all()?; - working_dir.copy_from("resources/", &["**"])?; - Ok((tmp_dir, working_dir)) -} - -pub fn create_fake_bin_archive( - working_dir: &ChildPath, - archive_name: &str, - bin_name: &str, -) -> Result { - let temp_archive_dir = working_dir.child("setup_archive"); - let archive = temp_archive_dir.child(archive_name); - let fake_bin = temp_archive_dir.child(bin_name); - fake_bin.write_binary(b"fake code")?; - - let mut fake_bin_file = File::open(fake_bin.path())?; - let gz_encoder = GzEncoder::new(File::create(archive.path())?, Compression::default()); - let mut builder = tar::Builder::new(gz_encoder); - builder.append_file(bin_name, &mut fake_bin_file)?; - builder.into_inner()?; - - Ok(archive) -} - -pub fn create_fake_test_data_archive( - working_dir: &ChildPath, - archive_name: &str, -) -> Result { - let image_file_names = [ - "pexels-ahmed-ツ-13524648.jpg", - "pexels-ahmed-ツ-14113084.jpg", - "pexels-aidan-roof-11757330.jpg", - ]; - let temp_archive_dir = working_dir.child("test-data-setup-archive"); - let archive = temp_archive_dir.child(archive_name); - for image_file_name in image_file_names.iter() { - let fake_image = temp_archive_dir.child(image_file_name); - fake_image.write_binary(b"fake image data")?; - } - - let gz_encoder = GzEncoder::new(File::create(archive.path())?, Compression::default()); - let mut builder = tar::Builder::new(gz_encoder); - for image_file_name in image_file_names.iter() { - let mut fake_file = File::open(temp_archive_dir.path().join(image_file_name))?; - builder.append_file(image_file_name, &mut fake_file)?; - } - builder.into_inner()?; - - Ok(archive) -} - -pub fn setup_default_terraform_runner(name: &str) -> MockTerraformRunnerInterface { - let mut terraform_runner = MockTerraformRunnerInterface::new(); - terraform_runner.expect_init().times(1).returning(|| Ok(())); - terraform_runner - .expect_workspace_list() - .times(1) - .returning(|| Ok(vec!["default".to_string(), "dev".to_string()])); - terraform_runner - .expect_workspace_new() - .times(1) - .with(eq(name.to_string())) - .returning(|_| Ok(())); - terraform_runner -} - -pub fn setup_deploy_s3_repository( - env_name: &str, - working_dir: &ChildPath, -) -> Result { - // For an explanation of what's happening here, see the comments on the function below. This - // function is the same, except it only deals with one archive. - let saved_archive_path = working_dir - .to_path_buf() - .join("safenode_rpc_client-latest-x86_64-unknown-linux-musl.tar.gz"); - let rpc_client_archive_path = - create_fake_bin_archive(working_dir, "rpc_client.tar.gz", RPC_CLIENT_BIN_NAME)?; - let mut s3_repository = MockS3RepositoryInterface::new(); - s3_repository - .expect_download_object() - .with( - eq("sn-node-rpc-client"), - eq("safenode_rpc_client-latest-x86_64-unknown-linux-musl.tar.gz"), - eq(saved_archive_path), - ) - .times(1) - .returning(move |_bucket_name, _object_path, archive_path| { - std::fs::copy(&rpc_client_archive_path, archive_path)?; - Ok(()) - }); - s3_repository - .expect_folder_exists() - .with(eq("sn-testnet"), eq(format!("testnet-logs/{env_name}"))) - .times(1) - .returning(|_, _| Ok(false)); - Ok(s3_repository) -} - -pub fn setup_test_data_s3_repository_for_custom_branch( - env_name: &str, - repo_owner: &str, - branch_name: &str, - working_dir: &ChildPath, -) -> Result { - // This function can be a little hard to understand. - // - // Two tar.gz archives are created: one with a fake `safe` binary and the other with three fake - // images that get used for test data. The system under test will actually extract these fake - // archives. In the real code, the archives will be fetched from S3, and that's where the mock - // comes in. In the test setup, the archives are stored in a temporary directory. - // - // The mock declares that it should expect two invocations of its `download_object` function, - // one for each archive. The mock's `returning` function defines a side effect, which copies - // the fake archive from its temporary directory to the location where the real archive would - // be downloaded, and the code then operates on the fake archive. - let saved_safe_archive_file_name = format!("safe-{env_name}-x86_64-unknown-linux-musl.tar.gz"); - let safe_s3_object_path = format!("{repo_owner}/{branch_name}/{saved_safe_archive_file_name}"); - let saved_safe_archive_path = working_dir - .to_path_buf() - .join(saved_safe_archive_file_name.clone()); - let fake_safe_client_archive_path = - create_fake_bin_archive(working_dir, "safe_client.tar.gz", "safe")?; - - let saved_test_data_archive_file_name = "test-data.tar.gz"; - let saved_test_data_archive_path = working_dir - .join("test-data") - .join(saved_test_data_archive_file_name); - let fake_test_data_archive_path = - create_fake_test_data_archive(working_dir, "test-data.tar.gz")?; - - let mut s3_repository = MockS3RepositoryInterface::new(); - s3_repository - .expect_download_object() - .with( - eq("sn-node"), - eq(safe_s3_object_path), - eq(saved_safe_archive_path), - ) - .times(1) - .returning(move |_bucket_name, _object_path, archive_path| { - std::fs::copy(&fake_safe_client_archive_path, archive_path)?; - Ok(()) - }); - - s3_repository - .expect_download_object() - .with( - eq("sn-testnet"), - eq(saved_test_data_archive_file_name), - eq(saved_test_data_archive_path), - ) - .times(1) - .returning(move |_bucket_name, _object_path, archive_path| { - std::fs::copy(&fake_test_data_archive_path, archive_path)?; - Ok(()) - }); - Ok(s3_repository) -} - -pub fn setup_test_data_s3_repository_for_versioned_binary( - working_dir: &ChildPath, -) -> Result { - let saved_test_data_archive_file_name = "test-data.tar.gz"; - let saved_test_data_archive_path = working_dir - .join("test-data") - .join(saved_test_data_archive_file_name); - let fake_test_data_archive_path = - create_fake_test_data_archive(working_dir, "test-data.tar.gz")?; - - let mut s3_repository = MockS3RepositoryInterface::new(); - s3_repository - .expect_download_object() - .with( - eq("sn-testnet"), - eq(saved_test_data_archive_file_name), - eq(saved_test_data_archive_path), - ) - .times(1) - .returning(move |_bucket_name, _object_path, archive_path| { - std::fs::copy(&fake_test_data_archive_path, archive_path)?; - Ok(()) - }); - Ok(s3_repository) -} - -pub fn setup_binary_repo_for_versioned_binary( - version: &str, - working_dir: &ChildPath, -) -> Result { - let saved_safe_archive_file_name = format!("safe-{version}-x86_64-unknown-linux-musl.tar.gz"); - let binary_url = format!("https://github.com/maidsafe/safe_network/releases/download/sn_cli-v{version}/{saved_safe_archive_file_name}"); - let saved_safe_archive_path = working_dir - .to_path_buf() - .join(saved_safe_archive_file_name.clone()); - let fake_safe_client_archive_path = - create_fake_bin_archive(working_dir, "safe_client.tar.gz", "safe")?; - - println!("binary_url = {binary_url}"); - println!("saved_safe_archive_path = {saved_safe_archive_path:#?}"); - - let mut binary_repo = MockSafeBinaryRepositoryInterface::new(); - binary_repo - .expect_download() - .with(eq(binary_url), eq(saved_safe_archive_path)) - .times(1) - .returning(move |_, dest_path| { - std::fs::copy(&fake_safe_client_archive_path, dest_path)?; - Ok(()) - }); - - Ok(binary_repo) -} diff --git a/src/tests/upload_test_data.rs b/src/tests/upload_test_data.rs deleted file mode 100644 index 2e1fc4d8..00000000 --- a/src/tests/upload_test_data.rs +++ /dev/null @@ -1,247 +0,0 @@ -// Copyright (c) 2023, MaidSafe. -// All rights reserved. -// -// This SAFE Network Software is licensed under the BSD-3-Clause license. -// Please see the LICENSE file for more details. - -use super::setup::*; -use crate::{ - manage_test_data::TestDataClient, - safe::{MockSafeBinaryRepositoryInterface, MockSafeClientInterface}, - SnCodebaseType, -}; -use assert_fs::prelude::*; -use color_eyre::Result; -use mockall::predicate::*; -use std::os::unix::fs::PermissionsExt; - -fn setup_default_safe_client() -> MockSafeClientInterface { - let mut safe_client_repository = MockSafeClientInterface::new(); - safe_client_repository - .expect_upload_file() - .with(always(), always()) - .times(3) - .returning(|_, _| Ok("hex address".to_string())); - safe_client_repository -} - -#[tokio::test] -async fn should_download_and_extract_the_custom_branch_safe_binary() -> Result<()> { - let (tmp_dir, working_dir) = setup_working_directory()?; - let downloaded_safe_archive = working_dir.child("safe-alpha-x86_64-unknown-linux-musl.tar.gz"); - let extracted_safe_bin = working_dir.child("safe"); - - let s3_repository = setup_test_data_s3_repository_for_custom_branch( - "alpha", - "jacderida", - "custom_branch", - &working_dir, - )?; - let test_data_client = TestDataClient::new( - working_dir.to_path_buf(), - Box::new(s3_repository), - Box::new(setup_default_safe_client()), - Box::new(MockSafeBinaryRepositoryInterface::new()), - ); - - let sn_codebase_type = SnCodebaseType::Branch { - repo_owner: "jacderida".to_string(), - branch: "custom_branch".to_string(), - safenode_features: None, - }; - test_data_client - .upload_test_data( - "alpha", - "/ip4/10.0.0.1/tcp/43627/p2p/12D3KooWAsY69M1HYAsvwsrF9BkQRywM6CWDvM78m1k92CPco7qr", - &sn_codebase_type, - ) - .await?; - - downloaded_safe_archive.assert(predicates::path::missing()); - extracted_safe_bin.assert(predicates::path::is_file()); - - let metadata = std::fs::metadata(extracted_safe_bin.path())?; - let permissions = metadata.permissions(); - assert!(permissions.mode() & 0o100 > 0, "File is not executable"); - - drop(tmp_dir); - Ok(()) -} -#[tokio::test] -async fn should_download_and_extract_the_versioned_safe_binary() -> Result<()> { - let (tmp_dir, working_dir) = setup_working_directory()?; - let downloaded_safe_archive = working_dir.child("safe-alpha-x86_64-unknown-linux-musl.tar.gz"); - let extracted_safe_bin = working_dir.child("safe"); - - let s3_repository = setup_test_data_s3_repository_for_versioned_binary(&working_dir)?; - let safe_binary_repository = setup_binary_repo_for_versioned_binary("0.82.1", &working_dir)?; - let test_data_client = TestDataClient::new( - working_dir.to_path_buf(), - Box::new(s3_repository), - Box::new(setup_default_safe_client()), - Box::new(safe_binary_repository), - ); - - let sn_codebase_type = SnCodebaseType::Versioned { - safe_version: "0.82.1".to_string(), - safenode_version: "ignored".to_string(), - }; - test_data_client - .upload_test_data( - "alpha", - "/ip4/10.0.0.1/tcp/43627/p2p/12D3KooWAsY69M1HYAsvwsrF9BkQRywM6CWDvM78m1k92CPco7qr", - &sn_codebase_type, - ) - .await?; - - downloaded_safe_archive.assert(predicates::path::missing()); - extracted_safe_bin.assert(predicates::path::is_file()); - - let metadata = std::fs::metadata(extracted_safe_bin.path())?; - let permissions = metadata.permissions(); - assert!(permissions.mode() & 0o100 > 0, "File is not executable"); - - drop(tmp_dir); - Ok(()) -} - -#[tokio::test] -async fn should_download_and_extract_the_test_data() -> Result<()> { - let (tmp_dir, working_dir) = setup_working_directory()?; - let test_data_dir = working_dir.child("test-data"); - test_data_dir.create_dir_all()?; - let downloaded_test_data_archive = test_data_dir.child("test-data.tar.gz"); - let extracted_data_item_1 = test_data_dir.child("pexels-ahmed-ツ-13524648.jpg"); - let extracted_data_item_2 = test_data_dir.child("pexels-ahmed-ツ-14113084.jpg"); - let extracted_data_item_3 = test_data_dir.child("pexels-aidan-roof-11757330.jpg"); - - let s3_repository = - setup_test_data_s3_repository_for_custom_branch("alpha", "maidsafe", "main", &working_dir)?; - let test_data_client = TestDataClient::new( - working_dir.to_path_buf(), - Box::new(s3_repository), - Box::new(setup_default_safe_client()), - Box::new(MockSafeBinaryRepositoryInterface::new()), - ); - - let sn_codebase_type = SnCodebaseType::Branch { - repo_owner: "maidsafe".to_string(), - branch: "main".to_string(), - safenode_features: None, - }; - test_data_client - .upload_test_data( - "alpha", - "/ip4/10.0.0.1/tcp/43627/p2p/12D3KooWAsY69M1HYAsvwsrF9BkQRywM6CWDvM78m1k92CPco7qr", - &sn_codebase_type, - ) - .await?; - - downloaded_test_data_archive.assert(predicates::path::missing()); - extracted_data_item_1.assert(predicates::path::is_file()); - extracted_data_item_2.assert(predicates::path::is_file()); - extracted_data_item_3.assert(predicates::path::is_file()); - - drop(tmp_dir); - Ok(()) -} - -#[tokio::test] -async fn should_upload_test_data_files() -> Result<()> { - let (tmp_dir, working_dir) = setup_working_directory()?; - let test_data_dir = working_dir.child("test-data"); - test_data_dir.create_dir_all()?; - let extracted_data_item_1 = test_data_dir.child("pexels-ahmed-ツ-13524648.jpg"); - let extracted_data_item_2 = test_data_dir.child("pexels-ahmed-ツ-14113084.jpg"); - let extracted_data_item_3 = test_data_dir.child("pexels-aidan-roof-11757330.jpg"); - - let mut safe_client_repository = MockSafeClientInterface::new(); - safe_client_repository - .expect_upload_file() - .with( - eq("/ip4/10.0.0.1/tcp/43627/p2p/12D3KooWAsY69M1HYAsvwsrF9BkQRywM6CWDvM78m1k92CPco7qr"), - eq(extracted_data_item_1.to_path_buf()), - ) - .times(1) - .returning(|_, _| { - Ok("58bc58f3d10f3cb1f9824f970d9d0bee3bbcb039b203ae8c4003caa93fc645aa".to_string()) - }); - safe_client_repository - .expect_upload_file() - .with( - eq("/ip4/10.0.0.1/tcp/43627/p2p/12D3KooWAsY69M1HYAsvwsrF9BkQRywM6CWDvM78m1k92CPco7qr"), - eq(extracted_data_item_2.to_path_buf()), - ) - .times(1) - .returning(|_, _| { - Ok("1d6cd408c8961940aefd06a1e736c45f703a8617d919ee9791122de39d547ca2".to_string()) - }); - safe_client_repository - .expect_upload_file() - .with( - eq("/ip4/10.0.0.1/tcp/43627/p2p/12D3KooWAsY69M1HYAsvwsrF9BkQRywM6CWDvM78m1k92CPco7qr"), - eq(extracted_data_item_3.to_path_buf()), - ) - .times(1) - .returning(|_, _| { - Ok("d27d605b1d4f94934530d3bce0c1c9f8db9bdf74294df3f0139ad22125a54967".to_string()) - }); - - let s3_repository = - setup_test_data_s3_repository_for_custom_branch("alpha", "maidsafe", "main", &working_dir)?; - let test_data_client = TestDataClient::new( - working_dir.to_path_buf(), - Box::new(s3_repository), - Box::new(safe_client_repository), - Box::new(MockSafeBinaryRepositoryInterface::new()), - ); - - let sn_codebase_type = SnCodebaseType::Branch { - repo_owner: "maidsafe".to_string(), - branch: "main".to_string(), - safenode_features: None, - }; - let download_links = test_data_client - .upload_test_data( - "alpha", - "/ip4/10.0.0.1/tcp/43627/p2p/12D3KooWAsY69M1HYAsvwsrF9BkQRywM6CWDvM78m1k92CPco7qr", - &sn_codebase_type, - ) - .await?; - - // Avoid assuming the order of the items in the list; they don't get returned in the same order - // on every machine. - assert_eq!( - download_links - .iter() - .find(|x| x.0 == "pexels-ahmed-ツ-13524648.jpg"), - Some(( - "pexels-ahmed-ツ-13524648.jpg".to_string(), - "58bc58f3d10f3cb1f9824f970d9d0bee3bbcb039b203ae8c4003caa93fc645aa".to_string() - )) - .as_ref() - ); - assert_eq!( - download_links - .iter() - .find(|x| x.0 == "pexels-ahmed-ツ-14113084.jpg"), - Some(( - "pexels-ahmed-ツ-14113084.jpg".to_string(), - "1d6cd408c8961940aefd06a1e736c45f703a8617d919ee9791122de39d547ca2".to_string() - )) - .as_ref() - ); - assert_eq!( - download_links - .iter() - .find(|x| x.0 == "pexels-aidan-roof-11757330.jpg"), - Some(( - "pexels-aidan-roof-11757330.jpg".to_string(), - "d27d605b1d4f94934530d3bce0c1c9f8db9bdf74294df3f0139ad22125a54967".to_string() - )) - .as_ref() - ); - - drop(tmp_dir); - Ok(()) -}