Skip to content

Commit

Permalink
Introduce TestServices helper
Browse files Browse the repository at this point in the history
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`.
  • Loading branch information
spacebear21 committed Jan 17, 2025
1 parent 8bc687d commit b9bbb96
Show file tree
Hide file tree
Showing 3 changed files with 68 additions and 89 deletions.
23 changes: 5 additions & 18 deletions payjoin-cli/tests/e2e.rs
Original file line number Diff line number Diff line change
Expand Up @@ -132,12 +132,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;
Expand All @@ -147,28 +145,17 @@ mod e2e {
type Result<T> = std::result::Result<T, BoxError>;

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;
Expand Down
42 changes: 42 additions & 0 deletions payjoin-test-utils/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
use std::env;
use std::result::Result;
use std::str::FromStr;
use std::sync::Arc;
use std::time::Duration;

Expand All @@ -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;

Expand All @@ -29,6 +32,45 @@ pub fn init_tracing() {
});
}

pub struct TestServices {
cert_key: (Vec<u8>, Vec<u8>),
directory: (u16, Option<JoinHandle<Result<(), BoxSendSyncError>>>),
ohttp_relay: (u16, Option<JoinHandle<Result<(), BoxSendSyncError>>>),
}

impl TestServices {
pub async fn initialize(db_host: String) -> Result<Self, BoxSendSyncError> {
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<u8> { 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<JoinHandle<Result<(), BoxSendSyncError>>> {
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<JoinHandle<Result<(), BoxSendSyncError>>> {
self.ohttp_relay.1.take()
}
}

pub async fn init_directory(
db_host: String,
local_cert_key: (Vec<u8>, Vec<u8>),
Expand Down
92 changes: 21 additions & 71 deletions payjoin/tests/integration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -174,9 +174,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;
Expand All @@ -189,19 +187,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"
Expand Down Expand Up @@ -232,26 +225,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(
Expand Down Expand Up @@ -304,26 +287,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(
Expand Down Expand Up @@ -442,26 +414,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(
Expand Down Expand Up @@ -661,25 +622,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(
Expand Down

0 comments on commit b9bbb96

Please sign in to comment.