Skip to content

Commit 6c49a11

Browse files
committed
test(e2ee): Add integration test for encrypted state events
1 parent 03138b3 commit 6c49a11

File tree

2 files changed

+151
-0
lines changed

2 files changed

+151
-0
lines changed

testing/matrix-sdk-integration-testing/src/tests/e2ee/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ use tracing::{debug, warn};
4343
use crate::helpers::{SyncTokenAwareClient, TestClientBuilder};
4444

4545
mod shared_history;
46+
mod state_events;
4647

4748
// This test reproduces a bug seen on clients that use the same `Client`
4849
// instance for both the usual sliding sync loop and for getting the event for a
Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
use std::{ops::Deref, time::Duration};
2+
3+
use anyhow::Result;
4+
use assert_matches2::assert_let;
5+
use assign::assign;
6+
use futures::{FutureExt, StreamExt, pin_mut};
7+
use matrix_sdk::{
8+
assert_let_decrypted_state_event_content,
9+
encryption::EncryptionSettings,
10+
ruma::{
11+
api::client::room::create_room::v3::{Request as CreateRoomRequest, RoomPreset},
12+
events::AnyStateEventContent,
13+
},
14+
};
15+
use matrix_sdk_common::deserialized_responses::ProcessedToDeviceEvent;
16+
use matrix_sdk_ui::sync_service::SyncService;
17+
use similar_asserts::assert_eq;
18+
use tracing::{Instrument, info};
19+
20+
use crate::helpers::{SyncTokenAwareClient, TestClientBuilder};
21+
22+
#[tokio::test(flavor = "multi_thread", worker_threads = 4)]
23+
async fn test_e2ee_state_events() -> Result<()> {
24+
let alice_span = tracing::info_span!("alice");
25+
let bob_span = tracing::info_span!("bob");
26+
27+
let encryption_settings =
28+
EncryptionSettings { auto_enable_cross_signing: true, ..Default::default() };
29+
30+
let alice = TestClientBuilder::new("alice")
31+
.use_sqlite()
32+
.encryption_settings(encryption_settings)
33+
.enable_share_history_on_invite(true)
34+
.build()
35+
.await?;
36+
37+
let sync_service_span = tracing::info_span!(parent: &alice_span, "sync_service");
38+
let alice_sync_service = SyncService::builder(alice.clone())
39+
.with_parent_span(sync_service_span)
40+
.build()
41+
.await
42+
.expect("Could not build alice sync service");
43+
44+
alice.encryption().wait_for_e2ee_initialization_tasks().await;
45+
alice_sync_service.start().await;
46+
47+
let bob = SyncTokenAwareClient::new(
48+
TestClientBuilder::new("bob")
49+
.encryption_settings(encryption_settings)
50+
.enable_share_history_on_invite(true)
51+
.build()
52+
.await?,
53+
);
54+
55+
// Alice creates an encrypted room ...
56+
let alice_room = alice
57+
.create_room(assign!(CreateRoomRequest::new(), {
58+
name: Some("Cat Photos".to_owned()),
59+
preset: Some(RoomPreset::PublicChat),
60+
}))
61+
.await?;
62+
alice_room.enable_encryption_with_state().await?;
63+
64+
// HACK: wait for sync
65+
let _ = tokio::time::sleep(Duration::from_secs(3)).await;
66+
67+
// (sanity checks)
68+
assert!(alice_room.encryption_state().is_encrypted(), "Encryption was not enabled.");
69+
assert!(
70+
alice_room.encryption_state().is_state_encrypted(),
71+
"State encryption was not enabled."
72+
);
73+
74+
info!(room_id = ?alice_room.room_id(), "Alice has created and enabled encryption in the room");
75+
76+
// ... and changes the room name
77+
let rename_event_id = alice_room
78+
.set_name("Dog Photos".to_owned())
79+
.await
80+
.expect("We should be able to rename the room")
81+
.event_id;
82+
83+
let bundle_stream = bob
84+
.encryption()
85+
.historic_room_key_stream()
86+
.await
87+
.expect("We should be able to get the bundle stream");
88+
89+
// Alice invites Bob to the room
90+
alice_room.invite_user_by_id(bob.user_id().unwrap()).await?;
91+
92+
// Alice is done. Bob has been invited and the room key bundle should have been
93+
// sent out. Let's stop syncing so the logs contain less noise.
94+
alice_sync_service.stop().await;
95+
96+
let bob_response = bob.sync_once().instrument(bob_span.clone()).await?;
97+
98+
// Bob should have received a to-device event with the payload
99+
assert_eq!(bob_response.to_device.len(), 1);
100+
let to_device_event = &bob_response.to_device[0];
101+
assert_let!(ProcessedToDeviceEvent::Decrypted { raw, .. } = to_device_event);
102+
assert_eq!(
103+
raw.get_field::<String>("type").unwrap().unwrap(),
104+
"io.element.msc4268.room_key_bundle"
105+
);
106+
107+
bob.get_room(alice_room.room_id()).expect("Bob should have received the invite");
108+
109+
pin_mut!(bundle_stream);
110+
111+
let info = bundle_stream
112+
.next()
113+
.now_or_never()
114+
.flatten()
115+
.expect("We should be notified about the received bundle");
116+
117+
assert_eq!(Some(info.sender.deref()), alice.user_id());
118+
assert_eq!(info.room_id, alice_room.room_id());
119+
120+
let bob_room = bob
121+
.join_room_by_id(alice_room.room_id())
122+
.instrument(bob_span.clone())
123+
.await
124+
.expect("Bob should be able to accept the invitation from Alice");
125+
126+
// Sync the room, so the rename event arrives.
127+
let _ = bob.sync_once().instrument(bob_span.clone()).await?;
128+
129+
// Check it has been applied.
130+
assert_eq!(
131+
"Dog Photos",
132+
bob_room.name().unwrap(),
133+
"Bob's copy of the room name should have updated."
134+
);
135+
136+
// Let's also check we can inspect the payload manually.
137+
let rename_event = bob_room
138+
.event(&rename_event_id, None)
139+
.instrument(bob_span.clone())
140+
.await
141+
.expect("Bob should be able to fetch the historic event.");
142+
143+
assert_let_decrypted_state_event_content!(
144+
AnyStateEventContent::RoomName(content) = rename_event
145+
);
146+
147+
assert_eq!("Dog Photos", content.name);
148+
149+
Ok(())
150+
}

0 commit comments

Comments
 (0)