From a5845af331f8ca9cb036c47d5fba275919304f24 Mon Sep 17 00:00:00 2001 From: Cyle Witruk Date: Thu, 3 Oct 2024 08:07:22 +0200 Subject: [PATCH 1/3] test context improvements --- signer/src/api/new_block.rs | 25 +- signer/src/context/mod.rs | 14 +- signer/src/network/libp2p/swarm.rs | 10 +- signer/src/network/mod.rs | 19 +- signer/src/stacks/api.rs | 2 + signer/src/storage/postgres.rs | 2 +- signer/src/testing/api_clients.rs | 140 ------ signer/src/testing/context.rs | 428 ++++++++++++++++-- signer/src/testing/mod.rs | 24 - signer/src/testing/transaction_coordinator.rs | 16 +- signer/src/testing/wallet.rs | 2 +- signer/src/transaction_coordinator.rs | 14 +- signer/tests/integration/complete_deposit.rs | 70 ++- signer/tests/integration/postgres.rs | 20 +- signer/tests/integration/withdrawal_accept.rs | 86 +++- 15 files changed, 615 insertions(+), 257 deletions(-) diff --git a/signer/src/api/new_block.rs b/signer/src/api/new_block.rs index 639e2a76..b495ebce 100644 --- a/signer/src/api/new_block.rs +++ b/signer/src/api/new_block.rs @@ -128,10 +128,9 @@ mod tests { use rand::rngs::OsRng; use test_case::test_case; - use crate::config::Settings; use crate::storage::in_memory::Store; use crate::storage::model::StacksPrincipal; - use crate::testing::NoopSignerContext; + use crate::testing::context::*; /// These were generated from a stacks node after running the /// "complete-deposit standard recipient", "accept-withdrawal", @@ -159,13 +158,16 @@ mod tests { where F: Fn(tokio::sync::MutexGuard<'_, Store>) -> bool, { - let db = Store::new_shared(); - - let ctx = NoopSignerContext::init(Settings::new_from_default_config().unwrap(), db.clone()) - .expect("failed to init context"); + let ctx = TestContext::builder() + .with_in_memory_storage() + .with_mocked_bitcoin_client() + .with_mocked_stacks_client() + .build(); let api = ApiState { ctx: ctx.clone() }; + let db = ctx.inner_storage(); + // Hey look, there is nothing here! assert!(table_is_empty(db.lock().await)); @@ -188,13 +190,16 @@ mod tests { where F: Fn(tokio::sync::MutexGuard<'_, Store>) -> bool, { - let db = Store::new_shared(); - - let ctx = NoopSignerContext::init(Settings::new_from_default_config().unwrap(), db.clone()) - .expect("failed to init context"); + let ctx = TestContext::builder() + .with_in_memory_storage() + .with_mocked_bitcoin_client() + .with_mocked_stacks_client() + .build(); let api = ApiState { ctx: ctx.clone() }; + let db = ctx.inner_storage(); + // Hey look, there is nothing here! assert!(table_is_empty(db.lock().await)); diff --git a/signer/src/context/mod.rs b/signer/src/context/mod.rs index 546f0255..8338d0ef 100644 --- a/signer/src/context/mod.rs +++ b/signer/src/context/mod.rs @@ -172,10 +172,8 @@ mod tests { use tokio::sync::Notify; use crate::{ - config::Settings, context::{Context as _, SignerEvent, SignerSignal}, - storage::in_memory::Store, - testing::NoopSignerContext, + testing::context::*, }; /// This test shows that cloning a context and signalling on the original @@ -185,11 +183,11 @@ mod tests { #[tokio::test] async fn context_clone_signalling_works() { // Create a context. - let context = NoopSignerContext::init( - Settings::new_from_default_config().unwrap(), - Store::new_shared(), - ) - .unwrap(); + let context = TestContext::builder() + .with_in_memory_storage() + .with_mocked_bitcoin_client() + .with_mocked_stacks_client() + .build(); // Clone the context. let context_clone = context.clone(); diff --git a/signer/src/network/libp2p/swarm.rs b/signer/src/network/libp2p/swarm.rs index e2fcc6e1..1614228f 100644 --- a/signer/src/network/libp2p/swarm.rs +++ b/signer/src/network/libp2p/swarm.rs @@ -218,7 +218,7 @@ impl SignerSwarm { #[cfg(test)] mod tests { - use crate::{config::Settings, storage::in_memory::Store, testing::NoopSignerContext}; + use crate::testing::context::*; use super::*; @@ -246,8 +246,12 @@ mod tests { let builder = SignerSwarmBuilder::new(&private_key); let mut swarm = builder.build().unwrap(); - let settings = Settings::new_from_default_config().unwrap(); - let ctx = NoopSignerContext::init(settings, Store::new_shared()).unwrap(); + let ctx = TestContext::builder() + .with_in_memory_storage() + .with_mocked_bitcoin_client() + .with_mocked_stacks_client() + .build(); + let term = ctx.get_termination_handle(); let timeout = tokio::time::timeout(Duration::from_secs(10), async { diff --git a/signer/src/network/mod.rs b/signer/src/network/mod.rs index f7b66001..9f8ee648 100644 --- a/signer/src/network/mod.rs +++ b/signer/src/network/mod.rs @@ -120,10 +120,8 @@ mod tests { use super::*; use crate::{ - config::Settings, keys::PrivateKey, - storage::in_memory::Store, - testing::{self, clear_env, NoopSignerContext}, + testing::{self, clear_env, context::*}, }; #[tokio::test] @@ -143,10 +141,17 @@ mod tests { let key1 = PrivateKey::new(&mut rand::thread_rng()); let key2 = PrivateKey::new(&mut rand::thread_rng()); - let settings = Settings::new_from_default_config().unwrap(); - - let context1 = NoopSignerContext::init(settings.clone(), Store::new_shared()).unwrap(); - let context2 = NoopSignerContext::init(settings, Store::new_shared()).unwrap(); + let context1 = TestContext::builder() + .with_in_memory_storage() + .with_mocked_bitcoin_client() + .with_mocked_stacks_client() + .build(); + + let context2 = TestContext::builder() + .with_in_memory_storage() + .with_mocked_bitcoin_client() + .with_mocked_stacks_client() + .build(); let term1 = context1.get_termination_handle(); let term2 = context2.get_termination_handle(); diff --git a/signer/src/stacks/api.rs b/signer/src/stacks/api.rs index b3a1b8e2..8ae90944 100644 --- a/signer/src/stacks/api.rs +++ b/signer/src/stacks/api.rs @@ -76,6 +76,7 @@ pub enum FeePriority { } /// A trait detailing the interface with the Stacks API and Stacks Nodes. +#[cfg_attr(any(test, feature = "testing"), mockall::automock)] pub trait StacksInteract { /// Retrieve the current signer set from the `sbtc-registry` contract. /// @@ -127,6 +128,7 @@ pub trait StacksInteract { /// /// This function usually uses the POST /v2/fees/transaction endpoint /// of a stacks node. + #[cfg_attr(any(test, feature = "testing"), mockall::concretize)] fn estimate_fees( &self, payload: &T, diff --git a/signer/src/storage/postgres.rs b/signer/src/storage/postgres.rs index 8be72eff..83fa3626 100644 --- a/signer/src/storage/postgres.rs +++ b/signer/src/storage/postgres.rs @@ -65,7 +65,7 @@ fn contract_transaction_kinds() -> &'static HashMap<&'static str, TransactionTyp /// This function extracts the signer relevant sBTC related transactions /// from the given blocks. -/// +/// /// Here the deployer is the address that deployed the sBTC smart /// contracts. pub fn extract_relevant_transactions( diff --git a/signer/src/testing/api_clients.rs b/signer/src/testing/api_clients.rs index 5db3ef07..bb29c181 100644 --- a/signer/src/testing/api_clients.rs +++ b/signer/src/testing/api_clients.rs @@ -2,148 +2,8 @@ use url::Url; -use crate::bitcoin::rpc::BitcoinTxInfo; -use crate::bitcoin::rpc::GetTxResponse; -use crate::bitcoin::BitcoinInteract; use crate::bitcoin::MockBitcoinInteract; -use crate::blocklist_client::BlocklistChecker; -use crate::config::Settings; -use crate::emily_client::EmilyInteract; use crate::error::Error; -use crate::stacks::api::StacksInteract; - -/// A no-op API client that doesn't do anything. It will panic if you -/// attempt to use it, but can be useful for fillers in testing. -#[derive(Clone)] -pub struct NoopApiClient; - -impl TryFrom<&[Url]> for NoopApiClient { - type Error = Error; - fn try_from(_value: &[Url]) -> Result { - Ok(NoopApiClient) - } -} - -impl TryFrom<&Settings> for NoopApiClient { - type Error = Error; - fn try_from(_value: &Settings) -> Result { - Ok(NoopApiClient) - } -} - -/// Noop implementation of the BitcoinInteract trait. -impl BitcoinInteract for NoopApiClient { - async fn get_tx(&self, _: &bitcoin::Txid) -> Result, Error> { - unimplemented!() - } - - async fn get_tx_info( - &self, - _: &bitcoin::Txid, - _: &bitcoin::BlockHash, - ) -> Result, Error> { - unimplemented!() - } - - async fn get_block( - &self, - _block_hash: &bitcoin::BlockHash, - ) -> Result, Error> { - unimplemented!() - } - - async fn estimate_fee_rate(&self) -> Result { - unimplemented!() - } - - async fn get_last_fee( - &self, - _utxo: bitcoin::OutPoint, - ) -> Result, Error> { - unimplemented!() - } - - async fn broadcast_transaction(&self, _tx: &bitcoin::Transaction) -> Result<(), Error> { - unimplemented!() - } -} - -/// Noop implementation of the StacksInteract trait. -impl StacksInteract for NoopApiClient { - async fn get_current_signer_set( - &self, - _contract_principal: &clarity::types::chainstate::StacksAddress, - ) -> Result, Error> { - unimplemented!() - } - - async fn get_account( - &self, - _address: &clarity::types::chainstate::StacksAddress, - ) -> Result { - unimplemented!() - } - - async fn submit_tx( - &self, - _tx: &blockstack_lib::chainstate::stacks::StacksTransaction, - ) -> Result { - unimplemented!() - } - - async fn get_block( - &self, - _block_id: clarity::types::chainstate::StacksBlockId, - ) -> Result { - unimplemented!() - } - - async fn get_tenure( - &self, - _block_id: clarity::types::chainstate::StacksBlockId, - ) -> Result, Error> { - unimplemented!() - } - - async fn get_tenure_info( - &self, - ) -> Result { - unimplemented!() - } - - async fn estimate_fees( - &self, - _payload: &T, - _priority: crate::stacks::api::FeePriority, - ) -> Result - where - T: crate::stacks::contracts::AsTxPayload + Send + Sync, - { - unimplemented!() - } - - fn nakamoto_start_height(&self) -> u64 { - unimplemented!() - } -} - -/// Noop implementation of the EmilyInteract trait. -impl EmilyInteract for NoopApiClient { - async fn get_deposits(&self) -> Result, Error> { - todo!() - } -} - -/// Noop implementation of the BlocklistChecker trait. -impl BlocklistChecker for NoopApiClient { - async fn can_accept( - &self, - _address: &str, - ) -> Result> - { - todo!() - } -} impl TryFrom<&[Url]> for MockBitcoinInteract { type Error = Error; diff --git a/signer/src/testing/context.rs b/signer/src/testing/context.rs index dae3bffe..d11ed815 100644 --- a/signer/src/testing/context.rs +++ b/signer/src/testing/context.rs @@ -3,6 +3,11 @@ use std::{ops::Deref, sync::Arc}; use bitcoin::Txid; +use blockstack_lib::{ + chainstate::{nakamoto::NakamotoBlock, stacks::StacksTransaction}, + net::api::gettenureinfo::RPCGetTenureInfo, +}; +use clarity::types::chainstate::{StacksAddress, StacksBlockId}; use tokio::sync::Mutex; use crate::{ @@ -10,12 +15,17 @@ use crate::{ config::Settings, context::{Context, SignerContext}, error::Error, - stacks::api::StacksInteract, - storage::in_memory::{SharedStore, Store}, + keys::PublicKey, + stacks::{ + api::{AccountInfo, FeePriority, MockStacksInteract, StacksInteract, SubmitTxResponse}, + contracts::AsTxPayload, + }, + storage::{ + in_memory::{SharedStore, Store}, + DbRead, DbWrite, + }, }; -use super::api_clients::NoopApiClient; - /// A [`Context`] which can be used for testing. /// /// This context is opinionated and uses a shared in-memory store and mocked @@ -25,36 +35,75 @@ use super::api_clients::NoopApiClient; /// as well as the different mocked clients, so you can modify their behavior as /// needed. #[derive(Clone)] -pub struct TestContext { +pub struct TestContext { /// The inner [`SignerContext`] which this context wraps. - pub inner: SignerContext, + pub inner: SignerContext, + + /// The raw inner storage implementation. + pub storage: Storage, + + /// The raw inner Bitcoin client. + pub bitcoin_client: Bitcoin, - /// The mocked bitcoin client. - pub bitcoin_client: BC, + /// The raw inner Stacks client. + pub stacks_client: Stacks, } -impl TestContext +impl TestContext where - BC: BitcoinInteract + Clone + Send + Sync, + Storage: DbRead + DbWrite + Clone + Sync + Send, + Bitcoin: BitcoinInteract + Clone + Send + Sync, + Stacks: StacksInteract + Clone + Send + Sync, { /// Create a new test context. - pub fn new(bitcoin_client: BC) -> Self { - let settings = Settings::new_from_default_config().unwrap(); - let store = Store::new_shared(); + pub fn new( + settings: Settings, + storage: Storage, + bitcoin_client: Bitcoin, + stacks_client: Stacks, + ) -> Self { + let context = SignerContext::new( + settings, + storage.clone(), + bitcoin_client.clone(), + stacks_client.clone(), + ); - let context = SignerContext::new(settings, store, bitcoin_client.clone(), NoopApiClient); + Self { + inner: context, + storage, + bitcoin_client, + stacks_client, + } + } - Self { inner: context, bitcoin_client } + /// Get an instance of the raw storage implementation. + pub fn inner_storage(&self) -> Storage { + self.storage.clone() } - /// Get an instance of the inner bitcoin client. This will be a clone of the - /// - pub fn inner_bitcoin_client(&self) -> BC { + /// Get an instance of the raw inner Bitcoin client. + pub fn inner_bitcoin_client(&self) -> Bitcoin { self.bitcoin_client.clone() } + + /// Get an instance of the raw inner Stacks client. + pub fn inner_stacks_client(&self) -> Stacks { + self.stacks_client.clone() + } +} + +impl TestContext<(), (), ()> { + /// Returns a builder for creating a new [`TestContext`]. The builder will + /// be initialized with settings from the default configuration file; use + /// the [`ContextBuilder::with_settings`] method to override these settings. + pub fn builder() -> ContextBuilder<(), (), ()> { + Default::default() + } } -impl TestContext> { +/// Provide extra methods for when using a mocked bitcoin client. +impl TestContext, Stacks> { /// Execute a closure with a mutable reference to the inner mocked /// bitcoin client. pub async fn with_bitcoin_client(&mut self, f: F) @@ -66,9 +115,24 @@ impl TestContext> { } } -impl Context for TestContext +/// Provide extra methods for when using a mocked stacks client. +impl TestContext> { + /// Execute a closure with a mutable reference to the inner mocked + /// stacks client. + pub async fn with_stacks_client(&mut self, f: F) + where + F: FnOnce(&mut MockStacksInteract), + { + let mut client = self.stacks_client.lock().await; + f(&mut client); + } +} + +impl Context for TestContext where - BC: BitcoinInteract + Clone + Send + Sync, + Storage: DbRead + DbWrite + Clone + Sync + Send, + Bitcoin: BitcoinInteract + Clone + Send + Sync, + Stacks: StacksInteract + Clone + Send + Sync, { fn config(&self) -> &Settings { self.inner.config() @@ -107,7 +171,7 @@ where } fn get_stacks_client(&self) -> impl StacksInteract + Clone { - NoopApiClient + self.inner.get_stacks_client() } } @@ -184,6 +248,299 @@ impl BitcoinInteract for WrappedMock { } } +impl StacksInteract for WrappedMock { + async fn get_current_signer_set( + &self, + contract_principal: &StacksAddress, + ) -> Result, Error> { + self.inner + .lock() + .await + .get_current_signer_set(contract_principal) + .await + } + + async fn get_account(&self, address: &StacksAddress) -> Result { + self.inner.lock().await.get_account(address).await + } + + async fn submit_tx(&self, tx: &StacksTransaction) -> Result { + self.inner.lock().await.submit_tx(tx).await + } + + async fn get_block(&self, block_id: StacksBlockId) -> Result { + self.inner.lock().await.get_block(block_id).await + } + + async fn get_tenure(&self, block_id: StacksBlockId) -> Result, Error> { + self.inner.lock().await.get_tenure(block_id).await + } + + async fn get_tenure_info(&self) -> Result { + self.inner.lock().await.get_tenure_info().await + } + + async fn estimate_fees(&self, payload: &T, priority: FeePriority) -> Result + where + T: AsTxPayload + Send + Sync, + { + self.inner + .lock() + .await + .estimate_fees(payload, priority) + .await + } + + fn nakamoto_start_height(&self) -> u64 { + tokio::runtime::Handle::current().block_on(async move { + let inner = self.inner.lock().await; + inner.nakamoto_start_height() + }) + } +} + +/// Struct which holds the current configuration of the context builder. +pub struct ContextConfig { + settings: crate::config::Settings, + storage: Storage, + bitcoin: Bitcoin, + stacks: Stacks, +} + +impl Default for ContextConfig<(), (), ()> { + fn default() -> Self { + Self { + settings: Settings::new_from_default_config().expect("failed to load default config"), + storage: (), + bitcoin: (), + stacks: (), + } + } +} + +/// State for the builder pattern. +pub trait BuilderState { + /// Consumes the builder, returning its current internal configuration. + fn get_config(self) -> ContextConfig; +} + +/// A builder for creating a [`TestContext`]. +pub struct ContextBuilder { + config: ContextConfig, +} + +impl ContextBuilder<(), (), ()> { + /// Create a new context builder. + pub fn new() -> Self { + Self { config: Default::default() } + } +} + +impl Default for ContextBuilder<(), (), ()> { + fn default() -> Self { + Self::new() + } +} + +impl BuilderState + for ContextBuilder +{ + fn get_config(self) -> ContextConfig { + self.config + } +} + +/// Trait for configuring the settings. These methods are always available. +pub trait ConfigureSettings +where + Self: Sized + BuilderState, +{ + /// Configure the context with the specified settings. + fn with_settings(self, settings: Settings) -> ContextBuilder { + let config = self.get_config(); + ContextBuilder { + config: ContextConfig { + settings, + storage: config.storage, + bitcoin: config.bitcoin, + stacks: config.stacks, + }, + } + } +} + +impl ConfigureSettings + for ContextBuilder +where + Self: BuilderState, +{ +} + +/// Trait for configuring the storage implementation. These methods are available +/// when the storage implementation has not been set yet. +pub trait ConfigureStorage +where + Self: Sized + BuilderState<(), Bitcoin, Stacks>, +{ + /// Configure the context with an in-memory storage implementation. + fn with_in_memory_storage(self) -> ContextBuilder { + let config = self.get_config(); + ContextBuilder { + config: ContextConfig { + settings: config.settings, + storage: Store::new_shared(), + bitcoin: config.bitcoin, + stacks: config.stacks, + }, + } + } + + /// Configure the context with the specified storage implementation. + fn with_storage( + self, + storage: Storage, + ) -> ContextBuilder { + let config = self.get_config(); + ContextBuilder { + config: ContextConfig { + settings: config.settings, + storage, + bitcoin: config.bitcoin, + stacks: config.stacks, + }, + } + } +} + +impl ConfigureStorage for ContextBuilder<(), Bitcoin, Stacks> where + Self: BuilderState<(), Bitcoin, Stacks> +{ +} + +/// Trait for configuring the Bitcoin client implementation. These methods are +/// available when the Bitcoin client implementation has not been set yet. +pub trait ConfigureBitcoinClient +where + Self: Sized + BuilderState, +{ + /// Configure the context with the specified Bitcoin client implementation. + fn with_bitcoin_client( + self, + bitcoin_client: Bitcoin, + ) -> ContextBuilder { + let config = self.get_config(); + ContextBuilder { + config: ContextConfig { + settings: config.settings, + storage: config.storage, + bitcoin: bitcoin_client, + stacks: config.stacks, + }, + } + } + + /// Configure the context to use a [`BitcoinCoreClient`](crate::bitcoin::rpc::BitcoinCoreClient) + /// with the first RPC endpoint from the settings. + fn with_first_bitcoin_core_client( + self, + ) -> ContextBuilder { + let config = self.get_config(); + let url = config.settings.bitcoin.rpc_endpoints.first().unwrap(); + let bitcoin_client = crate::bitcoin::rpc::BitcoinCoreClient::try_from(url).unwrap(); + ContextBuilder { + config: ContextConfig { + settings: config.settings, + storage: config.storage, + bitcoin: bitcoin_client, + stacks: config.stacks, + }, + } + } + + /// Configure the context with a mocked Bitcoin client. + fn with_mocked_bitcoin_client( + self, + ) -> ContextBuilder, Stacks> { + self.with_bitcoin_client(WrappedMock::default()) + } +} + +impl ConfigureBitcoinClient + for ContextBuilder +where + Self: Sized + BuilderState, +{ +} + +/// Trait for configuring the Stacks client implementation. These methods are +/// available when the Stacks client implementation has not been set yet. +pub trait ConfigureStacksClient +where + Self: Sized + BuilderState, +{ + /// Configure the context with the specified Stacks client implementation. + fn with_stacks_client( + self, + stacks_client: Stacks, + ) -> ContextBuilder { + let config = self.get_config(); + ContextBuilder { + config: ContextConfig { + settings: config.settings, + storage: config.storage, + bitcoin: config.bitcoin, + stacks: stacks_client, + }, + } + } + + /// Configure the context with a mocked stacks client. + fn with_mocked_stacks_client( + self, + ) -> ContextBuilder> { + self.with_stacks_client(WrappedMock::default()) + } +} + +impl ConfigureStacksClient + for ContextBuilder +where + Self: Sized + BuilderState, +{ +} + +/// Trait for building a [`TestContext`]. The [`BuildContext::build`] method +/// consumes the builder and returns a new [`TestContext`]. The method is only +/// available when all required components have been configured. +pub trait BuildContext +where + Self: Sized + BuilderState, +{ + /// Consume the builder and return a new [`TestContext`]. + fn build(self) -> TestContext; +} + +// TODO: We could probably move the entire builder and use it for the `SignerContext` +// as well with a separate `SignerContextBuilder` trait. +impl BuildContext + for ContextBuilder +where + Self: BuilderState, + Storage: DbRead + DbWrite + Clone + Sync + Send, + Bitcoin: BitcoinInteract + Clone + Send + Sync, + Stacks: StacksInteract + Clone + Send + Sync, +{ + fn build(self) -> TestContext { + let config = self.get_config(); + TestContext::new( + config.settings, + config.storage, + config.bitcoin, + config.stacks, + ) + } +} + #[cfg(test)] mod tests { use std::{ @@ -197,18 +554,33 @@ mod tests { use tokio::sync::Notify; use crate::{ - bitcoin::MockBitcoinInteract, context::{Context as _, SignerEvent, SignerSignal}, - testing::context::{TestContext, WrappedMock}, + testing::context::{ + BuildContext, ConfigureBitcoinClient, ConfigureStacksClient, ConfigureStorage, + ContextBuilder, TestContext, + }, }; + #[test] + fn can_build() { + let _builder = ContextBuilder::new() + .with_in_memory_storage() + .with_mocked_bitcoin_client() + .with_mocked_stacks_client() + .build(); + } + /// This test ensures that the context can be cloned and signals can be sent /// to both clones. #[tokio::test] async fn context_clone_signalling_works() { - let context = Arc::new(TestContext::new( - WrappedMock::::default(), - )); + let context = TestContext::builder() + .with_in_memory_storage() + .with_mocked_bitcoin_client() + .with_mocked_stacks_client() + .build(); + + let context = Arc::new(context); let mut recv = context.get_signal_receiver(); let recv_count = Arc::new(AtomicU8::new(0)); @@ -222,7 +594,7 @@ mod tests { signal }); - let context_clone = Arc::clone(&context); + let context_clone = context.clone(); let recv_count_clone = Arc::clone(&recv_count); let recv_task_started = Arc::new(AtomicBool::new(false)); let recv_task_started_clone = Arc::clone(&recv_task_started); diff --git a/signer/src/testing/mod.rs b/signer/src/testing/mod.rs index f48a39a2..126e2825 100644 --- a/signer/src/testing/mod.rs +++ b/signer/src/testing/mod.rs @@ -13,24 +13,17 @@ pub mod transaction_signer; pub mod wallet; pub mod wsts; -use api_clients::NoopApiClient; use bitcoin::key::TapTweak; use bitcoin::TapSighashType; use bitcoin::Witness; use secp256k1::SECP256K1; -use crate::bitcoin::rpc::BitcoinCoreClient; use crate::bitcoin::utxo::UnsignedTransaction; use crate::config::Settings; -use crate::context::SignerContext; -use crate::storage::postgres::PgStore; /// The path for the configuration file that we should use during testing. pub const DEFAULT_CONFIG_PATH: Option<&str> = Some("./src/config/default"); -/// A [`SignerContext`] which uses [`NoopApiClient`]s. -pub type NoopSignerContext = SignerContext; - impl Settings { /// Create a new `Settings` instance from the default configuration file. /// This is useful for testing. @@ -39,23 +32,6 @@ impl Settings { } } -/// A client that can be used for integration tests. The settings are -/// loaded from the default config toml, and the PgStore is assumed to -/// point to a test database. -pub type TestSignerContext = SignerContext; - -impl TestSignerContext { - /// Create a new one from the given database connection pool with the - /// default config settings. - pub fn from_db(db: PgStore) -> Self { - let config = Settings::new_from_default_config().unwrap(); - - let url = config.bitcoin.rpc_endpoints.first().unwrap(); - let bitcoin_client = BitcoinCoreClient::try_from(url).unwrap(); - Self::new(config, db, bitcoin_client, NoopApiClient) - } -} - /// Clears all signer-specific configuration environment variables. This is needed /// for a number of tests which use the `Settings` struct due to the fact that /// `cargo test` runs tests in threads, and environment variables are per-process. diff --git a/signer/src/testing/transaction_coordinator.rs b/signer/src/testing/transaction_coordinator.rs index 6a4980ce..f8a7f16e 100644 --- a/signer/src/testing/transaction_coordinator.rs +++ b/signer/src/testing/transaction_coordinator.rs @@ -16,8 +16,9 @@ use crate::keys::PrivateKey; use crate::keys::PublicKey; use crate::keys::SignerScriptPubKey; use crate::network; +use crate::stacks::api::StacksInteract; use crate::storage::model; -use crate::storage::DbRead as _; +use crate::storage::DbRead; use crate::storage::DbWrite; use crate::testing; use crate::testing::storage::model::TestData; @@ -109,7 +110,18 @@ pub struct TestEnvironment { pub test_model_parameters: testing::storage::model::Params, } -impl TestEnvironment>> { +impl + TestEnvironment< + TestContext< + Storage, + WrappedMock, // We specify this explicitly to gain access to the mock client + Stacks, + >, + > +where + Storage: DbRead + DbWrite + Clone + Sync + Send + 'static, + Stacks: StacksInteract + Clone + Sync + Send + 'static, +{ /// Assert that a coordinator should be able to coordiante a signing round pub async fn assert_should_be_able_to_coordinate_signing_rounds(mut self) { // Get a handle to our mocked bitcoin client. diff --git a/signer/src/testing/wallet.rs b/signer/src/testing/wallet.rs index 7a6619ef..8cbc0dfa 100644 --- a/signer/src/testing/wallet.rs +++ b/signer/src/testing/wallet.rs @@ -29,7 +29,7 @@ use crate::stacks::wallet::SignerWallet; /// A static for a test 2-3 multi-sig wallet. This wallet is loaded with /// funds in the local devnet environment. It matches the signer.deployer -/// address in the default config file. +/// address in the default config file. pub static WALLET: LazyLock<(SignerWallet, [Keypair; 3], u16)> = LazyLock::new(generate_wallet); /// Helper function for generating a test 2-3 multi-sig wallet diff --git a/signer/src/transaction_coordinator.rs b/signer/src/transaction_coordinator.rs index da4fc446..17007fca 100644 --- a/signer/src/transaction_coordinator.rs +++ b/signer/src/transaction_coordinator.rs @@ -598,11 +598,15 @@ pub fn coordinator_public_key( #[cfg(test)] mod tests { use crate::bitcoin::MockBitcoinInteract; + use crate::stacks::api::MockStacksInteract; + use crate::storage::in_memory::SharedStore; use crate::testing; - use crate::testing::context::{TestContext, WrappedMock}; + use crate::testing::context::*; use crate::testing::transaction_coordinator::TestEnvironment; - fn test_environment() -> TestEnvironment>> { + fn test_environment() -> TestEnvironment< + TestContext, WrappedMock>, + > { let test_model_parameters = testing::storage::model::Params { num_bitcoin_blocks: 20, num_stacks_blocks_per_bitcoin_block: 3, @@ -611,7 +615,11 @@ mod tests { num_signers_per_request: 7, }; - let context = TestContext::new(WrappedMock::::default()); + let context = TestContext::builder() + .with_in_memory_storage() + .with_mocked_bitcoin_client() + .with_mocked_stacks_client() + .build(); testing::transaction_coordinator::TestEnvironment { context, diff --git a/signer/tests/integration/complete_deposit.rs b/signer/tests/integration/complete_deposit.rs index 5937caff..80ff8615 100644 --- a/signer/tests/integration/complete_deposit.rs +++ b/signer/tests/integration/complete_deposit.rs @@ -13,7 +13,7 @@ use signer::stacks::contracts::ReqContext; use signer::storage::model::BitcoinBlockRef; use signer::storage::model::StacksPrincipal; use signer::testing; -use signer::testing::TestSignerContext; +use signer::testing::context::*; use fake::Fake; @@ -124,7 +124,11 @@ async fn complete_deposit_validation_happy_path() { // core. This will create a bitcoin core client that connects to the // bitcoin-core at the [bitcoin].endpoints[0] endpoint from the default // toml config file. - let ctx = TestSignerContext::from_db(db.clone()); + let ctx = TestContext::builder() + .with_storage(db.clone()) + .with_first_bitcoin_core_client() + .with_mocked_stacks_client() + .build(); // Check to see if validation passes. complete_deposit_tx.validate(&ctx, &req_ctx).await.unwrap(); @@ -178,7 +182,11 @@ async fn complete_deposit_validation_deployer_mismatch() { complete_deposit_tx.deployer = StacksAddress::p2pkh(false, &setup.signer_keys[0].into()); req_ctx.deployer = StacksAddress::p2pkh(false, &setup.signer_keys[1].into()); - let ctx = TestSignerContext::from_db(db.clone()); + let ctx = TestContext::builder() + .with_storage(db.clone()) + .with_first_bitcoin_core_client() + .with_mocked_stacks_client() + .build(); let validate_future = complete_deposit_tx.validate(&ctx, &req_ctx); match validate_future.await.unwrap_err() { @@ -232,7 +240,11 @@ async fn complete_deposit_validation_missing_deposit_request() { // and the corresponding request context. let (complete_deposit_tx, req_ctx) = make_complete_deposit(&setup); - let ctx = TestSignerContext::from_db(db.clone()); + let ctx = TestContext::builder() + .with_storage(db.clone()) + .with_first_bitcoin_core_client() + .with_mocked_stacks_client() + .build(); let validation_result = complete_deposit_tx.validate(&ctx, &req_ctx).await; match validation_result.unwrap_err() { @@ -292,7 +304,12 @@ async fn complete_deposit_validation_recipient_mismatch() { complete_deposit_tx.recipient = fake::Faker .fake_with_rng::(&mut rng) .into(); - let ctx = TestSignerContext::from_db(db.clone()); + + let ctx = TestContext::builder() + .with_storage(db.clone()) + .with_first_bitcoin_core_client() + .with_mocked_stacks_client() + .build(); let validate_future = complete_deposit_tx.validate(&ctx, &req_ctx); match validate_future.await.unwrap_err() { @@ -351,7 +368,12 @@ async fn complete_deposit_validation_invalid_mint_amount() { // Different: The amount cannot exceed the amount in the deposit // request. complete_deposit_tx.amount = setup.deposit_request.amount + 1; - let ctx = TestSignerContext::from_db(db.clone()); + + let ctx = TestContext::builder() + .with_storage(db.clone()) + .with_first_bitcoin_core_client() + .with_mocked_stacks_client() + .build(); let validate_future = complete_deposit_tx.validate(&ctx, &req_ctx); match validate_future.await.unwrap_err() { @@ -416,7 +438,11 @@ async fn complete_deposit_validation_fee_too_high() { // and the corresponding request context. let (complete_deposit_tx, req_ctx) = make_complete_deposit(&setup); - let ctx = TestSignerContext::from_db(db.clone()); + let ctx = TestContext::builder() + .with_storage(db.clone()) + .with_first_bitcoin_core_client() + .with_mocked_stacks_client() + .build(); let validate_future = complete_deposit_tx.validate(&ctx, &req_ctx); match validate_future.await.unwrap_err() { @@ -478,7 +504,11 @@ async fn complete_deposit_validation_sweep_tx_missing() { // exist. complete_deposit_tx.sweep_txid = fake::Faker.fake_with_rng(&mut rng); - let ctx = TestSignerContext::from_db(db.clone()); + let ctx = TestContext::builder() + .with_storage(db.clone()) + .with_first_bitcoin_core_client() + .with_mocked_stacks_client() + .build(); let validation_result = complete_deposit_tx.validate(&ctx, &req_ctx).await; match validation_result.unwrap_err() { @@ -549,7 +579,11 @@ async fn complete_deposit_validation_sweep_reorged() { block_height: 30000, }; - let ctx = TestSignerContext::from_db(db.clone()); + let ctx = TestContext::builder() + .with_storage(db.clone()) + .with_first_bitcoin_core_client() + .with_mocked_stacks_client() + .build(); let validation_result = complete_deposit_tx.validate(&ctx, &req_ctx).await; match validation_result.unwrap_err() { @@ -613,7 +647,11 @@ async fn complete_deposit_validation_deposit_not_in_sweep() { // of the prevout outpoints in the sweep transaction. complete_deposit_tx.outpoint.vout = 5000; - let ctx = TestSignerContext::from_db(db.clone()); + let ctx = TestContext::builder() + .with_storage(db.clone()) + .with_first_bitcoin_core_client() + .with_mocked_stacks_client() + .build(); let validation_result = complete_deposit_tx.validate(&ctx, &req_ctx).await; match validation_result.unwrap_err() { @@ -675,7 +713,11 @@ async fn complete_deposit_validation_deposit_incorrect_fee() { // would have thought. complete_deposit_tx.amount -= 1; - let ctx = TestSignerContext::from_db(db.clone()); + let ctx = TestContext::builder() + .with_storage(db.clone()) + .with_first_bitcoin_core_client() + .with_mocked_stacks_client() + .build(); let validation_result = complete_deposit_tx.validate(&ctx, &req_ctx).await; match validation_result.unwrap_err() { @@ -733,7 +775,11 @@ async fn complete_deposit_validation_deposit_invalid_sweep() { // and the corresponding request context. let (complete_deposit_tx, req_ctx) = make_complete_deposit(&setup); - let ctx = TestSignerContext::from_db(db.clone()); + let ctx = TestContext::builder() + .with_storage(db.clone()) + .with_first_bitcoin_core_client() + .with_mocked_stacks_client() + .build(); let validation_result = complete_deposit_tx.validate(&ctx, &req_ctx).await; match validation_result.unwrap_err() { diff --git a/signer/tests/integration/postgres.rs b/signer/tests/integration/postgres.rs index 95379d8d..2f664bc4 100644 --- a/signer/tests/integration/postgres.rs +++ b/signer/tests/integration/postgres.rs @@ -12,12 +12,14 @@ use blockstack_lib::types::chainstate::StacksAddress; use futures::StreamExt; use rand::seq::SliceRandom; +use signer::bitcoin::MockBitcoinInteract; use signer::config::Settings; use signer::context::Context; use signer::error::Error; use signer::keys::PublicKey; use signer::keys::SignerScriptPubKey as _; use signer::network; +use signer::stacks::api::MockStacksInteract; use signer::stacks::contracts::AcceptWithdrawalV1; use signer::stacks::contracts::AsContractCall; use signer::stacks::contracts::AsTxPayload as _; @@ -50,7 +52,7 @@ use signer::testing::wallet::ContractCallWrapper; use fake::Fake; use rand::SeedableRng; -use signer::testing::NoopSignerContext; +use signer::testing::context::*; use test_case::test_case; use crate::DATABASE_NUM; @@ -1360,8 +1362,13 @@ async fn get_signers_script_pubkeys_returns_non_empty_vec_old_rows() { } async fn transaction_coordinator_test_environment( -) -> testing::transaction_coordinator::TestEnvironment> -{ +) -> testing::transaction_coordinator::TestEnvironment< + TestContext< + storage::postgres::PgStore, + WrappedMock, + WrappedMock, + >, +> { use std::sync::atomic::Ordering; let test_model_parameters = testing::storage::model::Params { @@ -1375,8 +1382,11 @@ async fn transaction_coordinator_test_environment( let db_num = testing::storage::DATABASE_NUM.fetch_add(1, Ordering::SeqCst); let store = testing::storage::new_test_database(db_num, true).await; - let context = NoopSignerContext::init(Settings::new_from_default_config().unwrap(), store) - .expect("failed to init context"); + let context = TestContext::builder() + .with_storage(store) + .with_mocked_bitcoin_client() + .with_mocked_stacks_client() + .build(); testing::transaction_coordinator::TestEnvironment { context, diff --git a/signer/tests/integration/withdrawal_accept.rs b/signer/tests/integration/withdrawal_accept.rs index db5f2da5..2bf61f94 100644 --- a/signer/tests/integration/withdrawal_accept.rs +++ b/signer/tests/integration/withdrawal_accept.rs @@ -15,7 +15,7 @@ use signer::testing; use fake::Fake; use rand::SeedableRng; -use signer::testing::TestSignerContext; +use signer::testing::context::*; use crate::setup::backfill_bitcoin_blocks; use crate::setup::TestSweepSetup; @@ -121,7 +121,12 @@ async fn accept_withdrawal_validation_happy_path() { let (accept_withdrawal_tx, req_ctx) = make_withdrawal_accept(&setup); // This should not return an Err. - let ctx = TestSignerContext::from_db(db.clone()); + let ctx = TestContext::builder() + .with_storage(db.clone()) + .with_first_bitcoin_core_client() + .with_mocked_stacks_client() + .build(); + accept_withdrawal_tx.validate(&ctx, &req_ctx).await.unwrap(); testing::storage::drop_db(db).await; @@ -167,7 +172,12 @@ async fn accept_withdrawal_validation_deployer_mismatch() { accept_withdrawal_tx.deployer = StacksAddress::p2pkh(false, &setup.signer_keys[0].into()); req_ctx.deployer = StacksAddress::p2pkh(false, &setup.signer_keys[1].into()); - let ctx = TestSignerContext::from_db(db.clone()); + let ctx = TestContext::builder() + .with_storage(db.clone()) + .with_first_bitcoin_core_client() + .with_mocked_stacks_client() + .build(); + let validate_future = accept_withdrawal_tx.validate(&ctx, &req_ctx); match validate_future.await.unwrap_err() { Error::WithdrawalAcceptValidation(ref err) => { @@ -221,7 +231,12 @@ async fn accept_withdrawal_validation_missing_withdrawal_request() { // increments by 1 for each withdrawal request generated. accept_withdrawal_tx.request_id = u64::MAX; - let ctx = TestSignerContext::from_db(db.clone()); + let ctx = TestContext::builder() + .with_storage(db.clone()) + .with_first_bitcoin_core_client() + .with_mocked_stacks_client() + .build(); + let validation_result = accept_withdrawal_tx.validate(&ctx, &req_ctx).await; match validation_result.unwrap_err() { Error::WithdrawalAcceptValidation(ref err) => { @@ -275,7 +290,12 @@ async fn accept_withdrawal_validation_recipient_mismatch() { // Generate the transaction and corresponding request context. let (accept_withdrawal_tx, req_ctx) = make_withdrawal_accept(&setup); - let ctx = TestSignerContext::from_db(db.clone()); + let ctx = TestContext::builder() + .with_storage(db.clone()) + .with_first_bitcoin_core_client() + .with_mocked_stacks_client() + .build(); + let validation_result = accept_withdrawal_tx.validate(&ctx, &req_ctx).await; match validation_result.unwrap_err() { Error::WithdrawalAcceptValidation(ref err) => { @@ -327,7 +347,12 @@ async fn accept_withdrawal_validation_invalid_amount() { // Generate the transaction and corresponding request context. let (accept_withdrawal_tx, req_ctx) = make_withdrawal_accept(&setup); - let ctx = TestSignerContext::from_db(db.clone()); + let ctx = TestContext::builder() + .with_storage(db.clone()) + .with_first_bitcoin_core_client() + .with_mocked_stacks_client() + .build(); + let validation_result = accept_withdrawal_tx.validate(&ctx, &req_ctx).await; match validation_result.unwrap_err() { Error::WithdrawalAcceptValidation(ref err) => { @@ -381,7 +406,12 @@ async fn accept_withdrawal_validation_invalid_fee() { // Generate the transaction and corresponding request context. let (accept_withdrawal_tx, req_ctx) = make_withdrawal_accept(&setup); - let ctx = TestSignerContext::from_db(db.clone()); + let ctx = TestContext::builder() + .with_storage(db.clone()) + .with_first_bitcoin_core_client() + .with_mocked_stacks_client() + .build(); + let validate_future = accept_withdrawal_tx.validate(&ctx, &req_ctx); match validate_future.await.unwrap_err() { Error::WithdrawalAcceptValidation(ref err) => { @@ -437,7 +467,12 @@ async fn accept_withdrawal_validation_sweep_tx_missing() { let fake_txid: BitcoinTxId = fake::Faker.fake_with_rng(&mut rng); accept_withdrawal_tx.outpoint.txid = fake_txid.into(); - let ctx = TestSignerContext::from_db(db.clone()); + let ctx = TestContext::builder() + .with_storage(db.clone()) + .with_first_bitcoin_core_client() + .with_mocked_stacks_client() + .build(); + let validation_result = accept_withdrawal_tx.validate(&ctx, &req_ctx).await; match validation_result.unwrap_err() { Error::WithdrawalAcceptValidation(ref err) => { @@ -501,7 +536,12 @@ async fn accept_withdrawal_validation_sweep_reorged() { block_height: 30000, }; - let ctx = TestSignerContext::from_db(db.clone()); + let ctx = TestContext::builder() + .with_storage(db.clone()) + .with_first_bitcoin_core_client() + .with_mocked_stacks_client() + .build(); + let validation_result = accept_withdrawal_tx.validate(&ctx, &req_ctx).await; match validation_result.unwrap_err() { Error::WithdrawalAcceptValidation(ref err) => { @@ -558,7 +598,12 @@ async fn accept_withdrawal_validation_withdrawal_not_in_sweep() { // greater than 2 will do. accept_withdrawal_tx.outpoint = OutPoint::new(setup.sweep_tx_info.txid, 3); - let ctx = TestSignerContext::from_db(db.clone()); + let ctx = TestContext::builder() + .with_storage(db.clone()) + .with_first_bitcoin_core_client() + .with_mocked_stacks_client() + .build(); + let validation_result = accept_withdrawal_tx.validate(&ctx, &req_ctx).await; match validation_result.unwrap_err() { Error::WithdrawalAcceptValidation(ref err) => { @@ -612,7 +657,12 @@ async fn accept_withdrawal_validation_bitmap_mismatch() { let first_vote = *accept_withdrawal_tx.signer_bitmap.get(0).unwrap(); accept_withdrawal_tx.signer_bitmap.set(0, !first_vote); - let ctx = TestSignerContext::from_db(db.clone()); + let ctx = TestContext::builder() + .with_storage(db.clone()) + .with_first_bitcoin_core_client() + .with_mocked_stacks_client() + .build(); + let validation_result = accept_withdrawal_tx.validate(&ctx, &req_ctx).await; match validation_result.unwrap_err() { Error::WithdrawalAcceptValidation(ref err) => { @@ -666,7 +716,12 @@ async fn accept_withdrawal_validation_withdrawal_incorrect_fee() { // should be. accept_withdrawal_tx.tx_fee -= 1; - let ctx = TestSignerContext::from_db(db.clone()); + let ctx = TestContext::builder() + .with_storage(db.clone()) + .with_first_bitcoin_core_client() + .with_mocked_stacks_client() + .build(); + let validation_result = accept_withdrawal_tx.validate(&ctx, &req_ctx).await; match validation_result.unwrap_err() { Error::WithdrawalAcceptValidation(ref err) => { @@ -717,7 +772,12 @@ async fn accept_withdrawal_validation_withdrawal_invalid_sweep() { // Generate the transaction and corresponding request context. let (accept_withdrawal_tx, req_ctx) = make_withdrawal_accept(&setup); - let ctx = TestSignerContext::from_db(db.clone()); + let ctx = TestContext::builder() + .with_storage(db.clone()) + .with_first_bitcoin_core_client() + .with_mocked_stacks_client() + .build(); + let validation_result = accept_withdrawal_tx.validate(&ctx, &req_ctx).await; match validation_result.unwrap_err() { Error::WithdrawalAcceptValidation(ref err) => { From 5925b0ce1c2202cd05fe966894bc0c586371e820 Mon Sep 17 00:00:00 2001 From: Cyle Witruk Date: Thu, 3 Oct 2024 08:29:00 +0200 Subject: [PATCH 2/3] add .with_mocked_clients() convenience method to reduce boilerplate --- signer/src/api/new_block.rs | 6 ++--- signer/src/context/mod.rs | 3 +-- signer/src/network/libp2p/swarm.rs | 3 +-- signer/src/network/mod.rs | 6 ++--- signer/src/testing/context.rs | 39 +++++++++++++++++++++------ signer/src/transaction_coordinator.rs | 3 +-- signer/tests/integration/postgres.rs | 3 +-- 7 files changed, 39 insertions(+), 24 deletions(-) diff --git a/signer/src/api/new_block.rs b/signer/src/api/new_block.rs index b495ebce..99c0e755 100644 --- a/signer/src/api/new_block.rs +++ b/signer/src/api/new_block.rs @@ -160,8 +160,7 @@ mod tests { { let ctx = TestContext::builder() .with_in_memory_storage() - .with_mocked_bitcoin_client() - .with_mocked_stacks_client() + .with_mocked_clients() .build(); let api = ApiState { ctx: ctx.clone() }; @@ -192,8 +191,7 @@ mod tests { { let ctx = TestContext::builder() .with_in_memory_storage() - .with_mocked_bitcoin_client() - .with_mocked_stacks_client() + .with_mocked_clients() .build(); let api = ApiState { ctx: ctx.clone() }; diff --git a/signer/src/context/mod.rs b/signer/src/context/mod.rs index 8338d0ef..7bb5187f 100644 --- a/signer/src/context/mod.rs +++ b/signer/src/context/mod.rs @@ -185,8 +185,7 @@ mod tests { // Create a context. let context = TestContext::builder() .with_in_memory_storage() - .with_mocked_bitcoin_client() - .with_mocked_stacks_client() + .with_mocked_clients() .build(); // Clone the context. diff --git a/signer/src/network/libp2p/swarm.rs b/signer/src/network/libp2p/swarm.rs index 1614228f..e23be9f2 100644 --- a/signer/src/network/libp2p/swarm.rs +++ b/signer/src/network/libp2p/swarm.rs @@ -248,8 +248,7 @@ mod tests { let ctx = TestContext::builder() .with_in_memory_storage() - .with_mocked_bitcoin_client() - .with_mocked_stacks_client() + .with_mocked_clients() .build(); let term = ctx.get_termination_handle(); diff --git a/signer/src/network/mod.rs b/signer/src/network/mod.rs index 9f8ee648..88fc9855 100644 --- a/signer/src/network/mod.rs +++ b/signer/src/network/mod.rs @@ -143,14 +143,12 @@ mod tests { let context1 = TestContext::builder() .with_in_memory_storage() - .with_mocked_bitcoin_client() - .with_mocked_stacks_client() + .with_mocked_clients() .build(); let context2 = TestContext::builder() .with_in_memory_storage() - .with_mocked_bitcoin_client() - .with_mocked_stacks_client() + .with_mocked_clients() .build(); let term1 = context1.get_termination_handle(); diff --git a/signer/src/testing/context.rs b/signer/src/testing/context.rs index d11ed815..32287f08 100644 --- a/signer/src/testing/context.rs +++ b/signer/src/testing/context.rs @@ -509,6 +509,34 @@ where { } +/// Trait for configuring the context with mocked clients. These methods are +/// available when no clients have been configured yet. +pub trait ConfigureMockedClients +where + Self: Sized + BuilderState, +{ + /// Configure the context to use mocks for all client implementations. + fn with_mocked_clients( + self, + ) -> ContextBuilder, WrappedMock> + { + let config = self.get_config(); + ContextBuilder { + config: ContextConfig { + settings: config.settings, + storage: config.storage, + bitcoin: WrappedMock::default(), + stacks: WrappedMock::default(), + }, + } + } +} + +impl ConfigureMockedClients for ContextBuilder where + Self: Sized + BuilderState +{ +} + /// Trait for building a [`TestContext`]. The [`BuildContext::build`] method /// consumes the builder and returns a new [`TestContext`]. The method is only /// available when all required components have been configured. @@ -555,18 +583,14 @@ mod tests { use crate::{ context::{Context as _, SignerEvent, SignerSignal}, - testing::context::{ - BuildContext, ConfigureBitcoinClient, ConfigureStacksClient, ConfigureStorage, - ContextBuilder, TestContext, - }, + testing::context::*, }; #[test] fn can_build() { let _builder = ContextBuilder::new() .with_in_memory_storage() - .with_mocked_bitcoin_client() - .with_mocked_stacks_client() + .with_mocked_clients() .build(); } @@ -576,8 +600,7 @@ mod tests { async fn context_clone_signalling_works() { let context = TestContext::builder() .with_in_memory_storage() - .with_mocked_bitcoin_client() - .with_mocked_stacks_client() + .with_mocked_clients() .build(); let context = Arc::new(context); diff --git a/signer/src/transaction_coordinator.rs b/signer/src/transaction_coordinator.rs index 17007fca..2892dbdf 100644 --- a/signer/src/transaction_coordinator.rs +++ b/signer/src/transaction_coordinator.rs @@ -617,8 +617,7 @@ mod tests { let context = TestContext::builder() .with_in_memory_storage() - .with_mocked_bitcoin_client() - .with_mocked_stacks_client() + .with_mocked_clients() .build(); testing::transaction_coordinator::TestEnvironment { diff --git a/signer/tests/integration/postgres.rs b/signer/tests/integration/postgres.rs index 2f664bc4..448e4fc1 100644 --- a/signer/tests/integration/postgres.rs +++ b/signer/tests/integration/postgres.rs @@ -1384,8 +1384,7 @@ async fn transaction_coordinator_test_environment( let context = TestContext::builder() .with_storage(store) - .with_mocked_bitcoin_client() - .with_mocked_stacks_client() + .with_mocked_clients() .build(); testing::transaction_coordinator::TestEnvironment { From f4951ef29f8ea66b9a3c2ea436ff0d668395b9fa Mon Sep 17 00:00:00 2001 From: Cyle Witruk Date: Thu, 3 Oct 2024 10:32:16 +0200 Subject: [PATCH 3/3] pr comments --- signer/src/testing/context.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/signer/src/testing/context.rs b/signer/src/testing/context.rs index 32287f08..c8cd13a0 100644 --- a/signer/src/testing/context.rs +++ b/signer/src/testing/context.rs @@ -361,9 +361,7 @@ where ContextBuilder { config: ContextConfig { settings, - storage: config.storage, - bitcoin: config.bitcoin, - stacks: config.stacks, + ..config }, } } @@ -396,7 +394,7 @@ where } /// Configure the context with the specified storage implementation. - fn with_storage( + fn with_storage( self, storage: Storage, ) -> ContextBuilder {