matrix-rust-sdk/testing/matrix-sdk-integration-testing/src/tests/repeated_join.rs

155 lines
5.1 KiB
Rust

use std::{sync::Arc, time::Duration};
use anyhow::Result;
use assign::assign;
use matrix_sdk::{
event_handler::Ctx,
room::Room,
ruma::{
api::client::room::create_room::v3::Request as CreateRoomRequest,
events::room::member::{MembershipState, StrippedRoomMemberEvent},
},
Client, RoomMemberships, RoomState, StateStoreExt,
};
use tokio::sync::Notify;
use crate::helpers::get_client_for_user;
#[tokio::test(flavor = "multi_thread", worker_threads = 4)]
async fn test_repeated_join_leave() -> Result<()> {
let peter = get_client_for_user("peter".to_owned(), true).await?;
// FIXME: Run once with memory, once with SQLite
let karl = get_client_for_user("karl".to_owned(), false).await?;
let karl_id = karl.user_id().expect("karl has a userid!").to_owned();
// Create a room and invite karl.
let invite = vec![karl_id.clone()];
let request = assign!(CreateRoomRequest::new(), {
invite,
is_direct: true,
});
// Sync after 1 second to so that create_room receives the event it is waiting
// for.
let peter_clone = peter.clone();
tokio::spawn(async move {
tokio::time::sleep(Duration::from_secs(1)).await;
peter_clone.sync_once(Default::default()).await
});
let created_room = peter.create_room(request).await?;
let room_id = created_room.room_id();
// Sync karl once to ensure he got the invite.
karl.sync_once(Default::default()).await?;
// Continuously sync karl from now on.
let karl_clone = karl.clone();
let join_handle = tokio::spawn(async move {
karl_clone.sync(Default::default()).await.unwrap();
});
let invite_signal = Arc::new(Notify::new());
karl.add_event_handler_context(invite_signal.clone());
karl.add_event_handler(signal_on_invite);
for i in 0..3 {
println!("Iteration {i}");
// Test that karl has the expected state in its client.
assert!(karl.get_invited_room(room_id).is_some());
assert!(karl.get_joined_room(room_id).is_none());
assert!(karl.get_left_room(room_id).is_none());
let room = karl.get_room(room_id).expect("karl has the room");
let membership = room.get_member_no_sync(&karl_id).await?.expect("karl was invited");
assert_eq!(*membership.membership(), MembershipState::Invite);
// Join the room
println!("Joining..");
let room =
karl.get_invited_room(room_id).expect("karl has the room").accept_invitation().await?;
println!("Done");
let membership = room.get_member(&karl_id).await?.expect("karl joined");
assert_eq!(*membership.membership(), MembershipState::Join);
assert!(karl.get_invited_room(room_id).is_none());
assert!(karl.get_joined_room(room_id).is_some());
assert!(karl.get_left_room(room_id).is_none());
// Syncs can overwrite the internal state. If the sync lags behind because we
// change so often so fast, we can get errors in the asserts here. So we have to
// wait here a bit till the sync happened.
room.sync_up().await;
// Leave the room
println!("Leaving..");
let room = room.leave().await?;
println!("Done");
let membership = room.get_member(&karl_id).await?.expect("karl left");
assert_eq!(*membership.membership(), MembershipState::Leave);
assert!(karl.get_invited_room(room_id).is_none());
assert!(karl.get_joined_room(room_id).is_none());
assert!(karl.get_left_room(room_id).is_some());
// Invite karl again and wait for karl to receive the invite.
println!("Inviting..");
let room = peter.get_joined_room(room_id).expect("peter created the room!");
room.invite_user_by_id(&karl_id).await?;
println!("Waiting to receive invite..");
invite_signal.notified().await;
}
// Stop the sync.
join_handle.abort();
// Now check the underlying state store that it also has the correct information
// (for when the client restarts).
let invited = karl.store().get_user_ids(room_id, RoomMemberships::INVITE).await?;
assert_eq!(invited.len(), 1);
assert_eq!(invited[0], karl_id);
let joined = karl.store().get_user_ids(room_id, RoomMemberships::JOIN).await?;
assert!(!joined.contains(&karl_id));
let event = karl
.store()
.get_member_event(room_id, &karl_id)
.await?
.expect("member event should exist")
.deserialize()
.unwrap();
assert_eq!(*event.membership(), MembershipState::Invite);
// Yay, test succeeded
Ok(())
}
async fn signal_on_invite(
event: StrippedRoomMemberEvent,
room: Room,
client: Client,
sender: Ctx<Arc<Notify>>,
) {
let own_id = client.user_id().expect("client is logged in");
if event.sender == own_id {
return;
}
if room.state() != RoomState::Invited {
return;
}
if event.content.membership != MembershipState::Invite {
return;
}
let invited = &event.state_key;
if invited != own_id {
return;
}
// Send signal that we received an invite.
sender.notify_one();
}