Files
matrix-rust-sdk/testing/matrix-sdk-integration-testing/src/tests/sliding_sync/notification_client.rs
T
Ivan Enderlin bad1c683f8 chore(base): Rename fields of RoomUpdate.
This patch renames the fields in `RoomUpdate`: `join` becomes `joined`,
`leave` becomes `left`, `invite` becomes `invited`, to match their
associate type.
2025-04-15 14:17:07 +02:00

222 lines
8.3 KiB
Rust

// TODO: Remove this once all tests are re-enabled.
#![allow(unused)]
use std::sync::Arc;
use anyhow::{ensure, Result};
use assert_matches::assert_matches;
use assert_matches2::assert_let;
use matrix_sdk::{
config::SyncSettings,
ruma::{
api::client::room::create_room::v3::Request as CreateRoomRequest,
assign,
events::{
room::{member::MembershipState, message::RoomMessageEventContent},
AnyStrippedStateEvent, SyncMessageLikeEvent, TimelineEventType,
},
OwnedEventId,
},
RoomState,
};
use matrix_sdk_ui::{
notification_client::{
Error, NotificationClient, NotificationEvent, NotificationItem, NotificationProcessSetup,
NotificationStatus,
},
sync_service::SyncService,
};
use tracing::warn;
use crate::helpers::TestClientBuilder;
#[tokio::test(flavor = "multi_thread", worker_threads = 4)]
async fn test_notification() -> Result<()> {
// Create new users for each test run, to avoid conflicts with invites existing
// from previous runs.
let alice = TestClientBuilder::new("alice").use_sqlite().build().await?;
let bob = TestClientBuilder::new("bob").use_sqlite().build().await?;
let dummy_sync_service = Arc::new(SyncService::builder(bob.clone()).build().await?);
let process_setup =
NotificationProcessSetup::SingleProcess { sync_service: dummy_sync_service };
// Alice changes display name.
const ALICE_NAME: &str = "Alice, Queen of Cryptography";
alice.account().set_display_name(Some(ALICE_NAME)).await?;
// Initial setup: Alice creates a room, invites Bob.
const ROOM_NAME: &str = "Kingdom of Integration Testing";
let invite = vec![bob.user_id().expect("bob has a userid!").to_owned()];
let request = assign!(CreateRoomRequest::new(), {
invite,
name: Some(ROOM_NAME.to_owned()),
is_direct: true,
});
let alice_room = alice.create_room(request).await?;
let room_id = alice_room.room_id().to_owned();
// Bob receives a notification about it.
let bob_invite_response = bob.sync_once(Default::default()).await?;
let sync_token = bob_invite_response.next_batch;
let mut invited_rooms = bob_invite_response.rooms.invited;
assert_eq!(invited_rooms.len(), 1, "must be only one invitation");
let Some(invited_room) = invited_rooms.get(&room_id) else {
panic!("bob is invited in an unexpected room");
};
if let Some(event_id) = invited_room.invite_state.events.iter().find_map(|event| {
let Ok(AnyStrippedStateEvent::RoomMember(room_member_ev)) = event.deserialize() else {
return None;
};
if room_member_ev.content.membership != MembershipState::Invite {
return None;
}
let Ok(Some(event_id)) = event.get_field::<OwnedEventId>("event_id") else {
return None;
};
Some(event_id)
}) {
warn!("We found the invite event!");
// Try with sliding sync first.
let notification_client =
NotificationClient::new(bob.clone(), process_setup.clone()).await.unwrap();
assert_let!(
NotificationStatus::Event(notification) =
notification_client.get_notification_with_sliding_sync(&room_id, &event_id).await?
);
warn!("sliding_sync: checking invite notification");
assert_matches!(notification.event, NotificationEvent::Invite(_));
assert_eq!(notification.event.sender(), alice.user_id().unwrap());
assert_eq!(notification.joined_members_count, 0);
assert_eq!(notification.is_room_encrypted, Some(false));
assert!(notification.is_direct_message_room);
assert_matches!(notification.event, NotificationEvent::Invite(observed_invite) => {
assert_eq!(observed_invite.content.membership, MembershipState::Invite);
});
assert_eq!(notification.sender_display_name.as_deref(), Some(ALICE_NAME));
assert_eq!(notification.room_computed_display_name, ROOM_NAME);
// Then with /context.
let notification_client =
NotificationClient::new(bob.clone(), process_setup.clone()).await.unwrap();
let notification =
notification_client.get_notification_with_context(&room_id, &event_id).await;
// We aren't authorized to inspect events from rooms we were not invited to.
assert!(matches!(notification.unwrap_err(), Error::SdkError(matrix_sdk::Error::Http(..))));
} else {
warn!("Couldn't get the invite event.");
}
// Bob inspects the invite room.
{
let room = bob.get_room(&room_id).expect("bob doesn't know about the room");
ensure!(
room.state() == RoomState::Invited,
"The room alice invited bob in isn't an invite: {room:?}"
);
let details = room.invite_details().await?;
let sender = details.inviter.expect("invite details doesn't have inviter");
assert_eq!(sender.user_id(), alice.user_id().expect("alice has a user_id"));
}
bob.get_room(&room_id)
.unwrap()
// Bob joins the room.
.join()
.await?;
// Now Alice sends a message to Bob.
alice_room.send(RoomMessageEventContent::text_plain("Hello world!")).await?;
// In this sync, Bob receives the message from Alice.
let bob_response = bob.sync_once(SyncSettings::default().token(sync_token)).await?;
let mut joined_rooms = bob_response.rooms.joined.into_iter();
let (_, bob_room) = joined_rooms.next().expect("must have joined one room");
assert!(joined_rooms.next().is_none(), "no more joined rooms: {joined_rooms:#?}");
let event_id = bob_room
.timeline
.events
.iter()
.find_map(|event| {
let event = event.raw().deserialize().ok()?;
(event.event_type() == TimelineEventType::RoomMessage)
.then(|| event.event_id().to_owned())
})
.expect("missing message from alice in bob's client");
// Get the notification for the given message.
let check_notification = |is_sliding_sync: bool, notification: NotificationItem| {
warn!(
"{}: checking message notification",
if is_sliding_sync { "sliding sync" } else { "/context query" }
);
assert_eq!(notification.event.sender(), alice.user_id().unwrap());
if is_sliding_sync {
assert_eq!(notification.joined_members_count, 2);
} else {
// This can't be computed for /context, because we only get a single request,
// and not a full sync response that would contain a room summary.
warn!("joined member counts: {}", notification.joined_members_count);
}
assert_eq!(notification.is_room_encrypted, Some(false));
assert!(notification.is_direct_message_room);
assert_matches!(
notification.event,
NotificationEvent::Timeline(
matrix_sdk::ruma::events::AnySyncTimelineEvent::MessageLike(
matrix_sdk::ruma::events::AnySyncMessageLikeEvent::RoomMessage(
SyncMessageLikeEvent::Original(event)
)
)
) => {
assert_matches!(event.content.msgtype,
matrix_sdk::ruma::events::room::message::MessageType::Text(text) => {
assert_eq!(text.body, "Hello world!");
});
}
);
assert_eq!(notification.sender_display_name.as_deref(), Some(ALICE_NAME));
assert_eq!(notification.room_computed_display_name, ROOM_NAME);
};
// Check with sliding sync.
let notification_client =
NotificationClient::new(bob.clone(), process_setup.clone()).await.unwrap();
assert_let!(
NotificationStatus::Event(notification) =
notification_client.get_notification_with_sliding_sync(&room_id, &event_id).await?
);
check_notification(true, notification);
// Check with `/context`.
let notification_client = NotificationClient::new(bob.clone(), process_setup).await.unwrap();
let notification = notification_client
.get_notification_with_context(&room_id, &event_id)
.await?
.expect("missing notification for the message");
check_notification(false, notification);
Ok(())
}