Skip to content

Commit efb80d8

Browse files
committed
Share infrastructure between tests
Initialize OHTTP relay and payjoin directory once and reuse those connections across all v2 tests.
1 parent 974cdca commit efb80d8

File tree

1 file changed

+94
-73
lines changed

1 file changed

+94
-73
lines changed

payjoin/tests/integration.rs

+94-73
Original file line numberDiff line numberDiff line change
@@ -184,38 +184,81 @@ mod integration {
184184
use reqwest::{Client, ClientBuilder, Error, Response};
185185
use testcontainers_modules::redis::Redis;
186186
use testcontainers_modules::testcontainers::clients::Cli;
187+
use tokio::sync::OnceCell as AsyncOnceCell;
187188

188189
use super::*;
189190

190191
static TESTS_TIMEOUT: Lazy<Duration> = Lazy::new(|| Duration::from_secs(20));
191192
static WAIT_SERVICE_INTERVAL: Lazy<Duration> = Lazy::new(|| Duration::from_secs(3));
193+
static DIRECTORY_PORT: Lazy<u16> = Lazy::new(find_free_port);
194+
static OHTTP_RELAY_PORT: Lazy<u16> = Lazy::new(find_free_port);
195+
// Shared test infrastructure
196+
static TEST_INFRASTRUCTURE: AsyncOnceCell<TestInfrastructure> = AsyncOnceCell::const_new();
197+
198+
struct TestInfrastructure {
199+
directory: Url,
200+
ohttp_relay: Url,
201+
agent: Arc<Client>,
202+
cert: Vec<u8>,
203+
}
204+
205+
impl TestInfrastructure {
206+
async fn new() -> Result<Self, BoxError> {
207+
let (cert, key) = local_cert_key();
208+
let directory = Url::parse(&format!("https://localhost:{}", *DIRECTORY_PORT))?;
209+
let ohttp_relay = Url::parse(&format!("http://localhost:{}", *OHTTP_RELAY_PORT))?;
210+
let gateway_origin = http::Uri::from_str(directory.as_str())?;
211+
212+
// Start services in background tasks
213+
let _directory_handle =
214+
tokio::spawn(init_directory(*DIRECTORY_PORT, (cert.clone(), key)));
215+
let _relay_handle =
216+
tokio::spawn(ohttp_relay::listen_tcp(*OHTTP_RELAY_PORT, gateway_origin));
217+
218+
// Create HTTP agent
219+
let agent = Arc::new(http_agent(cert.clone())?);
220+
221+
// Wait for services to be ready
222+
wait_for_service_ready(ohttp_relay.clone(), agent.clone()).await?;
223+
wait_for_service_ready(directory.clone(), agent.clone()).await?;
224+
225+
Ok(Self { directory, ohttp_relay, agent, cert })
226+
}
227+
}
228+
229+
async fn init_infrastructure() -> &'static TestInfrastructure {
230+
TEST_INFRASTRUCTURE
231+
.get_or_init(|| async {
232+
TestInfrastructure::new()
233+
.await
234+
.expect("Failed to initialize test infrastructure")
235+
})
236+
.await
237+
}
192238

