Skip to content

Commit

Permalink
test(room): add test for subscribing to requests to join a room.
Browse files Browse the repository at this point in the history
Also add mocks for the `/members` endpoint.
  • Loading branch information
jmartinesp committed Dec 10, 2024
1 parent 8f0beb9 commit 84c2c1b
Show file tree
Hide file tree
Showing 3 changed files with 129 additions and 8 deletions.
5 changes: 1 addition & 4 deletions crates/matrix-sdk/src/room/request_to_join.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
use std::sync::Arc;

use ruma::{
EventId, OwnedEventId, OwnedMxcUri,
OwnedUserId, RoomId,
};
use ruma::{EventId, OwnedEventId, OwnedMxcUri, OwnedUserId, RoomId};

use crate::{room::RoomMember, Error, Room};

Expand Down
63 changes: 61 additions & 2 deletions crates/matrix-sdk/src/test_utils/mocks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,10 @@ use matrix_sdk_test::{
};
use ruma::{
directory::PublicRoomsChunk,
events::{AnyStateEvent, AnyTimelineEvent, MessageLikeEventType, StateEventType},
events::{
room::member::RoomMemberEvent, AnyStateEvent, AnyTimelineEvent, MessageLikeEventType,
StateEventType,
},
serde::Raw,
time::Duration,
MxcUri, OwnedEventId, OwnedRoomId, RoomId, ServerName,
Expand Down Expand Up @@ -607,6 +610,49 @@ impl MatrixMockServer {
.and(header("authorization", "Bearer 1234"));
MockEndpoint { mock, server: &self.server, endpoint: DeleteRoomKeysVersionEndpoint }
}

/// 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`].
Expand Down Expand Up @@ -1023,7 +1069,7 @@ impl<'a> MockEndpoint<'a, RoomSendEndpoint> {
///
/// let response = room.client().send(r, None).await.unwrap();
/// // The delayed `m.room.message` event type should be mocked by the server.
/// assert_eq!("$some_id", response.delay_id);
/// assert_eq!("$some_id", response.delay_id);
/// # anyhow::Ok(()) });
/// ```
pub fn with_delay(self, delay: Duration) -> Self {
Expand Down Expand Up @@ -1761,3 +1807,16 @@ impl<'a> MockEndpoint<'a, DeleteRoomKeysVersionEndpoint> {
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<Raw<RoomMemberEvent>>) -> MatrixMock<'a> {
let mock = self.mock.respond_with(ResponseTemplate::new(200).set_body_json(json!({
"chunk": members,
})));
MatrixMock { server: self.server, mock }
}
}
69 changes: 67 additions & 2 deletions crates/matrix-sdk/tests/integration/room/joined.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -24,7 +25,10 @@ use ruma::{
events::{
direct::DirectUserIdentifier,
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,
Expand Down Expand Up @@ -833,3 +837,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());
}

0 comments on commit 84c2c1b

Please sign in to comment.