From 5d8f0368732b6af51c1fb7ff8c42ec05d96e2f34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20Mart=C3=ADn?= Date: Wed, 27 Nov 2024 13:18:43 +0100 Subject: [PATCH] test(room): add test for subscribing to requests to join a room. Also add mocks for the `/members` endpoint. --- crates/matrix-sdk/src/test_utils/mocks.rs | 61 +++++++++++++++- .../tests/integration/room/joined.rs | 69 ++++++++++++++++++- 2 files changed, 127 insertions(+), 3 deletions(-) diff --git a/crates/matrix-sdk/src/test_utils/mocks.rs b/crates/matrix-sdk/src/test_utils/mocks.rs index 53cdbcee5dc..facc6b6d41b 100644 --- a/crates/matrix-sdk/src/test_utils/mocks.rs +++ b/crates/matrix-sdk/src/test_utils/mocks.rs @@ -27,7 +27,10 @@ use matrix_sdk_test::{ test_json, InvitedRoomBuilder, JoinedRoomBuilder, KnockedRoomBuilder, LeftRoomBuilder, SyncResponseBuilder, }; -use ruma::{directory::PublicRoomsChunk, MxcUri, OwnedEventId, OwnedRoomId, RoomId, ServerName}; +use ruma::{ + directory::PublicRoomsChunk, events::room::member::RoomMemberEvent, serde::Raw, MxcUri, + OwnedEventId, OwnedRoomId, RoomId, ServerName, +}; use serde::Deserialize; use serde_json::json; use wiremock::{ @@ -496,6 +499,49 @@ impl MatrixMockServer { let mock = Mock::given(method("POST")).and(path_regex(r"/_matrix/client/v3/publicRooms")); MockEndpoint { mock, server: &self.server, endpoint: PublicRoomsEndpoint } } + + /// Create a prebuilt mock for getting the room members in a room. + /// + /// # Examples + /// + /// ``` # + /// tokio_test::block_on(async { + /// use matrix_sdk_base::RoomMemberships; + /// use ruma::events::room::member::MembershipState; + /// use ruma::events::room::member::RoomMemberEventContent; + /// use ruma::user_id; + /// use matrix_sdk_test::event_factory::EventFactory; + /// use matrix_sdk::{ + /// ruma::{event_id, room_id}, + /// test_utils::mocks::MatrixMockServer, + /// }; + /// let mock_server = MatrixMockServer::new().await; + /// let client = mock_server.client_builder().build().await; + /// let event_id = event_id!("$id"); + /// let room_id = room_id!("!room_id:localhost"); + /// + /// let f = EventFactory::new().room(room_id); + /// let alice_user_id = user_id!("@alice:b.c"); + /// let alice_knock_event = f + /// .event(RoomMemberEventContent::new(MembershipState::Knock)) + /// .event_id(event_id) + /// .sender(alice_user_id) + /// .state_key(alice_user_id) + /// .into_raw_timeline() + /// .cast(); + /// + /// mock_server.mock_get_members().ok(vec![alice_knock_event]).mock_once().mount().await; + /// let room = mock_server.sync_joined_room(&client, room_id).await; + /// + /// let members = room.members(RoomMemberships::all()).await.unwrap(); + /// assert_eq!(members.len(), 1); + /// # }); + /// ``` + pub fn mock_get_members(&self) -> MockEndpoint<'_, GetRoomMembersEndpoint> { + let mock = + Mock::given(method("GET")).and(path_regex(r"^/_matrix/client/v3/rooms/.*/members$")); + MockEndpoint { mock, server: &self.server, endpoint: GetRoomMembersEndpoint } + } } /// Parameter to [`MatrixMockServer::sync_room`]. @@ -1129,3 +1175,16 @@ impl<'a> MockEndpoint<'a, PublicRoomsEndpoint> { MatrixMock { server: self.server, mock } } } + +/// A prebuilt mock for `GET /members` request. +pub struct GetRoomMembersEndpoint; + +impl<'a> MockEndpoint<'a, GetRoomMembersEndpoint> { + /// Returns a successful get members request with a list of members. + pub fn ok(self, members: Vec>) -> MatrixMock<'a> { + let mock = self.mock.respond_with(ResponseTemplate::new(200).set_body_json(json!({ + "chunk": members, + }))); + MatrixMock { server: self.server, mock } + } +} diff --git a/crates/matrix-sdk/tests/integration/room/joined.rs b/crates/matrix-sdk/tests/integration/room/joined.rs index 322b0269488..6f077c1f688 100644 --- a/crates/matrix-sdk/tests/integration/room/joined.rs +++ b/crates/matrix-sdk/tests/integration/room/joined.rs @@ -3,8 +3,9 @@ use std::{ time::Duration, }; -use futures_util::future::join_all; +use futures_util::{future::join_all, pin_mut}; use matrix_sdk::{ + assert_next_with_timeout, config::SyncSettings, room::{edit::EditedContent, Receipts, ReportedContentScore, RoomMemberRole}, test_utils::mocks::MatrixMockServer, @@ -23,7 +24,10 @@ use ruma::{ assign, event_id, events::{ receipt::ReceiptThread, - room::message::{RoomMessageEventContent, RoomMessageEventContentWithoutRelation}, + room::{ + member::{MembershipState, RoomMemberEventContent}, + message::{RoomMessageEventContent, RoomMessageEventContentWithoutRelation}, + }, TimelineEventType, }, int, mxc_uri, owned_event_id, room_id, thirdparty, user_id, OwnedUserId, TransactionId, @@ -800,3 +804,64 @@ async fn test_enable_encryption_doesnt_stay_unencrypted() { assert!(room.is_encrypted().await.unwrap()); } + +#[async_test] +async fn test_subscribe_to_requests_to_join() { + let server = MatrixMockServer::new().await; + let client = server.client_builder().build().await; + + server.mock_room_state_encryption().plain().mount().await; + + let room_id = room_id!("!a:b.c"); + let f = EventFactory::new().room(room_id); + + let alice_user_id = user_id!("@alice:b.c"); + let alice_knock_event_id = event_id!("$alice-knock:b.c"); + let alice_knock_event = f + .event(RoomMemberEventContent::new(MembershipState::Knock)) + .event_id(alice_knock_event_id) + .sender(alice_user_id) + .state_key(alice_user_id) + .into_raw_timeline() + .cast(); + + server.mock_get_members().ok(vec![alice_knock_event]).mock_once().mount().await; + + let room = server.sync_joined_room(&client, room_id).await; + let stream = room.subscribe_to_requests_to_join().await.unwrap(); + + pin_mut!(stream); + + // We receive an initial request to join from Alice + let initial = assert_next_with_timeout!(stream, 100); + assert!(!initial.is_empty()); + + let alices_request_to_join = &initial[0]; + assert_eq!(alices_request_to_join.event_id, alice_knock_event_id); + assert!(!alices_request_to_join.is_seen); + + // We then mark the request to join as seen + room.mark_requests_to_join_as_seen(&[alice_knock_event_id.to_owned()]).await.unwrap(); + + // Now it's received again as seen + let seen = assert_next_with_timeout!(stream, 100); + assert!(!seen.is_empty()); + let alices_seen_request_to_join = &seen[0]; + assert_eq!(alices_seen_request_to_join.event_id, alice_knock_event_id); + assert!(alices_seen_request_to_join.is_seen); + + // If we then receive a new member event for Alice that's not 'knock' + let alice_join_event_id = event_id!("$alice-join:b.c"); + let joined_room_builder = JoinedRoomBuilder::new(room_id).add_state_bulk(vec![f + .event(RoomMemberEventContent::new(MembershipState::Invite)) + .event_id(alice_join_event_id) + .sender(alice_user_id) + .state_key(alice_user_id) + .into_raw_timeline() + .cast()]); + server.sync_room(&client, joined_room_builder).await; + + // The requests to join are now empty + let updated_requests = assert_next_with_timeout!(stream, 100); + assert!(updated_requests.is_empty()); +}