Skip to content

Commit 895d677

Browse files
authored
Test utils feature unification (#1599)
2 parents 25cc42a + 16b314a commit 895d677

6 files changed

Lines changed: 225 additions & 197 deletions

File tree

payjoin-test-utils/Cargo.toml

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,22 +8,40 @@ repository = "https://github.com/payjoin/rust-payjoin"
88
rust-version = "1.85"
99
license = "MIT"
1010

11+
[features]
12+
default = ["v2"]
13+
v2 = [
14+
"payjoin/io",
15+
"payjoin/_manual-tls",
16+
"dep:axum-server",
17+
"dep:ohttp",
18+
"dep:rcgen",
19+
"dep:rustls",
20+
"dep:reqwest",
21+
"dep:http",
22+
"dep:tempfile",
23+
]
24+
1125
[dependencies]
12-
axum-server = { version = "0.8", features = ["tls-rustls-no-provider"] }
26+
axum-server = { version = "0.8", features = [
27+
"tls-rustls-no-provider",
28+
], optional = true }
1329
bitcoin = { version = "0.32.7", features = ["base64"] }
1430
corepc-node = { version = "0.10.0", features = ["download", "29_0"] }
15-
http = "1.3.1"
16-
ohttp = { package = "bitcoin-ohttp", version = "0.6.0" }
31+
http = { version = "1.3.1", optional = true }
32+
ohttp = { package = "bitcoin-ohttp", version = "0.6.0", optional = true }
1733
once_cell = "1.21.3"
18-
payjoin = { version = "0.25.0", features = ["io", "_manual-tls"] }
34+
payjoin = { version = "0.25.0", default-features = false }
1935
payjoin-mailroom = { path = "../payjoin-mailroom", features = ["_manual-tls"] }
2036
# Pin to 0.14.7 (last version to support our MSRV 1.85)
21-
rcgen = "=0.14.7"
37+
rcgen = { version = "=0.14.7", optional = true }
2238
reqwest = { version = "0.12.23", default-features = false, features = [
2339
"rustls-tls",
24-
] }
25-
rustls = { version = "0.23.31", default-features = false, features = ["ring"] }
26-
tempfile = "3.20.0"
40+
], optional = true }
41+
rustls = { version = "0.23.31", default-features = false, features = [
42+
"ring",
43+
], optional = true }
44+
tempfile = { version = "3.20.0", optional = true }
2745
# Pin to 0.3.45 (last version to support our MSRV 1.85)
2846
time = "=0.3.45"
2947
tokio = { version = "1.47.1", features = ["full"] }

payjoin-test-utils/src/lib.rs

Lines changed: 5 additions & 186 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,21 @@
11
use std::result::Result;
22
use std::str::FromStr;
3-
use std::sync::Arc;
4-
use std::time::Duration;
53

6-
use axum_server::tls_rustls::RustlsConfig;
74
use bitcoin::{Amount, Psbt};
85
pub use corepc_node; // re-export for convenience
96
use corepc_node::AddressType;
10-
use http::StatusCode;
11-
use ohttp::hpke::{Aead, Kdf, Kem};
12-
use ohttp::{KeyId, SymmetricSuite};
137
use once_cell::sync::{Lazy, OnceCell};
14-
use payjoin::io::{fetch_ohttp_keys_with_cert, Error as IOError};
15-
use payjoin::OhttpKeys;
16-
use rcgen::Certificate;
17-
use reqwest::{Client, ClientBuilder};
18-
use rustls::pki_types::CertificateDer;
19-
use rustls::RootCertStore;
20-
use tempfile::tempdir;
21-
use tokio::task::JoinHandle;
228
use tracing::Level;
239
use tracing_subscriber::{EnvFilter, FmtSubscriber};
2410

11+
#[cfg(feature = "v2")]
12+
pub mod v2;
13+
#[cfg(feature = "v2")]
14+
pub use v2::*;
15+
2516
pub type BoxError = Box<dyn std::error::Error + 'static>;
2617
pub type BoxSendSyncError = Box<dyn std::error::Error + Send + Sync>;
2718

28-
pub use payjoin::persist::{InMemoryPersister, SessionPersister};
29-
3019
static INIT_TRACING: OnceCell<()> = OnceCell::new();
3120

3221
pub fn init_tracing() {
@@ -41,137 +30,6 @@ pub fn init_tracing() {
4130
});
4231
}
4332

