From 424af98495438fc720012647297984158656df19 Mon Sep 17 00:00:00 2001 From: spacebear Date: Tue, 14 Jan 2025 18:45:21 -0500 Subject: [PATCH 1/5] Expose payjoin-test-utils as a standalone crate This new crate contains internal test utilities shared between payjoin integration tests and payjoin-cli e2e tests. It also provides an opportunity to create downstream test fixtures in payjoin-ffi and language bindings downstream of it. Co-authored-by: DanGould --- Cargo-minimal.lock | 24 +++++++ Cargo-recent.lock | 24 +++++++ Cargo.toml | 2 +- payjoin-test-utils/Cargo.toml | 25 ++++++++ payjoin-test-utils/src/lib.rs | 116 ++++++++++++++++++++++++++++++++++ payjoin/Cargo.toml | 1 + 6 files changed, 191 insertions(+), 1 deletion(-) create mode 100644 payjoin-test-utils/Cargo.toml create mode 100644 payjoin-test-utils/src/lib.rs diff --git a/Cargo-minimal.lock b/Cargo-minimal.lock index d9921c71e..72e24542d 100644 --- a/Cargo-minimal.lock +++ b/Cargo-minimal.lock @@ -1612,6 +1612,7 @@ dependencies = [ "ohttp-relay", "once_cell", "payjoin-directory", + "payjoin-test-utils", "rcgen", "reqwest", "rustls 0.22.4", @@ -1647,6 +1648,7 @@ dependencies = [ "once_cell", "payjoin", "payjoin-directory", + "payjoin-test-utils", "rcgen", "reqwest", "rustls 0.22.4", @@ -1682,6 +1684,28 @@ dependencies = [ "tracing-subscriber", ] +[[package]] +name = "payjoin-test-utils" +version = "0.1.0" +dependencies = [ + "bitcoin", + "bitcoincore-rpc", + "bitcoind", + "http", + "log", + "ohttp-relay", + "once_cell", + "payjoin-directory", + "rcgen", + "reqwest", + "testcontainers", + "testcontainers-modules", + "tokio", + "tracing", + "tracing-subscriber", + "url", +] + [[package]] name = "pbkdf2" version = "0.11.0" diff --git a/Cargo-recent.lock b/Cargo-recent.lock index d9921c71e..72e24542d 100644 --- a/Cargo-recent.lock +++ b/Cargo-recent.lock @@ -1612,6 +1612,7 @@ dependencies = [ "ohttp-relay", "once_cell", "payjoin-directory", + "payjoin-test-utils", "rcgen", "reqwest", "rustls 0.22.4", @@ -1647,6 +1648,7 @@ dependencies = [ "once_cell", "payjoin", "payjoin-directory", + "payjoin-test-utils", "rcgen", "reqwest", "rustls 0.22.4", @@ -1682,6 +1684,28 @@ dependencies = [ "tracing-subscriber", ] +[[package]] +name = "payjoin-test-utils" +version = "0.1.0" +dependencies = [ + "bitcoin", + "bitcoincore-rpc", + "bitcoind", + "http", + "log", + "ohttp-relay", + "once_cell", + "payjoin-directory", + "rcgen", + "reqwest", + "testcontainers", + "testcontainers-modules", + "tokio", + "tracing", + "tracing-subscriber", + "url", +] + [[package]] name = "pbkdf2" version = "0.11.0" diff --git a/Cargo.toml b/Cargo.toml index 532c7cda5..0efa8fd2b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,5 @@ [workspace] -members = ["payjoin", "payjoin-cli", "payjoin-directory"] +members = ["payjoin", "payjoin-cli", "payjoin-directory", "payjoin-test-utils"] resolver = "2" [patch.crates-io.payjoin] diff --git a/payjoin-test-utils/Cargo.toml b/payjoin-test-utils/Cargo.toml new file mode 100644 index 000000000..9e616ef84 --- /dev/null +++ b/payjoin-test-utils/Cargo.toml @@ -0,0 +1,25 @@ +[package] +name = "payjoin-test-utils" +version = "0.1.0" +edition = "2021" +authors = ["Dan Gould "] +rust-version = "1.63" +license = "MIT" + +[dependencies] +bitcoin = { version = "0.32.5", features = ["base64"] } +bitcoincore-rpc = "0.19.0" +bitcoind = { version = "0.36.0", features = ["0_21_2"] } +http = "1" +log = "0.4.7" +ohttp-relay = { version = "0.0.9", features = ["_test-util"] } +once_cell = "1" +payjoin-directory = { path = "../payjoin-directory", features = ["_danger-local-https"] } +rcgen = "0.11" +reqwest = { version = "0.12", default-features = false, features = ["rustls-tls"] } +testcontainers = "0.15.0" +testcontainers-modules = { version = "0.1.3", features = ["redis"] } +tokio = { version = "1.12.0", features = ["full"] } +tracing = "0.1.40" +tracing-subscriber = { version = "0.3.17", features = ["env-filter"] } +url = "2.2.2" diff --git a/payjoin-test-utils/src/lib.rs b/payjoin-test-utils/src/lib.rs new file mode 100644 index 000000000..44ddb6957 --- /dev/null +++ b/payjoin-test-utils/src/lib.rs @@ -0,0 +1,116 @@ +use std::env; +use std::sync::Arc; +use std::time::Duration; + +use bitcoin::Amount; +use bitcoind::bitcoincore_rpc::json::AddressType; +use bitcoind::bitcoincore_rpc::{self, RpcApi}; +use http::StatusCode; +use log::{log_enabled, Level}; +use once_cell::sync::OnceCell; +use reqwest::{Client, ClientBuilder}; +use tracing_subscriber::{EnvFilter, FmtSubscriber}; +use url::Url; + +pub type BoxError = Box; +pub type BoxSendSyncError = Box; + +static INIT_TRACING: OnceCell<()> = OnceCell::new(); + +pub fn init_tracing() { + INIT_TRACING.get_or_init(|| { + let subscriber = FmtSubscriber::builder() + .with_env_filter(EnvFilter::from_default_env()) + .with_test_writer() + .finish(); + + tracing::subscriber::set_global_default(subscriber) + .expect("failed to set global default subscriber"); + }); +} + +pub async fn init_directory( + db_host: String, + local_cert_key: (Vec, Vec), +) -> std::result::Result< + (u16, tokio::task::JoinHandle>), + BoxSendSyncError, +> { + println!("Database running on {}", db_host); + let timeout = Duration::from_secs(2); + payjoin_directory::listen_tcp_with_tls_on_free_port(db_host, timeout, local_cert_key).await +} + +// generates or gets a DER encoded localhost cert and key. +pub fn local_cert_key() -> (Vec, Vec) { + let cert = + rcgen::generate_simple_self_signed(vec!["0.0.0.0".to_string(), "localhost".to_string()]) + .expect("Failed to generate cert"); + let cert_der = cert.serialize_der().expect("Failed to serialize cert"); + let key_der = cert.serialize_private_key_der(); + (cert_der, key_der) +} + +pub fn init_bitcoind_sender_receiver( + sender_address_type: Option, + receiver_address_type: Option, +) -> Result<(bitcoind::BitcoinD, bitcoincore_rpc::Client, bitcoincore_rpc::Client), BoxError> { + let bitcoind_exe = + env::var("BITCOIND_EXE").ok().or_else(|| bitcoind::downloaded_exe_path().ok()).unwrap(); + let mut conf = bitcoind::Conf::default(); + conf.view_stdout = log_enabled!(Level::Debug); + let bitcoind = bitcoind::BitcoinD::with_conf(bitcoind_exe, &conf)?; + let receiver = bitcoind.create_wallet("receiver")?; + let receiver_address = receiver.get_new_address(None, receiver_address_type)?.assume_checked(); + let sender = bitcoind.create_wallet("sender")?; + let sender_address = sender.get_new_address(None, sender_address_type)?.assume_checked(); + bitcoind.client.generate_to_address(1, &receiver_address)?; + bitcoind.client.generate_to_address(101, &sender_address)?; + + assert_eq!( + Amount::from_btc(50.0)?, + receiver.get_balances()?.mine.trusted, + "receiver doesn't own bitcoin" + ); + + assert_eq!( + Amount::from_btc(50.0)?, + sender.get_balances()?.mine.trusted, + "sender doesn't own bitcoin" + ); + Ok((bitcoind, sender, receiver)) +} + +pub fn http_agent(cert_der: Vec) -> Result { + Ok(http_agent_builder(cert_der)?.build()?) +} + +fn http_agent_builder(cert_der: Vec) -> Result { + Ok(ClientBuilder::new() + .danger_accept_invalid_certs(true) + .use_rustls_tls() + .add_root_certificate(reqwest::tls::Certificate::from_der(cert_der.as_slice()).unwrap())) +} + +const TESTS_TIMEOUT: Duration = Duration::from_secs(20); +const WAIT_SERVICE_INTERVAL: Duration = Duration::from_secs(3); + +pub async fn wait_for_service_ready( + service_url: Url, + agent: Arc, +) -> Result<(), &'static str> { + let health_url = service_url.join("/health").map_err(|_| "Invalid URL")?; + let start = std::time::Instant::now(); + + while start.elapsed() < TESTS_TIMEOUT { + let request_result = + agent.get(health_url.as_str()).send().await.map_err(|_| "Bad request")?; + match request_result.status() { + StatusCode::OK => return Ok(()), + StatusCode::NOT_FOUND => return Err("Endpoint not found"), + _ => std::thread::sleep(WAIT_SERVICE_INTERVAL), + } + } + + Err("Timeout waiting for service to be ready") +} diff --git a/payjoin/Cargo.toml b/payjoin/Cargo.toml index 450b5d824..32b23ba01 100644 --- a/payjoin/Cargo.toml +++ b/payjoin/Cargo.toml @@ -42,6 +42,7 @@ serde_json = "1.0.108" bitcoind = { version = "0.36.0", features = ["0_21_2"] } http = "1" payjoin-directory = { path = "../payjoin-directory", features = ["_danger-local-https"] } +payjoin-test-utils = { path = "../payjoin-test-utils" } ohttp-relay = { version = "0.0.9", features = ["_test-util"] } once_cell = "1" rcgen = { version = "0.11" } From d2e9f35d9b201191c25a54902d38d47045c1fa7b Mon Sep 17 00:00:00 2001 From: spacebear Date: Tue, 14 Jan 2025 18:46:19 -0500 Subject: [PATCH 2/5] Consume test_utils in integration/e2e tests --- payjoin-cli/Cargo.toml | 1 + payjoin-cli/tests/e2e.rs | 147 ++--------------------------------- payjoin/tests/integration.rs | 119 ++-------------------------- 3 files changed, 15 insertions(+), 252 deletions(-) diff --git a/payjoin-cli/Cargo.toml b/payjoin-cli/Cargo.toml index 29990c221..b06fe83a5 100644 --- a/payjoin-cli/Cargo.toml +++ b/payjoin-cli/Cargo.toml @@ -54,6 +54,7 @@ nix = "0.26.4" ohttp-relay = { version = "0.0.9", features = ["_test-util"] } once_cell = "1" payjoin-directory = { path = "../payjoin-directory", features = ["_danger-local-https"] } +payjoin-test-utils = { path = "../payjoin-test-utils" } testcontainers = "0.15.0" testcontainers-modules = { version = "0.1.3", features = ["redis"] } tokio = { version = "1.12.0", features = ["full"] } diff --git a/payjoin-cli/tests/e2e.rs b/payjoin-cli/tests/e2e.rs index 2df2ef155..5f5aaf7a5 100644 --- a/payjoin-cli/tests/e2e.rs +++ b/payjoin-cli/tests/e2e.rs @@ -3,12 +3,9 @@ mod e2e { use std::env; use std::process::Stdio; - use bitcoincore_rpc::json::AddressType; - use bitcoind::bitcoincore_rpc::RpcApi; - use log::{log_enabled, Level}; use nix::sys::signal::{kill, Signal}; use nix::unistd::Pid; - use payjoin::bitcoin::Amount; + use payjoin_test_utils::init_bitcoind_sender_receiver; use tokio::fs; use tokio::io::{AsyncBufReadExt, AsyncWriteExt, BufReader}; use tokio::process::Command; @@ -23,34 +20,7 @@ mod e2e { #[cfg(not(feature = "v2"))] #[tokio::test(flavor = "multi_thread", worker_threads = 4)] async fn send_receive_payjoin() { - let bitcoind_exe = env::var("BITCOIND_EXE") - .ok() - .or_else(|| bitcoind::downloaded_exe_path().ok()) - .expect("version feature or env BITCOIND_EXE is required for tests"); - let mut conf = bitcoind::Conf::default(); - conf.view_stdout = log_enabled!(Level::Debug); - let bitcoind = bitcoind::BitcoinD::with_conf(bitcoind_exe, &conf).unwrap(); - let receiver = bitcoind.create_wallet("receiver").unwrap(); - let receiver_address = - receiver.get_new_address(None, Some(AddressType::Bech32)).unwrap().assume_checked(); - let sender = bitcoind.create_wallet("sender").unwrap(); - let sender_address = - sender.get_new_address(None, Some(AddressType::Bech32)).unwrap().assume_checked(); - bitcoind.client.generate_to_address(1, &receiver_address).unwrap(); - bitcoind.client.generate_to_address(101, &sender_address).unwrap(); - - assert_eq!( - Amount::from_btc(50.0).unwrap(), - receiver.get_balances().unwrap().mine.trusted, - "receiver doesn't own bitcoin" - ); - - assert_eq!( - Amount::from_btc(50.0).unwrap(), - sender.get_balances().unwrap().mine.trusted, - "sender doesn't own bitcoin" - ); - + let (bitcoind, _sender, _receiver) = init_bitcoind_sender_receiver(None, None).unwrap(); let temp_dir = env::temp_dir(); let receiver_db_path = temp_dir.join("receiver_db"); let sender_db_path = temp_dir.join("sender_db"); @@ -171,23 +141,17 @@ mod e2e { use std::path::PathBuf; use std::str::FromStr; use std::sync::Arc; - use std::time::Duration; - use http::StatusCode; - use once_cell::sync::{Lazy, OnceCell}; - use reqwest::{Client, ClientBuilder}; + use payjoin_test_utils::{ + http_agent, init_directory, init_tracing, local_cert_key, wait_for_service_ready, + BoxError, + }; use testcontainers::clients::Cli; use testcontainers_modules::redis::Redis; use tokio::process::Child; use url::Url; - type Error = Box; - type BoxSendSyncError = Box; - type Result = std::result::Result; - - static INIT_TRACING: OnceCell<()> = OnceCell::new(); - static TESTS_TIMEOUT: Lazy = Lazy::new(|| Duration::from_secs(20)); - static WAIT_SERVICE_INTERVAL: Lazy = Lazy::new(|| Duration::from_secs(3)); + type Result = std::result::Result; init_tracing(); let (cert, key) = local_cert_key(); @@ -225,33 +189,7 @@ mod e2e { receiver_db_path: PathBuf, sender_db_path: PathBuf, ) -> Result<()> { - let bitcoind_exe = env::var("BITCOIND_EXE") - .ok() - .or_else(|| bitcoind::downloaded_exe_path().ok()) - .expect("version feature or env BITCOIND_EXE is required for tests"); - let mut conf = bitcoind::Conf::default(); - conf.view_stdout = log_enabled!(Level::Debug); - let bitcoind = bitcoind::BitcoinD::with_conf(bitcoind_exe, &conf)?; - let receiver = bitcoind.create_wallet("receiver")?; - let receiver_address = - receiver.get_new_address(None, Some(AddressType::Bech32))?.assume_checked(); - let sender = bitcoind.create_wallet("sender")?; - let sender_address = - sender.get_new_address(None, Some(AddressType::Bech32))?.assume_checked(); - bitcoind.client.generate_to_address(1, &receiver_address)?; - bitcoind.client.generate_to_address(101, &sender_address)?; - - assert_eq!( - Amount::from_btc(50.0)?, - receiver.get_balances()?.mine.trusted, - "receiver doesn't own bitcoin" - ); - - assert_eq!( - Amount::from_btc(50.0)?, - sender.get_balances()?.mine.trusted, - "sender doesn't own bitcoin" - ); + let (bitcoind, _sender, _receiver) = init_bitcoind_sender_receiver(None, None)?; let temp_dir = env::temp_dir(); let cert_path = temp_dir.join("localhost.der"); tokio::fs::write(&cert_path, cert.clone()).await?; @@ -478,75 +416,6 @@ mod e2e { assert!(payjoin_sent.unwrap_or(false), "Payjoin send was not detected"); Ok(()) } - - async fn wait_for_service_ready(service_url: Url, agent: Arc) -> Result<()> { - let health_url = service_url.join("/health").map_err(|_| "Invalid URL")?; - let start = std::time::Instant::now(); - - while start.elapsed() < *TESTS_TIMEOUT { - let request_result = - agent.get(health_url.as_str()).send().await.map_err(|_| "Bad request")?; - - match request_result.status() { - StatusCode::OK => { - println!("READY {}", service_url); - return Ok(()); - } - StatusCode::NOT_FOUND => return Err("Endpoint not found".into()), - _ => std::thread::sleep(*WAIT_SERVICE_INTERVAL), - } - } - - Err("Timeout waiting for service to be ready".into()) - } - - async fn init_directory( - db_host: String, - local_cert_key: (Vec, Vec), - ) -> std::result::Result< - (u16, tokio::task::JoinHandle>), - BoxSendSyncError, - > { - println!("Database running on {}", db_host); - let timeout = Duration::from_secs(2); - payjoin_directory::listen_tcp_with_tls_on_free_port(db_host, timeout, local_cert_key) - .await - } - - // generates or gets a DER encoded localhost cert and key. - fn local_cert_key() -> (Vec, Vec) { - let cert = rcgen::generate_simple_self_signed(vec![ - "0.0.0.0".to_string(), - "localhost".to_string(), - ]) - .expect("Failed to generate cert"); - let cert_der = cert.serialize_der().expect("Failed to serialize cert"); - let key_der = cert.serialize_private_key_der(); - (cert_der, key_der) - } - - fn http_agent(cert_der: Vec) -> Result { - Ok(http_agent_builder(cert_der)?.build()?) - } - - fn http_agent_builder(cert_der: Vec) -> Result { - Ok(ClientBuilder::new() - .danger_accept_invalid_certs(true) - .use_rustls_tls() - .add_root_certificate(reqwest::tls::Certificate::from_der(cert_der.as_slice())?)) - } - - fn init_tracing() { - INIT_TRACING.get_or_init(|| { - let subscriber = tracing_subscriber::FmtSubscriber::builder() - .with_env_filter(tracing_subscriber::EnvFilter::from_default_env()) - .with_test_writer() - .finish(); - - tracing::subscriber::set_global_default(subscriber) - .expect("failed to set global default subscriber"); - }); - } } async fn cleanup_temp_file(path: &std::path::Path) { diff --git a/payjoin/tests/integration.rs b/payjoin/tests/integration.rs index 37cb6e06f..930980a32 100644 --- a/payjoin/tests/integration.rs +++ b/payjoin/tests/integration.rs @@ -1,6 +1,5 @@ mod integration { use std::collections::HashMap; - use std::env; use std::str::FromStr; use bitcoin::policy::DEFAULT_MIN_RELAY_TX_FEE; @@ -9,17 +8,13 @@ mod integration { use bitcoin::{Amount, FeeRate, OutPoint, TxIn, TxOut, Weight}; use bitcoind::bitcoincore_rpc::json::{AddressType, WalletProcessPsbtResult}; use bitcoind::bitcoincore_rpc::{self, RpcApi}; - use log::{log_enabled, Level}; - use once_cell::sync::{Lazy, OnceCell}; + use once_cell::sync::Lazy; use payjoin::receive::v1::build_v1_pj_uri; use payjoin::receive::InputPair; use payjoin::{PjUri, Request, Uri}; - use tracing_subscriber::{EnvFilter, FmtSubscriber}; + use payjoin_test_utils::{init_bitcoind_sender_receiver, init_tracing, BoxError}; use url::Url; - type BoxError = Box; - - static INIT_TRACING: OnceCell<()> = OnceCell::new(); static EXAMPLE_URL: Lazy = Lazy::new(|| Url::parse("https://example.com").expect("Invalid Url")); @@ -178,17 +173,15 @@ mod integration { use payjoin::receive::v2::{PayjoinProposal, Receiver, UncheckedProposal}; use payjoin::send::v2::SenderBuilder; use payjoin::{OhttpKeys, PjUri, UriExt}; - use reqwest::{Client, ClientBuilder, Error, Response}; + use payjoin_test_utils::{ + http_agent, init_directory, local_cert_key, wait_for_service_ready, + }; + use reqwest::{Client, Error, Response}; use testcontainers_modules::redis::Redis; use testcontainers_modules::testcontainers::clients::Cli; use super::*; - type BoxSendSyncError = Box; - - static TESTS_TIMEOUT: Lazy = Lazy::new(|| Duration::from_secs(20)); - static WAIT_SERVICE_INTERVAL: Lazy = Lazy::new(|| Duration::from_secs(3)); - #[tokio::test] async fn test_bad_ohttp_keys() { let bad_ohttp_keys = @@ -810,29 +803,6 @@ mod integration { } } - async fn init_directory( - db_host: String, - local_cert_key: (Vec, Vec), - ) -> Result<(u16, tokio::task::JoinHandle>), BoxSendSyncError> - { - println!("Database running on {}", db_host); - let timeout = Duration::from_secs(2); - payjoin_directory::listen_tcp_with_tls_on_free_port(db_host, timeout, local_cert_key) - .await - } - - // generates or gets a DER encoded localhost cert and key. - fn local_cert_key() -> (Vec, Vec) { - let cert = rcgen::generate_simple_self_signed(vec![ - "0.0.0.0".to_string(), - "localhost".to_string(), - ]) - .expect("Failed to generate cert"); - let cert_der = cert.serialize_der().expect("Failed to serialize cert"); - let key_der = cert.serialize_private_key_der(); - (cert_der, key_der) - } - fn handle_directory_proposal( receiver: &bitcoincore_rpc::Client, proposal: UncheckedProposal, @@ -915,39 +885,6 @@ mod integration { .unwrap() } - fn http_agent(cert_der: Vec) -> Result { - Ok(http_agent_builder(cert_der)?.build()?) - } - - fn http_agent_builder(cert_der: Vec) -> Result { - Ok(ClientBuilder::new() - .danger_accept_invalid_certs(true) - .use_rustls_tls() - .add_root_certificate( - reqwest::tls::Certificate::from_der(cert_der.as_slice()).unwrap(), - )) - } - - async fn wait_for_service_ready( - service_url: Url, - agent: Arc, - ) -> Result<(), &'static str> { - let health_url = service_url.join("/health").map_err(|_| "Invalid URL")?; - let start = std::time::Instant::now(); - - while start.elapsed() < *TESTS_TIMEOUT { - let request_result = - agent.get(health_url.as_str()).send().await.map_err(|_| "Bad request")?; - match request_result.status() { - StatusCode::OK => return Ok(()), - StatusCode::NOT_FOUND => return Err("Endpoint not found"), - _ => std::thread::sleep(*WAIT_SERVICE_INTERVAL), - } - } - - Err("Timeout waiting for service to be ready") - } - fn build_sweep_psbt( sender: &bitcoincore_rpc::Client, pj_uri: &PjUri, @@ -1155,50 +1092,6 @@ mod integration { } } - fn init_tracing() { - INIT_TRACING.get_or_init(|| { - let subscriber = FmtSubscriber::builder() - .with_env_filter(EnvFilter::from_default_env()) - .with_test_writer() - .finish(); - - tracing::subscriber::set_global_default(subscriber) - .expect("failed to set global default subscriber"); - }); - } - - fn init_bitcoind_sender_receiver( - sender_address_type: Option, - receiver_address_type: Option, - ) -> Result<(bitcoind::BitcoinD, bitcoincore_rpc::Client, bitcoincore_rpc::Client), BoxError> - { - let bitcoind_exe = - env::var("BITCOIND_EXE").ok().or_else(|| bitcoind::downloaded_exe_path().ok()).unwrap(); - let mut conf = bitcoind::Conf::default(); - conf.view_stdout = log_enabled!(Level::Debug); - let bitcoind = bitcoind::BitcoinD::with_conf(bitcoind_exe, &conf)?; - let receiver = bitcoind.create_wallet("receiver")?; - let receiver_address = - receiver.get_new_address(None, receiver_address_type)?.assume_checked(); - let sender = bitcoind.create_wallet("sender")?; - let sender_address = sender.get_new_address(None, sender_address_type)?.assume_checked(); - bitcoind.client.generate_to_address(1, &receiver_address)?; - bitcoind.client.generate_to_address(101, &sender_address)?; - - assert_eq!( - Amount::from_btc(50.0)?, - receiver.get_balances()?.mine.trusted, - "receiver doesn't own bitcoin" - ); - - assert_eq!( - Amount::from_btc(50.0)?, - sender.get_balances()?.mine.trusted, - "sender doesn't own bitcoin" - ); - Ok((bitcoind, sender, receiver)) - } - fn build_original_psbt( sender: &bitcoincore_rpc::Client, pj_uri: &PjUri, From 505d5e73701a9ea701bbf60a79e6db9144a4677a Mon Sep 17 00:00:00 2001 From: DanGould Date: Tue, 3 Dec 2024 13:29:36 -0500 Subject: [PATCH 3/5] Ignore unused test, don't just TODO it --- payjoin/tests/integration.rs | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/payjoin/tests/integration.rs b/payjoin/tests/integration.rs index 930980a32..9073ec456 100644 --- a/payjoin/tests/integration.rs +++ b/payjoin/tests/integration.rs @@ -55,16 +55,16 @@ mod integration { do_v1_to_v1(sender, receiver, false) } - // TODO: Not supported by bitcoind 0_21_2. Later versions fail for unknown reasons - //#[test] - //fn v1_to_v1_taproot() -> Result<(), BoxError> { - // init_tracing(); - // let (_bitcoind, sender, receiver) = init_bitcoind_sender_receiver( - // Some(AddressType::Bech32m), - // Some(AddressType::Bech32m), - // )?; - // do_v1_to_v1(sender, receiver, false) - //} + #[ignore] // TODO: Not supported by bitcoind 0_21_2. Later versions fail for unknown reasons + #[test] + fn v1_to_v1_taproot() -> Result<(), BoxError> { + init_tracing(); + let (_bitcoind, sender, receiver) = init_bitcoind_sender_receiver( + Some(AddressType::Bech32m), + Some(AddressType::Bech32m), + )?; + do_v1_to_v1(sender, receiver, false) + } fn do_v1_to_v1( sender: bitcoincore_rpc::Client, From 4c6e920fb6c582c9dca712821861f8dbdb2ccc60 Mon Sep 17 00:00:00 2001 From: spacebear Date: Fri, 17 Jan 2025 17:14:13 -0500 Subject: [PATCH 4/5] Introduce TestServices helper TestServices is a helper struct that initializes a Payjoin directory and OHTTP relay to facilitate v2 integration tests. It holds onto the running process handles until ownership is taken away explicitly with `take_*_handle`. --- payjoin-cli/tests/e2e.rs | 23 ++------- payjoin-test-utils/src/lib.rs | 42 ++++++++++++++++ payjoin/tests/integration.rs | 92 ++++++++--------------------------- 3 files changed, 68 insertions(+), 89 deletions(-) diff --git a/payjoin-cli/tests/e2e.rs b/payjoin-cli/tests/e2e.rs index 5f5aaf7a5..a9f41a2bb 100644 --- a/payjoin-cli/tests/e2e.rs +++ b/payjoin-cli/tests/e2e.rs @@ -139,12 +139,10 @@ mod e2e { #[tokio::test(flavor = "multi_thread", worker_threads = 4)] async fn send_receive_payjoin() { use std::path::PathBuf; - use std::str::FromStr; use std::sync::Arc; use payjoin_test_utils::{ - http_agent, init_directory, init_tracing, local_cert_key, wait_for_service_ready, - BoxError, + http_agent, init_tracing, wait_for_service_ready, BoxError, TestServices, }; use testcontainers::clients::Cli; use testcontainers_modules::redis::Redis; @@ -154,28 +152,17 @@ mod e2e { type Result = std::result::Result; init_tracing(); - let (cert, key) = local_cert_key(); let docker: Cli = Cli::default(); let db = docker.run(Redis); let db_host = format!("127.0.0.1:{}", db.get_host_port_ipv4(6379)); - let (port, directory_handle) = - init_directory(db_host, (cert.clone(), key)).await.expect("Failed to init directory"); - let directory = Url::parse(&format!("https://localhost:{}", port)).unwrap(); - - let gateway_origin = http::Uri::from_str(directory.as_str()).unwrap(); - let (ohttp_relay_port, ohttp_relay_handle) = - ohttp_relay::listen_tcp_on_free_port(gateway_origin) - .await - .expect("Failed to init ohttp relay"); - let ohttp_relay = Url::parse(&format!("http://localhost:{}", ohttp_relay_port)).unwrap(); - + let mut services = TestServices::initialize(db_host).await.unwrap(); let temp_dir = env::temp_dir(); let receiver_db_path = temp_dir.join("receiver_db"); let sender_db_path = temp_dir.join("sender_db"); let result: Result<()> = tokio::select! { - res = ohttp_relay_handle => Err(format!("Ohttp relay is long running: {:?}", res).into()), - res = directory_handle => Err(format!("Directory server is long running: {:?}", res).into()), - res = send_receive_cli_async(ohttp_relay, directory, cert, receiver_db_path.clone(), sender_db_path.clone()) => res.map_err(|e| format!("send_receive failed: {:?}", e).into()), + res = services.take_ohttp_relay_handle().unwrap() => Err(format!("Ohttp relay is long running: {:?}", res).into()), + res = services.take_directory_handle().unwrap() => Err(format!("Directory server is long running: {:?}", res).into()), + res = send_receive_cli_async(services.ohttp_relay_url(), services.directory_url(), services.cert(), receiver_db_path.clone(), sender_db_path.clone()) => res.map_err(|e| format!("send_receive failed: {:?}", e).into()), }; cleanup_temp_file(&receiver_db_path).await; diff --git a/payjoin-test-utils/src/lib.rs b/payjoin-test-utils/src/lib.rs index 44ddb6957..e630d3950 100644 --- a/payjoin-test-utils/src/lib.rs +++ b/payjoin-test-utils/src/lib.rs @@ -1,4 +1,6 @@ use std::env; +use std::result::Result; +use std::str::FromStr; use std::sync::Arc; use std::time::Duration; @@ -9,6 +11,7 @@ use http::StatusCode; use log::{log_enabled, Level}; use once_cell::sync::OnceCell; use reqwest::{Client, ClientBuilder}; +use tokio::task::JoinHandle; use tracing_subscriber::{EnvFilter, FmtSubscriber}; use url::Url; @@ -29,6 +32,45 @@ pub fn init_tracing() { }); } +pub struct TestServices { + cert_key: (Vec, Vec), + directory: (u16, Option>>), + ohttp_relay: (u16, Option>>), +} + +impl TestServices { + pub async fn initialize(db_host: String) -> Result { + let cert_key = local_cert_key(); + let directory = init_directory(db_host, cert_key.clone()).await?; + let gateway_origin = + http::Uri::from_str(&format!("https://localhost:{}", directory.0)).unwrap(); + let ohttp_relay = ohttp_relay::listen_tcp_on_free_port(gateway_origin).await?; + Ok(Self { + cert_key, + directory: (directory.0, Some(directory.1)), + ohttp_relay: (ohttp_relay.0, Some(ohttp_relay.1)), + }) + } + + pub fn cert(&self) -> Vec { self.cert_key.0.clone() } + + pub fn directory_url(&self) -> Url { + Url::parse(&format!("https://localhost:{}", self.directory.0)).unwrap() + } + + pub fn take_directory_handle(&mut self) -> Option>> { + self.directory.1.take() + } + + pub fn ohttp_relay_url(&self) -> Url { + Url::parse(&format!("http://localhost:{}", self.ohttp_relay.0)).unwrap() + } + + pub fn take_ohttp_relay_handle(&mut self) -> Option>> { + self.ohttp_relay.1.take() + } +} + pub async fn init_directory( db_host: String, local_cert_key: (Vec, Vec), diff --git a/payjoin/tests/integration.rs b/payjoin/tests/integration.rs index 9073ec456..890ea7f47 100644 --- a/payjoin/tests/integration.rs +++ b/payjoin/tests/integration.rs @@ -173,9 +173,7 @@ mod integration { use payjoin::receive::v2::{PayjoinProposal, Receiver, UncheckedProposal}; use payjoin::send::v2::SenderBuilder; use payjoin::{OhttpKeys, PjUri, UriExt}; - use payjoin_test_utils::{ - http_agent, init_directory, local_cert_key, wait_for_service_ready, - }; + use payjoin_test_utils::{http_agent, wait_for_service_ready, TestServices}; use reqwest::{Client, Error, Response}; use testcontainers_modules::redis::Redis; use testcontainers_modules::testcontainers::clients::Cli; @@ -188,19 +186,14 @@ mod integration { OhttpKeys::from_str("OH1QYPM5JXYNS754Y4R45QWE336QFX6ZR8DQGVQCULVZTV20TFVEYDMFQC") .expect("Invalid OhttpKeys"); - let (cert, key) = local_cert_key(); let docker: Cli = Cli::default(); let db = docker.run(Redis); let db_host = format!("127.0.0.1:{}", db.get_host_port_ipv4(6379)); - let (port, directory_handle) = init_directory(db_host, (cert.clone(), key)) - .await - .expect("Failed to init directory"); - let directory = Url::parse(&format!("https://localhost:{}", port)).unwrap(); - + let mut services = TestServices::initialize(db_host).await.unwrap(); tokio::select!( - err = directory_handle => panic!("Directory server exited early: {:?}", err), - res = try_request_with_bad_keys(directory, bad_ohttp_keys, cert) => { + err = services.take_directory_handle().unwrap() => panic!("Directory server exited early: {:?}", err), + res = try_request_with_bad_keys(services.directory_url(), bad_ohttp_keys, services.cert()) => { assert_eq!( res.unwrap().headers().get("content-type").unwrap(), "application/problem+json" @@ -231,26 +224,16 @@ mod integration { #[tokio::test] async fn test_session_expiration() { init_tracing(); - let (cert, key) = local_cert_key(); let docker: Cli = Cli::default(); let db = docker.run(Redis); let db_host = format!("127.0.0.1:{}", db.get_host_port_ipv4(6379)); - let (directory_port, directory_handle) = init_directory(db_host, (cert.clone(), key)) - .await - .expect("Failed to init directory"); - let directory = Url::parse(&format!("https://localhost:{}", directory_port)).unwrap(); - let gateway_origin = http::Uri::from_str(directory.as_str()).unwrap(); - let (ohttp_relay_port, ohttp_relay_handle) = - ohttp_relay::listen_tcp_on_free_port(gateway_origin) - .await - .expect("Failed to init ohttp relay"); - let ohttp_relay = - Url::parse(&format!("http://localhost:{}", ohttp_relay_port)).unwrap(); + let mut services = TestServices::initialize(db_host).await.unwrap(); + tokio::select!( - err = ohttp_relay_handle => panic!("Ohttp relay exited early: {:?}", err), - err = directory_handle => panic!("Directory server exited early: {:?}", err), - res = do_expiration_tests(ohttp_relay, directory, cert) => assert!(res.is_ok(), "v2 send receive failed: {:#?}", res) + err = services.take_ohttp_relay_handle().unwrap() => panic!("Ohttp relay exited early: {:?}", err), + err = services.take_directory_handle().unwrap() => panic!("Directory server exited early: {:?}", err), + res = do_expiration_tests(services.ohttp_relay_url(), services.directory_url(), services.cert()) => assert!(res.is_ok(), "v2 send receive failed: {:#?}", res) ); async fn do_expiration_tests( @@ -303,26 +286,15 @@ mod integration { #[tokio::test] async fn v2_to_v2() { init_tracing(); - let (cert, key) = local_cert_key(); let docker: Cli = Cli::default(); let db = docker.run(Redis); let db_host = format!("127.0.0.1:{}", db.get_host_port_ipv4(6379)); - let (directory_port, directory_handle) = init_directory(db_host, (cert.clone(), key)) - .await - .expect("Failed to init directory"); - let directory = Url::parse(&format!("https://localhost:{}", directory_port)).unwrap(); - let gateway_origin = http::Uri::from_str(directory.as_str()).unwrap(); - let (ohttp_relay_port, ohttp_relay_handle) = - ohttp_relay::listen_tcp_on_free_port(gateway_origin) - .await - .expect("Failed to init ohttp relay"); - let ohttp_relay = - Url::parse(&format!("http://localhost:{}", ohttp_relay_port)).unwrap(); + let mut services = TestServices::initialize(db_host).await.unwrap(); tokio::select!( - err = ohttp_relay_handle => panic!("Ohttp relay exited early: {:?}", err), - err = directory_handle => panic!("Directory server exited early: {:?}", err), - res = do_v2_send_receive(ohttp_relay, directory, cert) => assert!(res.is_ok(), "v2 send receive failed: {:#?}", res) + err = services.take_ohttp_relay_handle().unwrap() => panic!("Ohttp relay exited early: {:?}", err), + err = services.take_directory_handle().unwrap() => panic!("Directory server exited early: {:?}", err), + res = do_v2_send_receive(services.ohttp_relay_url(), services.directory_url(), services.cert()) => assert!(res.is_ok(), "v2 send receive failed: {:#?}", res) ); async fn do_v2_send_receive( @@ -441,26 +413,15 @@ mod integration { #[tokio::test] async fn v2_to_v2_mixed_input_script_types() { init_tracing(); - let (cert, key) = local_cert_key(); let docker: Cli = Cli::default(); let db = docker.run(Redis); let db_host = format!("127.0.0.1:{}", db.get_host_port_ipv4(6379)); - let (directory_port, directory_handle) = init_directory(db_host, (cert.clone(), key)) - .await - .expect("Failed to init directory"); - let directory = Url::parse(&format!("https://localhost:{}", directory_port)).unwrap(); - let gateway_origin = http::Uri::from_str(directory.as_str()).unwrap(); - let (ohttp_relay_port, ohttp_relay_handle) = - ohttp_relay::listen_tcp_on_free_port(gateway_origin) - .await - .expect("Failed to init ohttp relay"); - let ohttp_relay = - Url::parse(&format!("http://localhost:{}", ohttp_relay_port)).unwrap(); + let mut services = TestServices::initialize(db_host).await.unwrap(); tokio::select!( - err = ohttp_relay_handle => panic!("Ohttp relay exited early: {:?}", err), - err = directory_handle => panic!("Directory server exited early: {:?}", err), - res = do_v2_send_receive(ohttp_relay, directory, cert) => assert!(res.is_ok(), "v2 send receive failed: {:#?}", res) + err = services.take_ohttp_relay_handle().unwrap() => panic!("Ohttp relay exited early: {:?}", err), + err = services.take_directory_handle().unwrap() => panic!("Directory server exited early: {:?}", err), + res = do_v2_send_receive(services.ohttp_relay_url(), services.directory_url(), services.cert()) => assert!(res.is_ok(), "v2 send receive failed: {:#?}", res) ); async fn do_v2_send_receive( @@ -660,25 +621,14 @@ mod integration { #[tokio::test] async fn v1_to_v2() { init_tracing(); - let (cert, key) = local_cert_key(); let docker: Cli = Cli::default(); let db = docker.run(Redis); let db_host = format!("127.0.0.1:{}", db.get_host_port_ipv4(6379)); - let (directory_port, directory_handle) = init_directory(db_host, (cert.clone(), key)) - .await - .expect("Failed to init directory"); - let directory = Url::parse(&format!("https://localhost:{}", directory_port)).unwrap(); - let gateway_origin = http::Uri::from_str(directory.as_str()).unwrap(); - let (ohttp_relay_port, ohttp_relay_handle) = - ohttp_relay::listen_tcp_on_free_port(gateway_origin) - .await - .expect("Failed to init ohttp relay"); - let ohttp_relay = - Url::parse(&format!("http://localhost:{}", ohttp_relay_port)).unwrap(); + let mut services = TestServices::initialize(db_host).await.unwrap(); tokio::select!( - err = ohttp_relay_handle => panic!("Ohttp relay exited early: {:?}", err), - err = directory_handle => panic!("Directory server exited early: {:?}", err), - res = do_v1_to_v2(ohttp_relay, directory, cert) => assert!(res.is_ok()), + err = services.take_ohttp_relay_handle().unwrap() => panic!("Ohttp relay exited early: {:?}", err), + err = services.take_directory_handle().unwrap() => panic!("Directory server exited early: {:?}", err), + res = do_v1_to_v2(services.ohttp_relay_url(), services.directory_url(), services.cert()) => assert!(res.is_ok(), "v2 send receive failed: {:#?}", res) ); async fn do_v1_to_v2( From 9a017bc9f0770a61153f542a58e5e38f418e96bc Mon Sep 17 00:00:00 2001 From: spacebear Date: Fri, 17 Jan 2025 19:13:14 -0500 Subject: [PATCH 5/5] Add http_agent to TestServices and fetch ohttp keys Add http_agent: Arc and a corresponding getter since we initialize an agent in every V2 test, and helpers to wait until all services are ready and to fetch OHTTP keys. --- Cargo-minimal.lock | 1 + Cargo-recent.lock | 1 + payjoin-cli/tests/e2e.rs | 30 +++-------- payjoin-test-utils/Cargo.toml | 1 + payjoin-test-utils/src/lib.rs | 17 ++++++ payjoin/tests/integration.rs | 97 +++++++++++------------------------ 6 files changed, 57 insertions(+), 90 deletions(-) diff --git a/Cargo-minimal.lock b/Cargo-minimal.lock index 72e24542d..449050e9f 100644 --- a/Cargo-minimal.lock +++ b/Cargo-minimal.lock @@ -1695,6 +1695,7 @@ dependencies = [ "log", "ohttp-relay", "once_cell", + "payjoin", "payjoin-directory", "rcgen", "reqwest", diff --git a/Cargo-recent.lock b/Cargo-recent.lock index 72e24542d..449050e9f 100644 --- a/Cargo-recent.lock +++ b/Cargo-recent.lock @@ -1695,6 +1695,7 @@ dependencies = [ "log", "ohttp-relay", "once_cell", + "payjoin", "payjoin-directory", "rcgen", "reqwest", diff --git a/payjoin-cli/tests/e2e.rs b/payjoin-cli/tests/e2e.rs index a9f41a2bb..1866cbce1 100644 --- a/payjoin-cli/tests/e2e.rs +++ b/payjoin-cli/tests/e2e.rs @@ -139,15 +139,11 @@ mod e2e { #[tokio::test(flavor = "multi_thread", worker_threads = 4)] async fn send_receive_payjoin() { use std::path::PathBuf; - use std::sync::Arc; - use payjoin_test_utils::{ - http_agent, init_tracing, wait_for_service_ready, BoxError, TestServices, - }; + use payjoin_test_utils::{init_tracing, BoxError, TestServices}; use testcontainers::clients::Cli; use testcontainers_modules::redis::Redis; use tokio::process::Child; - use url::Url; type Result = std::result::Result; @@ -162,7 +158,7 @@ mod e2e { let result: Result<()> = tokio::select! { res = services.take_ohttp_relay_handle().unwrap() => Err(format!("Ohttp relay is long running: {:?}", res).into()), res = services.take_directory_handle().unwrap() => Err(format!("Directory server is long running: {:?}", res).into()), - res = send_receive_cli_async(services.ohttp_relay_url(), services.directory_url(), services.cert(), receiver_db_path.clone(), sender_db_path.clone()) => res.map_err(|e| format!("send_receive failed: {:?}", e).into()), + res = send_receive_cli_async(&services, receiver_db_path.clone(), sender_db_path.clone()) => res.map_err(|e| format!("send_receive failed: {:?}", e).into()), }; cleanup_temp_file(&receiver_db_path).await; @@ -170,28 +166,16 @@ mod e2e { assert!(result.is_ok(), "{}", result.unwrap_err()); async fn send_receive_cli_async( - ohttp_relay: Url, - directory: Url, - cert: Vec, + services: &TestServices, receiver_db_path: PathBuf, sender_db_path: PathBuf, ) -> Result<()> { let (bitcoind, _sender, _receiver) = init_bitcoind_sender_receiver(None, None)?; let temp_dir = env::temp_dir(); let cert_path = temp_dir.join("localhost.der"); - tokio::fs::write(&cert_path, cert.clone()).await?; - let agent = Arc::new(http_agent(cert.clone()).unwrap()); - wait_for_service_ready(ohttp_relay.clone(), agent.clone()).await?; - wait_for_service_ready(directory.clone(), agent).await?; - - // fetch for setup here since ohttp_relay doesn't know the certificate for the directory - // so payjoin-cli is set up with the mock_ohttp_relay which is the directory - let ohttp_keys = payjoin::io::fetch_ohttp_keys_with_cert( - ohttp_relay.clone(), - directory.clone(), - cert.clone(), - ) - .await?; + tokio::fs::write(&cert_path, services.cert()).await?; + services.wait_for_services_ready().await?; + let ohttp_keys = services.fetch_ohttp_keys().await?; let ohttp_keys_path = temp_dir.join("ohttp_keys"); tokio::fs::write(&ohttp_keys_path, ohttp_keys.encode()?).await?; @@ -201,7 +185,7 @@ mod e2e { let payjoin_cli = env!("CARGO_BIN_EXE_payjoin-cli"); - let directory = directory.as_str(); + let directory = &services.directory_url().to_string(); // Mock ohttp_relay since the ohttp_relay's http client doesn't have the certificate for the directory let mock_ohttp_relay = directory; diff --git a/payjoin-test-utils/Cargo.toml b/payjoin-test-utils/Cargo.toml index 9e616ef84..77edf6795 100644 --- a/payjoin-test-utils/Cargo.toml +++ b/payjoin-test-utils/Cargo.toml @@ -14,6 +14,7 @@ http = "1" log = "0.4.7" ohttp-relay = { version = "0.0.9", features = ["_test-util"] } once_cell = "1" +payjoin = { path = "../payjoin", features = ["io", "_danger-local-https"] } payjoin-directory = { path = "../payjoin-directory", features = ["_danger-local-https"] } rcgen = "0.11" reqwest = { version = "0.12", default-features = false, features = ["rustls-tls"] } diff --git a/payjoin-test-utils/src/lib.rs b/payjoin-test-utils/src/lib.rs index e630d3950..da18fa18a 100644 --- a/payjoin-test-utils/src/lib.rs +++ b/payjoin-test-utils/src/lib.rs @@ -10,6 +10,8 @@ use bitcoind::bitcoincore_rpc::{self, RpcApi}; use http::StatusCode; use log::{log_enabled, Level}; use once_cell::sync::OnceCell; +use payjoin::io::{fetch_ohttp_keys_with_cert, Error as IOError}; +use payjoin::OhttpKeys; use reqwest::{Client, ClientBuilder}; use tokio::task::JoinHandle; use tracing_subscriber::{EnvFilter, FmtSubscriber}; @@ -36,6 +38,7 @@ pub struct TestServices { cert_key: (Vec, Vec), directory: (u16, Option>>), ohttp_relay: (u16, Option>>), + http_agent: Arc, } impl TestServices { @@ -45,10 +48,12 @@ impl TestServices { let gateway_origin = http::Uri::from_str(&format!("https://localhost:{}", directory.0)).unwrap(); let ohttp_relay = ohttp_relay::listen_tcp_on_free_port(gateway_origin).await?; + let http_agent: Arc = Arc::new(http_agent(cert_key.0.clone()).unwrap()); Ok(Self { cert_key, directory: (directory.0, Some(directory.1)), ohttp_relay: (ohttp_relay.0, Some(ohttp_relay.1)), + http_agent, }) } @@ -69,6 +74,18 @@ impl TestServices { pub fn take_ohttp_relay_handle(&mut self) -> Option>> { self.ohttp_relay.1.take() } + + pub fn http_agent(&self) -> Arc { self.http_agent.clone() } + + pub async fn wait_for_services_ready(&self) -> Result<(), &'static str> { + wait_for_service_ready(self.ohttp_relay_url(), self.http_agent()).await?; + wait_for_service_ready(self.directory_url(), self.http_agent()).await?; + Ok(()) + } + + pub async fn fetch_ohttp_keys(&self) -> Result { + fetch_ohttp_keys_with_cert(self.ohttp_relay_url(), self.directory_url(), self.cert()).await + } } pub async fn init_directory( diff --git a/payjoin/tests/integration.rs b/payjoin/tests/integration.rs index 890ea7f47..b0bef745c 100644 --- a/payjoin/tests/integration.rs +++ b/payjoin/tests/integration.rs @@ -173,7 +173,7 @@ mod integration { use payjoin::receive::v2::{PayjoinProposal, Receiver, UncheckedProposal}; use payjoin::send::v2::SenderBuilder; use payjoin::{OhttpKeys, PjUri, UriExt}; - use payjoin_test_utils::{http_agent, wait_for_service_ready, TestServices}; + use payjoin_test_utils::TestServices; use reqwest::{Client, Error, Response}; use testcontainers_modules::redis::Redis; use testcontainers_modules::testcontainers::clients::Cli; @@ -193,7 +193,7 @@ mod integration { let mut services = TestServices::initialize(db_host).await.unwrap(); tokio::select!( err = services.take_directory_handle().unwrap() => panic!("Directory server exited early: {:?}", err), - res = try_request_with_bad_keys(services.directory_url(), bad_ohttp_keys, services.cert()) => { + res = try_request_with_bad_keys(&services, bad_ohttp_keys) => { assert_eq!( res.unwrap().headers().get("content-type").unwrap(), "application/problem+json" @@ -202,12 +202,12 @@ mod integration { ); async fn try_request_with_bad_keys( - directory: Url, + services: &TestServices, bad_ohttp_keys: OhttpKeys, - cert_der: Vec, ) -> Result { - let agent = Arc::new(http_agent(cert_der.clone()).unwrap()); - wait_for_service_ready(directory.clone(), agent.clone()).await.unwrap(); + let agent = services.http_agent(); + services.wait_for_services_ready().await.unwrap(); + let directory = services.directory_url(); let mock_ohttp_relay = directory.clone(); // pass through to directory let mock_address = Address::from_str("tb1q6d3a2w975yny0asuvd9a67ner4nks58ff0q8g4") .unwrap() @@ -233,25 +233,15 @@ mod integration { tokio::select!( err = services.take_ohttp_relay_handle().unwrap() => panic!("Ohttp relay exited early: {:?}", err), err = services.take_directory_handle().unwrap() => panic!("Directory server exited early: {:?}", err), - res = do_expiration_tests(services.ohttp_relay_url(), services.directory_url(), services.cert()) => assert!(res.is_ok(), "v2 send receive failed: {:#?}", res) + res = do_expiration_tests(&services) => assert!(res.is_ok(), "v2 send receive failed: {:#?}", res) ); - async fn do_expiration_tests( - ohttp_relay: Url, - directory: Url, - cert_der: Vec, - ) -> Result<(), BoxError> { + async fn do_expiration_tests(services: &TestServices) -> Result<(), BoxError> { let (_bitcoind, sender, receiver) = init_bitcoind_sender_receiver(None, None)?; - let agent = Arc::new(http_agent(cert_der.clone())?); - wait_for_service_ready(ohttp_relay.clone(), agent.clone()).await.unwrap(); - wait_for_service_ready(directory.clone(), agent.clone()).await.unwrap(); - let ohttp_keys = payjoin::io::fetch_ohttp_keys_with_cert( - ohttp_relay.clone(), - directory.clone(), - cert_der, - ) - .await?; - + services.wait_for_services_ready().await?; + let directory = services.directory_url(); + let ohttp_relay = services.ohttp_relay_url(); + let ohttp_keys = services.fetch_ohttp_keys().await?; // ********************** // Inside the Receiver: let address = receiver.get_new_address(None, None)?.assume_checked(); @@ -294,24 +284,15 @@ mod integration { tokio::select!( err = services.take_ohttp_relay_handle().unwrap() => panic!("Ohttp relay exited early: {:?}", err), err = services.take_directory_handle().unwrap() => panic!("Directory server exited early: {:?}", err), - res = do_v2_send_receive(services.ohttp_relay_url(), services.directory_url(), services.cert()) => assert!(res.is_ok(), "v2 send receive failed: {:#?}", res) + res = do_v2_send_receive(&services) => assert!(res.is_ok(), "v2 send receive failed: {:#?}", res) ); - async fn do_v2_send_receive( - ohttp_relay: Url, - directory: Url, - cert_der: Vec, - ) -> Result<(), BoxError> { + async fn do_v2_send_receive(services: &TestServices) -> Result<(), BoxError> { let (_bitcoind, sender, receiver) = init_bitcoind_sender_receiver(None, None)?; - let agent = Arc::new(http_agent(cert_der.clone())?); - wait_for_service_ready(ohttp_relay.clone(), agent.clone()).await.unwrap(); - wait_for_service_ready(directory.clone(), agent.clone()).await.unwrap(); - let ohttp_keys = payjoin::io::fetch_ohttp_keys_with_cert( - ohttp_relay.clone(), - directory.clone(), - cert_der.clone(), - ) - .await?; + let agent = services.http_agent(); + services.wait_for_services_ready().await?; + let directory = services.directory_url(); + let ohttp_keys = services.fetch_ohttp_keys().await?; // ********************** // Inside the Receiver: let address = receiver.get_new_address(None, None)?.assume_checked(); @@ -421,24 +402,15 @@ mod integration { tokio::select!( err = services.take_ohttp_relay_handle().unwrap() => panic!("Ohttp relay exited early: {:?}", err), err = services.take_directory_handle().unwrap() => panic!("Directory server exited early: {:?}", err), - res = do_v2_send_receive(services.ohttp_relay_url(), services.directory_url(), services.cert()) => assert!(res.is_ok(), "v2 send receive failed: {:#?}", res) + res = do_v2_send_receive(&services) => assert!(res.is_ok(), "v2 send receive failed: {:#?}", res) ); - async fn do_v2_send_receive( - ohttp_relay: Url, - directory: Url, - cert_der: Vec, - ) -> Result<(), BoxError> { + async fn do_v2_send_receive(services: &TestServices) -> Result<(), BoxError> { let (bitcoind, sender, receiver) = init_bitcoind_sender_receiver(None, None)?; - let agent = Arc::new(http_agent(cert_der.clone())?); - wait_for_service_ready(ohttp_relay.clone(), agent.clone()).await.unwrap(); - wait_for_service_ready(directory.clone(), agent.clone()).await.unwrap(); - let ohttp_keys = payjoin::io::fetch_ohttp_keys_with_cert( - ohttp_relay.clone(), - directory.clone(), - cert_der, - ) - .await?; + let agent = services.http_agent(); + services.wait_for_services_ready().await?; + let directory = services.directory_url(); + let ohttp_keys = services.fetch_ohttp_keys().await?; // ********************** // Inside the Receiver: // make utxos with different script types @@ -628,24 +600,15 @@ mod integration { tokio::select!( err = services.take_ohttp_relay_handle().unwrap() => panic!("Ohttp relay exited early: {:?}", err), err = services.take_directory_handle().unwrap() => panic!("Directory server exited early: {:?}", err), - res = do_v1_to_v2(services.ohttp_relay_url(), services.directory_url(), services.cert()) => assert!(res.is_ok(), "v2 send receive failed: {:#?}", res) + res = do_v1_to_v2(&services) => assert!(res.is_ok(), "v2 send receive failed: {:#?}", res) ); - async fn do_v1_to_v2( - ohttp_relay: Url, - directory: Url, - cert_der: Vec, - ) -> Result<(), BoxError> { + async fn do_v1_to_v2(services: &TestServices) -> Result<(), BoxError> { let (_bitcoind, sender, receiver) = init_bitcoind_sender_receiver(None, None)?; - let agent: Arc = Arc::new(http_agent(cert_der.clone())?); - wait_for_service_ready(ohttp_relay.clone(), agent.clone()).await?; - wait_for_service_ready(directory.clone(), agent.clone()).await?; - let ohttp_keys = payjoin::io::fetch_ohttp_keys_with_cert( - ohttp_relay.clone(), - directory.clone(), - cert_der.clone(), - ) - .await?; + let agent = services.http_agent(); + services.wait_for_services_ready().await?; + let directory = services.directory_url(); + let ohttp_keys = services.fetch_ohttp_keys().await?; let address = receiver.get_new_address(None, None)?.assume_checked(); let mut session =