diff --git a/Cargo.lock b/Cargo.lock index 8bcd65beb7..d00f8d60c1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4330,6 +4330,7 @@ dependencies = [ "futures", "git2", "itertools", + "lazy_static", "libc", "libloading", "masp_primitives", diff --git a/apps/Cargo.toml b/apps/Cargo.toml index eb4d1c110a..a367bf945e 100644 --- a/apps/Cargo.toml +++ b/apps/Cargo.toml @@ -97,6 +97,7 @@ file-lock.workspace = true flate2.workspace = true futures.workspace = true itertools.workspace = true +lazy_static.workspace= true libc.workspace = true libloading.workspace = true masp_primitives = { workspace = true, features = ["transparent-inputs"] } @@ -126,6 +127,7 @@ sha2.workspace = true signal-hook.workspace = true sysinfo.workspace = true tar.workspace = true +tempfile.workspace = true tendermint-config.workspace = true thiserror.workspace = true tokio = {workspace = true, features = ["full"]} @@ -149,7 +151,6 @@ namada = {path = "../shared", default-features = false, features = ["testing", " namada_test_utils = {path = "../test_utils"} bit-set.workspace = true proptest.workspace = true -tempfile.workspace = true test-log.workspace = true tokio-test.workspace = true diff --git a/apps/src/lib/cli.rs b/apps/src/lib/cli.rs index 343238a7c5..1129b3391c 100644 --- a/apps/src/lib/cli.rs +++ b/apps/src/lib/cli.rs @@ -7,12 +7,12 @@ //! respectively. pub mod context; -pub mod utils; +pub(super) mod utils; use clap::{ArgGroup, ArgMatches, ColorChoice}; use color_eyre::eyre::Result; -pub use utils::safe_exit; use utils::*; +pub use utils::{dispatch_prompt, safe_exit, TESTIN}; pub use self::context::Context; diff --git a/apps/src/lib/cli/utils.rs b/apps/src/lib/cli/utils.rs index 2a11fe53bc..7bcd9c5bd9 100644 --- a/apps/src/lib/cli/utils.rs +++ b/apps/src/lib/cli/utils.rs @@ -6,6 +6,7 @@ use std::str::FromStr; use clap::{ArgAction, ArgMatches}; use color_eyre::eyre::Result; +use lazy_static::lazy_static; use super::args; use super::context::{Context, FromContext}; @@ -343,25 +344,52 @@ pub fn safe_exit(_: i32) -> ! { panic!("Test failed because the client exited unexpectedly.") } +lazy_static! { + /// A replacement for stdin in testing. + pub static ref TESTIN: std::sync::Arc>> = + std::sync::Arc::new(std::sync::Mutex::new(vec![])); +} + +/// A generic function for displaying a prompt to users and reading +/// in their response. fn prompt_aux(mut reader: R, mut writer: W, question: &str) -> String where R: std::io::Read, W: Write, { write!(&mut writer, "{}", question).expect("Unable to write"); - std::io::stdout().flush().unwrap(); + writer.flush().unwrap(); let mut s = String::new(); reader.read_to_string(&mut s).expect("Unable to read"); s } -#[cfg(feature = "testing")] -pub fn prompt(question: &str) -> String { - let file = std::fs::File::open("stdin.mock").unwrap(); - prompt_aux(file, std::io::stdout(), question) +/// A function that chooses how to dispatch prompts +/// to users. There is a hierarchy of feature flags +/// that determines this. If no flags are set, +/// the question is printed to stdout and response +/// read from stdin. +pub fn dispatch_prompt(question: impl AsRef) -> String { + if cfg!(feature = "testing") { + prompt_aux( + TESTIN.lock().unwrap().as_slice(), + std::io::stdout(), + question.as_ref(), + ) + } else { + prompt_aux( + std::io::stdin().lock(), + std::io::stdout(), + question.as_ref(), + ) + } } -#[cfg(not(feature = "testing"))] -pub fn prompt(question: &str) -> String { - prompt_aux(std::io::stdin().lock(), std::io::stdout(), question) +#[macro_export] +/// A convenience macro for formatting the user prompt before +/// forwarding it to the `[dispatch_prompt]` method. +macro_rules! prompt { + ($($arg:tt)*) => {{ + $crate::cli::dispatch_prompt(format!("{}", format_args!($($arg)*))) + }} } diff --git a/apps/src/lib/client/rpc.rs b/apps/src/lib/client/rpc.rs index de1fd2fed3..b849d21916 100644 --- a/apps/src/lib/client/rpc.rs +++ b/apps/src/lib/client/rpc.rs @@ -50,10 +50,10 @@ use namada::types::token::{Change, Denomination, MaspDenom, TokenAddress}; use namada::types::{storage, token}; use tokio::time::Instant; -use crate::cli::utils::prompt; use crate::cli::{self, args}; use crate::facade::tendermint::merkle::proof::Proof; use crate::facade::tendermint_rpc::error::Error as TError; +use crate::prompt; use crate::wallet::CliWalletUtils; /// Query the status of a given transaction. @@ -440,8 +440,7 @@ pub async fn query_pinned_balance< } // If a suitable viewing key was not found, then demand it from the user if balance == pinned_error { - let vk_str = - prompt(&format!("Enter the viewing key for {}: ", owner)); + let vk_str = prompt!("Enter the viewing key for {}: ", owner); let fvk = match ExtendedViewingKey::from_str(vk_str.trim()) { Ok(fvk) => fvk, _ => { diff --git a/apps/src/lib/client/tx.rs b/apps/src/lib/client/tx.rs index a68738c479..e2683babba 100644 --- a/apps/src/lib/client/tx.rs +++ b/apps/src/lib/client/tx.rs @@ -10,6 +10,7 @@ use borsh::{BorshDeserialize, BorshSerialize}; use data_encoding::HEXLOWER_PERMISSIVE; use masp_proofs::prover::LocalTxProver; use namada::ledger::governance::storage as gov_storage; +use namada::ledger::queries::Client; use namada::ledger::rpc::{TxBroadcastData, TxResponse}; use namada::ledger::signing::TxSigningKey; use namada::ledger::wallet::{Wallet, WalletUtils}; @@ -35,7 +36,6 @@ use crate::client::signing::find_pk; use crate::client::tx::tx::ProcessTxResponse; use crate::config::TendermintMode; use crate::facade::tendermint_rpc::endpoint::broadcast::tx_sync::Response; -use crate::facade::tendermint_rpc::HttpClient; use crate::node::ledger::tendermint_node; use crate::wallet::{gen_validator_keys, read_and_confirm_encryption_password}; @@ -523,8 +523,8 @@ impl masp::ShieldedUtils for CLIShieldedUtils { } } -pub async fn submit_transfer( - client: &HttpClient, +pub async fn submit_transfer( + client: &C, mut ctx: Context, args: args::TxTransfer, ) -> Result<(), tx::Error> { diff --git a/apps/src/lib/client/utils.rs b/apps/src/lib/client/utils.rs index 272d74b6eb..6f20fe165a 100644 --- a/apps/src/lib/client/utils.rs +++ b/apps/src/lib/client/utils.rs @@ -47,41 +47,6 @@ const DEFAULT_NETWORK_CONFIGS_SERVER: &str = /// We do pre-genesis validator set up in this directory pub const PRE_GENESIS_DIR: &str = "pre-genesis"; -/// Environment variable set to reduce the amount of printing the CLI -/// tools perform. Extra prints, while good for UI, clog up test tooling. -pub const REDUCED_CLI_PRINTING: &str = "REDUCED_CLI_PRINTING"; - -macro_rules! cli_print { - ($($arg:tt)*) => {{ - if std::env::var(REDUCED_CLI_PRINTING) - .map(|v| if v.to_lowercase().trim() == "true" { - false - } else { - true - }).unwrap_or(true) { - let mut stdout = std::io::stdout().lock(); - _ = stdout.write_all(format!("{}", std::format_args!($($arg)*)).as_bytes()); - _ = stdout.flush(); - } - }}; -} - -#[allow(unused)] -macro_rules! cli_println { - ($($arg:tt)*) => {{ - if std::env::var(REDUCED_CLI_PRINTING) - .map(|v| if v.to_lowercase().trim() == "true" { - false - } else { - true - }).unwrap_or(true) { - let mut stdout = std::io::stdout().lock(); - _ = stdout.write_all(format!("{}\n", std::format_args!($($arg)*)).as_bytes()); - _ = stdout.flush(); - } - }}; -} - /// Configure Namada to join an existing network. The chain must be released in /// the repository. pub async fn join_network( @@ -1204,9 +1169,9 @@ where print!("{}", msg); _ = std::io::stdout().flush(); for c in spinny_wheel.chars().cycle() { - cli_print!("{}", c); + print!("{}", c); std::thread::sleep(std::time::Duration::from_secs(1)); - cli_print!("{}", (8u8 as char)); + print!("{}", (8u8 as char)); if task.is_finished() { break; } diff --git a/apps/src/lib/node/ledger/shell/mod.rs b/apps/src/lib/node/ledger/shell/mod.rs index 9a41d481bb..de5665bce9 100644 --- a/apps/src/lib/node/ledger/shell/mod.rs +++ b/apps/src/lib/node/ledger/shell/mod.rs @@ -13,6 +13,9 @@ mod prepare_proposal; mod process_proposal; pub(super) mod queries; mod stats; +#[cfg(any(test, feature = "testing"))] +#[allow(dead_code)] +pub mod testing; mod vote_extensions; use std::collections::{BTreeSet, HashSet}; @@ -368,9 +371,9 @@ where { /// The id of the current chain #[allow(dead_code)] - pub chain_id: ChainId, + chain_id: ChainId, /// The persistent storage with write log - pub wl_storage: WlStorage, + pub(super) wl_storage: WlStorage, /// Gas meter for the current block gas_meter: BlockGasMeter, /// Byzantine validators given from ABCI++ `prepare_proposal` are stored in @@ -378,16 +381,16 @@ where byzantine_validators: Vec, /// Path to the base directory with DB data and configs #[allow(dead_code)] - pub base_dir: PathBuf, + base_dir: PathBuf, /// Path to the WASM directory for files used in the genesis block. - pub wasm_dir: PathBuf, + pub(super) wasm_dir: PathBuf, /// Information about the running shell instance #[allow(dead_code)] mode: ShellMode, /// VP WASM compilation cache - pub vp_wasm_cache: VpCache, + pub(super) vp_wasm_cache: VpCache, /// Tx WASM compilation cache - pub tx_wasm_cache: TxCache, + pub(super) tx_wasm_cache: TxCache, /// Taken from config `storage_read_past_height_limit`. When set, will /// limit the how many block heights in the past can the storage be /// queried for reading values. @@ -1395,7 +1398,7 @@ where /// Helper functions and types for writing unit tests /// for the shell #[cfg(test)] -pub mod test_utils { +mod test_utils { use std::ops::{Deref, DerefMut}; use std::path::PathBuf; diff --git a/apps/src/lib/node/ledger/shell/prepare_proposal.rs b/apps/src/lib/node/ledger/shell/prepare_proposal.rs index f132f39e17..3fc5cd8983 100644 --- a/apps/src/lib/node/ledger/shell/prepare_proposal.rs +++ b/apps/src/lib/node/ledger/shell/prepare_proposal.rs @@ -100,7 +100,7 @@ where /// Otherwise, we return an allocator wrapped in an /// [`EncryptedTxBatchAllocator::WithEncryptedTxs`] value. #[inline] - pub fn get_encrypted_txs_allocator(&self) -> EncryptedTxBatchAllocator { + fn get_encrypted_txs_allocator(&self) -> EncryptedTxBatchAllocator { let pos_queries = self.wl_storage.pos_queries(); let is_2nd_height_off = pos_queries.is_deciding_offset_within_epoch(1); diff --git a/apps/src/lib/node/ledger/shell/process_proposal.rs b/apps/src/lib/node/ledger/shell/process_proposal.rs index bf4905fc0b..ce4f71a6f5 100644 --- a/apps/src/lib/node/ledger/shell/process_proposal.rs +++ b/apps/src/lib/node/ledger/shell/process_proposal.rs @@ -934,7 +934,7 @@ where /// Checks if it is not possible to include encrypted txs at the current /// block height. - pub fn encrypted_txs_not_allowed(&self) -> bool { + pub(super) fn encrypted_txs_not_allowed(&self) -> bool { let pos_queries = self.wl_storage.pos_queries(); let is_2nd_height_off = pos_queries.is_deciding_offset_within_epoch(1); let is_3rd_height_off = pos_queries.is_deciding_offset_within_epoch(2); diff --git a/tests/src/integration/client.rs b/apps/src/lib/node/ledger/shell/testing/client.rs similarity index 89% rename from tests/src/integration/client.rs rename to apps/src/lib/node/ledger/shell/testing/client.rs index 0dc997e668..d06a9db4be 100644 --- a/tests/src/integration/client.rs +++ b/apps/src/lib/node/ledger/shell/testing/client.rs @@ -1,5 +1,3 @@ -use std::fs::File; -use std::path::PathBuf; use std::sync::Arc; use clap::Command as App; @@ -9,25 +7,25 @@ use namada::ledger::eth_bridge::{bridge_pool, validator_set}; use namada::ledger::signing; use namada::ledger::tx::ProcessTxResponse; use namada::types::control_flow::ProceedOrElse; -use namada_apps::cli; -use namada_apps::cli::args::{CliToSdk, CliToSdkCtxless, Global}; -use namada_apps::cli::cmds::{ + +use super::node::MockNode; +use crate::cli; +use crate::cli::args::{CliToSdk, CliToSdkCtxless, Global}; +use crate::cli::cmds::{ Namada, NamadaClient, NamadaClientWithContext, NamadaRelayer, }; -use namada_apps::cli::utils::Cmd; -use namada_apps::cli::{args, cmds, Context}; -use namada_apps::client::tx::submit_reveal_aux; -use namada_apps::client::{rpc, tx}; -use namada_apps::wallet::cli_utils::{ +use crate::cli::utils::Cmd; +use crate::cli::{args, cmds, Context}; +use crate::client::tx::submit_reveal_aux; +use crate::client::{rpc, tx}; +use crate::node::ledger::shell::testing::utils::Bin; +use crate::wallet::cli_utils::{ address_add, address_key_add, address_key_find, address_list, address_or_alias_find, key_and_address_gen, key_and_address_restore, key_export, key_find, key_list, payment_address_gen, payment_addresses_list, spending_key_gen, spending_keys_list, }; -use crate::e2e::setup::Bin; -use crate::integration::node::MockNode; - pub fn run( node: &MockNode, who: Bin, @@ -115,7 +113,7 @@ impl MockNode { tx::submit_custom::(self, &mut ctx, args) .await?; if !dry_run { - namada_apps::wallet::save(&ctx.wallet) + crate::wallet::save(&ctx.wallet) .unwrap_or_else(|err| eprintln!("{}", err)); } else { println!( @@ -126,7 +124,7 @@ impl MockNode { } NamadaClientWithContext::TxTransfer(args) => { let args = args.0.to_sdk(&mut ctx); - submit_transfer(self, &mut ctx, args).await?; + tx::submit_transfer(self, ctx, args).await?; } NamadaClientWithContext::TxIbcTransfer(args) => { let args = args.0.to_sdk(&mut ctx); @@ -150,7 +148,7 @@ impl MockNode { ) .await?; if !dry_run { - namada_apps::wallet::save(&ctx.wallet) + crate::wallet::save(&ctx.wallet) .unwrap_or_else(|err| eprintln!("{}", err)); } else { println!( @@ -405,7 +403,7 @@ impl MockNode { let dry_run = args.tx.dry_run; tx::submit_custom::(self, &mut ctx, args).await?; if !dry_run { - namada_apps::wallet::save(&ctx.wallet) + crate::wallet::save(&ctx.wallet) .unwrap_or_else(|err| eprintln!("{}", err)); } else { println!( @@ -544,82 +542,6 @@ impl MockNode { } } -struct TempFile(PathBuf); -impl TempFile { - fn new(path: PathBuf) -> (Self, File) { - let f = File::create(&path).unwrap(); - (Self(path), f) - } -} - -impl Drop for TempFile { - fn drop(&mut self) { - _ = std::fs::remove_file(&self.0); - } -} - -/// Test helper that captures stdout of -/// a process. -pub struct CapturedOutput { - pub output: String, - pub result: T, - input: String, -} - -impl CapturedOutput { - pub fn with_input(input: String) -> Self { - Self { - output: "".to_string(), - result: (), - input, - } - } -} - -impl CapturedOutput { - pub(crate) fn of(func: F) -> Self - where - F: FnOnce() -> T, - { - std::io::set_output_capture(Some(Default::default())); - let mut capture = Self { - output: Default::default(), - result: func(), - input: Default::default(), - }; - let captured = std::io::set_output_capture(None); - let captured = captured.unwrap(); - let captured = Arc::try_unwrap(captured).unwrap(); - let captured = captured.into_inner().unwrap(); - capture.output = String::from_utf8(captured).unwrap(); - capture - } - - pub fn run(&self, func: F) -> CapturedOutput - where - F: FnOnce() -> U, - { - use std::io::Write; - let _temp = { - let (temp, mut f) = TempFile::new(PathBuf::from("stdin.mock")); - write!(&mut f, "{}", self.input).unwrap(); - temp - }; - CapturedOutput::of(func) - } - - /// Check if the captured output contains the regex. - pub fn matches(&self, needle: regex::Regex) -> bool { - needle.captures(&self.output).is_some() - } - - /// Check if the captured output contains the string. - pub fn contains(&self, needle: &str) -> bool { - let needle = regex::Regex::new(needle).unwrap(); - self.matches(needle) - } -} - async fn submit_transfer( client: &MockNode, ctx: &mut Context, @@ -650,14 +572,14 @@ async fn submit_transfer( resp.code == 1.to_string() && // And the its submission epoch doesn't match construction epoch tx_epoch.unwrap() != submission_epoch => - { - // Then we probably straddled an epoch boundary - println!( - "MASP transaction rejected and this may be due to the \ + { + // Then we probably straddled an epoch boundary + println!( + "MASP transaction rejected and this may be due to the \ epoch changing. Attempting to resubmit transaction.", - ); + ); - } + } _ => {} } Ok(()) diff --git a/apps/src/lib/node/ledger/shell/testing/mod.rs b/apps/src/lib/node/ledger/shell/testing/mod.rs new file mode 100644 index 0000000000..fff1df00ba --- /dev/null +++ b/apps/src/lib/node/ledger/shell/testing/mod.rs @@ -0,0 +1,3 @@ +pub mod client; +pub mod node; +pub mod utils; diff --git a/tests/src/integration/node.rs b/apps/src/lib/node/ledger/shell/testing/node.rs similarity index 94% rename from tests/src/integration/node.rs rename to apps/src/lib/node/ledger/shell/testing/node.rs index 5d60115997..7ffef50553 100644 --- a/tests/src/integration/node.rs +++ b/apps/src/lib/node/ledger/shell/testing/node.rs @@ -9,30 +9,30 @@ use namada::ledger::events::log::dumb_queries; use namada::ledger::queries::{ Client, EncodedResponseQuery, RequestCtx, RequestQuery, Router, RPC, }; -use namada::tendermint_rpc::endpoint::abci_info; -use namada::tendermint_rpc::SimpleRequest; -use namada_apps::facade::tendermint_proto::abci::response_process_proposal::ProposalStatus; -use namada_apps::facade::tendermint_proto::abci::RequestProcessProposal; -use namada_apps::facade::tendermint_rpc::endpoint::abci_info::AbciInfo; -use namada_apps::facade::tendermint_rpc::error::Error as RpcError; -use namada_apps::facade::{tendermint, tendermint_rpc}; -use namada_apps::node::ledger::shell::{ErrorCodes, Shell}; -use namada_apps::node::ledger::shims::abcipp_shim_types::shim::request::{ - FinalizeBlock, ProcessedTx, -}; -use namada_apps::node::ledger::shims::abcipp_shim_types::shim::response::TxResult; -use namada_apps::node::ledger::storage; -use namada_core::ledger::storage::{ +use namada::ledger::storage::{ LastBlock, Sha256Hasher, EPOCH_SWITCH_BLOCKS_DELAY, }; -use namada_core::types::hash::Hash; -use namada_core::types::storage::{BlockHash, BlockHeight, Epoch, Header}; -use namada_core::types::time::DateTimeUtc; +use namada::tendermint_rpc::endpoint::abci_info; +use namada::tendermint_rpc::SimpleRequest; +use namada::types::hash::Hash; +use namada::types::storage::{BlockHash, BlockHeight, Epoch, Header}; +use namada::types::time::DateTimeUtc; use num_traits::cast::FromPrimitive; use regex::Regex; use tokio::sync::mpsc::UnboundedReceiver; -use crate::e2e::setup::TestDir; +use crate::facade::tendermint_proto::abci::response_process_proposal::ProposalStatus; +use crate::facade::tendermint_proto::abci::RequestProcessProposal; +use crate::facade::tendermint_rpc::endpoint::abci_info::AbciInfo; +use crate::facade::tendermint_rpc::error::Error as RpcError; +use crate::facade::{tendermint, tendermint_rpc}; +use crate::node::ledger::shell::testing::utils::TestDir; +use crate::node::ledger::shell::{ErrorCodes, Shell}; +use crate::node::ledger::shims::abcipp_shim_types::shim::request::{ + FinalizeBlock, ProcessedTx, +}; +use crate::node::ledger::shims::abcipp_shim_types::shim::response::TxResult; +use crate::node::ledger::storage; /// Status of tx #[derive(Debug, Clone, PartialEq, Eq)] @@ -59,6 +59,10 @@ impl Drop for MockNode { if !self.keep_temp { ManuallyDrop::take(&mut self.test_dir).clean_up() } else { + println!( + "Keeping tempfile at {}", + self.test_dir.path().to_string_lossy() + ); ManuallyDrop::drop(&mut self.test_dir) } } @@ -68,12 +72,12 @@ impl Drop for MockNode { impl MockNode { pub fn genesis_dir(&self) -> PathBuf { self.test_dir - .as_ref() + .path() .join(self.shell.lock().unwrap().chain_id.to_string()) } pub fn genesis_path(&self) -> PathBuf { - self.test_dir.as_ref().join(format!( + self.test_dir.path().join(format!( "{}.toml", self.shell.lock().unwrap().chain_id.to_string() )) diff --git a/apps/src/lib/node/ledger/shell/testing/utils.rs b/apps/src/lib/node/ledger/shell/testing/utils.rs new file mode 100644 index 0000000000..e66ead21e7 --- /dev/null +++ b/apps/src/lib/node/ledger/shell/testing/utils.rs @@ -0,0 +1,48 @@ +use std::path::{Path, PathBuf}; + +use tempfile::tempdir; + +/// Namada binaries +#[derive(Debug)] +#[allow(dead_code)] +pub enum Bin { + Node, + Client, + Wallet, + Relayer, +} + +/// A temporary directory for testing +#[derive(Debug)] +pub struct TestDir(PathBuf); + +impl TestDir { + /// Creat a new temp directory. This will have to be manually + /// cleaned up. + pub fn new() -> Self { + let temp = tempdir().unwrap(); + Self(temp.into_path()) + } + + /// Get the path of the directory + pub fn path(&self) -> &Path { + &self.0 + } + + /// Manually remove the test directory from the + /// file system. + pub fn clean_up(self) { + if let Err(e) = std::fs::remove_dir_all(&self.0) { + println!( + "Failed to clean up test dir at {}: {e:?}", + self.0.to_string_lossy() + ); + } + } +} + +impl Default for TestDir { + fn default() -> Self { + Self::new() + } +} diff --git a/core/src/types/token.rs b/core/src/types/token.rs index efa3bfe361..b7c731e0e1 100644 --- a/core/src/types/token.rs +++ b/core/src/types/token.rs @@ -173,9 +173,7 @@ impl Amount { .checked_pow(Uint::from(denom)) .and_then(|scaling| scaling.checked_mul(uint.into())) { - Some(amount) => { - Ok(Self { raw: amount }) - } + Some(amount) => Ok(Self { raw: amount }), None => Err(AmountParseError::ConvertToDecimal), } } diff --git a/shared/src/ledger/queries/mod.rs b/shared/src/ledger/queries/mod.rs index 935b4253a4..ce689e6325 100644 --- a/shared/src/ledger/queries/mod.rs +++ b/shared/src/ledger/queries/mod.rs @@ -18,7 +18,7 @@ use super::storage_api; use crate::types::storage::BlockHeight; #[macro_use] -pub mod router; +mod router; mod shell; mod types; pub mod vp; @@ -93,7 +93,7 @@ pub fn require_no_data(request: &RequestQuery) -> storage_api::Result<()> { /// Queries testing helpers #[cfg(any(test, feature = "testing"))] -pub mod testing { +mod testing { use tempfile::TempDir; use tendermint_rpc::Response; diff --git a/tests/src/e2e/setup.rs b/tests/src/e2e/setup.rs index 05092e5002..7447cc2891 100644 --- a/tests/src/e2e/setup.rs +++ b/tests/src/e2e/setup.rs @@ -21,7 +21,6 @@ use eyre::{eyre, Context}; use itertools::{Either, Itertools}; use namada::types::chain::ChainId; use namada_apps::client::utils; -use namada_apps::client::utils::REDUCED_CLI_PRINTING; use namada_apps::config::genesis::genesis_config::{self, GenesisConfig}; use namada_apps::config::{ethereum_bridge, Config}; use namada_apps::{config, wallet}; @@ -155,7 +154,6 @@ pub fn network( eprintln!("Failed setting up colorful error reports {}", err); } }); - env::set_var(REDUCED_CLI_PRINTING, "true"); let working_dir = working_dir(); let test_dir = TestDir::new(); @@ -317,19 +315,6 @@ impl TestDir { pub fn path(&self) -> &Path { self.as_ref() } - - /// Manually remove the test directory from the - /// file system. - pub fn clean_up(self) { - if let Either::Right(path) = self.0 { - if let Err(e) = std::fs::remove_dir_all(&path) { - println!( - "Failed to clean up test dir at {}: {e:?}", - path.to_string_lossy() - ); - } - } - } } impl Drop for Test { diff --git a/tests/src/integration.rs b/tests/src/integration.rs index 38f750d7d8..8642e0e03c 100644 --- a/tests/src/integration.rs +++ b/tests/src/integration.rs @@ -1,4 +1,3 @@ -mod client; mod masp; -mod node; mod setup; +mod utils; diff --git a/tests/src/integration/masp.rs b/tests/src/integration/masp.rs index cf5b81e9d4..c48377896e 100644 --- a/tests/src/integration/masp.rs +++ b/tests/src/integration/masp.rs @@ -3,20 +3,20 @@ use std::path::PathBuf; use color_eyre::eyre::Result; use color_eyre::owo_colors::OwoColorize; use namada_apps::client::tx::CLIShieldedUtils; +use namada_apps::node::ledger::shell::testing::client::run; +use namada_apps::node::ledger::shell::testing::utils::Bin; use namada_core::types::address::{btc, eth, masp_rewards}; use namada_core::types::token; use namada_core::types::token::{DenominatedAmount, NATIVE_MAX_DECIMAL_PLACES}; use test_log::test; -use super::client::run; use super::setup; use crate::e2e::setup::constants::{ AA_PAYMENT_ADDRESS, AA_VIEWING_KEY, AB_PAYMENT_ADDRESS, AB_VIEWING_KEY, AC_PAYMENT_ADDRESS, AC_VIEWING_KEY, ALBERT, A_SPENDING_KEY, BB_PAYMENT_ADDRESS, BERTHA, BTC, B_SPENDING_KEY, CHRISTEL, ETH, MASP, NAM, }; -use crate::e2e::setup::Bin; -use crate::integration::client::CapturedOutput; +use crate::integration::utils::CapturedOutput; /// In this test we verify that users of the MASP receive the correct rewards /// for leaving their assets in the pool for varying periods of time. diff --git a/tests/src/integration/setup.rs b/tests/src/integration/setup.rs index 86e987c54d..df74c5f6f1 100644 --- a/tests/src/integration/setup.rs +++ b/tests/src/integration/setup.rs @@ -11,17 +11,20 @@ use namada_apps::config::genesis::genesis_config::GenesisConfig; use namada_apps::config::TendermintMode; use namada_apps::facade::tendermint::Timeout; use namada_apps::facade::tendermint_proto::google::protobuf::Timestamp; +use namada_apps::node::ledger::shell::testing::node::MockNode; +use namada_apps::node::ledger::shell::testing::utils::TestDir; use namada_apps::node::ledger::shell::Shell; use namada_core::types::address::Address; use namada_core::types::chain::{ChainId, ChainIdPrefix}; use toml::value::Table; -use super::node::MockNode; use crate::e2e::setup::{ - copy_wasm_to_chain_dir, get_all_wasms_hashes, TestDir, ENV_VAR_KEEP_TEMP, - SINGLE_NODE_NET_GENESIS, + copy_wasm_to_chain_dir, get_all_wasms_hashes, SINGLE_NODE_NET_GENESIS, }; +/// Env. var for keeping temporary files created by the integration tests +const ENV_VAR_KEEP_TEMP: &str = "NAMADA_INT_KEEP_TEMP"; + /// Setup a network with a single genesis validator node. pub fn setup() -> Result { initialize_genesis(|genesis| genesis) @@ -36,14 +39,7 @@ pub fn initialize_genesis( Ok(val) => val.to_ascii_lowercase() != "false", _ => false, }; - let test_dir = { - std::env::set_var(ENV_VAR_KEEP_TEMP, "true"); - let dir = TestDir::new(); - if !keep_temp { - std::env::remove_var(ENV_VAR_KEEP_TEMP) - } - dir - }; + let test_dir = TestDir::new(); // Open the source genesis file let mut genesis = genesis_config::open_genesis_config( diff --git a/tests/src/integration/utils.rs b/tests/src/integration/utils.rs new file mode 100644 index 0000000000..f626a001ee --- /dev/null +++ b/tests/src/integration/utils.rs @@ -0,0 +1,83 @@ +use std::fs::File; +use std::path::PathBuf; +use std::sync::Arc; + +struct TempFile(PathBuf); +impl TempFile { + fn new(path: PathBuf) -> (Self, File) { + let f = File::create(&path).unwrap(); + (Self(path), f) + } +} + +impl Drop for TempFile { + fn drop(&mut self) { + _ = std::fs::remove_file(&self.0); + } +} + +/// Test helper that captures stdout of +/// a process. +pub struct CapturedOutput { + pub output: String, + pub result: T, + input: String, +} + +impl CapturedOutput { + pub fn with_input(input: String) -> Self { + Self { + output: "".to_string(), + result: (), + input, + } + } +} + +impl CapturedOutput { + /// Run a client command and capture + /// the output to the mocked stdout. + pub(crate) fn of(func: F) -> Self + where + F: FnOnce() -> T, + { + std::io::set_output_capture(Some(Default::default())); + let mut capture = Self { + output: Default::default(), + result: func(), + input: Default::default(), + }; + let captured = std::io::set_output_capture(None); + let captured = captured.unwrap(); + let captured = Arc::try_unwrap(captured).unwrap(); + let captured = captured.into_inner().unwrap(); + capture.output = String::from_utf8(captured).unwrap(); + capture + } + + /// Run a client command with input to the mocked stdin and capture + /// the output to the mocked stdout + pub fn run(&self, func: F) -> CapturedOutput + where + F: FnOnce() -> U, + { + { + // write the input to the mocked stdin + let mut buf = namada_apps::cli::TESTIN.lock().unwrap(); + buf.clear(); + buf.extend_from_slice(self.input.as_bytes()); + } + CapturedOutput::of(func) + } + + /// Check if the captured output contains the regex. + pub fn matches(&self, needle: regex::Regex) -> bool { + needle.captures(&self.output).is_some() + } + + /// Check if the captured output contains the string. + pub fn contains(&self, needle: &str) -> bool { + let needle = regex::Regex::new(needle).unwrap(); + self.matches(needle) + } +} diff --git a/wasm/checksums.json b/wasm/checksums.json index 3503fc0ab1..67d1547d8d 100644 --- a/wasm/checksums.json +++ b/wasm/checksums.json @@ -1,21 +1,21 @@ { - "tx_bond.wasm": "tx_bond.73c37f7a398802677b8032e340e0087b79435160e9f6589238e2f22112f52154.wasm", - "tx_bridge_pool.wasm": "tx_bridge_pool.b6749252f36b3d0a98c662f6d726bf73f51319fab085586653029998acc5b681.wasm", + "tx_bond.wasm": "tx_bond.e1528eb25894ef3c949039098f03c967f3ac54fe85b1ceaf7188ae8530f82f09.wasm", + "tx_bridge_pool.wasm": "tx_bridge_pool.7e5ef98663de40a9d57b3a41237fbd990e45fcab69963964c68691e33419984a.wasm", "tx_change_validator_commission.wasm": "tx_change_validator_commission.9678d081b00754a308ff2933e82ba12eb435c253255634c2f66eef0b5ed7664f.wasm", - "tx_ibc.wasm": "tx_ibc.b34c623b497c24f1aa762760cd577293c93908b1c9a2fc5f369a4278fef6caa4.wasm", + "tx_ibc.wasm": "tx_ibc.03f5035cdaebf9791ffdc1c6680fb49e85905fded1cb6af9c6346971fff3d18d.wasm", "tx_init_account.wasm": "tx_init_account.4f459874b97ba38e1896154b5a21aede2a903e423bb9791d7c484e5d41bbf645.wasm", - "tx_init_proposal.wasm": "tx_init_proposal.30fd2ecdf47601d9bd753f923e1d5ee6284293ad54ddbc01d93b5d108c5ee71b.wasm", + "tx_init_proposal.wasm": "tx_init_proposal.7d853deb8f46cec9994d91a23cf54f59e087362d01bdedc1665dbd9edaeb2875.wasm", "tx_init_validator.wasm": "tx_init_validator.374cc193df2d8f52709d75d8b6a3ee0c899e167e94213600edaf49405f74710c.wasm", "tx_reveal_pk.wasm": "tx_reveal_pk.e2223fd749dd49887a10cf48deb99b67caf65259d78b697ca9627a59d9932aab.wasm", - "tx_transfer.wasm": "tx_transfer.d13ec7dd2cc4c0270eba21a6111f42b51f2bd0aa30f16aa01e0a29f1f3a46e13.wasm", + "tx_transfer.wasm": "tx_transfer.77b50bd69208af0b2481f709411f8531474902bd1a16d3976ca72878662015c2.wasm", "tx_unbond.wasm": "tx_unbond.026172d96c52b6c1d24d97e0c327415cd4bd20120efc565f94a3401594596fce.wasm", "tx_unjail_validator.wasm": "tx_unjail_validator.47b4cb5e76967bb1f1264d4e682ac7aa15ab5a59d36335bdd1ec8862e06f435e.wasm", "tx_update_vp.wasm": "tx_update_vp.4b677b8fd176a1d73b2b7754b4e0c5de1d835f3205d0b9200089c6245c5261ef.wasm", "tx_vote_proposal.wasm": "tx_vote_proposal.3db37f2f2c4489f0903cb3d82455fffc2a21fc6c3d802483f6904686c8c9d0bc.wasm", - "tx_withdraw.wasm": "tx_withdraw.1bc5ac31b098d31c686cc08ec586c397d6883469856f6941d3c83f3fad91cd44.wasm", + "tx_withdraw.wasm": "tx_withdraw.25b0da34e26726dbf9d6a34fa5995d916433cef6b1dc8de61649352b7a21011b.wasm", "vp_implicit.wasm": "vp_implicit.d3d7ec77e15aaacbffaa68208b6660e5b98412c3738d346b5bbd43630896827f.wasm", - "vp_masp.wasm": "vp_masp.a7bd36ca89fb526f3de19b7007cbd8c0b3ae5ad29eef05f71a9fdd2ac3005d91.wasm", - "vp_testnet_faucet.wasm": "vp_testnet_faucet.befeb34ef989d052be0ce05d2d2155a265f5868f9b8e76adaaf265a7fdf1dacd.wasm", + "vp_masp.wasm": "vp_masp.57bd2fbfd79eb4c1ff0b2380db013d99463bfec8cb998153e168611b1a53eb14.wasm", + "vp_testnet_faucet.wasm": "vp_testnet_faucet.7dd6f780b372e0f0fead49243ffc0e2ae76f28962cf471e6d5ef20b1bbce78f7.wasm", "vp_token.wasm": "vp_token.9aa48172e5432e32f8a99263ffdbcfee301175e59f9ad827ec8e54560a779d45.wasm", "vp_user.wasm": "vp_user.810fb7c65cd18985f91bd87caa28e9ad93df5d442816e08b7c2f4d01bef6246e.wasm", "vp_validator.wasm": "vp_validator.8e9855ecf63d2c27b4e2860fd2f828e4bda8a4ab798bf8eb3edb224a5a65ae02.wasm"