Files
matrix-rust-sdk/crates/matrix-sdk/tests/integration/encryption/shared_history.rs
Kévin Commaille 1eb8f6ac16 Fix shared history test
Signed-off-by: Kévin Commaille <zecakeh@tedomum.fr>
2025-08-12 17:25:22 +03:00

203 lines
6.5 KiB
Rust

use futures_util::{FutureExt, StreamExt};
use matrix_sdk::{
assert_decrypted_message_eq, assert_next_matches_with_timeout,
deserialized_responses::{TimelineEvent, UnableToDecryptInfo, UnableToDecryptReason},
encryption::EncryptionSettings,
test_utils::mocks::MatrixMockServer,
};
use matrix_sdk_test::{
async_test, event_factory::EventFactory, InvitedRoomBuilder, JoinedRoomBuilder, StateTestEvent,
};
use ruma::{
device_id, event_id, events::room::message::RoomMessageEventContent, mxc_uri, room_id, user_id,
};
#[async_test]
async fn test_shared_history_out_of_order() {
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;
let bob = matrix_mock_server
.client_builder_for_crypto_end_to_end(bob_user_id, bob_device_id)
.on_builder(|builder| {
builder
.with_enable_share_history_on_invite(true)
.with_encryption_settings(encryption_settings)
})
.build()
.await;
matrix_mock_server.exchange_e2ee_identities(&alice, &bob).await;
let event_factory = EventFactory::new().room(room_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(StateTestEvent::Create)
.add_state_event(StateTestEvent::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")
.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 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
.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.into_raw()),
);
})
.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");
let details = bob_room
.invite_acceptance_details()
.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()
.do_not_expect_access_token()
.ok_bytes(bundle)
.mock_once()
.named("media_download")
.mount()
.await;
let mut room_key_stream = bob
.encryption()
.room_keys_received_stream()
.await
.expect("We should be able to listen to received room keys");
matrix_mock_server
.mock_sync()
.ok_and_run(&bob, |builder| {
builder.add_to_device_event(
bundle_info
.deserialize_as()
.expect("We should be able to deserialize the bundle info"),
);
})
.await;
let bundle_notification = bundle_stream
.next()
.now_or_never()
.flatten()
.expect("We should have been notiifed about the received bundle");
assert_eq!(bundle_notification.sender, alice_user_id);
assert_eq!(bundle_notification.room_id, room_id);
assert_next_matches_with_timeout!(room_key_stream, 1000, Ok(room_key_infos) => assert_eq!(room_key_infos.len(), 1));
let event = event_receiver.await.expect("We should have received Alice's event");
matrix_mock_server
.mock_room_event()
.room(room_id)
.match_event_id()
.ok(TimelineEvent::from_utd(
event,
UnableToDecryptInfo { session_id: None, reason: UnableToDecryptReason::Unknown },
))
.mock_once()
.mount()
.await;
let event = bob_room
.event(&event_id, None)
.await
.expect("Bob should be able to fetch the event Alice has sent");
assert_decrypted_message_eq!(
event,
"It's a secret to everybody",
"The decrypted event should match the message Alice has sent"
);
}