diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 53b8d8ab..3ca43347 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -31,8 +31,8 @@ jobs: steps: - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac - - name: Test Dependencies - uses: ./.github/actions/test-dependencies + - name: Build Dependencies + uses: ./.github/actions/build-dependencies with: github-token: ${{ secrets.GITHUB_TOKEN }} @@ -51,8 +51,8 @@ jobs: steps: - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac - - name: Test Dependencies - uses: ./.github/actions/test-dependencies + - name: Build Dependencies + uses: ./.github/actions/build-dependencies with: github-token: ${{ secrets.GITHUB_TOKEN }} diff --git a/Cargo.lock b/Cargo.lock index 04b158a5..ca59f348 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8432,6 +8432,7 @@ dependencies = [ "bitcoin-serai", "ciphersuite", "dalek-ff-group", + "dockertest", "env_logger", "flexible-transcript", "frost-schnorrkel", @@ -8448,6 +8449,7 @@ dependencies = [ "secp256k1", "serai-client", "serai-db", + "serai-docker-tests", "serai-env", "serai-message-queue", "serai-processor-messages", diff --git a/processor/Cargo.toml b/processor/Cargo.toml index a146317c..2a7622be 100644 --- a/processor/Cargo.toml +++ b/processor/Cargo.toml @@ -70,6 +70,9 @@ frost = { package = "modular-frost", path = "../crypto/frost", features = ["test env_logger = "0.10" +dockertest = "0.4" +serai-docker-tests = { path = "../tests/docker" } + [features] secp256k1 = ["k256", "frost/secp256k1"] bitcoin = ["dep:secp256k1", "secp256k1", "bitcoin-serai", "serai-client/bitcoin"] diff --git a/processor/src/tests/literal/mod.rs b/processor/src/tests/literal/mod.rs index d0958345..47c8b913 100644 --- a/processor/src/tests/literal/mod.rs +++ b/processor/src/tests/literal/mod.rs @@ -1,5 +1,11 @@ +use dockertest::{ + PullPolicy, StartPolicy, LogOptions, LogAction, LogPolicy, LogSource, Image, + TestBodySpecification, DockerOperations, DockerTest, +}; + #[cfg(feature = "bitcoin")] mod bitcoin { + use super::*; use crate::networks::{Network, Bitcoin}; #[test] @@ -13,14 +19,47 @@ mod bitcoin { check::= bitcoin_serai::wallet::DUST }>>(); } - async fn bitcoin() -> Bitcoin { - let bitcoin = Bitcoin::new("http://serai:seraidex@127.0.0.1:18443".to_string()).await; + fn spawn_bitcoin() -> DockerTest { + serai_docker_tests::build("bitcoin".to_string()); + + let composition = TestBodySpecification::with_image( + Image::with_repository("serai-dev-bitcoin").pull_policy(PullPolicy::Never), + ) + .replace_cmd(vec![ + "bitcoind".to_string(), + "-txindex".to_string(), + "-regtest".to_string(), + format!("-rpcuser=serai"), + format!("-rpcpassword=seraidex"), + "-rpcbind=0.0.0.0".to_string(), + "-rpcallowip=0.0.0.0/0".to_string(), + "-rpcport=8332".to_string(), + ]) + .set_start_policy(StartPolicy::Strict) + .set_log_options(Some(LogOptions { + action: LogAction::Forward, + policy: LogPolicy::OnError, + source: LogSource::Both, + })) + .set_publish_all_ports(true); + + let mut test = DockerTest::new().with_network(dockertest::Network::Isolated); + test.provide_container(composition); + test + } + + async fn bitcoin(ops: &DockerOperations) -> Bitcoin { + let handle = ops.handle("serai-dev-bitcoin").host_port(8332).unwrap(); + // TODO: Replace with a check if the node has booted + tokio::time::sleep(core::time::Duration::from_secs(20)).await; + let bitcoin = Bitcoin::new(format!("http://serai:seraidex@{}:{}", handle.0, handle.1)).await; bitcoin.fresh_chain().await; bitcoin } test_network!( Bitcoin, + spawn_bitcoin, bitcoin, bitcoin_key_gen, bitcoin_scanner, @@ -33,10 +72,44 @@ mod bitcoin { #[cfg(feature = "monero")] mod monero { + use super::*; use crate::networks::{Network, Monero}; - async fn monero() -> Monero { - let monero = Monero::new("http://127.0.0.1:18081".to_string()); + fn spawn_monero() -> DockerTest { + serai_docker_tests::build("monero".to_string()); + + let composition = TestBodySpecification::with_image( + Image::with_repository("serai-dev-monero").pull_policy(PullPolicy::Never), + ) + .replace_cmd(vec![ + "monerod".to_string(), + "--regtest".to_string(), + "--offline".to_string(), + "--fixed-difficulty=1".to_string(), + "--rpc-bind-ip=0.0.0.0".to_string(), + format!("--rpc-login=serai:seraidex"), + "--rpc-access-control-origins=*".to_string(), + "--confirm-external-bind".to_string(), + "--non-interactive".to_string(), + ]) + .set_start_policy(StartPolicy::Strict) + .set_log_options(Some(LogOptions { + action: LogAction::Forward, + policy: LogPolicy::OnError, + source: LogSource::Both, + })) + .set_publish_all_ports(true); + + let mut test = DockerTest::new().with_network(dockertest::Network::Isolated); + test.provide_container(composition); + test + } + + async fn monero(ops: &DockerOperations) -> Monero { + let handle = ops.handle("serai-dev-monero").host_port(18081).unwrap(); + // TODO: Replace with a check if the node has booted + tokio::time::sleep(core::time::Duration::from_secs(20)).await; + let monero = Monero::new(format!("http://serai:seraidex@{}:{}", handle.0, handle.1)); while monero.get_latest_block_number().await.unwrap() < 150 { monero.mine_block().await; } @@ -45,6 +118,7 @@ mod monero { test_network!( Monero, + spawn_monero, monero, monero_key_gen, monero_scanner, diff --git a/processor/src/tests/mod.rs b/processor/src/tests/mod.rs index 13af66bf..9f91d572 100644 --- a/processor/src/tests/mod.rs +++ b/processor/src/tests/mod.rs @@ -20,39 +20,11 @@ lazy_static::lazy_static! { static ref INIT_LOGGER: () = env_logger::init(); } -#[macro_export] -macro_rules! sequential { - () => { - lazy_static::lazy_static! { - static ref SEQUENTIAL: tokio::sync::Mutex<()> = tokio::sync::Mutex::new(()); - } - }; -} - -#[macro_export] -macro_rules! async_sequential { - ($(async fn $name: ident() $body: block)*) => { - $( - #[tokio::test] - async fn $name() { - *$crate::tests::INIT_LOGGER; - let guard = SEQUENTIAL.lock().await; - let local = tokio::task::LocalSet::new(); - local.run_until(async move { - if let Err(err) = tokio::task::spawn_local(async move { $body }).await { - drop(guard); - Err(err).unwrap() - } - }).await; - } - )* - } -} - #[macro_export] macro_rules! test_network { ( $N: ident, + $docker: ident, $network: ident, $key_gen: ident, $scanner: ident, @@ -66,42 +38,50 @@ macro_rules! test_network { test_addresses, }; - // This doesn't interact with a node and accordingly doesn't need to be run sequentially + // This doesn't interact with a node and accordingly doesn't need to be run #[tokio::test] async fn $key_gen() { test_key_gen::<$N>().await; } - sequential!(); - - async_sequential! { - async fn $scanner() { - test_scanner($network().await).await; - } + #[test] + fn $scanner() { + let docker = $docker(); + docker.run(|ops| async move { + test_scanner($network(&ops).await).await; + }); } - async_sequential! { - async fn $signer() { - test_signer($network().await).await; - } + #[test] + fn $signer() { + let docker = $docker(); + docker.run(|ops| async move { + test_signer($network(&ops).await).await; + }); } - async_sequential! { - async fn $wallet() { - test_wallet($network().await).await; - } + #[test] + fn $wallet() { + let docker = $docker(); + docker.run(|ops| async move { + test_wallet($network(&ops).await).await; + }); } - async_sequential! { - async fn $addresses() { - test_addresses($network().await).await; - } + #[test] + fn $addresses() { + let docker = $docker(); + docker.run(|ops| async move { + test_addresses($network(&ops).await).await; + }); } - async_sequential! { - async fn $no_deadlock_in_multisig_completed() { - test_no_deadlock_in_multisig_completed($network().await).await; - } + #[test] + fn $no_deadlock_in_multisig_completed() { + let docker = $docker(); + docker.run(|ops| async move { + test_no_deadlock_in_multisig_completed($network(&ops).await).await; + }); } }; }