44-
pub struct TestServices {
45-
cert: Certificate,
46-
directory: (u16, Option<JoinHandle<Result<(), BoxSendSyncError>>>),
47-
ohttp_relay: (u16, Option<JoinHandle<Result<(), BoxSendSyncError>>>),
48-
http_agent: Arc<Client>,
49-
}
50-
51-
impl TestServices {
52-
pub async fn initialize() -> Result<Self, BoxSendSyncError> {
53-
// TODO add a UUID, and cleanup guard to delete after on successful run
54-
let cert = local_cert_key();
55-
let cert_der = cert.cert.der().to_vec();
56-
let key_der = cert.signing_key.serialize_der();
57-
let cert_key = (cert_der.clone(), key_der);
58-
59-
let mut root_store = RootCertStore::empty();
60-
root_store.add(CertificateDer::from(cert.cert.der().to_vec())).unwrap();
61-
62-
let directory = init_directory(cert_key, root_store.clone()).await?;
63-
let ohttp_relay = init_ohttp_relay(root_store, None).await?;
64-
65-
let http_agent: Arc<Client> = Arc::new(http_agent(cert_der)?);
66-
67-
Ok(Self {
68-
cert: cert.cert,
69-
directory: (directory.0, Some(directory.1)),
70-
ohttp_relay: (ohttp_relay.0, Some(ohttp_relay.1)),
71-
http_agent,
72-
})
73-
}
74-
75-
pub fn cert(&self) -> Vec<u8> { self.cert.der().to_vec() }
76-
77-
pub fn directory_url(&self) -> String { format!("https://localhost:{}", self.directory.0) }
78-
79-
pub fn take_directory_handle(&mut self) -> JoinHandle<Result<(), BoxSendSyncError>> {
80-
self.directory.1.take().expect("directory handle not found")
81-
}
82-
83-
pub fn ohttp_relay_url(&self) -> String { format!("http://localhost:{}", self.ohttp_relay.0) }
84-
85-
pub fn ohttp_gateway_url(&self) -> String {
86-
format!("{}/.well-known/ohttp-gateway", self.directory_url())
87-
}
88-
89-
pub fn take_ohttp_relay_handle(&mut self) -> JoinHandle<Result<(), BoxSendSyncError>> {
90-
self.ohttp_relay.1.take().expect("ohttp relay handle not found")
91-
}
92-
93-
pub fn http_agent(&self) -> Arc<Client> { self.http_agent.clone() }
94-
95-
pub async fn wait_for_services_ready(&self) -> Result<(), &'static str> {
96-
wait_for_service_ready(&self.ohttp_relay_url(), self.http_agent()).await?;
97-
wait_for_service_ready(&self.directory_url(), self.http_agent()).await?;
98-
Ok(())
99-
}
100-
101-
pub async fn fetch_ohttp_keys(&self) -> Result<OhttpKeys, IOError> {
102-
fetch_ohttp_keys_with_cert(
103-
self.ohttp_relay_url().as_str(),
104-
self.directory_url().as_str(),
105-
&self.cert(),
106-
)
107-
.await
108-
}
109-
}
110-
111-
pub async fn init_directory(
112-
local_cert_key: (Vec<u8>, Vec<u8>),
113-
root_store: RootCertStore,
114-
) -> std::result::Result<
115-
(u16, tokio::task::JoinHandle<std::result::Result<(), BoxSendSyncError>>),
116-
BoxSendSyncError,
117-
> {
118-
let tempdir = tempdir()?;
119-
let config = payjoin_mailroom::config::Config::new(
120-
"[::]:0".parse().expect("valid listener address"),
121-
tempdir.path().to_path_buf(),
122-
Duration::from_secs(2),
123-
Some(payjoin_mailroom::config::V1Config::default()),
124-
);
125-
126-
let tls_config = RustlsConfig::from_der(vec![local_cert_key.0], local_cert_key.1).await?;
127-
128-
let (port, handle) =
129-
payjoin_mailroom::serve_manual_tls(config, Some(tls_config), root_store, None)
130-
.await
131-
.map_err(|e| e.to_string())?;
132-
133-
let handle = tokio::spawn(async move {
134-
let _tempdir = tempdir; // keep the tempdir until the directory shuts down
135-
handle.await.map_err(|e| e.to_string())?.map_err(|e| e.to_string().into())
136-
});
137-
138-
Ok((port, handle))
139-
}
140-
141-
pub async fn init_ohttp_relay(
142-
root_store: RootCertStore,
143-
default_gateway: Option<payjoin_mailroom::ohttp_relay::GatewayUri>,
144-
) -> std::result::Result<
145-
(u16, tokio::task::JoinHandle<std::result::Result<(), BoxSendSyncError>>),
146-
BoxSendSyncError,
147-
> {
148-
let tempdir = tempdir()?;
149-
let config = payjoin_mailroom::config::Config::new(
150-
"[::]:0".parse().expect("valid listener address"),
151-
tempdir.path().to_path_buf(),
152-
Duration::from_secs(2),
153-
None,
154-
);
155-
156-
let (port, handle) =
157-
payjoin_mailroom::serve_manual_tls(config, None, root_store, default_gateway)
158-
.await
159-
.map_err(|e| e.to_string())?;
160-
161-
let handle = tokio::spawn(async move {
162-
let _tempdir = tempdir; // keep the tempdir until the relay shuts down
163-
handle.await.map_err(|e| e.to_string())?.map_err(|e| e.to_string().into())
164-
});
165-
166-
Ok((port, handle))
167-
}
168-
169-
/// generate or get a DER encoded localhost cert and key.
170-
pub fn local_cert_key() -> rcgen::CertifiedKey<rcgen::KeyPair> {
171-
rcgen::generate_simple_self_signed(vec!["0.0.0.0".to_string(), "localhost".to_string()])
172-
.expect("Failed to generate cert")
173-
}
174-
17533
pub fn init_bitcoind() -> Result<corepc_node::Node, BoxError> {
17634
let bitcoind_exe = corepc_node::exe_path()?;
17735
let mut conf = corepc_node::Conf::default();
@@ -226,10 +84,6 @@ fn create_and_fund_wallets<W: AsRef<str>>(
22684
Ok(funded_wallets)
22785
}
22886

229-
pub fn http_agent(cert_der: Vec<u8>) -> Result<Client, BoxSendSyncError> {
230-
Ok(http_agent_builder(cert_der).build()?)
231-
}
232-
23387
pub fn init_bitcoind_multi_sender_single_reciever(
23488
number_of_senders: usize,
23589
) -> Result<(corepc_node::Node, Vec<corepc_node::Client>, corepc_node::Client), BoxError> {
@@ -243,43 +97,8 @@ pub fn init_bitcoind_multi_sender_single_reciever(
24397
Ok((bitcoind, senders, receiver))
24498
}
24599

246-
fn http_agent_builder(cert_der: Vec<u8>) -> ClientBuilder {
247-
ClientBuilder::new().http1_only().use_rustls_tls().add_root_certificate(
248-
reqwest::tls::Certificate::from_der(cert_der.as_slice())
249-
.expect("cert_der should be a valid DER-encoded certificate"),
250-
)
251-
}
252-
253-
const TESTS_TIMEOUT: Duration = Duration::from_secs(20);
254-
const WAIT_SERVICE_INTERVAL: Duration = Duration::from_secs(3);
255-
256-
pub async fn wait_for_service_ready(
257-
service_url: &str,
258-
agent: Arc<Client>,
259-
) -> Result<(), &'static str> {
260-
let health_url = format!("{}/health", service_url.trim_end_matches("/"));
261-
let start = std::time::Instant::now();
262-
263-
while start.elapsed() < TESTS_TIMEOUT {
264-
let request_result =
265-
agent.get(health_url.clone()).send().await.map_err(|_| "Bad request")?;
266-
match request_result.status() {
267-
StatusCode::OK => return Ok(()),
268-
StatusCode::NOT_FOUND => return Err("Endpoint not found"),
269-
_ => std::thread::sleep(WAIT_SERVICE_INTERVAL),
270-
}
271-
}
272-
273-
Err("Timeout waiting for service to be ready")
274-
}
275-
276100
pub static EXAMPLE_URL: &str = "https://example.com";
277101

278-
pub const KEY_ID: KeyId = 1;
279-
pub const KEM: Kem = Kem::K256Sha256;
280-
pub const SYMMETRIC: &[SymmetricSuite] =
281-
&[ohttp::SymmetricSuite::new(Kdf::HkdfSha256, Aead::ChaCha20Poly1305)];
282-
283102
// https://github.com/bitcoin/bips/blob/master/bip-0078.mediawiki#user-content-span_idtestvectorsspanTest_vectors
284103
// | InputScriptType | Original PSBT Fee rate | maxadditionalfeecontribution | additionalfeeoutputindex|
285104
// |-----------------|-----------------------|------------------------------|-------------------------|

0 commit comments

Comments
 (0)