Skip to content

Commit c70a005

Browse files
committed
WidgetDriver: integration tests for the toDevice feature.
1 parent 4fba759 commit c70a005

File tree

3 files changed

+233
-35
lines changed
  • crates/matrix-sdk
  • testing/matrix-sdk-test/src/sync_builder

3 files changed

+233
-35
lines changed

crates/matrix-sdk/src/test_utils/mocks/mod.rs

+61
Original file line numberDiff line numberDiff line change
@@ -778,6 +778,56 @@ impl MatrixMockServer {
778778
self.mock_endpoint(mock, DeleteRoomKeysVersionEndpoint).expect_default_access_token()
779779
}
780780

781+
/// Creates a prebuilt mock for the `/sendToDevice` endpoint.
782+
///
783+
/// This mock can be used to simulate sending to-device messages in tests.
784+
/// # Examples
785+
///
786+
/// ```
787+
/// # #[cfg(feature = "e2e-encryption")]
788+
/// # {
789+
/// # tokio_test::block_on(async {
790+
/// use std::collections::BTreeMap;
791+
/// use matrix_sdk::{
792+
/// ruma::{
793+
/// serde::Raw,
794+
/// api::client::to_device::send_event_to_device::v3::Request as ToDeviceRequest,
795+
/// to_device::DeviceIdOrAllDevices,
796+
/// user_id,owned_device_id
797+
/// },
798+
/// test_utils::mocks::MatrixMockServer,
799+
/// };
800+
/// use serde_json::json;
801+
///
802+
/// let mock_server = MatrixMockServer::new().await;
803+
/// let client = mock_server.client_builder().build().await;
804+
///
805+
/// mock_server.mock_send_to_device().ok().mock_once().mount().await;
806+
///
807+
/// let request = ToDeviceRequest::new_raw(
808+
/// "m.custom.event".into(),
809+
/// "txn_id".into(),
810+
/// BTreeMap::from([
811+
/// (user_id!("@alice:localhost").to_owned(), BTreeMap::from([(
812+
/// DeviceIdOrAllDevices::AllDevices,
813+
/// Raw::new(&ruma::events::AnyToDeviceEventContent::Dummy(ruma::events::dummy::ToDeviceDummyEventContent {})).unwrap(),
814+
/// )])),
815+
/// ])
816+
/// );
817+
///
818+
/// client
819+
/// .send(request)
820+
/// .await
821+
/// .expect("We should be able to send a to-device message");
822+
/// # anyhow::Ok(()) });
823+
/// # }
824+
/// ```
825+
pub fn mock_send_to_device(&self) -> MockEndpoint<'_, SendToDeviceEndpoint> {
826+
let mock =
827+
Mock::given(method("PUT")).and(path_regex(r"^/_matrix/client/v3/sendToDevice/.*/.*"));
828+
self.mock_endpoint(mock, SendToDeviceEndpoint).expect_default_access_token()
829+
}
830+
781831
/// Create a prebuilt mock for getting the room members in a room.
782832
///
783833
/// # Examples
@@ -2315,6 +2365,17 @@ impl<'a> MockEndpoint<'a, DeleteRoomKeysVersionEndpoint> {
23152365
}
23162366
}
23172367

2368+
/// A prebuilt mock for the `/sendToDevice` endpoint.
2369+
///
2370+
/// This mock can be used to simulate sending to-device messages in tests.
2371+
pub struct SendToDeviceEndpoint;
2372+
impl<'a> MockEndpoint<'a, SendToDeviceEndpoint> {
2373+
/// Returns a successful response with default data.
2374+
pub fn ok(self) -> MatrixMock<'a> {
2375+
self.respond_with(ResponseTemplate::new(200).set_body_json(json!({})))
2376+
}
2377+
}
2378+
23182379
/// A prebuilt mock for `GET /members` request.
23192380
pub struct GetRoomMembersEndpoint;
23202381

crates/matrix-sdk/tests/integration/widget.rs

