Skip to content

Commit 2c93f9f

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

File tree

2 files changed

+152
-0
lines changed

2 files changed

+152
-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: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
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+
.http_proxy("http://localhost:8080/".to_owned())
50+
.encryption_settings(encryption_settings)
51+
.enable_share_history_on_invite(true)
52+
.build()
53+
.await?,
54+
);
55+
56+
// Alice creates an encrypted room ...
57+
let alice_room = alice
58+
.create_room(assign!(CreateRoomRequest::new(), {
59+
name: Some("Cat Photos".to_owned()),
60+
preset: Some(RoomPreset::PublicChat),
61+
}))
62+
.await?;
63+
alice_room.enable_encryption_with_state().await?;
64+
65+
// HACK: wait for sync
66+
let _ = tokio::time::sleep(Duration::from_secs(3)).await;
67+
68+
// (sanity checks)
69+
assert!(alice_room.encryption_state().is_encrypted(), "Encryption was not enabled.");
70+
assert!(
71+
alice_room.encryption_state().is_state_encrypted(),
72+
"State encryption was not enabled."
73+
);
74+
75+
info!(room_id = ?alice_room.room_id(), "Alice has created and enabled encryption in the room");
76+
77+
// ... and changes the room name
78+
let rename_event_id = alice_room
79+
.set_name("Dog Photos".to_owned())
80+
.await
81+
.expect("We should be able to rename the room")
82+
.event_id;
83+
84+
let bundle_stream = bob
85+
.encryption()
86+
.historic_room_key_stream()
87+
.await
88+
.expect("We should be able to get the bundle stream");
89+
90+
// Alice invites Bob to the room
91+
alice_room.invite_user_by_id(bob.user_id().unwrap()).await?;
92+
93+
// Alice is done. Bob has been invited and the room key bundle should have been
94+
// sent out. Let's stop syncing so the logs contain less noise.
95+
alice_sync_service.stop().await;
96+
97+
let bob_response = bob.sync_once().instrument(bob_span.clone()).await?;
98+
99+
// Bob should have received a to-device event with the payload
100+
assert_eq!(bob_response.to_device.len(), 1);
101+
let to_device_event = &bob_response.to_device[0];
102+
assert_let!(ProcessedToDeviceEvent::Decrypted { raw, .. } = to_device_event);
103+
assert_eq!(
104+
raw.get_field::<String>("type").unwrap().unwrap(),
105+
"io.element.msc4268.room_key_bundle"
106+
);
107+
108+
bob.get_room(alice_room.room_id()).expect("Bob should have received the invite");
109+
110+
pin_mut!(bundle_stream);
111+
112+
let info = bundle_stream
113+
.next()
114+
.now_or_never()
115+
.flatten()
116+
.expect("We should be notified about the received bundle");
117+
118+
assert_eq!(Some(info.sender.deref()), alice.user_id());
119+
assert_eq!(info.room_id, alice_room.room_id());
120+
121+
let bob_room = bob
122+
.join_room_by_id(alice_room.room_id())
123+
.instrument(bob_span.clone())
124+
.await
125+
.expect("Bob should be able to accept the invitation from Alice");
126+
127+
// Sync the room, so the rename event arrives.
128+
let _ = bob.sync_once().instrument(bob_span.clone()).await?;
129+
130+
// Check it has been applied.
131+
assert_eq!(
132+
"Dog Photos",
133+
bob_room.name().unwrap(),
134+
"Bob's copy of the room name should have updated."
135+
);
136+
137+
// Let's also check we can inspect the payload manually.
138+
let rename_event = bob_room
139+
.event(&rename_event_id, None)
140+
.instrument(bob_span.clone())
141+
.await
142+
.expect("Bob should be able to fetch the historic event.");
143+
144+
assert_let_decrypted_state_event_content!(
145+
AnyStateEventContent::RoomName(content) = rename_event
146+
);
147+
148+
assert_eq!("Dog Photos", content.name);
149+
150+
Ok(())
151+
}

0 commit comments

Comments
 (0)