test: get rid of EventBuilder (#4560)

This gets rid of `EventBuilder`, and makes more usage of the
`EventFactory`, which is more ergonomic to create test events.

A large part of
https://github.com/matrix-org/matrix-rust-sdk/issues/3716.
This commit is contained in:
Benjamin Bouvier
2025-01-21 10:23:03 +01:00
committed by GitHub
parent dbadfe19b0
commit 0c74abbc50
21 changed files with 564 additions and 824 deletions
+12 -26
View File
@@ -1,23 +1,20 @@
use std::{sync::Arc, time::Duration};
use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion, Throughput};
use matrix_sdk::{
config::SyncSettings, test_utils::logged_in_client_with_server, utils::IntoRawStateEventContent,
};
use matrix_sdk::{config::SyncSettings, test_utils::logged_in_client_with_server};
use matrix_sdk_base::{
store::StoreConfig, BaseClient, RoomInfo, RoomState, SessionMeta, StateChanges, StateStore,
};
use matrix_sdk_sqlite::SqliteStateStore;
use matrix_sdk_test::{
event_factory::EventFactory, EventBuilder, JoinedRoomBuilder, StateTestEvent,
SyncResponseBuilder,
event_factory::EventFactory, JoinedRoomBuilder, StateTestEvent, SyncResponseBuilder,
};
use matrix_sdk_ui::{timeline::TimelineFocus, Timeline};
use ruma::{
api::client::membership::get_member_events,
device_id,
events::room::member::{RoomMemberEvent, RoomMemberEventContent},
owned_room_id, owned_user_id,
events::room::member::{MembershipState, RoomMemberEvent},
mxc_uri, owned_room_id, owned_user_id,
serde::Raw,
user_id, EventId, MilliSecondsSinceUnixEpoch, OwnedEventId, OwnedUserId,
};
@@ -35,28 +32,17 @@ pub fn receive_all_members_benchmark(c: &mut Criterion) {
let runtime = Builder::new_multi_thread().build().expect("Can't create runtime");
let room_id = owned_room_id!("!room:example.com");
let ev_builder = EventBuilder::new();
let f = EventFactory::new().room(&room_id);
let mut member_events: Vec<Raw<RoomMemberEvent>> = Vec::with_capacity(MEMBERS_IN_ROOM);
let member_content_json = json!({
"avatar_url": "mxc://example.org/SEsfnsuifSDFSSEF",
"displayname": "Alice Margatroid",
"membership": "join",
"reason": "Looking for support",
});
let member_content: Raw<RoomMemberEventContent> =
member_content_json.into_raw_state_event_content().cast();
for i in 0..MEMBERS_IN_ROOM {
let user_id = OwnedUserId::try_from(format!("@user_{}:matrix.org", i)).unwrap();
let state_key = user_id.to_string();
let event: Raw<RoomMemberEvent> = ev_builder
.make_state_event(
&user_id,
&room_id,
&state_key,
member_content.deserialize().unwrap(),
None,
)
.cast();
let event = f
.member(&user_id)
.membership(MembershipState::Join)
.avatar_url(mxc_uri!("mxc://example.org/SEsfnsuifSDFSSEF"))
.display_name("Alice Margatroid")
.reason("Looking for support")
.into_raw();
member_events.push(event);
}
+80 -94
View File
@@ -621,7 +621,7 @@ mod tests {
use eyeball_im::Vector;
use matrix_sdk_common::{deserialized_responses::SyncTimelineEvent, ring_buffer::RingBuffer};
use matrix_sdk_test::{sync_timeline_event, EventBuilder};
use matrix_sdk_test::{event_factory::EventFactory, sync_timeline_event};
use ruma::{
event_id,
events::receipt::{ReceiptThread, ReceiptType},
@@ -975,12 +975,12 @@ mod tests {
let ev1 = sync_timeline_message(other_user_id, receipt_event_id, "A");
let ev2 = sync_timeline_message(other_user_id, "$2", "A");
let receipt_event = EventBuilder::new().make_receipt_event_content([(
receipt_event_id.to_owned(),
ReceiptType::Read,
user_id.to_owned(),
ReceiptThread::Unthreaded,
)]);
let f = EventFactory::new();
let receipt_event = f
.read_receipts()
.add(receipt_event_id, user_id, ReceiptType::Read, ReceiptThread::Unthreaded)
.build();
let mut read_receipts = Default::default();
compute_unread_counts(
@@ -1035,30 +1035,32 @@ mod tests {
// Given a receipt event marking events 1-3 as read using a combination of
// different thread and privacy types,
let f = EventFactory::new();
for receipt_type_1 in &[ReceiptType::Read, ReceiptType::ReadPrivate] {
for receipt_thread_1 in &[ReceiptThread::Unthreaded, ReceiptThread::Main] {
for receipt_type_2 in &[ReceiptType::Read, ReceiptType::ReadPrivate] {
for receipt_thread_2 in &[ReceiptThread::Unthreaded, ReceiptThread::Main] {
let receipt_event = EventBuilder::new().make_receipt_event_content([
(
owned_event_id!("$2"),
let receipt_event = f
.read_receipts()
.add(
event_id!("$2"),
user_id,
receipt_type_1.clone(),
user_id.to_owned(),
receipt_thread_1.clone(),
),
(
owned_event_id!("$3"),
)
.add(
event_id!("$3"),
user_id,
receipt_type_2.clone(),
user_id.to_owned(),
receipt_thread_2.clone(),
),
(
owned_event_id!("$1"),
)
.add(
event_id!("$1"),
user_id,
receipt_type_1.clone(),
user_id.to_owned(),
receipt_thread_2.clone(),
),
]);
)
.build();
// When I compute the notifications for this room (with no new events),
let mut read_receipts = RoomReadReceipts::default();
@@ -1118,12 +1120,11 @@ mod tests {
let events = make_test_events(user_id!("@bob:example.org"));
let receipt_event = EventBuilder::new().make_receipt_event_content([(
owned_event_id!("$6"),
ReceiptType::Read,
user_id.clone(),
ReceiptThread::Unthreaded,
)]);
let f = EventFactory::new();
let receipt_event = f
.read_receipts()
.add(event_id!("$6"), &user_id, ReceiptType::Read, ReceiptThread::Unthreaded)
.build();
let mut read_receipts = RoomReadReceipts::default();
assert!(read_receipts.pending.is_empty());
@@ -1154,12 +1155,11 @@ mod tests {
let events = make_test_events(user_id!("@bob:example.org"));
let receipt_event = EventBuilder::new().make_receipt_event_content([(
owned_event_id!("$1"),
ReceiptType::Read,
user_id.clone(),
ReceiptThread::Unthreaded,
)]);
let f = EventFactory::new();
let receipt_event = f
.read_receipts()
.add(event_id!("$1"), &user_id, ReceiptType::Read, ReceiptThread::Unthreaded)
.build();
// Sync with a read receipt *and* a single event that was already known: in that
// case, only consider the new events in isolation, and compute the
@@ -1412,21 +1412,25 @@ mod tests {
#[test]
fn test_receipt_selector_handle_new_receipt() {
let myself = owned_user_id!("@alice:example.org");
let myself = user_id!("@alice:example.org");
let events = make_test_events(user_id!("@bob:example.org"));
let f = EventFactory::new();
{
// Thread receipts are ignored.
let mut selector = ReceiptSelector::new(&events, None);
let receipt_event = EventBuilder::new().make_receipt_event_content([(
owned_event_id!("$5"),
ReceiptType::Read,
myself.clone(),
ReceiptThread::Thread(owned_event_id!("$2")),
)]);
let receipt_event = f
.read_receipts()
.add(
event_id!("$5"),
myself,
ReceiptType::Read,
ReceiptThread::Thread(owned_event_id!("$2")),
)
.build();
let pending = selector.handle_new_receipt(&myself, &receipt_event);
let pending = selector.handle_new_receipt(myself, &receipt_event);
assert!(pending.is_empty());
let best_receipt = selector.select();
@@ -1440,14 +1444,12 @@ mod tests {
// receipt.
let mut selector = ReceiptSelector::new(&events, None);
let receipt_event = EventBuilder::new().make_receipt_event_content([(
owned_event_id!("$6"),
receipt_type.clone(),
myself.clone(),
receipt_thread.clone(),
)]);
let receipt_event = f
.read_receipts()
.add(event_id!("$6"), myself, receipt_type.clone(), receipt_thread.clone())
.build();
let pending = selector.handle_new_receipt(&myself, &receipt_event);
let pending = selector.handle_new_receipt(myself, &receipt_event);
assert_eq!(pending[0], event_id!("$6"));
assert_eq!(pending.len(), 1);
@@ -1460,14 +1462,12 @@ mod tests {
// receipt.
let mut selector = ReceiptSelector::new(&events, None);
let receipt_event = EventBuilder::new().make_receipt_event_content([(
owned_event_id!("$3"),
receipt_type.clone(),
myself.clone(),
receipt_thread.clone(),
)]);
let receipt_event = f
.read_receipts()
.add(event_id!("$3"), myself, receipt_type.clone(), receipt_thread.clone())
.build();
let pending = selector.handle_new_receipt(&myself, &receipt_event);
let pending = selector.handle_new_receipt(myself, &receipt_event);
assert!(pending.is_empty());
let best_receipt = selector.select();
@@ -1479,14 +1479,12 @@ mod tests {
// better receipt.
let mut selector = ReceiptSelector::new(&events, Some(event_id!("$4")));
let receipt_event = EventBuilder::new().make_receipt_event_content([(
owned_event_id!("$3"),
receipt_type.clone(),
myself.clone(),
receipt_thread.clone(),
)]);
let receipt_event = f
.read_receipts()
.add(event_id!("$3"), myself, receipt_type.clone(), receipt_thread.clone())
.build();
let pending = selector.handle_new_receipt(&myself, &receipt_event);
let pending = selector.handle_new_receipt(myself, &receipt_event);
assert!(pending.is_empty());
let best_receipt = selector.select();
@@ -1498,14 +1496,12 @@ mod tests {
// new better receipt.
let mut selector = ReceiptSelector::new(&events, Some(event_id!("$2")));
let receipt_event = EventBuilder::new().make_receipt_event_content([(
owned_event_id!("$3"),
receipt_type.clone(),
myself.clone(),
receipt_thread.clone(),
)]);
let receipt_event = f
.read_receipts()
.add(event_id!("$3"), myself, receipt_type.clone(), receipt_thread.clone())
.build();
let pending = selector.handle_new_receipt(&myself, &receipt_event);
let pending = selector.handle_new_receipt(myself, &receipt_event);
assert!(pending.is_empty());
let best_receipt = selector.select();
@@ -1519,23 +1515,14 @@ mod tests {
// new better receipt.
let mut selector = ReceiptSelector::new(&events, Some(event_id!("$2")));
let receipt_event = EventBuilder::new().make_receipt_event_content([
(
owned_event_id!("$4"),
ReceiptType::ReadPrivate,
myself.clone(),
ReceiptThread::Unthreaded,
),
(
owned_event_id!("$6"),
ReceiptType::ReadPrivate,
myself.clone(),
ReceiptThread::Main,
),
(owned_event_id!("$3"), ReceiptType::Read, myself.clone(), ReceiptThread::Main),
]);
let receipt_event = f
.read_receipts()
.add(event_id!("$4"), myself, ReceiptType::ReadPrivate, ReceiptThread::Unthreaded)
.add(event_id!("$6"), myself, ReceiptType::ReadPrivate, ReceiptThread::Main)
.add(event_id!("$3"), myself, ReceiptType::Read, ReceiptThread::Main)
.build();
let pending = selector.handle_new_receipt(&myself, &receipt_event);
let pending = selector.handle_new_receipt(myself, &receipt_event);
assert_eq!(pending.len(), 1);
assert_eq!(pending[0], event_id!("$6"));
@@ -1573,7 +1560,7 @@ mod tests {
#[test]
fn test_compute_unread_counts_with_implicit_receipt() {
let user_id = owned_user_id!("@alice:example.org");
let user_id = user_id!("@alice:example.org");
let bob = user_id!("@bob:example.org");
let room_id = room_id!("!room:example.org");
@@ -1581,7 +1568,7 @@ mod tests {
let mut events = make_test_events(bob);
// One by me,
events.push_back(sync_timeline_message(&user_id, "$6", "A mulatto, an albino"));
events.push_back(sync_timeline_message(user_id, "$6", "A mulatto, an albino"));
// And others by Bob,
events.push_back(sync_timeline_message(bob, "$7", "A mosquito, my libido"));
@@ -1590,19 +1577,18 @@ mod tests {
let events: Vec<_> = events.into_iter().collect();
// I have a read receipt attached to one of Bob's event sent before my message,
let receipt_event = EventBuilder::new().make_receipt_event_content([(
owned_event_id!("$3"),
ReceiptType::Read,
user_id.clone(),
ReceiptThread::Unthreaded,
)]);
let f = EventFactory::new();
let receipt_event = f
.read_receipts()
.add(event_id!("$3"), user_id, ReceiptType::Read, ReceiptThread::Unthreaded)
.build();
let mut read_receipts = RoomReadReceipts::default();
// And I compute the unread counts for all those new events (no previous events
// in that room),
compute_unread_counts(
&user_id,
user_id,
room_id,
Some(&receipt_event),
Vector::new(),
@@ -17,19 +17,20 @@ use assert_matches2::assert_let;
use eyeball_im::VectorDiff;
use futures_util::StreamExt;
use matrix_sdk::deserialized_responses::SyncTimelineEvent;
use matrix_sdk_test::{async_test, sync_timeline_event, ALICE, BOB, CAROL};
use matrix_sdk_test::{
async_test, event_factory::PreviousMembership, sync_timeline_event, ALICE, BOB, CAROL,
};
use ruma::{
events::{
receipt::{Receipt, ReceiptThread, ReceiptType},
room::{
member::{MembershipState, RedactedRoomMemberEventContent, RoomMemberEventContent},
member::{MembershipState, RedactedRoomMemberEventContent},
message::MessageType,
name::RoomNameEventContent,
topic::RedactedRoomTopicEventContent,
},
FullStateEventContent,
},
owned_event_id, owned_mxc_uri, MilliSecondsSinceUnixEpoch,
mxc_uri, owned_event_id, MilliSecondsSinceUnixEpoch,
};
use stream_assert::assert_next_matches;
@@ -148,16 +149,9 @@ async fn test_room_member() {
let timeline = TestTimeline::new();
let mut stream = timeline.subscribe_events().await;
let mut first_room_member_content = RoomMemberEventContent::new(MembershipState::Invite);
first_room_member_content.displayname = Some("Alice".to_owned());
timeline
.handle_live_state_event_with_state_key(
&BOB,
ALICE.to_owned(),
first_room_member_content.clone(),
None,
)
.await;
// Bob invites Alice.
let f = &timeline.factory;
timeline.handle_live_event(f.member(&BOB).invited(&ALICE).display_name("Alice")).await;
let item = assert_next_matches!(stream, VectorDiff::PushBack { value } => value);
assert!(item.can_be_replied_to());
@@ -165,14 +159,12 @@ async fn test_room_member() {
assert_matches!(membership.content(), FullStateEventContent::Original { .. });
assert_matches!(membership.change(), Some(MembershipChange::Invited));
let mut second_room_member_content = RoomMemberEventContent::new(MembershipState::Join);
second_room_member_content.displayname = Some("Alice".to_owned());
timeline
.handle_live_state_event_with_state_key(
&ALICE,
ALICE.to_owned(),
second_room_member_content.clone(),
Some(first_room_member_content),
.handle_live_event(
f.member(&ALICE)
.membership(MembershipState::Join)
.display_name("Alice")
.previous(PreviousMembership::new(MembershipState::Invite).display_name("Alice")),
)
.await;
@@ -181,14 +173,12 @@ async fn test_room_member() {
assert_matches!(membership.content(), FullStateEventContent::Original { .. });
assert_matches!(membership.change(), Some(MembershipChange::InvitationAccepted));
let mut third_room_member_content = RoomMemberEventContent::new(MembershipState::Join);
third_room_member_content.displayname = Some("Alice In Wonderland".to_owned());
timeline
.handle_live_state_event_with_state_key(
&ALICE,
ALICE.to_owned(),
third_room_member_content.clone(),
Some(second_room_member_content),
.handle_live_event(
f.member(&ALICE)
.membership(MembershipState::Join)
.display_name("Alice In Wonderland")
.previous(PreviousMembership::new(MembershipState::Join).display_name("Alice")),
)
.await;
@@ -197,15 +187,16 @@ async fn test_room_member() {
assert_matches!(profile.displayname_change(), Some(_));
assert_matches!(profile.avatar_url_change(), None);
let mut fourth_room_member_content = RoomMemberEventContent::new(MembershipState::Join);
fourth_room_member_content.displayname = Some("Alice In Wonderland".to_owned());
fourth_room_member_content.avatar_url = Some(owned_mxc_uri!("mxc://lolcathost.io/abc"));
timeline
.handle_live_state_event_with_state_key(
&ALICE,
ALICE.to_owned(),
fourth_room_member_content.clone(),
Some(third_room_member_content),
.handle_live_event(
f.member(&ALICE)
.membership(MembershipState::Join)
.display_name("Alice In Wonderland")
.avatar_url(mxc_uri!("mxc://lolcathost.io/abc"))
.previous(
PreviousMembership::new(MembershipState::Join)
.display_name("Alice In Wonderland"),
),
)
.await;
@@ -217,14 +208,13 @@ async fn test_room_member() {
{
// No avatar or display name in the new room member event content, but it's
// possible to get the previous one using the getters.
let room_member_content = RoomMemberEventContent::new(MembershipState::Leave);
timeline
.handle_live_state_event_with_state_key(
&ALICE,
ALICE.to_owned(),
room_member_content,
Some(fourth_room_member_content),
.handle_live_event(
f.member(&ALICE).membership(MembershipState::Leave).previous(
PreviousMembership::new(MembershipState::Join)
.display_name("Alice In Wonderland")
.avatar_url(mxc_uri!("mxc://lolcathost.io/abc")),
),
)
.await;
@@ -240,11 +230,11 @@ async fn test_room_member() {
}
timeline
.handle_live_redacted_state_event_with_state_key(
.handle_live_event(f.redacted_state(
&ALICE,
ALICE.to_owned(),
ALICE.as_str(),
RedactedRoomMemberEventContent::new(MembershipState::Join),
)
))
.await;
let item = assert_next_matches!(stream, VectorDiff::PushBack { value } => value);
@@ -258,9 +248,8 @@ async fn test_other_state() {
let timeline = TestTimeline::new();
let mut stream = timeline.subscribe().await;
timeline
.handle_live_state_event(&ALICE, RoomNameEventContent::new("Alice's room".to_owned()), None)
.await;
let f = &timeline.factory;
timeline.handle_live_event(f.room_name("Alice's room").sender(&ALICE)).await;
let item = assert_next_matches!(stream, VectorDiff::PushBack { value } => value);
assert_let!(TimelineItemContent::OtherState(ev) = item.as_event().unwrap().content());
@@ -272,7 +261,9 @@ async fn test_other_state() {
let date_divider = assert_next_matches!(stream, VectorDiff::PushFront { value } => value);
assert!(date_divider.is_date_divider());
timeline.handle_live_redacted_state_event(&ALICE, RedactedRoomTopicEventContent::new()).await;
timeline
.handle_live_event(f.redacted_state(&ALICE, "", RedactedRoomTopicEventContent::new()))
.await;
let item = assert_next_matches!(stream, VectorDiff::PushBack { value } => value);
assert_let!(TimelineItemContent::OtherState(ev) = item.as_event().unwrap().content());
@@ -41,9 +41,7 @@ async fn test_live_redacted() {
let f = &timeline.factory;
timeline
.handle_live_redacted_message_event(*ALICE, RedactedRoomMessageEventContent::new())
.await;
timeline.handle_live_event(f.redacted(*ALICE, RedactedRoomMessageEventContent::new())).await;
let item = assert_next_matches!(stream, VectorDiff::PushBack { value } => value);
let redacted_event_id = item.as_event().unwrap().event_id().unwrap();
@@ -21,10 +21,8 @@ use matrix_sdk::deserialized_responses::SyncTimelineEvent;
use matrix_sdk_test::{async_test, sync_timeline_event, ALICE, BOB};
use ruma::events::{
room::{
member::{MembershipState, RoomMemberEventContent},
member::MembershipState,
message::{MessageType, RedactedRoomMessageEventContent},
name::RoomNameEventContent,
topic::RoomTopicEventContent,
},
AnySyncTimelineEvent, TimelineEventType,
};
@@ -82,7 +80,7 @@ async fn test_default_filter() {
let item = assert_next_matches!(stream, VectorDiff::PushBack { value } => value);
let third_event_id = item.as_event().unwrap().event_id().unwrap();
timeline.handle_live_event(f.reaction(third_event_id, "+1".to_owned()).sender(&BOB)).await;
timeline.handle_live_event(f.reaction(third_event_id, "+1").sender(&BOB)).await;
timeline.handle_live_event(f.redaction(second_event_id).sender(&BOB)).await;
let item = assert_next_matches!(stream, VectorDiff::Set { index: 3, value } => value);
assert_eq!(item.as_event().unwrap().reactions().len(), 1);
@@ -102,22 +100,11 @@ async fn test_filter_always_false() {
let f = &timeline.factory;
timeline.handle_live_event(f.text_msg("The first message").sender(&ALICE)).await;
timeline
.handle_live_redacted_message_event(&ALICE, RedactedRoomMessageEventContent::new())
.await;
timeline.handle_live_event(f.redacted(&ALICE, RedactedRoomMessageEventContent::new())).await;
timeline
.handle_live_state_event_with_state_key(
&ALICE,
ALICE.to_owned(),
RoomMemberEventContent::new(MembershipState::Join),
None,
)
.await;
timeline.handle_live_event(f.member(&ALICE).membership(MembershipState::Join)).await;
timeline
.handle_live_state_event(&ALICE, RoomNameEventContent::new("Alice's room".to_owned()), None)
.await;
timeline.handle_live_event(f.room_name("Alice's room").sender(&ALICE)).await;
assert_eq!(timeline.controller.items().await.len(), 0);
}
@@ -136,23 +123,12 @@ async fn test_custom_filter() {
let _item = assert_next_matches!(stream, VectorDiff::PushBack { value } => value);
let _date_divider = assert_next_matches!(stream, VectorDiff::PushFront { value } => value);
timeline
.handle_live_redacted_message_event(&ALICE, RedactedRoomMessageEventContent::new())
.await;
timeline.handle_live_event(f.redacted(&ALICE, RedactedRoomMessageEventContent::new())).await;
let _item = assert_next_matches!(stream, VectorDiff::PushBack { value } => value);
timeline
.handle_live_state_event_with_state_key(
&ALICE,
ALICE.to_owned(),
RoomMemberEventContent::new(MembershipState::Join),
None,
)
.await;
timeline.handle_live_event(f.member(&ALICE).membership(MembershipState::Join)).await;
timeline
.handle_live_state_event(&ALICE, RoomNameEventContent::new("Alice's room".to_owned()), None)
.await;
timeline.handle_live_event(f.room_name("Alice's room").sender(&ALICE)).await;
assert_eq!(timeline.controller.items().await.len(), 3);
}
@@ -204,28 +180,10 @@ async fn test_event_type_filter_include_only_room_names() {
// Add a non-encrypted message event
timeline.handle_live_event(f.text_msg("The first message").sender(&ALICE)).await;
// Add a couple of room name events
timeline
.handle_live_state_event(
&ALICE,
RoomNameEventContent::new("A new room name".to_owned()),
None,
)
.await;
timeline
.handle_live_state_event(
&ALICE,
RoomNameEventContent::new("A new room name (again)".to_owned()),
None,
)
.await;
timeline.handle_live_event(f.room_name("A new room name").sender(&ALICE)).await;
timeline.handle_live_event(f.room_name("A new room name (again)").sender(&ALICE)).await;
// And a different state event
timeline
.handle_live_state_event(
&ALICE,
RoomTopicEventContent::new("A new room topic".to_owned()),
None,
)
.await;
timeline.handle_live_event(f.room_topic("A new room topic").sender(&ALICE)).await;
// The timeline should contain only the room name events
let event_items: Vec<Arc<TimelineItem>> = timeline.get_event_items().await;
@@ -252,28 +210,10 @@ async fn test_event_type_filter_exclude_messages() {
// Add a message event
timeline.handle_live_event(f.text_msg("The first message").sender(&ALICE)).await;
// Add a couple of room name state events
timeline
.handle_live_state_event(
&ALICE,
RoomNameEventContent::new("A new room name".to_owned()),
None,
)
.await;
timeline
.handle_live_state_event(
&ALICE,
RoomNameEventContent::new("A new room name (again)".to_owned()),
None,
)
.await;
timeline.handle_live_event(f.room_name("A new room name").sender(&ALICE)).await;
timeline.handle_live_event(f.room_name("A new room name (again)").sender(&ALICE)).await;
// And a different state event
timeline
.handle_live_state_event(
&ALICE,
RoomTopicEventContent::new("A new room topic".to_owned()),
None,
)
.await;
timeline.handle_live_event(f.room_topic("A new room topic").sender(&ALICE)).await;
// The timeline should contain everything except for the message event.
let event_items: Vec<Arc<TimelineItem>> = timeline.get_event_items().await;
+7 -66
View File
@@ -36,17 +36,14 @@ use matrix_sdk::{
use matrix_sdk_base::{
crypto::types::events::CryptoContextInfo, latest_event::LatestEvent, RoomInfo, RoomState,
};
use matrix_sdk_test::{
event_factory::EventFactory, EventBuilder, ALICE, BOB, DEFAULT_TEST_ROOM_ID,
};
use matrix_sdk_test::{event_factory::EventFactory, ALICE, BOB, DEFAULT_TEST_ROOM_ID};
use ruma::{
event_id,
events::{
reaction::ReactionEventContent,
receipt::{Receipt, ReceiptThread, ReceiptType},
relation::{Annotation, RelationType},
AnyMessageLikeEventContent, AnyTimelineEvent, EmptyStateKey,
RedactedMessageLikeEventContent, RedactedStateEventContent, StaticStateEventContent,
AnyMessageLikeEventContent, AnyTimelineEvent,
},
int,
power_levels::NotificationPowerLevels,
@@ -86,7 +83,6 @@ mod virt;
struct TestTimeline {
controller: TimelineController<TestRoomDataProvider>,
event_builder: EventBuilder,
/// An [`EventFactory`] that can be used for creating events in this
/// timeline.
pub factory: EventFactory,
@@ -111,7 +107,6 @@ impl TestTimeline {
None,
Some(false),
),
event_builder: EventBuilder::new(),
factory: EventFactory::new(),
}
}
@@ -125,7 +120,6 @@ impl TestTimeline {
None,
Some(false),
),
event_builder: EventBuilder::new(),
factory: EventFactory::new(),
}
}
@@ -139,7 +133,6 @@ impl TestTimeline {
Some(hook),
Some(true),
),
event_builder: EventBuilder::new(),
factory: EventFactory::new(),
}
}
@@ -154,7 +147,6 @@ impl TestTimeline {
None,
Some(encrypted),
),
event_builder: EventBuilder::new(),
factory: EventFactory::new(),
}
}
@@ -181,61 +173,6 @@ impl TestTimeline {
self.controller.items().await.len()
}
async fn handle_live_redacted_message_event<C>(&self, sender: &UserId, content: C)
where
C: RedactedMessageLikeEventContent,
{
let ev = self.event_builder.make_sync_redacted_message_event(sender, content);
self.handle_live_event(SyncTimelineEvent::new(ev)).await;
}
async fn handle_live_state_event<C>(&self, sender: &UserId, content: C, prev_content: Option<C>)
where
C: StaticStateEventContent<StateKey = EmptyStateKey>,
{
let ev = self.event_builder.make_sync_state_event(sender, "", content, prev_content);
self.handle_live_event(SyncTimelineEvent::new(ev)).await;
}
async fn handle_live_state_event_with_state_key<C>(
&self,
sender: &UserId,
state_key: C::StateKey,
content: C,
prev_content: Option<C>,
) where
C: StaticStateEventContent,
{
let ev = self.event_builder.make_sync_state_event(
sender,
state_key.as_ref(),
content,
prev_content,
);
self.handle_live_event(SyncTimelineEvent::new(ev)).await;
}
async fn handle_live_redacted_state_event<C>(&self, sender: &UserId, content: C)
where
C: RedactedStateEventContent<StateKey = EmptyStateKey>,
{
let ev = self.event_builder.make_sync_redacted_state_event(sender, "", content);
self.handle_live_event(SyncTimelineEvent::new(ev)).await;
}
async fn handle_live_redacted_state_event_with_state_key<C>(
&self,
sender: &UserId,
state_key: C::StateKey,
content: C,
) where
C: RedactedStateEventContent,
{
let ev =
self.event_builder.make_sync_redacted_state_event(sender, state_key.as_ref(), content);
self.handle_live_event(SyncTimelineEvent::new(ev)).await;
}
async fn handle_live_event(&self, event: impl Into<SyncTimelineEvent>) {
let event = event.into();
self.controller
@@ -272,7 +209,11 @@ impl TestTimeline {
&self,
receipts: impl IntoIterator<Item = (OwnedEventId, ReceiptType, OwnedUserId, ReceiptThread)>,
) {
let ev_content = self.event_builder.make_receipt_event_content(receipts);
let mut read_receipt = self.factory.read_receipts();
for (event_id, tyype, user_id, thread) in receipts {
read_receipt = read_receipt.add(&event_id, &user_id, tyype, thread);
}
let ev_content = read_receipt.build();
self.controller.handle_read_receipts(ev_content).await;
}
@@ -1,4 +1,4 @@
use matrix_sdk_base::deserialized_responses::SyncTimelineEvent;
use fakes::poll_a2;
use matrix_sdk_test::{async_test, ALICE, BOB};
use ruma::{
events::{
@@ -164,26 +164,27 @@ async fn test_events_received_before_start_are_not_lost() {
let poll_id: OwnedEventId = EventId::new(server_name!("dummy.server"));
// Alice votes
timeline.send_poll_response(&ALICE, vec!["id_up"], &poll_id).await;
timeline.send_poll_response(&ALICE, vec!["0"], &poll_id).await;
// Now Bob also votes
timeline.send_poll_response(&BOB, vec!["id_up"], &poll_id).await;
timeline.send_poll_response(&BOB, vec!["0"], &poll_id).await;
// Alice changes her mind and votes again
timeline.send_poll_response(&ALICE, vec!["id_down"], &poll_id).await;
timeline.send_poll_response(&ALICE, vec!["1"], &poll_id).await;
// Poll finishes
timeline.send_poll_end(&ALICE, "ENDED", &poll_id).await;
// Now the start event arrives
timeline.send_poll_start_with_id(&ALICE, &poll_id, fakes::poll_a()).await;
let start_ev = poll_a2(&timeline.factory).sender(&ALICE).event_id(&poll_id);
timeline.handle_live_event(start_ev).await;
// Now Bob votes again but his vote won't count
timeline.send_poll_response(&BOB, vec!["id_down"], &poll_id).await;
timeline.send_poll_response(&BOB, vec!["1"], &poll_id).await;
let results = timeline.poll_state().await.results();
assert_eq!(results.votes["id_up"], vec![BOB.to_string()]);
assert_eq!(results.votes["id_down"], vec![ALICE.to_string()]);
assert_eq!(results.votes["0"], vec![BOB.to_string()]);
assert_eq!(results.votes["1"], vec![ALICE.to_string()]);
}
impl TestTimeline {
@@ -206,20 +207,6 @@ impl TestTimeline {
self.handle_live_event(self.factory.event(event_content).sender(sender)).await;
}
async fn send_poll_start_with_id(
&self,
sender: &UserId,
event_id: &EventId,
content: UnstablePollStartContentBlock,
) {
let event_content = AnyMessageLikeEventContent::UnstablePollStart(
NewUnstablePollStartEventContent::new(content).into(),
);
let event =
self.event_builder.make_sync_message_event_with_id(sender, event_id, event_content);
self.handle_live_event(SyncTimelineEvent::new(event)).await;
}
async fn send_poll_response(&self, sender: &UserId, answers: Vec<&str>, poll_id: &EventId) {
let event_content = AnyMessageLikeEventContent::UnstablePollResponse(
UnstablePollResponseEventContent::new(
@@ -263,11 +250,19 @@ fn assert_poll_start_eq(a: &UnstablePollStartContentBlock, b: &UnstablePollStart
}
mod fakes {
use matrix_sdk_test::event_factory::{EventBuilder, EventFactory};
use ruma::events::poll::{
start::PollKind,
unstable_start::{UnstablePollAnswer, UnstablePollAnswers, UnstablePollStartContentBlock},
unstable_start::{
UnstablePollAnswer, UnstablePollAnswers, UnstablePollStartContentBlock,
UnstablePollStartEventContent,
},
};
pub fn poll_a2(f: &EventFactory) -> EventBuilder<UnstablePollStartEventContent> {
f.poll_start("Up or down?", "Up or down?", vec!["Up", "Down"])
}
pub fn poll_a() -> UnstablePollStartContentBlock {
let mut content = UnstablePollStartContentBlock::new(
"Up or down?",
@@ -113,7 +113,7 @@ async fn test_add_reaction_success() {
// When the remote echo is received from sync,
let f = EventFactory::new();
timeline.handle_live_event(f.reaction(&event_id, REACTION_KEY.to_owned()).sender(*ALICE)).await;
timeline.handle_live_event(f.reaction(&event_id, REACTION_KEY).sender(*ALICE)).await;
// The reaction is still present on the item, as a remote echo.
assert_reaction_is_updated!(stream, &event_id, item_pos, true);
@@ -132,9 +132,7 @@ async fn test_redact_reaction_success() {
// A reaction is added by sync.
let reaction_id = event_id!("$reaction_id");
timeline
.handle_live_event(
f.reaction(&event_id, REACTION_KEY.to_owned()).sender(&ALICE).event_id(reaction_id),
)
.handle_live_event(f.reaction(&event_id, REACTION_KEY).sender(&ALICE).event_id(reaction_id))
.await;
assert_reaction_is_updated!(stream, &event_id, item_pos, true);
@@ -198,7 +196,7 @@ async fn test_initial_reaction_timestamp_is_stored() {
.add_events_at(
[
// Reaction comes first.
f.reaction(&message_event_id, REACTION_KEY.to_owned())
f.reaction(&message_event_id, REACTION_KEY)
.server_ts(reaction_timestamp)
.into_sync(),
// Event comes next.
@@ -15,11 +15,9 @@
use assert_matches::assert_matches;
use assert_matches2::assert_let;
use eyeball_im::VectorDiff;
use matrix_sdk_base::deserialized_responses::SyncTimelineEvent;
use matrix_sdk_test::{async_test, ALICE, BOB};
use ruma::events::{
reaction::RedactedReactionEventContent,
room::{message::OriginalSyncRoomMessageEvent, name::RoomNameEventContent},
reaction::RedactedReactionEventContent, room::message::OriginalSyncRoomMessageEvent,
FullStateEventContent,
};
use stream_assert::assert_next_matches;
@@ -37,13 +35,7 @@ async fn test_redact_state_event() {
let f = &timeline.factory;
timeline
.handle_live_state_event(
&ALICE,
RoomNameEventContent::new("Fancy room name".to_owned()),
None,
)
.await;
timeline.handle_live_event(f.room_name("Fancy room name").sender(&ALICE)).await;
let item = assert_next_matches!(stream, VectorDiff::PushBack { value } => value);
assert_let!(TimelineItemContent::OtherState(state) = item.content());
@@ -114,7 +106,7 @@ async fn test_reaction_redaction() {
let msg_event_id = item.event_id().unwrap();
timeline.handle_live_event(f.reaction(msg_event_id, "+1".to_owned()).sender(&BOB)).await;
timeline.handle_live_event(f.reaction(msg_event_id, "+1").sender(&BOB)).await;
let item = assert_next_matches!(stream, VectorDiff::Set { index: 0, value } => value);
assert_eq!(item.reactions().len(), 1);
@@ -138,12 +130,7 @@ async fn test_reaction_redaction_timeline_filter() {
timeline
.controller
.add_events_at(
[SyncTimelineEvent::new(
timeline
.event_builder
.make_sync_redacted_message_event(*ALICE, RedactedReactionEventContent::new()),
)]
.into_iter(),
[f.redacted(*ALICE, RedactedReactionEventContent::new())].into_iter(),
TimelineNewItemPosition::End { origin: RemoteEventOrigin::Sync },
)
.await;
@@ -151,7 +138,7 @@ async fn test_reaction_redaction_timeline_filter() {
assert_eq!(timeline.controller.items().await.len(), 0);
// Adding a live redacted reaction does nothing.
timeline.handle_live_redacted_message_event(&ALICE, RedactedReactionEventContent::new()).await;
timeline.handle_live_event(f.redacted(&ALICE, RedactedReactionEventContent::new())).await;
assert_eq!(timeline.controller.items().await.len(), 0);
// Adding a room message
@@ -161,9 +148,7 @@ async fn test_reaction_redaction_timeline_filter() {
assert_eq!(timeline.controller.items().await.len(), 2);
// Reaction is attached to the message and doesn't add a timeline item.
timeline
.handle_live_event(f.reaction(item.event_id().unwrap(), "+1".to_owned()).sender(&BOB))
.await;
timeline.handle_live_event(f.reaction(item.event_id().unwrap(), "+1").sender(&BOB)).await;
let item = assert_next_matches!(stream, VectorDiff::Set { index: 0, value } => value);
let reaction_event_id = item.event_id().unwrap();
assert_eq!(timeline.controller.items().await.len(), 2);
@@ -242,7 +242,7 @@ async fn test_focused_timeline_reacts() {
// This event must be ignored.
f.text_msg("this is a sync event").sender(*ALICE).into(),
// This event must not be ignored.
f.reaction(target_event, "👍".to_owned()).sender(*BOB).into(),
f.reaction(target_event, "👍").sender(*BOB).into(),
]));
// Sync the room.
@@ -22,18 +22,15 @@ use futures_util::{
};
use matrix_sdk::{config::SyncSettings, test_utils::logged_in_client_with_server};
use matrix_sdk_test::{
async_test, mocks::mock_encryption_state, EventBuilder, JoinedRoomBuilder, StateTestEvent,
SyncResponseBuilder, ALICE, BOB,
async_test, event_factory::EventFactory, mocks::mock_encryption_state, JoinedRoomBuilder,
StateTestEvent, SyncResponseBuilder, ALICE, BOB,
};
use matrix_sdk_ui::timeline::{
AnyOtherFullStateEventContent, LiveBackPaginationStatus, RoomExt, TimelineItemContent,
};
use once_cell::sync::Lazy;
use ruma::{
events::{
room::message::{MessageType, RoomMessageEventContent},
FullStateEventContent,
},
events::{room::message::MessageType, FullStateEventContent},
room_id,
};
use serde_json::{json, Value as JsonValue};
@@ -257,7 +254,7 @@ async fn test_wait_for_token() {
let (client, server) = logged_in_client_with_server().await;
let sync_settings = SyncSettings::new().timeout(Duration::from_millis(3000));
let event_builder = EventBuilder::new();
let f = EventFactory::new();
let mut sync_builder = SyncResponseBuilder::new();
sync_builder.add_joined_room(JoinedRoomBuilder::new(room_id));
@@ -285,10 +282,7 @@ async fn test_wait_for_token() {
sync_builder.add_joined_room(
JoinedRoomBuilder::new(room_id)
.add_timeline_event(event_builder.make_sync_message_event(
&ALICE,
RoomMessageEventContent::text_plain("live event!"),
))
.add_timeline_event(f.text_msg("live event!").sender(&ALICE))
.set_timeline_prev_batch(from.to_owned())
.set_timeline_limited(),
);
@@ -321,7 +315,7 @@ async fn test_dedup_pagination() {
let (client, server) = logged_in_client_with_server().await;
let sync_settings = SyncSettings::new().timeout(Duration::from_millis(3000));
let event_builder = EventBuilder::new();
let f = EventFactory::new();
let mut sync_builder = SyncResponseBuilder::new();
sync_builder.add_joined_room(JoinedRoomBuilder::new(room_id));
@@ -353,10 +347,7 @@ async fn test_dedup_pagination() {
sync_builder.add_joined_room(
JoinedRoomBuilder::new(room_id)
.add_timeline_event(event_builder.make_sync_message_event(
&ALICE,
RoomMessageEventContent::text_plain("live event!"),
))
.add_timeline_event(f.text_msg("live event!").sender(&ALICE))
.set_timeline_prev_batch(from.to_owned()),
);
mock_sync(&server, sync_builder.build_json_sync_response(), None).await;
@@ -383,7 +374,7 @@ async fn test_timeline_reset_while_paginating() {
let (client, server) = logged_in_client_with_server().await;
let sync_settings = SyncSettings::new().timeout(Duration::from_millis(3000));
let event_builder = EventBuilder::new();
let f = EventFactory::new();
let mut sync_builder = SyncResponseBuilder::new();
sync_builder.add_joined_room(JoinedRoomBuilder::new(room_id));
@@ -398,10 +389,7 @@ async fn test_timeline_reset_while_paginating() {
sync_builder.add_joined_room(
JoinedRoomBuilder::new(room_id)
.add_timeline_event(event_builder.make_sync_message_event(
&ALICE,
RoomMessageEventContent::text_plain("live event!"),
))
.add_timeline_event(f.text_msg("live event!").sender(&ALICE))
.set_timeline_prev_batch("pagination_1".to_owned())
.set_timeline_limited(),
);
@@ -413,10 +401,7 @@ async fn test_timeline_reset_while_paginating() {
// response, resetting the timeline
sync_builder.add_joined_room(
JoinedRoomBuilder::new(room_id)
.add_timeline_event(event_builder.make_sync_message_event(
&BOB,
RoomMessageEventContent::text_plain("new live event."),
))
.add_timeline_event(f.text_msg("new live event.").sender(&BOB))
.set_timeline_prev_batch("pagination_2".to_owned())
.set_timeline_limited(),
);
@@ -17,14 +17,11 @@ use std::{sync::Arc, time::Duration};
use assert_matches::assert_matches;
use matrix_sdk::{config::SyncSettings, test_utils::logged_in_client_with_server};
use matrix_sdk_test::{
async_test, mocks::mock_encryption_state, EventBuilder, JoinedRoomBuilder, SyncResponseBuilder,
ALICE, BOB, CAROL, DEFAULT_TEST_ROOM_ID,
async_test, event_factory::EventFactory, mocks::mock_encryption_state, JoinedRoomBuilder,
SyncResponseBuilder, ALICE, BOB, CAROL, DEFAULT_TEST_ROOM_ID,
};
use matrix_sdk_ui::timeline::{RoomExt, TimelineDetails};
use ruma::events::room::{
member::{MembershipState, RoomMemberEventContent},
message::RoomMessageEventContent,
};
use ruma::events::room::member::MembershipState;
use serde_json::json;
use wiremock::{
matchers::{method, path_regex},
@@ -38,7 +35,7 @@ async fn test_update_sender_profiles() {
let (client, server) = logged_in_client_with_server().await;
let sync_settings = SyncSettings::new().timeout(Duration::from_millis(3000));
let event_builder = EventBuilder::new();
let f = EventFactory::new();
let mut sync_builder = SyncResponseBuilder::new();
sync_builder.add_joined_room(JoinedRoomBuilder::new(&DEFAULT_TEST_ROOM_ID));
@@ -54,24 +51,15 @@ async fn test_update_sender_profiles() {
sync_builder.add_joined_room(
JoinedRoomBuilder::new(&DEFAULT_TEST_ROOM_ID)
// Alice accepts an invite into the room
.add_timeline_event(event_builder.make_sync_state_event(
&ALICE,
ALICE.as_str(),
RoomMemberEventContent::new(MembershipState::Join),
Some(RoomMemberEventContent::new(MembershipState::Invite)),
))
.add_timeline_event(
f.member(&ALICE)
.membership(MembershipState::Join)
.previous(MembershipState::Invite),
)
// Bob sends a text message
.add_timeline_event(event_builder.make_sync_message_event(
&BOB,
RoomMessageEventContent::text_plain("text message event"),
))
.add_timeline_event(f.text_msg("text message event").sender(&BOB))
// Carol kicks bob
.add_timeline_event(event_builder.make_sync_state_event(
&CAROL,
BOB.as_str(),
RoomMemberEventContent::new(MembershipState::Leave),
Some(RoomMemberEventContent::new(MembershipState::Join)),
)),
.add_timeline_event(f.member(&CAROL).kicked(&BOB).previous(MembershipState::Join)),
);
mock_sync(&server, sync_builder.build_json_sync_response(), None).await;
@@ -118,36 +106,20 @@ async fn test_update_sender_profiles() {
TimelineDetails::Error(_)
);
let f = f.room(&DEFAULT_TEST_ROOM_ID);
// Try again, this time we mock the endpoint to get a useful response.
Mock::given(method("GET"))
.and(path_regex(r"/_matrix/client/r0/rooms/.*/members"))
.respond_with(ResponseTemplate::new(200).set_body_json(json!({
"chunk": [
// This event wasn't previously observed by the client
event_builder.make_state_event(
&CAROL,
&DEFAULT_TEST_ROOM_ID,
CAROL.as_str(),
RoomMemberEventContent::new(MembershipState::Join),
None,
),
f.member(&CAROL).membership(MembershipState::Join).into_raw_timeline(),
// Same as above
event_builder.make_state_event(
&ALICE,
&DEFAULT_TEST_ROOM_ID,
ALICE.as_str(),
RoomMemberEventContent::new(MembershipState::Join),
Some(RoomMemberEventContent::new(MembershipState::Invite)),
),
f.member(&ALICE).membership(MembershipState::Join).previous(MembershipState::Invite).into_raw_timeline(),
event_builder.make_state_event(
&CAROL,
&DEFAULT_TEST_ROOM_ID,
BOB.as_str(),
RoomMemberEventContent::new(MembershipState::Leave),
Some(RoomMemberEventContent::new(MembershipState::Join)),
),
f.member(&CAROL).kicked(&BOB).previous(MembershipState::Join).into_raw_timeline(),
]
})))
.mount(&server)
@@ -21,8 +21,8 @@ use futures_util::StreamExt;
use matrix_sdk::{config::SyncSettings, test_utils::logged_in_client_with_server, Error};
use matrix_sdk_base::store::QueueWedgeError;
use matrix_sdk_test::{
async_test, mocks::mock_encryption_state, EventBuilder, JoinedRoomBuilder, SyncResponseBuilder,
ALICE,
async_test, event_factory::EventFactory, mocks::mock_encryption_state, JoinedRoomBuilder,
SyncResponseBuilder, ALICE,
};
use matrix_sdk_ui::timeline::{EventItemOrigin, EventSendState, RoomExt};
use ruma::{
@@ -314,7 +314,7 @@ async fn test_clear_with_echoes() {
let (client, server) = logged_in_client_with_server().await;
let sync_settings = SyncSettings::new().timeout(Duration::from_millis(3000));
let event_builder = EventBuilder::new();
let f = EventFactory::new();
let mut sync_builder = SyncResponseBuilder::new();
sync_builder.add_joined_room(JoinedRoomBuilder::new(room_id));
@@ -355,12 +355,10 @@ async fn test_clear_with_echoes() {
timeline.send(RoomMessageEventContent::text_plain("Pending").into()).await.unwrap();
// Another message comes in.
sync_builder.add_joined_room(JoinedRoomBuilder::new(room_id).add_timeline_event(
event_builder.make_sync_message_event(
&ALICE,
RoomMessageEventContent::text_plain("another message"),
),
));
sync_builder.add_joined_room(
JoinedRoomBuilder::new(room_id)
.add_timeline_event(f.text_msg("another message").sender(&ALICE)),
);
mock_sync(&server, sync_builder.build_json_sync_response(), None).await;
client.sync_once(sync_settings.clone()).await.unwrap();
@@ -475,23 +473,23 @@ async fn test_no_duplicate_date_divider() {
assert_pending!(timeline_stream);
// Now have the sync return both events with more data.
let ev_builder = EventBuilder::new();
let f = EventFactory::new();
let now = MilliSecondsSinceUnixEpoch::now();
ev_builder.set_next_ts(now.0.into());
f.set_next_ts(now.0.into());
sync_response_builder.add_joined_room(
JoinedRoomBuilder::new(room_id)
.add_timeline_event(ev_builder.make_sync_message_event_with_id(
client.user_id().unwrap(),
event_id!("$PyHxV5mYzjetBUT3qZq7V95GOzxb02EP"),
RoomMessageEventContent::text_plain("First!"),
))
.add_timeline_event(ev_builder.make_sync_message_event_with_id(
client.user_id().unwrap(),
event_id!("$5E2kLK/Sg342bgBU9ceEIEPYpbFaqJpZ"),
RoomMessageEventContent::text_plain("Second."),
)),
.add_timeline_event(
f.text_msg("First!")
.sender(client.user_id().unwrap())
.event_id(event_id!("$PyHxV5mYzjetBUT3qZq7V95GOzxb02EP")),
)
.add_timeline_event(
f.text_msg("Second.")
.sender(client.user_id().unwrap())
.event_id(event_id!("$5E2kLK/Sg342bgBU9ceEIEPYpbFaqJpZ")),
),
);
mock_sync(&server, sync_response_builder.build_json_sync_response(), None).await;
@@ -199,7 +199,7 @@ async fn test_redact_failed() {
sync_builder.add_joined_room(
JoinedRoomBuilder::new(room_id)
.add_timeline_event(f.text_msg("hello").sender(&ALICE).event_id(event_id))
.add_timeline_event(f.reaction(event_id, "😆".to_owned()).sender(user_id)),
.add_timeline_event(f.reaction(event_id, "😆").sender(user_id)),
);
mock_sync(&server, sync_builder.build_json_sync_response(), None).await;
@@ -7,8 +7,8 @@ use futures_util::StreamExt;
use matrix_sdk::{config::SyncSettings, test_utils::logged_in_client_with_server};
use matrix_sdk_base::timeout::timeout;
use matrix_sdk_test::{
async_test, event_factory::EventFactory, mocks::mock_encryption_state, EventBuilder,
JoinedRoomBuilder, SyncResponseBuilder, ALICE, BOB, CAROL,
async_test, event_factory::EventFactory, mocks::mock_encryption_state, JoinedRoomBuilder,
SyncResponseBuilder, ALICE, BOB, CAROL,
};
use matrix_sdk_ui::timeline::{
Error as TimelineError, EventSendState, RoomExt, TimelineDetails, TimelineItemContent,
@@ -18,13 +18,10 @@ use ruma::{
events::{
reaction::RedactedReactionEventContent,
relation::InReplyTo,
room::message::{
AddMentions, ForwardThread, OriginalRoomMessageEvent, Relation, ReplyWithinThread,
RoomMessageEventContent, RoomMessageEventContentWithoutRelation,
},
Mentions, MessageLikeUnsigned,
room::message::{ForwardThread, Relation, RoomMessageEventContentWithoutRelation},
Mentions,
},
owned_event_id, owned_room_id, owned_user_id, room_id, uint, MilliSecondsSinceUnixEpoch,
owned_event_id, room_id,
};
use serde_json::json;
use stream_assert::assert_next_matches;
@@ -251,7 +248,6 @@ async fn test_transfer_in_reply_to_details_to_re_received_item() {
async fn test_send_reply() {
let room_id = room_id!("!a98sd12bjh:example.org");
let (client, server) = logged_in_client_with_server().await;
let event_builder = EventBuilder::new();
let sync_settings = SyncSettings::new().timeout(Duration::from_millis(3000));
let mut sync_builder = SyncResponseBuilder::new();
@@ -269,13 +265,12 @@ async fn test_send_reply() {
timeline.subscribe_filter_map(|item| item.as_event().cloned()).await;
let event_id_from_bob = event_id!("$event_from_bob");
sync_builder.add_joined_room(JoinedRoomBuilder::new(room_id).add_timeline_event(
event_builder.make_sync_message_event_with_id(
&BOB,
event_id_from_bob,
RoomMessageEventContent::text_plain("Hello from Bob"),
let f = EventFactory::new();
sync_builder.add_joined_room(
JoinedRoomBuilder::new(room_id).add_timeline_event(
f.text_msg("Hello from Bob").sender(&BOB).event_id(event_id_from_bob),
),
));
);
mock_sync(&server, sync_builder.build_json_sync_response(), None).await;
let _response = client.sync_once(sync_settings.clone()).await.unwrap();
@@ -364,7 +359,6 @@ async fn test_send_reply() {
async fn test_send_reply_to_self() {
let room_id = room_id!("!a98sd12bjh:example.org");
let (client, server) = logged_in_client_with_server().await;
let event_builder = EventBuilder::new();
let sync_settings = SyncSettings::new().timeout(Duration::from_millis(3000));
let mut sync_builder = SyncResponseBuilder::new();
@@ -382,13 +376,14 @@ async fn test_send_reply_to_self() {
timeline.subscribe_filter_map(|item| item.as_event().cloned()).await;
let event_id_from_self = event_id!("$event_from_self");
sync_builder.add_joined_room(JoinedRoomBuilder::new(room_id).add_timeline_event(
event_builder.make_sync_message_event_with_id(
client.user_id().expect("Client must have a user ID"),
event_id_from_self,
RoomMessageEventContent::text_plain("Hello from self"),
let f = EventFactory::new();
sync_builder.add_joined_room(
JoinedRoomBuilder::new(room_id).add_timeline_event(
f.text_msg("Hello from self")
.sender(client.user_id().expect("Client must have a user ID"))
.event_id(event_id_from_self),
),
));
);
mock_sync(&server, sync_builder.build_json_sync_response(), None).await;
let _response = client.sync_once(sync_settings.clone()).await.unwrap();
@@ -462,7 +457,6 @@ async fn test_send_reply_to_self() {
async fn test_send_reply_to_threaded() {
let room_id = room_id!("!a98sd12bjh:example.org");
let (client, server) = logged_in_client_with_server().await;
let event_builder = EventBuilder::new();
let sync_settings = SyncSettings::new().timeout(Duration::from_millis(3000));
let mut sync_builder = SyncResponseBuilder::new();
@@ -479,27 +473,18 @@ async fn test_send_reply_to_threaded() {
let (_, mut timeline_stream) =
timeline.subscribe_filter_map(|item| item.as_event().cloned()).await;
let thread_root = OriginalRoomMessageEvent {
content: RoomMessageEventContent::text_plain("Thread root"),
event_id: owned_event_id!("$thread_root"),
origin_server_ts: MilliSecondsSinceUnixEpoch(uint!(10_000)),
room_id: owned_room_id!("!testroomid:example.org"),
sender: owned_user_id!("@user:example.org"),
unsigned: MessageLikeUnsigned::default(),
};
let thread_root = owned_event_id!("$thread_root");
let event_id_1 = event_id!("$event1");
sync_builder.add_joined_room(JoinedRoomBuilder::new(room_id).add_timeline_event(
event_builder.make_sync_message_event_with_id(
&BOB,
event_id_1,
RoomMessageEventContent::text_plain("Hello, World!").make_for_thread(
&thread_root,
ReplyWithinThread::No,
AddMentions::No,
),
let f = EventFactory::new();
sync_builder.add_joined_room(
JoinedRoomBuilder::new(room_id).add_timeline_event(
f.text_msg("Hello, World!")
.sender(&BOB)
.event_id(event_id_1)
.in_thread(&thread_root, &thread_root),
),
));
);
mock_sync(&server, sync_builder.build_json_sync_response(), None).await;
let _response = client.sync_once(sync_settings.clone()).await.unwrap();
@@ -570,7 +555,6 @@ async fn test_send_reply_to_threaded() {
async fn test_send_reply_with_event_id() {
let room_id = room_id!("!a98sd12bjh:example.org");
let (client, server) = logged_in_client_with_server().await;
let event_builder = EventBuilder::new();
let sync_settings = SyncSettings::new().timeout(Duration::from_millis(3000));
let mut sync_builder = SyncResponseBuilder::new();
@@ -588,11 +572,9 @@ async fn test_send_reply_with_event_id() {
timeline.subscribe_filter_map(|item| item.as_event().cloned()).await;
let event_id_from_bob = event_id!("$event_from_bob");
let raw_event_from_bob = event_builder.make_sync_message_event_with_id(
&BOB,
event_id_from_bob,
RoomMessageEventContent::text_plain("Hello from Bob"),
);
let f = EventFactory::new();
let raw_event_from_bob =
f.text_msg("Hello from Bob").sender(&BOB).event_id(event_id_from_bob).into_raw_sync();
sync_builder.add_joined_room(
JoinedRoomBuilder::new(room_id).add_timeline_event(raw_event_from_bob.clone()),
);
@@ -689,7 +671,6 @@ async fn test_send_reply_with_event_id_that_is_redacted() {
// the reply.
let room_id = room_id!("!a98sd12bjh:example.org");
let (client, server) = logged_in_client_with_server().await;
let event_builder = EventBuilder::new();
let sync_settings = SyncSettings::new().timeout(Duration::from_millis(3000));
let mut sync_builder = SyncResponseBuilder::new();
@@ -707,11 +688,11 @@ async fn test_send_reply_with_event_id_that_is_redacted() {
timeline.subscribe_filter_map(|item| item.as_event().cloned()).await;
let redacted_event_id_from_bob = event_id!("$event_from_bob");
let raw_redacted_event_from_bob = event_builder.make_sync_redacted_message_event_with_id(
&BOB,
redacted_event_id_from_bob,
RedactedReactionEventContent::new(),
);
let f = EventFactory::new();
let raw_redacted_event_from_bob = f
.redacted(&BOB, RedactedReactionEventContent::new())
.event_id(redacted_event_id_from_bob)
.into_raw_sync();
sync_builder.add_joined_room(
JoinedRoomBuilder::new(room_id).add_timeline_event(raw_redacted_event_from_bob.clone()),
);
@@ -21,15 +21,12 @@ use futures_util::{pin_mut, StreamExt};
use matrix_sdk::{config::SyncSettings, test_utils::logged_in_client_with_server};
use matrix_sdk_test::{
async_test, event_factory::EventFactory, mocks::mock_encryption_state, sync_timeline_event,
EventBuilder, GlobalAccountDataTestEvent, JoinedRoomBuilder, SyncResponseBuilder, ALICE, BOB,
GlobalAccountDataTestEvent, JoinedRoomBuilder, SyncResponseBuilder, ALICE, BOB,
};
use matrix_sdk_ui::timeline::{RoomExt, TimelineDetails, TimelineItemContent};
use ruma::{
event_id,
events::room::{
member::{MembershipState, RoomMemberEventContent},
message::{MessageType, RoomMessageEventContent},
},
events::room::{member::MembershipState, message::MessageType},
room_id, user_id,
};
use serde_json::json;
@@ -43,7 +40,7 @@ async fn test_batched() {
let (client, server) = logged_in_client_with_server().await;
let sync_settings = SyncSettings::new().timeout(Duration::from_millis(3000));
let event_builder = EventBuilder::new();
let f = EventFactory::new();
let mut sync_builder = SyncResponseBuilder::new();
sync_builder.add_joined_room(JoinedRoomBuilder::new(room_id));
@@ -66,20 +63,11 @@ async fn test_batched() {
sync_builder.add_joined_room(
JoinedRoomBuilder::new(room_id)
.add_timeline_event(event_builder.make_sync_message_event(
&ALICE,
RoomMessageEventContent::text_plain("text message event"),
))
.add_timeline_event(event_builder.make_sync_state_event(
&BOB,
BOB.as_str(),
RoomMemberEventContent::new(MembershipState::Join),
Some(RoomMemberEventContent::new(MembershipState::Invite)),
))
.add_timeline_event(event_builder.make_sync_message_event(
&BOB,
RoomMessageEventContent::notice_plain("notice message event"),
)),
.add_timeline_event(f.text_msg("text message event").sender(&ALICE))
.add_timeline_event(
f.member(&BOB).membership(MembershipState::Join).previous(MembershipState::Invite),
)
.add_timeline_event(f.notice("notice message event").sender(&BOB)),
);
mock_sync(&server, sync_builder.build_json_sync_response(), None).await;
@@ -940,7 +940,7 @@ mod tests {
assert_relations(
room_id,
f.text_msg("Original event").event_id(original_id).into(),
f.reaction(original_id, ":D".to_owned()).event_id(related_id).into(),
f.reaction(original_id, ":D").event_id(related_id).into(),
f,
)
.await;
@@ -958,7 +958,7 @@ mod tests {
f.poll_start("Poll start event", "A poll question", vec!["An answer"])
.event_id(original_id)
.into(),
f.poll_response("1", original_id).event_id(related_id).into(),
f.poll_response(vec!["1"], original_id).event_id(related_id).into(),
f,
)
.await;
+20 -47
View File
@@ -25,21 +25,11 @@ use matrix_sdk::{
Client,
};
use matrix_sdk_common::{executor::spawn, timeout::timeout};
use matrix_sdk_test::{
async_test, event_factory::EventFactory, EventBuilder, JoinedRoomBuilder, ALICE, BOB,
};
use matrix_sdk_test::{async_test, event_factory::EventFactory, JoinedRoomBuilder, ALICE, BOB};
use once_cell::sync::Lazy;
use ruma::{
event_id,
events::{
room::{
member::{MembershipState, RoomMemberEventContent},
message::RoomMessageEventContent,
name::RoomNameEventContent,
topic::RoomTopicEventContent,
},
MessageLikeEventType, StateEventType,
},
events::{room::member::MembershipState, MessageLikeEventType, StateEventType},
owned_room_id,
serde::JsonObject,
user_id, OwnedRoomId,
@@ -308,7 +298,7 @@ async fn test_read_messages_with_msgtype_capabilities() {
let chunk2 = vec![
f.notice("custom content").event_id(event_id!("$msda7m0df9E9op3")).into_raw_timeline(),
f.text_msg("hello").event_id(event_id!("$msda7m0df9E9op5")).into_raw_timeline(),
f.reaction(event_id!("$event_id"), "annotation".to_owned()).into_raw_timeline(),
f.reaction(event_id!("$event_id"), "annotation").into_raw_timeline(),
];
mock_server
.mock_room_messages()
@@ -398,51 +388,34 @@ async fn test_receive_live_events() {
// No messages from the driver yet
assert_matches!(recv_message(&driver_handle).now_or_never(), None);
let f = EventFactory::new();
mock_server
.mock_sync()
.ok_and_run(&client, |sync_builder| {
let event_builder = EventBuilder::new();
sync_builder.add_joined_room(
JoinedRoomBuilder::new(&ROOM_ID)
// text message from alice - matches filter #2
.add_timeline_event(event_builder.make_sync_message_event(
&ALICE,
RoomMessageEventContent::text_plain("simple text message"),
))
.add_timeline_event(f.text_msg("simple text message").sender(&ALICE))
// emote from alice - doesn't match
.add_timeline_event(event_builder.make_sync_message_event(
&ALICE,
RoomMessageEventContent::emote_plain("emote message"),
))
.add_timeline_event(f.emote("emote message").sender(&ALICE))
// pointless member event - matches filter #4
.add_timeline_event(event_builder.make_sync_state_event(
user_id!("@example:localhost"),
"@example:localhost",
RoomMemberEventContent::new(MembershipState::Join),
Some(RoomMemberEventContent::new(MembershipState::Join)),
))
.add_timeline_event(
f.member(user_id!("@example:localhost"))
.membership(MembershipState::Join)
.previous(MembershipState::Join),
)
// kick alice - doesn't match because the `#@example:localhost` bit
// is about the state_key, not the sender
.add_timeline_event(event_builder.make_sync_state_event(
user_id!("@example:localhost"),
ALICE.as_str(),
RoomMemberEventContent::new(MembershipState::Ban),
Some(RoomMemberEventContent::new(MembershipState::Join)),
))
// set room tpoic - doesn't match
.add_timeline_event(event_builder.make_sync_state_event(
&BOB,
"",
RoomTopicEventContent::new("new room topic".to_owned()),
None,
))
.add_timeline_event(
f.member(user_id!("@example:localhost"))
.banned(&ALICE)
.previous(MembershipState::Join),
)
// set room topic - doesn't match
.add_timeline_event(f.room_topic("new room topic").sender(&BOB))
// set room name - matches filter #3
.add_timeline_event(event_builder.make_sync_state_event(
&BOB,
"",
RoomNameEventContent::new("New Room Name".to_owned()),
None,
)),
.add_timeline_event(f.room_name("New Room Name").sender(&BOB)),
);
})
.await;
@@ -1,210 +0,0 @@
// Copyright 2023 The Matrix.org Foundation C.I.C.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! Unit tests (based on private methods) for the timeline API.
use std::{
collections::BTreeMap,
sync::atomic::{AtomicU64, Ordering::SeqCst},
};
use ruma::{
events::{
receipt::{Receipt, ReceiptEventContent, ReceiptThread, ReceiptType},
AnySyncTimelineEvent, AnyTimelineEvent, MessageLikeEventContent,
RedactedMessageLikeEventContent, RedactedStateEventContent, StateEventContent,
},
serde::Raw,
server_name, EventId, MilliSecondsSinceUnixEpoch, OwnedEventId, OwnedUserId, RoomId, UserId,
};
use serde_json::{json, Value as JsonValue};
#[derive(Default)]
pub struct EventBuilder {
next_ts: AtomicU64,
}
impl EventBuilder {
pub fn new() -> EventBuilder {
Self { next_ts: AtomicU64::new(0) }
}
pub fn next_server_ts(&self) -> MilliSecondsSinceUnixEpoch {
MilliSecondsSinceUnixEpoch(
self.next_ts
.fetch_add(1, SeqCst)
.try_into()
.expect("server timestamp should fit in js_int::UInt"),
)
}
/// Set the next server timestamp.
///
/// Timestamps will continue to increase by 1 (millisecond) from that value.
pub fn set_next_ts(&self, value: u64) {
self.next_ts.store(value, SeqCst);
}
pub fn make_state_event<C: StateEventContent>(
&self,
sender: &UserId,
room_id: &RoomId,
state_key: &str,
content: C,
prev_content: Option<C>,
) -> Raw<AnyTimelineEvent> {
let unsigned = if let Some(prev_content) = prev_content {
json!({ "prev_content": prev_content })
} else {
json!({})
};
timeline_event!({
"type": content.event_type(),
"state_key": state_key,
"content": content,
"event_id": EventId::new(server_name!("dummy.server")),
"sender": sender,
"room_id": room_id,
"origin_server_ts": self.next_server_ts(),
"unsigned": unsigned,
})
}
pub fn make_sync_message_event<C: MessageLikeEventContent>(
&self,
sender: &UserId,
content: C,
) -> Raw<AnySyncTimelineEvent> {
let event_id = EventId::new(server_name!("dummy.server"));
self.make_sync_message_event_with_id(sender, &event_id, content)
}
pub fn make_sync_message_event_with_id<C: MessageLikeEventContent>(
&self,
sender: &UserId,
event_id: &EventId,
content: C,
) -> Raw<AnySyncTimelineEvent> {
sync_timeline_event!({
"type": content.event_type(),
"content": content,
"event_id": event_id,
"sender": sender,
"origin_server_ts": self.next_server_ts(),
})
}
pub fn make_sync_redacted_message_event<C: RedactedMessageLikeEventContent>(
&self,
sender: &UserId,
content: C,
) -> Raw<AnySyncTimelineEvent> {
sync_timeline_event!({
"type": content.event_type(),
"content": content,
"event_id": EventId::new(server_name!("dummy.server")),
"sender": sender,
"origin_server_ts": self.next_server_ts(),
"unsigned": self.make_redacted_unsigned(sender),
})
}
pub fn make_sync_redacted_message_event_with_id<C: RedactedMessageLikeEventContent>(
&self,
sender: &UserId,
event_id: &EventId,
content: C,
) -> Raw<AnySyncTimelineEvent> {
sync_timeline_event!({
"type": content.event_type(),
"content": content,
"event_id": event_id,
"sender": sender,
"origin_server_ts": self.next_server_ts(),
"unsigned": self.make_redacted_unsigned(sender),
})
}
pub fn make_sync_state_event<C: StateEventContent>(
&self,
sender: &UserId,
state_key: &str,
content: C,
prev_content: Option<C>,
) -> Raw<AnySyncTimelineEvent> {
let unsigned = if let Some(prev_content) = prev_content {
json!({ "prev_content": prev_content })
} else {
json!({})
};
sync_timeline_event!({
"type": content.event_type(),
"state_key": state_key,
"content": content,
"event_id": EventId::new(server_name!("dummy.server")),
"sender": sender,
"origin_server_ts": self.next_server_ts(),
"unsigned": unsigned,
})
}
pub fn make_sync_redacted_state_event<C: RedactedStateEventContent>(
&self,
sender: &UserId,
state_key: &str,
content: C,
) -> Raw<AnySyncTimelineEvent> {
sync_timeline_event!({
"type": content.event_type(),
"state_key": state_key,
"content": content,
"event_id": EventId::new(server_name!("dummy.server")),
"sender": sender,
"origin_server_ts": self.next_server_ts(),
"unsigned": self.make_redacted_unsigned(sender),
})
}
pub fn make_receipt_event_content(
&self,
receipts: impl IntoIterator<Item = (OwnedEventId, ReceiptType, OwnedUserId, ReceiptThread)>,
) -> ReceiptEventContent {
let mut ev_content = ReceiptEventContent(BTreeMap::new());
for (event_id, receipt_type, user_id, thread) in receipts {
let event_map = ev_content.entry(event_id).or_default();
let receipt_map = event_map.entry(receipt_type).or_default();
let mut receipt = Receipt::new(self.next_server_ts());
receipt.thread = thread;
receipt_map.insert(user_id, receipt);
}
ev_content
}
fn make_redacted_unsigned(&self, sender: &UserId) -> JsonValue {
json!({
"redacted_because": {
"content": {},
"event_id": EventId::new(server_name!("dummy.server")),
"sender": sender,
"origin_server_ts": self.next_server_ts(),
"type": "m.room.redaction",
},
})
}
}
+267 -29
View File
@@ -26,16 +26,16 @@ use matrix_sdk_common::deserialized_responses::{
use ruma::{
events::{
member_hints::MemberHintsEventContent,
message::TextContentBlock,
poll::{
end::PollEndEventContent,
response::{PollResponseEventContent, SelectionsContentBlock},
unstable_end::UnstablePollEndEventContent,
unstable_response::UnstablePollResponseEventContent,
unstable_start::{
NewUnstablePollStartEventContent, ReplacementUnstablePollStartEventContent,
UnstablePollAnswer, UnstablePollStartContentBlock, UnstablePollStartEventContent,
},
},
reaction::ReactionEventContent,
receipt::{Receipt, ReceiptEventContent, ReceiptThread, ReceiptType},
relation::{Annotation, InReplyTo, Replacement, Thread},
room::{
encrypted::{EncryptedEventScheme, RoomEncryptedEventContent},
@@ -44,13 +44,16 @@ use ruma::{
FormattedBody, ImageMessageEventContent, MessageType, Relation,
RoomMessageEventContent, RoomMessageEventContentWithoutRelation,
},
name::RoomNameEventContent,
redaction::RoomRedactionEventContent,
topic::RoomTopicEventContent,
},
AnySyncTimelineEvent, AnyTimelineEvent, BundledMessageLikeRelations, EventContent,
RedactedMessageLikeEventContent, RedactedStateEventContent,
},
serde::Raw,
server_name, EventId, MilliSecondsSinceUnixEpoch, OwnedEventId, OwnedMxcUri, OwnedRoomId,
OwnedTransactionId, OwnedUserId, RoomId, TransactionId, UInt, UserId,
server_name, EventId, MilliSecondsSinceUnixEpoch, MxcUri, OwnedEventId, OwnedMxcUri,
OwnedRoomId, OwnedTransactionId, OwnedUserId, RoomId, TransactionId, UInt, UserId,
};
use serde::Serialize;
use serde_json::json;
@@ -71,12 +74,43 @@ impl TimestampArg for u64 {
}
}
#[derive(Debug, Default, Serialize)]
struct Unsigned {
/// A thin copy of [`ruma::events::UnsignedRoomRedactionEvent`].
#[derive(Debug, Serialize)]
struct RedactedBecause {
/// Data specific to the event type.
content: RoomRedactionEventContent,
/// The globally unique event identifier for the user who sent the event.
event_id: OwnedEventId,
/// The fully-qualified ID of the user who sent this event.
sender: OwnedUserId,
/// Timestamp in milliseconds on originating homeserver when this event was
/// sent.
origin_server_ts: MilliSecondsSinceUnixEpoch,
}
#[derive(Debug, Serialize)]
struct Unsigned<C: EventContent> {
#[serde(skip_serializing_if = "Option::is_none")]
prev_content: Option<C>,
#[serde(skip_serializing_if = "Option::is_none")]
transaction_id: Option<OwnedTransactionId>,
#[serde(rename = "m.relations", skip_serializing_if = "Option::is_none")]
relations: Option<BundledMessageLikeRelations<Raw<AnySyncTimelineEvent>>>,
#[serde(skip_serializing_if = "Option::is_none")]
redacted_because: Option<RedactedBecause>,
}
// rustc can't derive Default because C isn't marked as `Default` 🤔 oh well.
impl<C: EventContent> Default for Unsigned<C> {
fn default() -> Self {
Self { prev_content: None, transaction_id: None, relations: None, redacted_because: None }
}
}
#[derive(Debug)]
@@ -87,7 +121,7 @@ pub struct EventBuilder<C: EventContent> {
redacts: Option<OwnedEventId>,
content: C,
server_ts: MilliSecondsSinceUnixEpoch,
unsigned: Option<Unsigned>,
unsigned: Option<Unsigned<C>>,
state_key: Option<String>,
}
@@ -142,7 +176,7 @@ where
}
#[inline(always)]
fn construct_json<T>(self, requires_room: bool) -> Raw<T> {
fn construct_json(self, requires_room: bool) -> serde_json::Value {
let event_id = self
.event_id
.or_else(|| {
@@ -150,11 +184,18 @@ where
})
.unwrap_or_else(|| EventId::new(server_name!("dummy.org")));
// Use the `sender` preferably, or resort to the `redacted_because` sender if
// none has been set.
let sender = self
.sender
.or_else(|| Some(self.unsigned.as_ref()?.redacted_because.as_ref()?.sender.clone()))
.expect("we should have a sender user id at this point");
let mut json = json!({
"type": self.content.event_type(),
"content": self.content,
"event_id": event_id,
"sender": self.sender.expect("we should have a sender user id at this point"),
"sender": sender,
"origin_server_ts": self.server_ts,
});
@@ -177,7 +218,7 @@ where
map.insert("state_key".to_owned(), json!(state_key));
}
Raw::new(map).unwrap().cast()
json
}
/// Build an event from the [`EventBuilder`] and convert it into a
@@ -186,11 +227,11 @@ where
/// The generic argument `T` allows you to automatically cast the [`Raw`]
/// event into any desired type.
pub fn into_raw<T>(self) -> Raw<T> {
self.construct_json(true)
Raw::new(&self.construct_json(true)).unwrap().cast()
}
pub fn into_raw_timeline(self) -> Raw<AnyTimelineEvent> {
self.construct_json(true)
Raw::new(&self.construct_json(true)).unwrap().cast()
}
pub fn into_timeline(self) -> TimelineEvent {
@@ -198,7 +239,7 @@ where
}
pub fn into_raw_sync(self) -> Raw<AnySyncTimelineEvent> {
self.construct_json(false)
Raw::new(&self.construct_json(false)).unwrap().cast()
}
pub fn into_sync(self) -> SyncTimelineEvent {
@@ -360,6 +401,11 @@ impl EventFactory {
self.event(RoomMessageEventContent::text_plain(content.into()))
}
/// Create a new plain emote `m.room.message`.
pub fn emote(&self, content: impl Into<String>) -> EventBuilder<RoomMessageEventContent> {
self.event(RoomMessageEventContent::emote_plain(content.into()))
}
/// Create a new `m.room.member` event for the given member.
///
/// The given member will be used as the `sender` as well as the `state_key`
@@ -403,6 +449,22 @@ impl EventFactory {
event
}
/// Create a state event for the topic.
pub fn room_topic(&self, topic: impl Into<String>) -> EventBuilder<RoomTopicEventContent> {
let mut event = self.event(RoomTopicEventContent::new(topic.into()));
// The state key is empty for a room topic state event.
event.state_key = Some("".to_owned());
event
}
/// Create a state event for the room name.
pub fn room_name(&self, name: impl Into<String>) -> EventBuilder<RoomNameEventContent> {
let mut event = self.event(RoomNameEventContent::new(name.into()));
// The state key is empty for a room name state event.
event.state_key = Some("".to_owned());
event
}
/// Create a new `m.member_hints` event with the given service members.
///
/// ```
@@ -449,18 +511,68 @@ impl EventFactory {
pub fn reaction(
&self,
event_id: &EventId,
annotation: String,
annotation: impl Into<String>,
) -> EventBuilder<ReactionEventContent> {
self.event(ReactionEventContent::new(Annotation::new(event_id.to_owned(), annotation)))
self.event(ReactionEventContent::new(Annotation::new(
event_id.to_owned(),
annotation.into(),
)))
}
/// Create a redaction for the given event id.
/// Create a live redaction for the given event id.
///
/// Note: this is not a redacted event, but a redaction event, that will
/// cause another event to be redacted.
pub fn redaction(&self, event_id: &EventId) -> EventBuilder<RoomRedactionEventContent> {
let mut builder = self.event(RoomRedactionEventContent::new_v11(event_id.to_owned()));
builder.redacts = Some(event_id.to_owned());
builder
}
/// Create a redacted event, with extra information in the unsigned section
/// about the redaction itself.
pub fn redacted<T: RedactedMessageLikeEventContent>(
&self,
redacter: &UserId,
content: T,
) -> EventBuilder<T> {
let mut builder = self.event(content);
let redacted_because = RedactedBecause {
content: RoomRedactionEventContent::default(),
event_id: EventId::new(server_name!("dummy.server")),
sender: redacter.to_owned(),
origin_server_ts: self.next_server_ts(),
};
builder.unsigned.get_or_insert_with(Default::default).redacted_because =
Some(redacted_because);
builder
}
/// Create a redacted state event, with extra information in the unsigned
/// section about the redaction itself.
pub fn redacted_state<T: RedactedStateEventContent>(
&self,
redacter: &UserId,
state_key: impl Into<String>,
content: T,
) -> EventBuilder<T> {
let mut builder = self.event(content);
let redacted_because = RedactedBecause {
content: RoomRedactionEventContent::default(),
event_id: EventId::new(server_name!("dummy.server")),
sender: redacter.to_owned(),
origin_server_ts: self.next_server_ts(),
};
builder.unsigned.get_or_insert_with(Default::default).redacted_because =
Some(redacted_because);
builder.state_key = Some(state_key.into());
builder
}
/// Create a poll start event given a text, the question and the possible
/// answers.
pub fn poll_start(
@@ -510,13 +622,13 @@ impl EventFactory {
/// start event id.
pub fn poll_response(
&self,
answer_id: impl Into<String>,
answers: Vec<impl Into<String>>,
poll_start_id: &EventId,
) -> EventBuilder<PollResponseEventContent> {
let selection_content: SelectionsContentBlock = vec![answer_id.into()].into();
let poll_response_content =
PollResponseEventContent::new(selection_content, poll_start_id.to_owned());
self.event(poll_response_content)
) -> EventBuilder<UnstablePollResponseEventContent> {
self.event(UnstablePollResponseEventContent::new(
answers.into_iter().map(Into::into).collect(),
poll_start_id.to_owned(),
))
}
/// Create a poll response with the given text and the associated poll start
@@ -525,12 +637,8 @@ impl EventFactory {
&self,
content: impl Into<String>,
poll_start_id: &EventId,
) -> EventBuilder<PollEndEventContent> {
let poll_end_content = PollEndEventContent::new(
TextContentBlock::plain(content.into()),
poll_start_id.to_owned(),
);
self.event(poll_end_content)
) -> EventBuilder<UnstablePollEndEventContent> {
self.event(UnstablePollEndEventContent::new(content.into(), poll_start_id.to_owned()))
}
/// Creates a plain (unencrypted) image event content referencing the given
@@ -544,6 +652,11 @@ impl EventFactory {
self.event(RoomMessageEventContent::new(MessageType::Image(image_event_content)))
}
/// Create a read receipt event.
pub fn read_receipts(&self) -> ReadReceiptBuilder<'_> {
ReadReceiptBuilder { factory: self, content: ReceiptEventContent(Default::default()) }
}
/// Set the next server timestamp.
///
/// Timestamps will continue to increase by 1 (millisecond) from that value.
@@ -562,9 +675,134 @@ impl EventBuilder<RoomMemberEventContent> {
self
}
/// Set that the sender of this event invited the user passed as a parameter
/// here.
pub fn invited(mut self, invited_user: &UserId) -> Self {
assert_ne!(
self.sender.as_deref().unwrap(),
invited_user,
"invited user and sender can't be the same person"
);
self.content.membership = MembershipState::Invite;
self.state_key = Some(invited_user.to_string());
self
}
/// Set that the sender of this event kicked the user passed as a parameter
/// here.
pub fn kicked(mut self, kicked_user: &UserId) -> Self {
assert_ne!(
self.sender.as_deref().unwrap(),
kicked_user,
"kicked user and sender can't be the same person, otherwise it's just a Leave"
);
self.content.membership = MembershipState::Leave;
self.state_key = Some(kicked_user.to_string());
self
}
/// Set that the sender of this event banned the user passed as a parameter
/// here.
pub fn banned(mut self, banned_user: &UserId) -> Self {
assert_ne!(
self.sender.as_deref().unwrap(),
banned_user,
"a user can't ban itself" // hopefully
);
self.content.membership = MembershipState::Ban;
self.state_key = Some(banned_user.to_string());
self
}
/// Set the display name of the `m.room.member` event.
pub fn display_name(mut self, display_name: impl Into<String>) -> Self {
self.content.displayname = Some(display_name.into());
self
}
/// Set the avatar URL of the `m.room.member` event.
pub fn avatar_url(mut self, url: &MxcUri) -> Self {
self.content.avatar_url = Some(url.to_owned());
self
}
/// Set the reason field of the `m.room.member` event.
pub fn reason(mut self, reason: impl Into<String>) -> Self {
self.content.reason = Some(reason.into());
self
}
/// Set the previous membership state (in the unsigned section).
pub fn previous(mut self, previous: impl Into<PreviousMembership>) -> Self {
let previous = previous.into();
let mut prev_content = RoomMemberEventContent::new(previous.state);
if let Some(avatar_url) = previous.avatar_url {
prev_content.avatar_url = Some(avatar_url);
}
if let Some(display_name) = previous.display_name {
prev_content.displayname = Some(display_name);
}
self.unsigned.get_or_insert_with(Default::default).prev_content = Some(prev_content);
self
}
}
pub struct ReadReceiptBuilder<'a> {
factory: &'a EventFactory,
content: ReceiptEventContent,
}
impl ReadReceiptBuilder<'_> {
/// Add a single read receipt to the event.
pub fn add(
mut self,
event_id: &EventId,
user_id: &UserId,
tyype: ReceiptType,
thread: ReceiptThread,
) -> Self {
let by_event = self.content.0.entry(event_id.to_owned()).or_default();
let by_type = by_event.entry(tyype).or_default();
let mut receipt = Receipt::new(self.factory.next_server_ts());
receipt.thread = thread;
by_type.insert(user_id.to_owned(), receipt);
self
}
/// Finalize the builder into the receipt event content.
pub fn build(self) -> ReceiptEventContent {
self.content
}
}
pub struct PreviousMembership {
state: MembershipState,
avatar_url: Option<OwnedMxcUri>,
display_name: Option<String>,
}
impl PreviousMembership {
pub fn new(state: MembershipState) -> Self {
Self { state, avatar_url: None, display_name: None }
}
pub fn avatar_url(mut self, url: &MxcUri) -> Self {
self.avatar_url = Some(url.to_owned());
self
}
pub fn display_name(mut self, name: impl Into<String>) -> Self {
self.display_name = Some(name.into());
self
}
}
impl From<MembershipState> for PreviousMembership {
fn from(state: MembershipState) -> Self {
Self::new(state)
}
}
+4 -9
View File
@@ -111,8 +111,6 @@ pub mod __macro_support {
pub use tracing_subscriber;
}
mod event_builder;
#[cfg(not(target_arch = "wasm32"))]
pub mod mocks;
@@ -121,13 +119,10 @@ pub mod notification_settings;
mod sync_builder;
pub mod test_json;
pub use self::{
event_builder::EventBuilder,
sync_builder::{
bulk_room_members, EphemeralTestEvent, GlobalAccountDataTestEvent, InvitedRoomBuilder,
JoinedRoomBuilder, KnockedRoomBuilder, LeftRoomBuilder, PresenceTestEvent,
RoomAccountDataTestEvent, StateTestEvent, StrippedStateTestEvent, SyncResponseBuilder,
},
pub use self::sync_builder::{
bulk_room_members, EphemeralTestEvent, GlobalAccountDataTestEvent, InvitedRoomBuilder,
JoinedRoomBuilder, KnockedRoomBuilder, LeftRoomBuilder, PresenceTestEvent,
RoomAccountDataTestEvent, StateTestEvent, StrippedStateTestEvent, SyncResponseBuilder,
};
pub static ALICE: Lazy<&UserId> = Lazy::new(|| user_id!("@alice:server.name"));