+163-33
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ async fn run_test_driver(
8585

8686
async fn recv_message(driver_handle: &WidgetDriverHandle) -> JsonObject {
8787
let fut = pin!(driver_handle.recv());
88-
let msg = timeout(fut, Duration::from_secs(1)).await.unwrap();
88+
let msg = timeout(fut, Duration::from_secs(10)).await.unwrap();
8989
serde_json::from_str(&msg.unwrap()).unwrap()
9090
}
9191

@@ -95,15 +95,15 @@ async fn send_request(
9595
action: &str,
9696
data: impl Serialize,
9797
) {
98-
let sent = driver_handle
99-
.send(json_string!({
100-
"api": "fromWidget",
101-
"widgetId": WIDGET_ID,
102-
"requestId": request_id,
103-
"action": action,
104-
"data": data,
105-
}))
106-
.await;
98+
let json_string = json_string!({
99+
"api": "fromWidget",
100+
"widgetId": WIDGET_ID,
101+
"requestId": request_id,
102+
"action": action,
103+
"data": data,
104+
});
105+
println!("Json string sent from the widget {}", json_string);
106+
let sent = driver_handle.send(json_string).await;
107107
assert!(sent);
108108
}
109109

@@ -381,6 +381,8 @@ async fn test_receive_live_events() {
381381
"org.matrix.msc2762.receive.event:m.room.message#m.text",
382382
"org.matrix.msc2762.receive.state_event:m.room.name#",
383383
"org.matrix.msc2762.receive.state_event:m.room.member#@example:localhost",
384+
"org.matrix.msc2762.receive.state_event:m.room.member#@example:localhost",
385+
"org.matrix.msc3819.receive.to_device:my.custom.to.device"
384386
]),
385387
)
386388
.await;
@@ -417,32 +419,63 @@ async fn test_receive_live_events() {
417419
// set room name - matches filter #3
418420
.add_timeline_event(f.room_name("New Room Name").sender(&BOB)),
419421
);
422+
// to device message - doesn't match
423+
sync_builder.add_to_device_event(json!({
424+
"sender": "@alice:example.com",
425+
"type": "m.not_matching.to.device",
426+
"content": {
427+
"a": "test",
428+
}
429+
}));
430+
// to device message - matches filter #6
431+
sync_builder.add_to_device_event(json!({
432+
"sender": "@alice:example.com",
433+
"type": "my.custom.to.device",
434+
"content": {
435+
"a": "test",
436+
}
437+
}
438+
));
420439
})
421440
.await;
422441

423-
let msg = recv_message(&driver_handle).await;
424-
assert_eq!(msg["api"], "toWidget");
425-
assert_eq!(msg["action"], "send_event");
426-
assert_eq!(msg["data"]["type"], "m.room.message");
427-
assert_eq!(msg["data"]["room_id"], ROOM_ID.as_str());
428-
assert_eq!(msg["data"]["content"]["msgtype"], "m.text");
429-
assert_eq!(msg["data"]["content"]["body"], "simple text message");
430-
431-
let msg = recv_message(&driver_handle).await;
432-
assert_eq!(msg["api"], "toWidget");
433-
assert_eq!(msg["action"], "send_event");
434-
assert_eq!(msg["data"]["type"], "m.room.member");
435-
assert_eq!(msg["data"]["room_id"], ROOM_ID.as_str());
436-
assert_eq!(msg["data"]["state_key"], "@example:localhost");
437-
assert_eq!(msg["data"]["content"]["membership"], "join");
438-
assert_eq!(msg["data"]["unsigned"]["prev_content"]["membership"], "join");
442+
// The to device and room events are racing -> we dont know the order and just
443+
// need to store them separately.
444+
let mut to_device: JsonObject = JsonObject::new();
445+
let mut events = vec![];
446+
for _ in 0..4 {
447+
let msg = recv_message(&driver_handle).await;
448+
if msg["action"] == "send_to_device" {
449+
to_device = msg;
450+
} else {
451+
events.push(msg);
452+
}
453+
}
439454

440-
let msg = recv_message(&driver_handle).await;
441-
assert_eq!(msg["api"], "toWidget");
442-
assert_eq!(msg["action"], "send_event");
443-
assert_eq!(msg["data"]["type"], "m.room.name");
444-
assert_eq!(msg["data"]["sender"], BOB.as_str());
445-
assert_eq!(msg["data"]["content"]["name"], "New Room Name");
455+
assert_eq!(events[0]["api"], "toWidget");
456+
assert_eq!(events[0]["action"], "send_event");
457+
assert_eq!(events[0]["data"]["type"], "m.room.message");
458+
assert_eq!(events[0]["data"]["room_id"], ROOM_ID.as_str());
459+
assert_eq!(events[0]["data"]["content"]["msgtype"], "m.text");
460+
assert_eq!(events[0]["data"]["content"]["body"], "simple text message");
461+
462+
assert_eq!(events[1]["api"], "toWidget");
463+
assert_eq!(events[1]["action"], "send_event");
464+
assert_eq!(events[1]["data"]["type"], "m.room.member");
465+
assert_eq!(events[1]["data"]["room_id"], ROOM_ID.as_str());
466+
assert_eq!(events[1]["data"]["state_key"], "@example:localhost");
467+
assert_eq!(events[1]["data"]["content"]["membership"], "join");
468+
assert_eq!(events[1]["data"]["unsigned"]["prev_content"]["membership"], "join");
469+
470+
assert_eq!(events[2]["api"], "toWidget");
471+
assert_eq!(events[2]["action"], "send_event");
472+
assert_eq!(events[2]["data"]["type"], "m.room.name");
473+
assert_eq!(events[2]["data"]["sender"], BOB.as_str());
474+
assert_eq!(events[2]["data"]["content"]["name"], "New Room Name");
475+
476+
assert_eq!(to_device["api"], "toWidget");
477+
assert_eq!(to_device["action"], "send_to_device");
478+
assert_eq!(to_device["data"]["type"], "my.custom.to.device");
446479

