mirror of
https://github.com/matrix-org/matrix-rust-sdk.git
synced 2026-02-05 21:22:25 -05:00
test(notification client): check msc4306 behavior in the notification client too
This commit is contained in:
@@ -7,6 +7,7 @@ use std::{
|
||||
use assert_matches::assert_matches;
|
||||
use assert_matches2::assert_let;
|
||||
use matrix_sdk::{
|
||||
ThreadingSupport,
|
||||
config::SyncSettings,
|
||||
test_utils::{logged_in_client_with_server, mocks::MatrixMockServer},
|
||||
};
|
||||
@@ -23,8 +24,8 @@ use matrix_sdk_ui::{
|
||||
};
|
||||
use ruma::{
|
||||
RoomVersionId, event_id,
|
||||
events::{TimelineEventType, room::member::MembershipState},
|
||||
mxc_uri, room_id, user_id,
|
||||
events::{Mentions, TimelineEventType, room::member::MembershipState},
|
||||
mxc_uri, owned_user_id, room_id, user_id,
|
||||
};
|
||||
use serde_json::json;
|
||||
use wiremock::{
|
||||
@@ -114,6 +115,234 @@ async fn test_notification_client_with_context() {
|
||||
assert_eq!(item.sender_avatar_url, Some(sender_avatar_url.to_string()));
|
||||
}
|
||||
|
||||
#[async_test]
|
||||
async fn test_subscribed_threads_get_notifications() {
|
||||
let server = MatrixMockServer::new().await;
|
||||
let client = server
|
||||
.client_builder()
|
||||
.on_builder(|builder| {
|
||||
builder.with_threading_support(ThreadingSupport::Enabled { with_subscriptions: true })
|
||||
})
|
||||
.build()
|
||||
.await;
|
||||
|
||||
let sender = user_id!("@user:example.org");
|
||||
let room_id = room_id!("!a98sd12bjh:example.org");
|
||||
let f = EventFactory::new().room(room_id).sender(sender);
|
||||
|
||||
// First, mock an empty sync so the room is known.
|
||||
server.mock_room_state_encryption().plain().mount().await;
|
||||
|
||||
// To have access to the push rules context, we must know the own's member
|
||||
// event.
|
||||
let room = server
|
||||
.sync_room(
|
||||
&client,
|
||||
JoinedRoomBuilder::new(room_id).add_state_event(
|
||||
f.member(client.user_id().unwrap())
|
||||
.membership(MembershipState::Join)
|
||||
.display_name("Jean-Michel Rouille"),
|
||||
),
|
||||
)
|
||||
.await;
|
||||
|
||||
// Sanity check: we can create push rules context.
|
||||
room.push_context().await.unwrap().unwrap();
|
||||
|
||||
// Create a notification client.
|
||||
let sync_service = Arc::new(SyncService::builder(client.clone()).build().await.unwrap());
|
||||
let process_setup = NotificationProcessSetup::SingleProcess { sync_service };
|
||||
let notification_client = NotificationClient::new(client, process_setup).await.unwrap();
|
||||
|
||||
// For a thread I'm subscribed to,
|
||||
let thread_root = event_id!("$thread_root");
|
||||
server
|
||||
.mock_get_thread_subscription()
|
||||
.match_thread_id(thread_root.to_owned())
|
||||
.ok(false)
|
||||
.expect(2)
|
||||
.mount()
|
||||
.await;
|
||||
|
||||
let sender_member_event =
|
||||
f.member(sender).membership(MembershipState::Join).display_name("John Diaspora").into_raw();
|
||||
|
||||
// Considering an in-thread message,
|
||||
let in_thread_event = {
|
||||
let event_id = event_id!("$example_event_id");
|
||||
let event = f
|
||||
.text_msg("hello to you too!")
|
||||
.event_id(event_id)
|
||||
.server_ts(152049794)
|
||||
.in_thread(thread_root, thread_root)
|
||||
.into_event();
|
||||
|
||||
server
|
||||
.mock_room_event_context()
|
||||
.match_event_id()
|
||||
.ok(event.clone(), "", "", vec![sender_member_event.clone()])
|
||||
.mock_once()
|
||||
.mount()
|
||||
.await;
|
||||
|
||||
// Then I get a notification for this message.
|
||||
let item =
|
||||
notification_client.get_notification_with_context(room_id, event_id).await.unwrap();
|
||||
assert_matches!(item, NotificationStatus::Event(..));
|
||||
|
||||
event
|
||||
};
|
||||
|
||||
// Considering the thread root event,
|
||||
let event = f
|
||||
.text_msg("hello world")
|
||||
.event_id(thread_root)
|
||||
.server_ts(152049793)
|
||||
.with_bundled_thread_summary(in_thread_event.raw().clone().cast_unchecked(), 1, false)
|
||||
.into_event();
|
||||
|
||||
server
|
||||
.mock_room_event_context()
|
||||
.match_event_id()
|
||||
.ok(event.clone(), "", "", vec![sender_member_event])
|
||||
.mock_once()
|
||||
.mount()
|
||||
.await;
|
||||
|
||||
// Then I get a notification for the thread root as well.
|
||||
let item =
|
||||
notification_client.get_notification_with_context(room_id, thread_root).await.unwrap();
|
||||
assert_matches!(item, NotificationStatus::Event(..));
|
||||
}
|
||||
|
||||
#[async_test]
|
||||
async fn test_unsubscribed_threads_get_notifications() {
|
||||
let server = MatrixMockServer::new().await;
|
||||
let client = server
|
||||
.client_builder()
|
||||
.on_builder(|builder| {
|
||||
builder.with_threading_support(ThreadingSupport::Enabled { with_subscriptions: true })
|
||||
})
|
||||
.build()
|
||||
.await;
|
||||
|
||||
let sender = user_id!("@user:example.org");
|
||||
let room_id = room_id!("!a98sd12bjh:example.org");
|
||||
let f = EventFactory::new().room(room_id).sender(sender);
|
||||
|
||||
// First, mock an empty sync so the room is known.
|
||||
server.mock_room_state_encryption().plain().mount().await;
|
||||
|
||||
// To have access to the push rules context, we must know the own's member
|
||||
// event.
|
||||
let room = server
|
||||
.sync_room(
|
||||
&client,
|
||||
JoinedRoomBuilder::new(room_id).add_state_event(
|
||||
f.member(client.user_id().unwrap())
|
||||
.membership(MembershipState::Join)
|
||||
.display_name("Jean-Michel Rouille"),
|
||||
),
|
||||
)
|
||||
.await;
|
||||
|
||||
// Sanity check: we can create push rules context.
|
||||
room.push_context().await.unwrap().unwrap();
|
||||
|
||||
// Create a notification client.
|
||||
let sync_service = Arc::new(SyncService::builder(client.clone()).build().await.unwrap());
|
||||
let process_setup = NotificationProcessSetup::SingleProcess { sync_service };
|
||||
let notification_client = NotificationClient::new(client.clone(), process_setup).await.unwrap();
|
||||
|
||||
// For a thread with an unknown subscription status (note: we're not mocking the
|
||||
// get endpoint, since 404 is equivalent to no thread status),
|
||||
let thread_root = event_id!("$thread_root");
|
||||
|
||||
let sender_member_event =
|
||||
f.member(sender).membership(MembershipState::Join).display_name("John Diaspora").into_raw();
|
||||
|
||||
// Considering a random in-thread message,
|
||||
let first_thread_response = event_id!("$thread_response1");
|
||||
let in_thread_event = {
|
||||
// Note: contains a mention, but not of me.
|
||||
let event = f
|
||||
.text_msg("hello to you too!")
|
||||
.event_id(first_thread_response)
|
||||
.server_ts(152049794)
|
||||
.in_thread(thread_root, thread_root)
|
||||
.mentions(Mentions::with_user_ids([owned_user_id!("@rando:example.org")]))
|
||||
.into_event();
|
||||
|
||||
server
|
||||
.mock_room_event_context()
|
||||
.match_event_id()
|
||||
.ok(event.clone(), "", "", vec![sender_member_event.clone()])
|
||||
.mock_once()
|
||||
.mount()
|
||||
.await;
|
||||
|
||||
// Then I don't get a notification for it.
|
||||
let item = notification_client
|
||||
.get_notification_with_context(room_id, first_thread_response)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_matches!(item, NotificationStatus::EventFilteredOut);
|
||||
|
||||
event
|
||||
};
|
||||
|
||||
// Considering the thread root event,
|
||||
{
|
||||
let event = f
|
||||
.text_msg("hello world")
|
||||
.event_id(thread_root)
|
||||
.server_ts(152049793)
|
||||
.with_bundled_thread_summary(in_thread_event.raw().clone().cast_unchecked(), 1, false)
|
||||
.into_event();
|
||||
|
||||
server
|
||||
.mock_room_event_context()
|
||||
.match_event_id()
|
||||
.ok(event.clone(), "", "", vec![sender_member_event.clone()])
|
||||
.mock_once()
|
||||
.mount()
|
||||
.await;
|
||||
|
||||
// I do get a notification about it, because it's not technically in the thread.
|
||||
let item =
|
||||
notification_client.get_notification_with_context(room_id, thread_root).await.unwrap();
|
||||
assert_matches!(item, NotificationStatus::Event(..));
|
||||
}
|
||||
|
||||
// But if a new in-thread event mentions me, then I would get subscribed,
|
||||
{
|
||||
let thread_response2 = event_id!("$thread_response2");
|
||||
// Note: event mentions me.
|
||||
let event = f
|
||||
.text_msg("hello world")
|
||||
.event_id(thread_response2)
|
||||
.server_ts(152049793)
|
||||
.in_thread(thread_root, first_thread_response)
|
||||
.mentions(Mentions::with_user_ids(vec![client.user_id().unwrap().to_owned()]))
|
||||
.into_event();
|
||||
|
||||
server
|
||||
.mock_room_event_context()
|
||||
.match_event_id()
|
||||
.ok(event.clone(), "", "", vec![sender_member_event])
|
||||
.mock_once()
|
||||
.mount()
|
||||
.await;
|
||||
|
||||
// Then I do get a notification for the thread root either.
|
||||
let item = notification_client
|
||||
.get_notification_with_context(room_id, thread_response2)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_matches!(item, NotificationStatus::Event(..));
|
||||
}
|
||||
}
|
||||
|
||||
#[async_test]
|
||||
async fn test_notification_client_sliding_sync() {
|
||||
let room_id = room_id!("!a98sd12bjh:example.org");
|
||||
@@ -929,7 +1158,12 @@ async fn test_notification_client_context_filters_out_events_from_ignored_users(
|
||||
.into_event();
|
||||
|
||||
// Mock the /context response
|
||||
server.mock_room_event_context().ok(event, "start", "end").mock_once().mount().await;
|
||||
server
|
||||
.mock_room_event_context()
|
||||
.ok(event, "start", "end", Vec::new())
|
||||
.mock_once()
|
||||
.mount()
|
||||
.await;
|
||||
|
||||
let dummy_sync_service = Arc::new(SyncService::builder(client.clone()).build().await.unwrap());
|
||||
let process_setup =
|
||||
|
||||
@@ -2505,15 +2505,16 @@ impl<'a> MockEndpoint<'a, RoomEventContextEndpoint> {
|
||||
self
|
||||
}
|
||||
|
||||
/// Returns an endpoint that emulates success
|
||||
/// Returns an endpoint that emulates success.
|
||||
pub fn ok(
|
||||
self,
|
||||
event: TimelineEvent,
|
||||
start: impl Into<String>,
|
||||
end: impl Into<String>,
|
||||
state_events: Vec<Raw<AnyStateEvent>>,
|
||||
) -> MatrixMock<'a> {
|
||||
let event_path = if self.endpoint.match_event_id {
|
||||
let event_id = event.kind.event_id().expect("an event id is required");
|
||||
let event_id = event.event_id().expect("an event id is required");
|
||||
// The event id should begin with `$`, which would be taken as the end of the
|
||||
// regex so we need to escape it
|
||||
event_id.as_str().replace("$", "\\$")
|
||||
@@ -2531,7 +2532,7 @@ impl<'a> MockEndpoint<'a, RoomEventContextEndpoint> {
|
||||
"event": event.into_raw().json(),
|
||||
"end": end.into(),
|
||||
"start": start.into(),
|
||||
"state": []
|
||||
"state": state_events
|
||||
})));
|
||||
MatrixMock { server: self.server, mock }
|
||||
}
|
||||
|
||||
@@ -371,6 +371,12 @@ impl EventBuilder<RoomMessageEventContent> {
|
||||
self
|
||||
}
|
||||
|
||||
/// Adds the given mentions to the current event.
|
||||
pub fn mentions(mut self, mentions: Mentions) -> Self {
|
||||
self.content.mentions = Some(mentions);
|
||||
self
|
||||
}
|
||||
|
||||
/// Adds a replacement relation to the current event, with the new content
|
||||
/// passed.
|
||||
pub fn edit(
|
||||
|
||||
Reference in New Issue
Block a user