From 642d9704468db7708b565f6e90661f92726e9edf Mon Sep 17 00:00:00 2001 From: Sam Greenbury Date: Tue, 7 Jan 2025 21:05:42 +0000 Subject: [PATCH 1/2] Update test servers in mobile with ephemeral ports --- crates/trustchain-ffi/src/mobile.rs | 59 ++++++++++++++++----- crates/trustchain-http/src/lib.rs | 1 - crates/trustchain-http/src/utils.rs | 28 ---------- crates/trustchain-http/tests/attestation.rs | 26 ++++++++- 4 files changed, 70 insertions(+), 44 deletions(-) delete mode 100644 crates/trustchain-http/src/utils.rs diff --git a/crates/trustchain-ffi/src/mobile.rs b/crates/trustchain-ffi/src/mobile.rs index aed6edef..c1c412be 100644 --- a/crates/trustchain-ffi/src/mobile.rs +++ b/crates/trustchain-ffi/src/mobile.rs @@ -276,8 +276,8 @@ pub fn create_operation_mnemonic(mnemonic: String) -> Result { #[cfg(test)] mod tests { use ssi::vc::CredentialOrJWT; - use trustchain_core::utils::canonicalize_str; - use trustchain_http::utils::init_http; + use trustchain_core::utils::{canonicalize_str, init}; + use trustchain_http::config::HTTPConfig; use crate::config::parse_toml; @@ -416,29 +416,63 @@ mod tests { "did": "did:ion:test:EiA1dZD7jVkS5ZP7JJO01t6HgTU3eeLpbKEV1voOFWJV0g" }"#; + fn init_http_ephemeral() -> u16 { + init(); + // Create channel to receive port number from the thread with the server + let (tx, rx) = std::sync::mpsc::channel(); + std::thread::spawn(move || { + let http_config = HTTPConfig { + host: "127.0.0.1".parse().unwrap(), + port: 0, + server_did: Some( + "did:ion:test:EiBVpjUxXeSRJpvj2TewlX9zNF3GKMCKWwGmKBZqF6pk_A".to_owned(), + ), + root_event_time: Some(1666265405), + ..Default::default() + }; + let rt = Runtime::new().unwrap(); + rt.block_on(async { + let server = trustchain_http::server::http_server(http_config); + // Send assigned ephemeral port number to receiver + tx.send(server.local_addr().port()).unwrap(); + server.await.unwrap(); + }); + }); + // Receive port number to return for client + rx.recv().unwrap() + } + + fn ffi_opts_with_port(ffi_config: &str, port: u16) -> String { + let mut ffi_config = parse_toml(ffi_config); + ffi_config + .endpoint_options + .as_mut() + .unwrap() + .trustchain_endpoint + .port = port; + serde_json::to_string(&ffi_config).unwrap() + } + #[test] #[ignore = "integration test requires ION, MongoDB, IPFS and Bitcoin RPC"] fn test_did_resolve() { - init_http(); + let ffi_opts = ffi_opts_with_port(TEST_FFI_CONFIG, init_http_ephemeral()); let did = "did:ion:test:EiAtHHKFJWAk5AsM3tgCut3OiBY4ekHTf66AAjoysXL65Q".to_string(); - let ffi_opts = serde_json::to_string(&parse_toml(TEST_FFI_CONFIG)).unwrap(); did_resolve(did, ffi_opts).unwrap(); } #[test] #[ignore = "integration test requires ION, MongoDB, IPFS and Bitcoin RPC"] fn test_did_verify() { - init_http(); + let ffi_opts = ffi_opts_with_port(TEST_FFI_CONFIG, init_http_ephemeral()); let did = "did:ion:test:EiAtHHKFJWAk5AsM3tgCut3OiBY4ekHTf66AAjoysXL65Q".to_string(); - let ffi_opts = serde_json::to_string(&parse_toml(TEST_FFI_CONFIG)).unwrap(); did_verify(did, ffi_opts).unwrap(); } #[test] #[ignore = "integration test requires ION, MongoDB, IPFS and Bitcoin RPC"] fn test_vc_verify_credential() { - init_http(); - let ffi_opts = serde_json::to_string(&parse_toml(TEST_FFI_CONFIG)).unwrap(); + let ffi_opts = ffi_opts_with_port(TEST_FFI_CONFIG, init_http_ephemeral()); let credential: Credential = serde_json::from_str(TEST_CREDENTIAL).unwrap(); vc_verify_credential(serde_json::to_string(&credential).unwrap(), ffi_opts).unwrap(); } @@ -446,7 +480,7 @@ mod tests { #[test] #[ignore = "integration test requires ION, MongoDB, IPFS and Bitcoin RPC"] fn test_vc_verify_rss_credential() { - let ffi_opts = serde_json::to_string(&parse_toml(TEST_FFI_CONFIG_RSS)).unwrap(); + let ffi_opts = ffi_opts_with_port(TEST_FFI_CONFIG_RSS, init_http_ephemeral()); let credential: Credential = serde_json::from_str(TEST_CREDENTIAL_RSS).unwrap(); vc_verify_credential(serde_json::to_string(&credential).unwrap(), ffi_opts).unwrap(); } @@ -454,7 +488,7 @@ mod tests { #[test] #[ignore = "integration test requires ION, MongoDB, IPFS and Bitcoin RPC"] fn test_vc_redact_rss_credential() { - let ffi_opts = serde_json::to_string(&parse_toml(TEST_FFI_CONFIG_RSS)).unwrap(); + let ffi_opts = ffi_opts_with_port(TEST_FFI_CONFIG_RSS, init_http_ephemeral()); let credential: Credential = serde_json::from_str(TEST_CREDENTIAL_RSS).unwrap(); let credential_subject_mask: CredentialSubject = serde_json::from_str(TEST_CREDENTIAL_SUBJECT_MASK).unwrap(); @@ -470,7 +504,7 @@ mod tests { #[test] #[ignore = "integration test requires ION, MongoDB, IPFS and Bitcoin RPC"] fn test_vp_issue_presentation() { - let ffi_opts = serde_json::to_string(&parse_toml(TEST_FFI_CONFIG)).unwrap(); + let ffi_opts = ffi_opts_with_port(TEST_FFI_CONFIG_RSS, init_http_ephemeral()); let credential: Credential = serde_json::from_str(TEST_CREDENTIAL).unwrap(); let root_plus_1_did: &str = "did:ion:test:EiBVpjUxXeSRJpvj2TewlX9zNF3GKMCKWwGmKBZqF6pk_A"; let presentation: Presentation = Presentation { @@ -510,8 +544,7 @@ mod tests { #[test] #[ignore = "integration test requires ION, MongoDB, IPFS and Bitcoin RPC"] fn test_vp_verify_presentation() { - init_http(); - let ffi_opts = serde_json::to_string(&parse_toml(TEST_FFI_CONFIG)).unwrap(); + let ffi_opts = ffi_opts_with_port(TEST_FFI_CONFIG, init_http_ephemeral()); vp_verify_presentation(TEST_PRESENTATION.to_string(), ffi_opts).unwrap(); } diff --git a/crates/trustchain-http/src/lib.rs b/crates/trustchain-http/src/lib.rs index b8d92d72..7f6840e5 100644 --- a/crates/trustchain-http/src/lib.rs +++ b/crates/trustchain-http/src/lib.rs @@ -17,7 +17,6 @@ pub mod server; pub mod state; pub mod static_handlers; pub mod store; -pub mod utils; pub mod verifier; /// Fragment for service ID of Trustchain attestion diff --git a/crates/trustchain-http/src/utils.rs b/crates/trustchain-http/src/utils.rs deleted file mode 100644 index 6e29f52a..00000000 --- a/crates/trustchain-http/src/utils.rs +++ /dev/null @@ -1,28 +0,0 @@ -use crate::config::HTTPConfig; -use std::sync::Once; -use tokio::runtime::Runtime; -use trustchain_core::utils::init; - -static INIT_HTTP: Once = Once::new(); -pub fn init_http() { - INIT_HTTP.call_once(|| { - init(); - let http_config = HTTPConfig { - host: "127.0.0.1".parse().unwrap(), - port: 8081, - server_did: Some( - "did:ion:test:EiBVpjUxXeSRJpvj2TewlX9zNF3GKMCKWwGmKBZqF6pk_A".to_owned(), - ), - root_event_time: Some(1666265405), - ..Default::default() - }; - - // Run test server in own thread - std::thread::spawn(|| { - let rt = Runtime::new().unwrap(); - rt.block_on(async { - crate::server::http_server(http_config).await.unwrap(); - }); - }); - }); -} diff --git a/crates/trustchain-http/tests/attestation.rs b/crates/trustchain-http/tests/attestation.rs index c671a23c..8ef1c6d0 100644 --- a/crates/trustchain-http/tests/attestation.rs +++ b/crates/trustchain-http/tests/attestation.rs @@ -1,4 +1,5 @@ /// Integration test for attestation challenge-response process. +use tokio::runtime::Runtime; use trustchain_core::verifier::Verifier; use trustchain_http::attestation_encryption_utils::{josekit_to_ssi_jwk, ssi_to_josekit_jwk}; use trustchain_http::attestation_utils::{ @@ -6,24 +7,45 @@ use trustchain_http::attestation_utils::{ IdentityCRInitiation, }; use trustchain_http::attestor::present_identity_challenge; +use trustchain_http::config::HTTPConfig; use trustchain_http::requester::{ identity_response, initiate_content_challenge, initiate_identity_challenge, }; -use trustchain_http::utils::init_http; use trustchain_ion::{trustchain_resolver, verifier::TrustchainVerifier}; // The root event time of DID documents used in integration test below. const ROOT_EVENT_TIME_1: u64 = 1666265405; use mockall::automock; -use trustchain_core::utils::extract_keys; +use trustchain_core::utils::{extract_keys, init}; #[automock] pub trait AttestationUtils { fn attestation_request_path(&self) -> String; } +fn init_http() { + init(); + let http_config = HTTPConfig { + host: "127.0.0.1".parse().unwrap(), + port: 8081, + server_did: Some("did:ion:test:EiBVpjUxXeSRJpvj2TewlX9zNF3GKMCKWwGmKBZqF6pk_A".to_owned()), + root_event_time: Some(1666265405), + ..Default::default() + }; + + // Run test server in own thread + std::thread::spawn(|| { + let rt = Runtime::new().unwrap(); + rt.block_on(async { + trustchain_http::server::http_server(http_config) + .await + .unwrap(); + }); + }); +} + #[tokio::test] #[ignore] async fn attestation_challenge_response() { From 63b6933cefc2c4bd438aa335072c177bfe2e8388 Mon Sep 17 00:00:00 2001 From: Sam Greenbury Date: Mon, 13 Jan 2025 18:03:51 +0000 Subject: [PATCH 2/2] Add assert that port 8081 is not in use --- Cargo.toml | 1 + README.md | 2 +- crates/trustchain-http/Cargo.toml | 1 + crates/trustchain-http/tests/attestation.rs | 7 ++++++- 4 files changed, 9 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 2f0ffb06..3e0ef574 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -45,6 +45,7 @@ log = "0.4" mockall = "0.11.4" mongodb = "2.3.1" petgraph = "0.6" +port_check = "0.2.1" ps_sig = { git = "https://github.com/alan-turing-institute/RSS.git", rev = "ec9386e125d87c5f54898b34fbe0883b3b36ffd4" } qrcode = "0.12.0" rand = "0.8" diff --git a/README.md b/README.md index 27c3edf9..7eb30e6d 100644 --- a/README.md +++ b/README.md @@ -38,7 +38,7 @@ cargo build ``` Install the Trustchain CLI with: ```shell -cargo install --path trustchain-cli +cargo install --path crates/trustchain-cli ``` ## Usage Guide diff --git a/crates/trustchain-http/Cargo.toml b/crates/trustchain-http/Cargo.toml index 1f38faef..a52cc3ac 100644 --- a/crates/trustchain-http/Cargo.toml +++ b/crates/trustchain-http/Cargo.toml @@ -44,4 +44,5 @@ trustchain-ion = { path = "../trustchain-ion" } axum-test-helper = { workspace = true } itertools = { workspace = true } mockall = { workspace = true } +port_check = { workspace = true } tempfile = { workspace = true } diff --git a/crates/trustchain-http/tests/attestation.rs b/crates/trustchain-http/tests/attestation.rs index 8ef1c6d0..310655b0 100644 --- a/crates/trustchain-http/tests/attestation.rs +++ b/crates/trustchain-http/tests/attestation.rs @@ -1,4 +1,5 @@ -/// Integration test for attestation challenge-response process. +//! Integration test for attestation challenge-response process. +use port_check::is_port_reachable; use tokio::runtime::Runtime; use trustchain_core::verifier::Verifier; use trustchain_http::attestation_encryption_utils::{josekit_to_ssi_jwk, ssi_to_josekit_jwk}; @@ -27,6 +28,10 @@ pub trait AttestationUtils { fn init_http() { init(); + assert!( + !is_port_reachable("127.0.0.1:8081"), + "Port 8081 is required for Challenge-Response integration test but 8081 is already in use." + ); let http_config = HTTPConfig { host: "127.0.0.1".parse().unwrap(), port: 8081,