193239
#[tokio::test]
194240
async fn test_bad_ohttp_keys() {
195241
let bad_ohttp_keys =
196242
OhttpKeys::from_str("AQO6SMScPUqSo60A7MY6Ak2hDO0CGAxz7BLYp60syRu0gw")
197243
.expect("Invalid OhttpKeys");
198-
199-
let (cert, key) = local_cert_key();
200-
let port = find_free_port();
201-
let directory = Url::parse(&format!("https://localhost:{}", port)).unwrap();
202-
tokio::select!(
203-
err = init_directory(port, (cert.clone(), key)) => panic!("Directory server exited early: {:?}", err),
204-
res = try_request_with_bad_keys(directory, bad_ohttp_keys, cert) => {
205-
assert_eq!(
206-
res.unwrap().headers().get("content-type").unwrap(),
207-
"application/problem+json"
208-
);
209-
}
244+
let infra = init_infrastructure().await;
245+
let res = try_request_with_bad_keys(
246+
infra.directory.clone(),
247+
bad_ohttp_keys,
248+
infra.cert.clone(),
249+
)
250+
.await;
251+
assert_eq!(
252+
res.unwrap().headers().get("content-type").unwrap(),
253+
"application/problem+json"
210254
);
211255

212256
async fn try_request_with_bad_keys(
213257
directory: Url,
214258
bad_ohttp_keys: OhttpKeys,
215259
cert_der: Vec<u8>,
216260
) -> Result<Response, Error> {
217-
let agent = Arc::new(http_agent(cert_der.clone()).unwrap());
218-
wait_for_service_ready(directory.clone(), agent.clone()).await.unwrap();
261+
let agent = Arc::new(http_agent(cert_der).unwrap());
219262
let mock_ohttp_relay = directory.clone(); // pass through to directory
220263
let mock_address = Address::from_str("tb1q6d3a2w975yny0asuvd9a67ner4nks58ff0q8g4")
221264
.unwrap()
@@ -230,18 +273,14 @@ mod integration {
230273
#[tokio::test]
231274
async fn test_session_expiration() {
232275
init_tracing();
233-
let (cert, key) = local_cert_key();
234-
let ohttp_relay_port = find_free_port();
235-
let ohttp_relay =
236-
Url::parse(&format!("http://localhost:{}", ohttp_relay_port)).unwrap();
237-
let directory_port = find_free_port();
238-
let directory = Url::parse(&format!("https://localhost:{}", directory_port)).unwrap();
239-
let gateway_origin = http::Uri::from_str(directory.as_str()).unwrap();
240-
tokio::select!(
241-
err = ohttp_relay::listen_tcp(ohttp_relay_port, gateway_origin) => panic!("Ohttp relay exited early: {:?}", err),
242-
err = init_directory(directory_port, (cert.clone(), key)) => panic!("Directory server exited early: {:?}", err),
243-
res = do_expiration_tests(ohttp_relay, directory, cert) => assert!(res.is_ok(), "v2 send receive failed: {:#?}", res)
244-
);
276+
let infra = init_infrastructure().await;
277+
let res = do_expiration_tests(
278+
infra.ohttp_relay.clone(),
279+
infra.directory.clone(),
280+
infra.cert.clone(),
281+
)
282+
.await;
283+
assert!(res.is_ok(), "v2 send receive failed: {:#?}", res);
245284

246285
async fn do_expiration_tests(
247286
ohttp_relay: Url,
@@ -250,8 +289,6 @@ mod integration {
250289
) -> Result<(), BoxError> {
251290
let (_bitcoind, sender, receiver) = init_bitcoind_sender_receiver(None, None)?;
252291
let agent = Arc::new(http_agent(cert_der.clone())?);
253-
wait_for_service_ready(ohttp_relay.clone(), agent.clone()).await.unwrap();
254-
wait_for_service_ready(directory.clone(), agent.clone()).await.unwrap();
255292
let ohttp_keys =
256293
payjoin::io::fetch_ohttp_keys(ohttp_relay, directory.clone(), cert_der.clone())
257294
.await?;
@@ -298,18 +335,14 @@ mod integration {
298335
#[tokio::test]
299336
async fn v2_to_v2() {
300337
init_tracing();
301-
let (cert, key) = local_cert_key();
302-
let ohttp_relay_port = find_free_port();
303-
let ohttp_relay =
304-
Url::parse(&format!("http://localhost:{}", ohttp_relay_port)).unwrap();
305-
let directory_port = find_free_port();
306-
let directory = Url::parse(&format!("https://localhost:{}", directory_port)).unwrap();
307-
let gateway_origin = http::Uri::from_str(directory.as_str()).unwrap();
308-
tokio::select!(
309-
err = ohttp_relay::listen_tcp(ohttp_relay_port, gateway_origin) => panic!("Ohttp relay exited early: {:?}", err),
310-
err = init_directory(directory_port, (cert.clone(), key)) => panic!("Directory server exited early: {:?}", err),
311-
res = do_v2_send_receive(ohttp_relay, directory, cert) => assert!(res.is_ok(), "v2 send receive failed: {:#?}", res)
312-
);
338+
let infra = init_infrastructure().await;
339+
let res = do_v2_send_receive(
340+
infra.ohttp_relay.clone(),
341+
infra.directory.clone(),
342+
infra.cert.clone(),
343+
)
344+
.await;
345+
assert!(res.is_ok(), "v2 send receive failed: {:#?}", res);
313346

314347
async fn do_v2_send_receive(
315348
ohttp_relay: Url,
@@ -318,8 +351,6 @@ mod integration {
318351
) -> Result<(), BoxError> {
319352
let (_bitcoind, sender, receiver) = init_bitcoind_sender_receiver(None, None)?;
320353
let agent = Arc::new(http_agent(cert_der.clone())?);
321-
wait_for_service_ready(ohttp_relay.clone(), agent.clone()).await.unwrap();
322-
wait_for_service_ready(directory.clone(), agent.clone()).await.unwrap();
323354
let ohttp_keys =
324355
payjoin::io::fetch_ohttp_keys(ohttp_relay, directory.clone(), cert_der.clone())
325356
.await?;
@@ -430,18 +461,14 @@ mod integration {
430461
#[tokio::test]
431462
async fn v2_to_v2_mixed_input_script_types() {
432463
init_tracing();
433-
let (cert, key) = local_cert_key();
434-
let ohttp_relay_port = find_free_port();
435-
let ohttp_relay =
436-
Url::parse(&format!("http://localhost:{}", ohttp_relay_port)).unwrap();
437-
let directory_port = find_free_port();
438-
let directory = Url::parse(&format!("https://localhost:{}", directory_port)).unwrap();
439-
let gateway_origin = http::Uri::from_str(directory.as_str()).unwrap();
440-
tokio::select!(
441-
err = ohttp_relay::listen_tcp(ohttp_relay_port, gateway_origin) => panic!("Ohttp relay exited early: {:?}", err),
442-
err = init_directory(directory_port, (cert.clone(), key)) => panic!("Directory server exited early: {:?}", err),
443-
res = do_v2_send_receive(ohttp_relay, directory, cert) => assert!(res.is_ok(), "v2 send receive failed: {:#?}", res)
444-
);
464+
let infra = init_infrastructure().await;
465+
let res = do_v2_send_receive(
466+
infra.ohttp_relay.clone(),
467+
infra.directory.clone(),
468+
infra.cert.clone(),
469+
)
470+
.await;
471+
assert!(res.is_ok(), "v2 send receive failed: {:#?}", res);
445472

446473
async fn do_v2_send_receive(
447474
ohttp_relay: Url,
@@ -450,15 +477,12 @@ mod integration {
450477
) -> Result<(), BoxError> {
451478
let (bitcoind, sender, receiver) = init_bitcoind_sender_receiver(None, None)?;
452479
let agent = Arc::new(http_agent(cert_der.clone())?);
453-
wait_for_service_ready(ohttp_relay.clone(), agent.clone()).await.unwrap();
454-
wait_for_service_ready(directory.clone(), agent.clone()).await.unwrap();
455480
let ohttp_keys =
456481
payjoin::io::fetch_ohttp_keys(ohttp_relay, directory.clone(), cert_der.clone())
457482
.await?;
458483
// **********************
459484
// Inside the Receiver:
460485
// make utxos with different script types
461-
462486
let legacy_address =
463487
receiver.get_new_address(None, Some(AddressType::Legacy))?.assume_checked();
464488
let nested_segwit_address =
@@ -647,18 +671,11 @@ mod integration {
647671
#[tokio::test]
648672
async fn v1_to_v2() {
649673
init_tracing();
650-
let (cert, key) = local_cert_key();
651-
let ohttp_relay_port = find_free_port();
652-
let ohttp_relay =
653-
Url::parse(&format!("http://localhost:{}", ohttp_relay_port)).unwrap();
654-
let directory_port = find_free_port();
655-
let directory = Url::parse(&format!("https://localhost:{}", directory_port)).unwrap();
656-
let gateway_origin = http::Uri::from_str(directory.as_str()).unwrap();
657-
tokio::select!(
658-
err = ohttp_relay::listen_tcp(ohttp_relay_port, gateway_origin) => panic!("Ohttp relay exited early: {:?}", err),
659-
err = init_directory(directory_port, (cert.clone(), key)) => panic!("Directory server exited early: {:?}", err),
660-
res = do_v1_to_v2(ohttp_relay, directory, cert) => assert!(res.is_ok()),
661-
);
674+
let infra = init_infrastructure().await;
675+
let res =
676+
do_v1_to_v2(infra.ohttp_relay.clone(), infra.directory.clone(), infra.cert.clone())
677+
.await;
678+
assert!(res.is_ok());
662679

663680
async fn do_v1_to_v2(
664681
ohttp_relay: Url,
@@ -667,8 +684,6 @@ mod integration {
667684
) -> Result<(), BoxError> {
668685
let (_bitcoind, sender, receiver) = init_bitcoind_sender_receiver(None, None)?;
669686
let agent: Arc<Client> = Arc::new(http_agent(cert_der.clone())?);
670-
wait_for_service_ready(ohttp_relay.clone(), agent.clone()).await?;
671-
wait_for_service_ready(directory.clone(), agent.clone()).await?;
672687
let ohttp_keys =
673688
payjoin::io::fetch_ohttp_keys(ohttp_relay, directory.clone(), cert_der.clone())
674689
.await?;
@@ -780,13 +795,19 @@ mod integration {
780795
async fn init_directory(
781796
port: u16,
782797
local_cert_key: (Vec<u8>, Vec<u8>),
783-
) -> Result<(), BoxError> {
798+
) -> Result<(), Box<dyn std::error::Error + Send + Sync + 'static>> {
784799
let docker: Cli = Cli::default();
785800
let timeout = Duration::from_secs(2);
786801
let db = docker.run(Redis);
787802
let db_host = format!("127.0.0.1:{}", db.get_host_port_ipv4(6379));
788803
println!("Database running on {}", db.get_host_port_ipv4(6379));
789-
payjoin_directory::listen_tcp_with_tls(port, db_host, timeout, local_cert_key).await
804+
payjoin_directory::listen_tcp_with_tls(port, db_host, timeout, local_cert_key)
805+
.await
806+
.map_err(|e| {
807+
let err_string = e.to_string();
808+
Box::new(std::io::Error::new(std::io::ErrorKind::Other, err_string))
809+
as Box<dyn std::error::Error + Send + Sync + 'static>
810+
})
790811
}
791812

792813
// generates or gets a DER encoded localhost cert and key.

0 commit comments

Comments
 (0)