447480
// No more messages from the driver
448481
assert_matches!(recv_message(&driver_handle).now_or_never(), None);
@@ -816,7 +849,6 @@ async fn test_send_redaction() {
816849
]),
817850
)
818851
.await;
819-
820852
mock_server.mock_room_redact().ok(event_id!("$redact_event_id")).mock_once().mount().await;
821853

822854
send_request(
@@ -843,6 +875,104 @@ async fn test_send_redaction() {
843875
assert_eq!(redact_room_id, "!a98sd12bjh:example.org");
844876
}
845877

878+
async fn send_to_device_test_helper(
879+
request_id: &str,
880+
data: JsonValue,
881+
expected_response: JsonValue,
882+
calls: u64,
883+
) -> JsonValue {
884+
let (_, mock_server, driver_handle) = run_test_driver(false).await;
885+
886+
negotiate_capabilities(
887+
&driver_handle,
888+
json!([
889+
"org.matrix.msc3819.send.to_device:my.custom.to_device_type",
890+
"org.matrix.msc3819.send.to_device:my.other_type"
891+
]),
892+
)
893+
.await;
894+
895+
mock_server.mock_send_to_device().ok().expect(calls).mount().await;
896+
897+
send_request(&driver_handle, request_id, "send_to_device", data).await;
898+
899+
// Receive the response
900+
let msg = recv_message(&driver_handle).await;
901+
assert_eq!(msg["api"], "fromWidget");
902+
assert_eq!(msg["action"], "send_to_device");
903+
let response = msg["response"].clone();
904+
assert_eq!(
905+
serde_json::to_string(&response).unwrap(),
906+
serde_json::to_string(&expected_response).unwrap()
907+
);
908+
909+
response
910+
}
911+
912+
#[async_test]
913+
async fn test_send_to_device_event() {
914+
send_to_device_test_helper(
915+
"id_my.custom.to_device_type",
916+
json!({
917+
"type":"my.custom.to_device_type",
918+
"encrypted": false,
919+
"messages":{
920+
"@username:test.org": {
921+
"DEVICEID": {
922+
"param1":"test",
923+
},
924+
},
925+
}
926+
}),
927+
json! {{}},
928+
1,
929+
)
930+
.await;
931+
}
932+
933+
#[async_test]
934+
async fn test_error_to_device_event_no_permission() {
935+
send_to_device_test_helper(
936+
"id_my.unallowed_type",
937+
json!({
938+
"type": "my.unallowed_type",
939+
"encrypted": false,
940+
"messages": {
941+
"@username:test.org": {
942+
"DEVICEID": {
943+
"param1":"test",
944+
},
945+
},
946+
}
947+
}),
948+
// this means the server did not get the correct event type
949+
json! {{"error": {"message": "Not allowed to send to-device message of type: my.unallowed_type"}}},
950+
0
951+
)
952+
.await;
953+
}
954+
955+
#[async_test]
956+
async fn test_send_encrypted_to_device_event() {
957+
send_to_device_test_helper(
958+
"my.custom.to_device_type",
959+
json!({
960+
"type": "my.custom.to_device_type",
961+
"encrypted": true,
962+
"messages":{
963+
"@username:test.org": {
964+
"DEVICEID": {
965+
"param1":"test",
966+
},
967+
},
968+
}
969+
}),
970+
json! {{}},
971+
1,
972+
)
973+
.await;
974+
}
975+
846976
async fn negotiate_capabilities(driver_handle: &WidgetDriverHandle, caps: JsonValue) {
847977
{
848978
// Receive toWidget capabilities request

testing/matrix-sdk-test/src/sync_builder/mod.rs

+9-2
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use ruma::{
88
},
99
IncomingResponse,
1010
},
11-
events::{presence::PresenceEvent, AnyGlobalAccountDataEvent},
11+
events::{presence::PresenceEvent, AnyGlobalAccountDataEvent, AnyToDeviceEvent},
1212
serde::Raw,
1313
OwnedRoomId, OwnedUserId, UserId,
1414
};
@@ -58,6 +58,7 @@ pub struct SyncResponseBuilder {
5858
batch_counter: i64,
5959
/// The device lists of the user.
6060
changed_device_lists: Vec<OwnedUserId>,
61+
to_device_events: Vec<Raw<AnyToDeviceEvent>>,
6162
}
6263

6364
impl SyncResponseBuilder {
@@ -162,6 +163,12 @@ impl SyncResponseBuilder {
162163
self
163164
}
164165

166+
/// Add a to device event.
167+
pub fn add_to_device_event(&mut self, event: JsonValue) -> &mut Self {
168+
self.to_device_events.push(from_json_value(event).unwrap());
169+
self
170+
}
171+
165172
/// Builds a sync response as a JSON Value containing the events we queued
166173
/// so far.
167174
///
@@ -191,7 +198,7 @@ impl SyncResponseBuilder {
191198
"knock": self.knocked_rooms,
192199
},
193200
"to_device": {
194-
"events": []
201+
"events": self.to_device_events,
195202
},
196203
"presence": {
197204
"events": self.presence,

0 commit comments

Comments
 (0)