From c864fac82319aa7efa288e26bee877b89a05a545 Mon Sep 17 00:00:00 2001 From: Skye Elliot Date: Wed, 4 Mar 2026 14:19:59 +0000 Subject: [PATCH] test(sdk): De-duplicate shared history integration tests Signed-off-by: Skye Elliot --- .../integration/encryption/shared_history.rs | 204 +++++++----------- 1 file changed, 73 insertions(+), 131 deletions(-) diff --git a/crates/matrix-sdk/tests/integration/encryption/shared_history.rs b/crates/matrix-sdk/tests/integration/encryption/shared_history.rs index 2c714b125..13d00533f 100644 --- a/crates/matrix-sdk/tests/integration/encryption/shared_history.rs +++ b/crates/matrix-sdk/tests/integration/encryption/shared_history.rs @@ -1,21 +1,53 @@ use futures_util::{FutureExt, StreamExt}; use matrix_sdk::{ - assert_decrypted_message_eq, assert_next_matches_with_timeout, + Client, assert_decrypted_message_eq, assert_next_matches_with_timeout, deserialized_responses::{TimelineEvent, UnableToDecryptInfo, UnableToDecryptReason}, encryption::EncryptionSettings, test_utils::mocks::MatrixMockServer, }; +use matrix_sdk_base::crypto::types::events::room::encrypted::EncryptedToDeviceEvent; use matrix_sdk_test::{ InvitedRoomBuilder, JoinedRoomBuilder, async_test, event_factory::EventFactory, }; use ruma::{ - RoomVersionId, device_id, event_id, events::room::message::RoomMessageEventContent, mxc_uri, - room_id, user_id, + OwnedEventId, RoomVersionId, device_id, event_id, + events::{AnySyncTimelineEvent, room::message::RoomMessageEventContent}, + mxc_uri, room_id, + serde::Raw, + user_id, }; use tempfile::tempdir; -#[async_test] -async fn test_shared_history_out_of_order() { +/// Helper struct to collect test data together. +struct Test { + matrix_mock_server: MatrixMockServer, + alice: Client, + bob: Client, + bob_room: matrix_sdk::Room, + /// The encrypted event Alice sent before Bob joined. + event_id: OwnedEventId, + /// The raw bytes of the uploaded key bundle. + bundle: Vec, + /// The captured to-device event carrying bundle info. + bundle_info: Raw, + /// Receiver for the original event sent by Alice. + event_receiver: tokio::sync::oneshot::Receiver>, +} + +/// Sets up the shared-history scenario up to the point where Bob has joined the +/// room and the bundle info to-device event is ready to be delivered. +/// +/// Both tests below share this identical preamble: +/// +/// - Server + Alice + Bob clients created +/// - E2EE identities exchanged +/// - Alice creates an encrypted room and sends a message +/// - Alice invites Bob, triggering key bundle upload +/// - Bob syncs the invite and joins +/// - Bundle details are verified +async fn setup_shared_history( + bob_builder_fn: impl FnOnce(matrix_sdk::ClientBuilder) -> matrix_sdk::ClientBuilder, +) -> Test { let room_id = room_id!("!test:localhost"); let mxid = mxc_uri!("mxc://localhost/12345"); @@ -44,7 +76,7 @@ async fn test_shared_history_out_of_order() { let bob = matrix_mock_server .client_builder_for_crypto_end_to_end(bob_user_id, bob_device_id) .on_builder(|builder| { - builder + bob_builder_fn(builder) .with_enable_share_history_on_invite(true) .with_encryption_settings(encryption_settings) }) @@ -106,12 +138,6 @@ async fn test_shared_history_out_of_order() { room.invite_user_by_id(bob_user_id).await.expect("We should be able to invite Bob"); let bundle = receiver.await.expect("We should have received a bundle now."); - let mut bundle_stream = bob - .encryption() - .historic_room_key_stream() - .await - .expect("We should be able to get the bundle stream"); - let bob_member_event = event_factory.member(alice_user_id).invited(bob_user_id); matrix_mock_server @@ -143,6 +169,33 @@ async fn test_shared_history_out_of_order() { ); let bundle_info = bundle_info.await; + + Test { matrix_mock_server, alice, bob, bob_room, event_id, bundle, bundle_info, event_receiver } +} + +#[async_test] +async fn test_shared_history_out_of_order() { + let room_id = room_id!("!test:localhost"); + let alice_user_id = user_id!("@alice:localhost"); + let alice_device_id = device_id!("ALICEDEVICE"); + + let Test { + matrix_mock_server, + bob, + bob_room, + event_id, + bundle, + bundle_info, + event_receiver, + .. + } = setup_shared_history(|builder| builder).await; + + let mut bundle_stream = bob + .encryption() + .historic_room_key_stream() + .await + .expect("We should be able to get the bundle stream"); + matrix_mock_server .mock_authed_media_download() .expect_any_access_token() @@ -217,132 +270,21 @@ async fn test_shared_history_out_of_order() { #[async_test] async fn test_shared_history_crash_before_import() { let room_id = room_id!("!test:localhost"); - let mxid = mxc_uri!("mxc://localhost/12345"); - let alice_user_id = user_id!("@alice:localhost"); let alice_device_id = device_id!("ALICEDEVICE"); let bob_user_id = user_id!("@bob:localhost"); let bob_device_id = device_id!("BOBDEVICE"); - let matrix_mock_server = MatrixMockServer::new().await; - matrix_mock_server.mock_crypto_endpoints_preset().await; - matrix_mock_server.mock_invite_user_by_id().ok().mock_once().mount().await; - - let encryption_settings = - EncryptionSettings { auto_enable_cross_signing: true, ..Default::default() }; - - let alice = matrix_mock_server - .client_builder_for_crypto_end_to_end(alice_user_id, alice_device_id) - .on_builder(|builder| { - builder - .with_enable_share_history_on_invite(true) - .with_encryption_settings(encryption_settings) - }) - .build() - .await; - // Use a common store path for Bob so we can persist invite acceptance details // over the crash. let bob_sqlite_path = tempdir().unwrap(); + let encryption_settings = + EncryptionSettings { auto_enable_cross_signing: true, ..Default::default() }; - let bob = matrix_mock_server - .client_builder_for_crypto_end_to_end(bob_user_id, bob_device_id) - .on_builder(|builder| { - builder - .sqlite_store(bob_sqlite_path.path(), None) - .with_enable_share_history_on_invite(true) - .with_encryption_settings(encryption_settings) - }) - .build() - .await; + let Test { + matrix_mock_server, alice, bob, event_id, bundle, bundle_info, event_receiver, .. + } = setup_shared_history(|builder| builder.sqlite_store(bob_sqlite_path.path(), None)).await; - matrix_mock_server.exchange_e2ee_identities(&alice, &bob).await; - - let event_factory = EventFactory::new().room(room_id).sender(alice_user_id); - let alice_member_event = event_factory.member(alice_user_id).into_raw(); - - matrix_mock_server - .mock_sync() - .ok_and_run(&alice, |builder| { - builder.add_joined_room( - JoinedRoomBuilder::new(room_id) - .add_state_event(event_factory.create(alice_user_id, RoomVersionId::V1)) - .add_state_event(event_factory.room_encryption()), - ); - }) - .await; - - let room = - alice.get_room(room_id).expect("Alice should have access to the room now that we synced"); - - let event_id = event_id!("$some_id"); - let (event_receiver, mock) = - matrix_mock_server.mock_room_send().ok_with_capture(event_id, alice_user_id); - - mock.mock_once().named("send").mount().await; - - matrix_mock_server - .mock_get_members() - .ok(vec![alice_member_event.clone()]) - .mock_once() - .mount() - .await; - - let event_id = room - .send(RoomMessageEventContent::text_plain("It's a secret to everybody")) - .await - .expect("We should be able to send an initial message") - .response - .event_id; - - matrix_mock_server - .mock_authenticated_media_config() - .ok_default() - .mock_once() - .named("media_config") - .mount() - .await; - - let (receiver, upload_mock) = matrix_mock_server.mock_upload().ok_with_capture(mxid); - upload_mock.mock_once().mount().await; - - let (_guard, bundle_info) = matrix_mock_server.mock_capture_put_to_device(alice_user_id).await; - - room.invite_user_by_id(bob_user_id).await.expect("We should be able to invite Bob"); - let bundle = receiver.await.expect("We should have received a bundle now."); - - let bob_member_event = event_factory.member(alice_user_id).invited(bob_user_id); - - matrix_mock_server - .mock_sync() - .ok_and_run(&bob, |builder| { - builder.add_invited_room( - InvitedRoomBuilder::new(room_id) - .add_state_event(alice_member_event.cast()) - .add_state_event(bob_member_event), - ); - }) - .await; - - let bob_room = bob.get_room(room_id).expect("Bob should have access to the invited room"); - - matrix_mock_server.mock_room_join(room_id).ok().mock_once().named("join").mount().await; - bob_room.join().await.expect("Bob should be able to join the room"); - - // Bob should have persisted the room bundle state. - let details = bob - .get_pending_key_bundle_details_for_room(room_id) - .await - .expect("Bob should be able to get the pending key bundle details for the room") - .expect("We should have stored invite acceptance details"); - - assert_eq!( - details.inviter, - alice.user_id().unwrap(), - "We should have recorded that Alice has invited us" - ); - - let bundle_info = bundle_info.await; matrix_mock_server .mock_authed_media_download() .expect_any_access_token() @@ -380,6 +322,9 @@ async fn test_shared_history_crash_before_import() { .flatten() .expect("We should have been notified about the received bundle"); + assert_eq!(bundle_notification.sender, alice_user_id); + assert_eq!(bundle_notification.room_id, room_id); + // .. but crashes before they finish importing the bundle. drop(bob); @@ -409,9 +354,6 @@ async fn test_shared_history_crash_before_import() { "The persisted pending key bundle details should be identical" ); - assert_eq!(bundle_notification.sender, alice_user_id); - assert_eq!(bundle_notification.room_id, room_id); - // Bob's restarted client should successfully import the room keys. let mut room_key_stream = bob_restarted .encryption()