From 4ab12543ceca1c9459e124a67047cd633e1aeeea Mon Sep 17 00:00:00 2001 From: Johannes Marbach Date: Tue, 25 Nov 2025 16:25:13 +0100 Subject: [PATCH] feat(testing): allow specifying expiration duration in MockedRendezvousServer Signed-off-by: Johannes Marbach --- .../src/authentication/oauth/qrcode/grant.rs | 27 ++++++--- .../src/authentication/oauth/qrcode/login.rs | 20 +++++-- .../oauth/qrcode/secure_channel.rs | 56 ++++++++++++++++--- 3 files changed, 79 insertions(+), 24 deletions(-) diff --git a/crates/matrix-sdk/src/authentication/oauth/qrcode/grant.rs b/crates/matrix-sdk/src/authentication/oauth/qrcode/grant.rs index e393f7318..2ebbb0a1e 100644 --- a/crates/matrix-sdk/src/authentication/oauth/qrcode/grant.rs +++ b/crates/matrix-sdk/src/authentication/oauth/qrcode/grant.rs @@ -674,7 +674,8 @@ mod test { #[async_test] async fn test_grant_login_with_generated_qr_code() { let server = MatrixMockServer::new().await; - let rendezvous_server = MockedRendezvousServer::new(server.server(), "abcdEFG12345").await; + let rendezvous_server = + MockedRendezvousServer::new(server.server(), "abcdEFG12345", Duration::MAX).await; debug!("Set up rendezvous server mock at {}", rendezvous_server.rendezvous_url); let device_authorization_grant = AuthorizationGrant { @@ -817,7 +818,8 @@ mod test { #[async_test] async fn test_grant_login_with_scanned_qr_code() { let server = MatrixMockServer::new().await; - let rendezvous_server = MockedRendezvousServer::new(server.server(), "abcdEFG12345").await; + let rendezvous_server = + MockedRendezvousServer::new(server.server(), "abcdEFG12345", Duration::MAX).await; debug!("Set up rendezvous server mock at {}", rendezvous_server.rendezvous_url); let device_authorization_grant = AuthorizationGrant { @@ -943,7 +945,8 @@ mod test { #[async_test] async fn test_grant_login_with_scanned_qr_code_with_homeserver_swap() { let server = MatrixMockServer::new().await; - let rendezvous_server = MockedRendezvousServer::new(server.server(), "abcdEFG12345").await; + let rendezvous_server = + MockedRendezvousServer::new(server.server(), "abcdEFG12345", Duration::MAX).await; debug!("Set up rendezvous server mock at {}", rendezvous_server.rendezvous_url); let device_authorization_grant = AuthorizationGrant { @@ -1072,7 +1075,8 @@ mod test { async fn test_grant_login_with_generated_qr_code_unexpected_message_instead_of_login_protocol() { let server = MatrixMockServer::new().await; - let rendezvous_server = MockedRendezvousServer::new(server.server(), "abcdEFG12345").await; + let rendezvous_server = + MockedRendezvousServer::new(server.server(), "abcdEFG12345", Duration::MAX).await; debug!("Set up rendezvous server mock at {}", rendezvous_server.rendezvous_url); server.mock_upload_keys().ok().expect(1).named("upload_keys").mount().await; @@ -1188,7 +1192,8 @@ mod test { #[async_test] async fn test_grant_login_with_scanned_qr_code_unexpected_message_instead_of_login_protocol() { let server = MatrixMockServer::new().await; - let rendezvous_server = MockedRendezvousServer::new(server.server(), "abcdEFG12345").await; + let rendezvous_server = + MockedRendezvousServer::new(server.server(), "abcdEFG12345", Duration::MAX).await; debug!("Set up rendezvous server mock at {}", rendezvous_server.rendezvous_url); server.mock_upload_keys().ok().expect(1).named("upload_keys").mount().await; @@ -1290,7 +1295,8 @@ mod test { #[async_test] async fn test_grant_login_with_generated_qr_code_device_already_exists() { let server = MatrixMockServer::new().await; - let rendezvous_server = MockedRendezvousServer::new(server.server(), "abcdEFG12345").await; + let rendezvous_server = + MockedRendezvousServer::new(server.server(), "abcdEFG12345", Duration::MAX).await; debug!("Set up rendezvous server mock at {}", rendezvous_server.rendezvous_url); let device_authorization_grant = AuthorizationGrant { @@ -1415,7 +1421,8 @@ mod test { #[async_test] async fn test_grant_login_with_scanned_qr_code_device_already_exists() { let server = MatrixMockServer::new().await; - let rendezvous_server = MockedRendezvousServer::new(server.server(), "abcdEFG12345").await; + let rendezvous_server = + MockedRendezvousServer::new(server.server(), "abcdEFG12345", Duration::MAX).await; debug!("Set up rendezvous server mock at {}", rendezvous_server.rendezvous_url); let device_authorization_grant = AuthorizationGrant { @@ -1525,7 +1532,8 @@ mod test { #[async_test] async fn test_grant_login_with_generated_qr_code_device_not_created() { let server = MatrixMockServer::new().await; - let rendezvous_server = MockedRendezvousServer::new(server.server(), "abcdEFG12345").await; + let rendezvous_server = + MockedRendezvousServer::new(server.server(), "abcdEFG12345", Duration::MAX).await; debug!("Set up rendezvous server mock at {}", rendezvous_server.rendezvous_url); let device_authorization_grant = AuthorizationGrant { @@ -1660,7 +1668,8 @@ mod test { #[async_test] async fn test_grant_login_with_scanned_qr_code_device_not_created() { let server = MatrixMockServer::new().await; - let rendezvous_server = MockedRendezvousServer::new(server.server(), "abcdEFG12345").await; + let rendezvous_server = + MockedRendezvousServer::new(server.server(), "abcdEFG12345", Duration::MAX).await; debug!("Set up rendezvous server mock at {}", rendezvous_server.rendezvous_url); let device_authorization_grant = AuthorizationGrant { diff --git a/crates/matrix-sdk/src/authentication/oauth/qrcode/login.rs b/crates/matrix-sdk/src/authentication/oauth/qrcode/login.rs index d0d4ff026..a0b11719e 100644 --- a/crates/matrix-sdk/src/authentication/oauth/qrcode/login.rs +++ b/crates/matrix-sdk/src/authentication/oauth/qrcode/login.rs @@ -484,6 +484,8 @@ impl<'a> LoginWithGeneratedQrCode<'a> { #[cfg(all(test, not(target_family = "wasm")))] mod test { + use std::time::Duration; + use assert_matches2::{assert_let, assert_matches}; use futures_util::StreamExt; use matrix_sdk_base::crypto::types::{SecretsBundle, qr_login::QrCodeModeData}; @@ -583,7 +585,8 @@ mod test { #[async_test] async fn test_qr_login() { let server = MatrixMockServer::new().await; - let rendezvous_server = MockedRendezvousServer::new(server.server(), "abcdEFG12345").await; + let rendezvous_server = + MockedRendezvousServer::new(server.server(), "abcdEFG12345", Duration::MAX).await; let (sender, receiver) = tokio::sync::oneshot::channel(); let oauth_server = server.oauth(); @@ -742,7 +745,8 @@ mod test { #[async_test] async fn test_generated_qr_login() { let server = MatrixMockServer::new().await; - let rendezvous_server = MockedRendezvousServer::new(server.server(), "abcdEFG12345").await; + let rendezvous_server = + MockedRendezvousServer::new(server.server(), "abcdEFG12345", Duration::MAX).await; let (qr_sender, qr_receiver) = tokio::sync::oneshot::channel(); let (cctx_sender, cctx_receiver) = tokio::sync::oneshot::channel(); @@ -842,7 +846,8 @@ mod test { #[async_test] async fn test_generated_qr_login_with_homeserver_swap() { let server = MatrixMockServer::new().await; - let rendezvous_server = MockedRendezvousServer::new(server.server(), "abcdEFG12345").await; + let rendezvous_server = + MockedRendezvousServer::new(server.server(), "abcdEFG12345", Duration::MAX).await; let (qr_sender, qr_receiver) = tokio::sync::oneshot::channel(); let (cctx_sender, cctx_receiver) = tokio::sync::oneshot::channel(); @@ -948,7 +953,8 @@ mod test { alice_behavior: AliceBehaviour, ) -> Result<(), QRCodeLoginError> { let server = MatrixMockServer::new().await; - let rendezvous_server = MockedRendezvousServer::new(server.server(), "abcdEFG12345").await; + let rendezvous_server = + MockedRendezvousServer::new(server.server(), "abcdEFG12345", Duration::MAX).await; let (sender, receiver) = tokio::sync::oneshot::channel(); let oauth_server = server.oauth(); @@ -1020,7 +1026,8 @@ mod test { alice_behavior: AliceBehaviour, ) -> Result<(), QRCodeLoginError> { let server = MatrixMockServer::new().await; - let rendezvous_server = MockedRendezvousServer::new(server.server(), "abcdEFG12345").await; + let rendezvous_server = + MockedRendezvousServer::new(server.server(), "abcdEFG12345", Duration::MAX).await; let (qr_sender, qr_receiver) = tokio::sync::oneshot::channel(); let (cctx_sender, cctx_receiver) = tokio::sync::oneshot::channel(); @@ -1239,7 +1246,8 @@ mod test { #[async_test] async fn test_device_authorization_endpoint_missing() { let server = MatrixMockServer::new().await; - let rendezvous_server = MockedRendezvousServer::new(server.server(), "abcdEFG12345").await; + let rendezvous_server = + MockedRendezvousServer::new(server.server(), "abcdEFG12345", Duration::MAX).await; let (sender, receiver) = tokio::sync::oneshot::channel(); let oauth_server = server.oauth(); diff --git a/crates/matrix-sdk/src/authentication/oauth/qrcode/secure_channel.rs b/crates/matrix-sdk/src/authentication/oauth/qrcode/secure_channel.rs index 184442b5f..eef27f930 100644 --- a/crates/matrix-sdk/src/authentication/oauth/qrcode/secure_channel.rs +++ b/crates/matrix-sdk/src/authentication/oauth/qrcode/secure_channel.rs @@ -229,14 +229,18 @@ impl EstablishedSecureChannel { #[cfg(all(test, not(target_family = "wasm")))] pub(super) mod test { - use std::sync::{ - Arc, Mutex, - atomic::{AtomicU8, Ordering}, + use std::{ + sync::{ + Arc, Mutex, + atomic::{AtomicU8, Ordering}, + }, + time::Duration, }; use matrix_sdk_base::crypto::types::qr_login::QrCodeMode; use matrix_sdk_common::executor::spawn; use matrix_sdk_test::async_test; + use ruma::time::Instant; use serde_json::json; use similar_asserts::assert_eq; use url::Url; @@ -252,7 +256,9 @@ pub(super) mod test { pub struct MockedRendezvousServer { pub homeserver_url: Url, pub rendezvous_url: Url, + expiration: Duration, content: Arc>>, + created: Arc>>, etag: Arc, post_guard: MockGuard, put_guard: MockGuard, @@ -260,8 +266,9 @@ pub(super) mod test { } impl MockedRendezvousServer { - pub async fn new(server: &MockServer, location: &str) -> Self { + pub async fn new(server: &MockServer, location: &str, expiration: Duration) -> Self { let content: Arc>> = Mutex::default().into(); + let created: Arc>> = Mutex::default().into(); let etag = Arc::new(AtomicU8::new(0)); let homeserver_url = Url::parse(&server.uri()) @@ -275,7 +282,9 @@ pub(super) mod test { .register_as_scoped( Mock::given(method("POST")) .and(path("/_matrix/client/unstable/org.matrix.msc4108/rendezvous")) - .respond_with( + .respond_with({ + *created.lock().unwrap() = Some(Instant::now()); + ResponseTemplate::new(200) .append_header("X-Max-Bytes", "10240") .append_header("ETag", "1") @@ -283,8 +292,8 @@ pub(super) mod test { .append_header("Last-Modified", "Wed, 07 Sep 2022 14:27:51 GMT") .set_body_json(json!({ "url": rendezvous_url, - })), - ), + })) + }), ) .await; @@ -292,9 +301,18 @@ pub(super) mod test { .register_as_scoped( Mock::given(method("PUT")).and(path("/abcdEFG12345")).respond_with({ let content = content.clone(); + let created = created.clone(); let etag = etag.clone(); move |request: &wiremock::Request| { + // Fail the request if the session has expired. + if created.lock().unwrap().unwrap().elapsed() > expiration { + return ResponseTemplate::new(404).set_body_json(json!({ + "errcode": "M_NOT_FOUND", + "error": "This rendezvous session does not exist.", + })); + } + *content.lock().unwrap() = Some(String::from_utf8(request.body.clone()).unwrap()); let current_etag = etag.fetch_add(1, Ordering::SeqCst); @@ -312,9 +330,18 @@ pub(super) mod test { .register_as_scoped( Mock::given(method("GET")).and(path("/abcdEFG12345")).respond_with({ let content = content.clone(); + let created = created.clone(); let etag = etag.clone(); move |request: &wiremock::Request| { + // Fail the request if the session has expired. + if created.lock().unwrap().unwrap().elapsed() > expiration { + return ResponseTemplate::new(404).set_body_json(json!({ + "errcode": "M_NOT_FOUND", + "error": "This rendezvous session does not exist.", + })); + } + let requested_etag = request.headers.get("if-none-match").map(|etag| { str::parse::(std::str::from_utf8(etag.as_bytes()).unwrap()) .unwrap() @@ -344,14 +371,25 @@ pub(super) mod test { ) .await; - Self { content, etag, post_guard, put_guard, get_guard, homeserver_url, rendezvous_url } + Self { + expiration, + content, + created, + etag, + post_guard, + put_guard, + get_guard, + homeserver_url, + rendezvous_url, + } } } #[async_test] async fn test_creation() { let server = MockServer::start().await; - let rendezvous_server = MockedRendezvousServer::new(&server, "abcdEFG12345").await; + let rendezvous_server = + MockedRendezvousServer::new(&server, "abcdEFG12345", Duration::MAX).await; let client = HttpClient::new(reqwest::Client::new(), Default::default()); let alice = SecureChannel::reciprocate(client, &rendezvous_server.homeserver_url)