From e8d57ca675718c59ad16e9bf33c09a7a5c324c13 Mon Sep 17 00:00:00 2001 From: Timo Date: Fri, 8 Nov 2024 13:39:37 +0100 Subject: [PATCH 1/4] refactor(widget, mocks): Use `MockMatrixServer` in the widget integration tests where appropriate. --- crates/matrix-sdk/src/test_utils/mocks.rs | 97 ++++++++++++++++++- crates/matrix-sdk/tests/integration/widget.rs | 49 +++++----- 2 files changed, 117 insertions(+), 29 deletions(-) diff --git a/crates/matrix-sdk/src/test_utils/mocks.rs b/crates/matrix-sdk/src/test_utils/mocks.rs index 6f923eb73c..5b0ec803e0 100644 --- a/crates/matrix-sdk/src/test_utils/mocks.rs +++ b/crates/matrix-sdk/src/test_utils/mocks.rs @@ -24,10 +24,15 @@ use matrix_sdk_test::{ test_json, InvitedRoomBuilder, JoinedRoomBuilder, KnockedRoomBuilder, LeftRoomBuilder, SyncResponseBuilder, }; -use ruma::{api::MatrixVersion, device_id, user_id, MxcUri, OwnedEventId, OwnedRoomId, RoomId}; -use serde_json::json; +use ruma::{ + api::MatrixVersion, + device_id, + events::{MessageLikeEventType, StateEventType}, + user_id, MxcUri, OwnedEventId, OwnedRoomId, RoomId, +}; +use serde_json::{json, Value}; use wiremock::{ - matchers::{body_partial_json, header, method, path, path_regex}, + matchers::{body_partial_json, header, method, path, path_regex, query_param}, Mock, MockBuilder, MockGuard, MockServer, Respond, ResponseTemplate, Times, }; @@ -316,6 +321,65 @@ impl MatrixMockServer { MockEndpoint { mock, server: &self.server, endpoint: RoomSendEndpoint } } + /// Creates a prebuilt mock for sending an event with a specific type in a + /// room. + /// + /// Note: works with *any* room. + /// + /// Similar to: [mock_room_send] + /// + /// # Examples + /// + /// see also [mock_room_send] for more context. + /// + /// ``` + /// let event_id = event_id!("$some_id"); + /// mock_server + /// .mock_room_send_for_type("m.room.message") + /// .ok(event_id) + /// .expect(1) + /// .mount() + /// .await; + /// ``` + pub fn mock_room_send_for_type( + &self, + event_type: MessageLikeEventType, + ) -> MockEndpoint<'_, RoomSendEndpoint> { + let mock = Mock::given(method("PUT")) + .and(path_regex(format!(r"^/_matrix/client/r0/rooms/.*/send/{}", event_type))) + .and(header("authorization", "Bearer 1234")); + MockEndpoint { mock, server: &self.server, endpoint: RoomSendEndpoint } + } + + /// Creates a prebuilt mock for sending a state event in a room. + /// + /// Similar to: [mock_room_send] + /// + /// Note: works with *any* room. + /// Note: works with *any* event type. + pub fn mock_room_state(&self) -> MockEndpoint<'_, RoomSendEndpoint> { + let mock = Mock::given(method("PUT")) + .and(path_regex(r"^/_matrix/client/r0/rooms/.*/state/.*")) + .and(header("authorization", "Bearer 1234")); + MockEndpoint { mock, server: &self.server, endpoint: RoomSendEndpoint } + } + + /// Creates a prebuilt mock for sending a state event with a specific type + /// in a room. + /// + /// Similar to: [mock_room_send] + /// + /// Note: works with *any* room. + pub fn mock_room_state_for_type( + &self, + state_type: StateEventType, + ) -> MockEndpoint<'_, RoomSendEndpoint> { + let mock = Mock::given(method("PUT")) + .and(path_regex(format!(r"^/_matrix/client/r0/rooms/.*/state/{}", state_type))) + .and(header("authorization", "Bearer 1234")); + MockEndpoint { mock, server: &self.server, endpoint: RoomSendEndpoint } + } + /// Creates a prebuilt mock for asking whether *a* room is encrypted or not. /// /// Note: Applies to all rooms. @@ -431,6 +495,17 @@ impl MatrixMockServer { } } + /// Create a prebuild mock for reading room message with the `/messages` endpoint. + pub fn mock_room_messages(&self, limit: Option) -> MockEndpoint<'_, RoomMessagesEndpoint> { + let mut mock = Mock::given(method("GET")) + .and(path_regex(r"^/_matrix/client/r0/rooms/.*/messages$")) + .and(header("authorization", "Bearer 1234")); + if let Some(l) = limit { + mock = mock.and(query_param("limit", l.to_string())); + } + MockEndpoint { mock, server: &self.server, endpoint: RoomMessagesEndpoint {} } + } + /// Create a prebuilt mock for uploading media. pub fn mock_upload(&self) -> MockEndpoint<'_, UploadEndpoint> { let mock = Mock::given(method("POST")) @@ -757,7 +832,7 @@ impl<'a> MockEndpoint<'a, RoomSendEndpoint> { /// ); /// # anyhow::Ok(()) }); /// ``` - pub fn body_matches_partial_json(self, body: serde_json::Value) -> Self { + pub fn body_matches_partial_json(self, body: Value) -> Self { Self { mock: self.mock.and(body_partial_json(body)), ..self } } @@ -980,6 +1055,20 @@ impl<'a> MockEndpoint<'a, RoomEventEndpoint> { } } +/// A prebuilt mock for the `/messages` endpoint. +pub struct RoomMessagesEndpoint; + +/// A prebuilt mock for getting a room messages in a room. +impl<'a> MockEndpoint<'a, RoomMessagesEndpoint> { + /// Returns a messages endpoint that emulates success, i.e. the messages + /// provided as `response` could be retrieved. + pub fn ok(self, response: impl Into) -> MatrixMock<'a> { + let body: Value = response.into(); + let mock = self.mock.respond_with(ResponseTemplate::new(200).set_body_json(body)); + MatrixMock { server: self.server, mock } + } +} + /// A prebuilt mock for uploading media. pub struct UploadEndpoint; diff --git a/crates/matrix-sdk/tests/integration/widget.rs b/crates/matrix-sdk/tests/integration/widget.rs index ecd0035393..a7c58ead08 100644 --- a/crates/matrix-sdk/tests/integration/widget.rs +++ b/crates/matrix-sdk/tests/integration/widget.rs @@ -29,11 +29,14 @@ use matrix_sdk_test::{async_test, EventBuilder, JoinedRoomBuilder, ALICE, BOB}; use once_cell::sync::Lazy; use ruma::{ event_id, - events::room::{ - member::{MembershipState, RoomMemberEventContent}, - message::RoomMessageEventContent, - name::RoomNameEventContent, - topic::RoomTopicEventContent, + events::{ + room::{ + member::{MembershipState, RoomMemberEventContent}, + message::RoomMessageEventContent, + name::RoomNameEventContent, + topic::RoomTopicEventContent, + }, + StateEventType, }, owned_room_id, serde::JsonObject, @@ -335,14 +338,8 @@ async fn test_read_messages_with_msgtype_capabilities() { "end": "t47409-4357353_219380_26003_2269", "start": "t392-516_47314_0_7_1_1_1_11444_1" }); - Mock::given(method("GET")) - .and(path_regex(r"^/_matrix/client/v3/rooms/.*/messages$")) - .and(header("authorization", "Bearer 1234")) - .and(query_param("limit", "3")) - .respond_with(ResponseTemplate::new(200).set_body_json(response_json)) - .expect(1) - .mount(mock_server.server()) - .await; + + mock_server.mock_room_messages(Some(3)).ok(response_json).mock_once().mount().await; // Ask the driver to read messages send_request( @@ -508,11 +505,11 @@ async fn test_send_room_message() { negotiate_capabilities(&driver_handle, json!(["org.matrix.msc2762.send.event:m.room.message"])) .await; - Mock::given(method("PUT")) - .and(path_regex(r"^/_matrix/client/v3/rooms/.*/send/m.room.message/.*$")) - .respond_with(ResponseTemplate::new(200).set_body_json(json!({ "event_id": "$foobar" }))) - .expect(1) - .mount(mock_server.server()) + mock_server + .mock_room_send_for_type("m.room.message".into()) + .ok(event_id!("$foobar")) + .mock_once() + .mount() .await; send_request( @@ -549,11 +546,11 @@ async fn test_send_room_name() { ) .await; - Mock::given(method("PUT")) - .and(path_regex(r"^/_matrix/client/v3/rooms/.*/state/m.room.name/?$")) - .respond_with(ResponseTemplate::new(200).set_body_json(json!({ "event_id": "$foobar" }))) - .expect(1) - .mount(mock_server.server()) + mock_server + .mock_room_state_for_type(StateEventType::RoomName) + .ok(event_id!("$foobar")) + .mock_once() + .mount() .await; send_request( @@ -594,7 +591,8 @@ async fn test_send_delayed_message_event() { .await; Mock::given(method("PUT")) - .and(path_regex(r"^/_matrix/client/v3/rooms/.*/send/m.room.message/.*$")) + .and(path_regex(r"^/_matrix/client/v3/rooms/.*/send/m.room.message/.*")) + .and(query_param("org.matrix.msc4140.delay", "1000")) .respond_with(ResponseTemplate::new(200).set_body_json(json!({ "delay_id": "1234", }))) @@ -641,7 +639,8 @@ async fn test_send_delayed_state_event() { .await; Mock::given(method("PUT")) - .and(path_regex(r"^/_matrix/client/v3/rooms/.*/state/m.room.name/?$")) + .and(path_regex(r"^/_matrix/client/v3/rooms/.*/state/m.room.name/.*")) + .and(query_param("org.matrix.msc4140.delay", "1000")) .respond_with(ResponseTemplate::new(200).set_body_json(json!({ "delay_id": "1234", }))) From ee7a1c92918292721884d40f66035242c3e8ceab Mon Sep 17 00:00:00 2001 From: Timo Date: Wed, 13 Nov 2024 19:09:22 +0100 Subject: [PATCH 2/4] test fixes --- crates/matrix-sdk/src/test_utils/mocks.rs | 40 ++++++++++++++++++++--- 1 file changed, 35 insertions(+), 5 deletions(-) diff --git a/crates/matrix-sdk/src/test_utils/mocks.rs b/crates/matrix-sdk/src/test_utils/mocks.rs index 5b0ec803e0..8e7cae5f49 100644 --- a/crates/matrix-sdk/src/test_utils/mocks.rs +++ b/crates/matrix-sdk/src/test_utils/mocks.rs @@ -333,20 +333,49 @@ impl MatrixMockServer { /// see also [mock_room_send] for more context. /// /// ``` + /// # tokio_test::block_on(async { + /// use matrix_sdk::{ruma::{room_id, event_id}, test_utils::mocks::MatrixMockServer}; + /// use serde_json::json; + /// + /// let mock_server = MatrixMockServer::new().await; + /// let client = mock_server.client_builder().build().await; + /// + /// mock_server.mock_room_state_encryption().plain().mount().await; + /// + /// let room = mock_server + /// .sync_joined_room(&client, room_id!("!room_id:localhost")) + /// .await; + /// /// let event_id = event_id!("$some_id"); /// mock_server - /// .mock_room_send_for_type("m.room.message") + /// .mock_room_send_for_type("m.room.message".into()) /// .ok(event_id) /// .expect(1) /// .mount() /// .await; + /// + /// let responseNotMocked = room.send_raw("m.room.reaction", json!({ "body": "Hello world" })).await?; + /// + /// assert!( + /// responseNotMocked, + /// "The event ID we mocked should match the one we received when we sent the event" + /// ); + /// + /// let response = room.send_raw("m.room.message", json!({ "body": "Hello world" })).await?; + /// + /// assert_eq!( + /// event_id, + /// response.event_id, + /// "The event ID we mocked should match the one we received when we sent the event" + /// ); + /// # anyhow::Ok(()) }); /// ``` pub fn mock_room_send_for_type( &self, event_type: MessageLikeEventType, ) -> MockEndpoint<'_, RoomSendEndpoint> { let mock = Mock::given(method("PUT")) - .and(path_regex(format!(r"^/_matrix/client/r0/rooms/.*/send/{}", event_type))) + .and(path_regex(format!(r"^/_matrix/client/v3/rooms/.*/send/{}", event_type))) .and(header("authorization", "Bearer 1234")); MockEndpoint { mock, server: &self.server, endpoint: RoomSendEndpoint } } @@ -375,7 +404,7 @@ impl MatrixMockServer { state_type: StateEventType, ) -> MockEndpoint<'_, RoomSendEndpoint> { let mock = Mock::given(method("PUT")) - .and(path_regex(format!(r"^/_matrix/client/r0/rooms/.*/state/{}", state_type))) + .and(path_regex(format!(r"^/_matrix/client/v3/rooms/.*/state/{}", state_type))) .and(header("authorization", "Bearer 1234")); MockEndpoint { mock, server: &self.server, endpoint: RoomSendEndpoint } } @@ -495,10 +524,11 @@ impl MatrixMockServer { } } - /// Create a prebuild mock for reading room message with the `/messages` endpoint. + /// Create a prebuild mock for reading room message with the `/messages` + /// endpoint. pub fn mock_room_messages(&self, limit: Option) -> MockEndpoint<'_, RoomMessagesEndpoint> { let mut mock = Mock::given(method("GET")) - .and(path_regex(r"^/_matrix/client/r0/rooms/.*/messages$")) + .and(path_regex(r"^/_matrix/client/v3/rooms/.*/messages$")) .and(header("authorization", "Bearer 1234")); if let Some(l) = limit { mock = mock.and(query_param("limit", l.to_string())); From b2a12eb11132d664a9b7b874da2a3936cf1b04ab Mon Sep 17 00:00:00 2001 From: Timo Date: Wed, 13 Nov 2024 19:56:57 +0100 Subject: [PATCH 3/4] more test adjustments --- crates/matrix-sdk/src/test_utils/mocks.rs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/crates/matrix-sdk/src/test_utils/mocks.rs b/crates/matrix-sdk/src/test_utils/mocks.rs index 8e7cae5f49..d08052d215 100644 --- a/crates/matrix-sdk/src/test_utils/mocks.rs +++ b/crates/matrix-sdk/src/test_utils/mocks.rs @@ -354,15 +354,13 @@ impl MatrixMockServer { /// .mount() /// .await; /// - /// let responseNotMocked = room.send_raw("m.room.reaction", json!({ "body": "Hello world" })).await?; + /// let responseNotMocked = room.send_raw("m.room.reaction", json!({ "body": "Hello world" })).await; + /// // The `m.room.reaction` event type should not be mocked by the server. + /// assert!(responseNotMocked.is_err()); /// - /// assert!( - /// responseNotMocked, - /// "The event ID we mocked should match the one we received when we sent the event" - /// ); /// /// let response = room.send_raw("m.room.message", json!({ "body": "Hello world" })).await?; - /// + /// // The `m.room.message` event type should be mocked by the server. /// assert_eq!( /// event_id, /// response.event_id, From f3f39d26f7fce1bbcedd5e4ce1014a131480458a Mon Sep 17 00:00:00 2001 From: Timo Date: Fri, 15 Nov 2024 16:13:15 +0100 Subject: [PATCH 4/4] review and more tests --- crates/matrix-sdk/src/test_utils/mocks.rs | 86 +++++++++++++++++++++-- 1 file changed, 80 insertions(+), 6 deletions(-) diff --git a/crates/matrix-sdk/src/test_utils/mocks.rs b/crates/matrix-sdk/src/test_utils/mocks.rs index d08052d215..6631fdd770 100644 --- a/crates/matrix-sdk/src/test_utils/mocks.rs +++ b/crates/matrix-sdk/src/test_utils/mocks.rs @@ -326,11 +326,11 @@ impl MatrixMockServer { /// /// Note: works with *any* room. /// - /// Similar to: [mock_room_send] + /// Similar to: [`MatrixMockServer::mock_room_send`] /// /// # Examples /// - /// see also [mock_room_send] for more context. + /// see also [`MatrixMockServer::mock_room_send`] for more context. /// /// ``` /// # tokio_test::block_on(async { @@ -380,13 +380,50 @@ impl MatrixMockServer { /// Creates a prebuilt mock for sending a state event in a room. /// - /// Similar to: [mock_room_send] + /// Similar to: [`MatrixMockServer::mock_room_send`] /// /// Note: works with *any* room. /// Note: works with *any* event type. + /// + /// ``` + /// # tokio_test::block_on(async { + /// use matrix_sdk::{ruma::{room_id, event_id}, test_utils::mocks::MatrixMockServer}; + /// use serde_json::json; + /// + /// let mock_server = MatrixMockServer::new().await; + /// let client = mock_server.client_builder().build().await; + /// + /// mock_server.mock_room_state_encryption().plain().mount().await; + /// + /// let room = mock_server + /// .sync_joined_room(&client, room_id!("!room_id:localhost")) + /// .await; + /// + /// let event_id = event_id!("$some_id"); + /// mock_server + /// .mock_room_state() + /// .ok(event_id) + /// .expect(1) + /// .mount() + /// .await; + /// + /// let responseNotMocked = room.send_raw("m.room.create", json!({ "body": "Hello world" })).await; + /// // The `/send` endpoint should not be mocked by the server. + /// assert!(responseNotMocked.is_err()); + /// + /// + /// let response = room.send_state_event_raw("m.room.message", "my_key", json!({ "body": "Hello world" })).await?; + /// // The `/state` endpoint should be mocked by the server. + /// assert_eq!( + /// event_id, + /// response.event_id, + /// "The event ID we mocked should match the one we received when we sent the event" + /// ); + /// # anyhow::Ok(()) }); + /// ``` pub fn mock_room_state(&self) -> MockEndpoint<'_, RoomSendEndpoint> { let mock = Mock::given(method("PUT")) - .and(path_regex(r"^/_matrix/client/r0/rooms/.*/state/.*")) + .and(path_regex(r"^/_matrix/client/v3/rooms/.*/state/.*")) .and(header("authorization", "Bearer 1234")); MockEndpoint { mock, server: &self.server, endpoint: RoomSendEndpoint } } @@ -394,9 +431,46 @@ impl MatrixMockServer { /// Creates a prebuilt mock for sending a state event with a specific type /// in a room. /// - /// Similar to: [mock_room_send] + /// Similar to: [`MatrixMockServer::mock_room_send`] /// /// Note: works with *any* room. + /// + /// ``` + /// # tokio_test::block_on(async { + /// use matrix_sdk::{ruma::{room_id, event_id}, test_utils::mocks::MatrixMockServer}; + /// use serde_json::json; + /// + /// let mock_server = MatrixMockServer::new().await; + /// let client = mock_server.client_builder().build().await; + /// + /// mock_server.mock_room_state_encryption().plain().mount().await; + /// + /// let room = mock_server + /// .sync_joined_room(&client, room_id!("!room_id:localhost")) + /// .await; + /// + /// let event_id = event_id!("$some_id"); + /// mock_server + /// .mock_room_state_for_type("m.room.avatar".into()) + /// .ok(event_id) + /// .expect(1) + /// .mount() + /// .await; + /// + /// let responseNotMocked = room.send_state_event_raw("m.room.create", "my_key", json!({ "body": "Hello world" })).await; + /// // The `m.room.create` type should not be mocked by the server. + /// assert!(responseNotMocked.is_err()); + /// + /// + /// let response = room.send_state_event_raw("m.room.avatar", "my_key", json!({ "body": "Hello world" })).await?; + /// // The `m.room.avatar` type should be mocked by the server. + /// assert_eq!( + /// event_id, + /// response.event_id, + /// "The event ID we mocked should match the one we received when we sent the event" + /// ); + /// # anyhow::Ok(()) }); + /// ``` pub fn mock_room_state_for_type( &self, state_type: StateEventType, @@ -522,7 +596,7 @@ impl MatrixMockServer { } } - /// Create a prebuild mock for reading room message with the `/messages` + /// Create a prebuild mock for paginating room message with the `/messages` /// endpoint. pub fn mock_room_messages(&self, limit: Option) -> MockEndpoint<'_, RoomMessagesEndpoint> { let mut mock = Mock::given(method("GET"))