From 0c74abbc506113cbbf546c9fec0da69b33fa9045 Mon Sep 17 00:00:00 2001 From: Benjamin Bouvier Date: Tue, 21 Jan 2025 10:23:03 +0100 Subject: [PATCH] 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. --- benchmarks/benches/room_bench.rs | 38 +-- crates/matrix-sdk-base/src/read_receipts.rs | 178 +++++------ .../matrix-sdk-ui/src/timeline/tests/basic.rs | 91 +++--- .../matrix-sdk-ui/src/timeline/tests/edit.rs | 4 +- .../src/timeline/tests/event_filter.rs | 88 +----- .../matrix-sdk-ui/src/timeline/tests/mod.rs | 73 +---- .../matrix-sdk-ui/src/timeline/tests/polls.rs | 41 ++- .../src/timeline/tests/reactions.rs | 8 +- .../src/timeline/tests/redaction.rs | 27 +- .../tests/integration/timeline/focus_event.rs | 2 +- .../tests/integration/timeline/pagination.rs | 35 +-- .../tests/integration/timeline/profiles.rs | 62 +--- .../tests/integration/timeline/queue.rs | 40 ++- .../tests/integration/timeline/reactions.rs | 2 +- .../tests/integration/timeline/replies.rs | 87 ++--- .../tests/integration/timeline/subscribe.rs | 28 +- crates/matrix-sdk/src/event_cache/room/mod.rs | 4 +- crates/matrix-sdk/tests/integration/widget.rs | 67 ++-- testing/matrix-sdk-test/src/event_builder.rs | 210 ------------- testing/matrix-sdk-test/src/event_factory.rs | 296 ++++++++++++++++-- testing/matrix-sdk-test/src/lib.rs | 13 +- 21 files changed, 567 insertions(+), 827 deletions(-) delete mode 100644 testing/matrix-sdk-test/src/event_builder.rs diff --git a/benchmarks/benches/room_bench.rs b/benchmarks/benches/room_bench.rs index feebf4019a5..e8ac47ab006 100644 --- a/benchmarks/benches/room_bench.rs +++ b/benchmarks/benches/room_bench.rs @@ -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> = 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 = - 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 = 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); } diff --git a/crates/matrix-sdk-base/src/read_receipts.rs b/crates/matrix-sdk-base/src/read_receipts.rs index 5b640658cb6..c95d47a9d1b 100644 --- a/crates/matrix-sdk-base/src/read_receipts.rs +++ b/crates/matrix-sdk-base/src/read_receipts.rs @@ -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 pending = selector.handle_new_receipt(&myself, &receipt_event); + 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); 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 pending = selector.handle_new_receipt(&myself, &receipt_event); + 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); 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(), diff --git a/crates/matrix-sdk-ui/src/timeline/tests/basic.rs b/crates/matrix-sdk-ui/src/timeline/tests/basic.rs index bb0adbeba00..a9350283240 100644 --- a/crates/matrix-sdk-ui/src/timeline/tests/basic.rs +++ b/crates/matrix-sdk-ui/src/timeline/tests/basic.rs @@ -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()); diff --git a/crates/matrix-sdk-ui/src/timeline/tests/edit.rs b/crates/matrix-sdk-ui/src/timeline/tests/edit.rs index a0efc1370c1..ed90d46c57a 100644 --- a/crates/matrix-sdk-ui/src/timeline/tests/edit.rs +++ b/crates/matrix-sdk-ui/src/timeline/tests/edit.rs @@ -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(); diff --git a/crates/matrix-sdk-ui/src/timeline/tests/event_filter.rs b/crates/matrix-sdk-ui/src/timeline/tests/event_filter.rs index eb9e3bbafaf..5cfd714f66c 100644 --- a/crates/matrix-sdk-ui/src/timeline/tests/event_filter.rs +++ b/crates/matrix-sdk-ui/src/timeline/tests/event_filter.rs @@ -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> = 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> = timeline.get_event_items().await; diff --git a/crates/matrix-sdk-ui/src/timeline/tests/mod.rs b/crates/matrix-sdk-ui/src/timeline/tests/mod.rs index d47e0b78dba..23bab244a79 100644 --- a/crates/matrix-sdk-ui/src/timeline/tests/mod.rs +++ b/crates/matrix-sdk-ui/src/timeline/tests/mod.rs @@ -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, - 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(&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(&self, sender: &UserId, content: C, prev_content: Option) - where - C: StaticStateEventContent, - { - 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( - &self, - sender: &UserId, - state_key: C::StateKey, - content: C, - prev_content: Option, - ) 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(&self, sender: &UserId, content: C) - where - C: RedactedStateEventContent, - { - 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( - &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) { let event = event.into(); self.controller @@ -272,7 +209,11 @@ impl TestTimeline { &self, receipts: impl IntoIterator, ) { - 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; } diff --git a/crates/matrix-sdk-ui/src/timeline/tests/polls.rs b/crates/matrix-sdk-ui/src/timeline/tests/polls.rs index 74d678a9955..e93e69ba601 100644 --- a/crates/matrix-sdk-ui/src/timeline/tests/polls.rs +++ b/crates/matrix-sdk-ui/src/timeline/tests/polls.rs @@ -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 { + 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?", diff --git a/crates/matrix-sdk-ui/src/timeline/tests/reactions.rs b/crates/matrix-sdk-ui/src/timeline/tests/reactions.rs index 46f89bc0606..cea3ffc1804 100644 --- a/crates/matrix-sdk-ui/src/timeline/tests/reactions.rs +++ b/crates/matrix-sdk-ui/src/timeline/tests/reactions.rs @@ -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. diff --git a/crates/matrix-sdk-ui/src/timeline/tests/redaction.rs b/crates/matrix-sdk-ui/src/timeline/tests/redaction.rs index 196e61ed058..b62b0d96a7b 100644 --- a/crates/matrix-sdk-ui/src/timeline/tests/redaction.rs +++ b/crates/matrix-sdk-ui/src/timeline/tests/redaction.rs @@ -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); diff --git a/crates/matrix-sdk-ui/tests/integration/timeline/focus_event.rs b/crates/matrix-sdk-ui/tests/integration/timeline/focus_event.rs index 29a68e0d939..ccb20874258 100644 --- a/crates/matrix-sdk-ui/tests/integration/timeline/focus_event.rs +++ b/crates/matrix-sdk-ui/tests/integration/timeline/focus_event.rs @@ -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. diff --git a/crates/matrix-sdk-ui/tests/integration/timeline/pagination.rs b/crates/matrix-sdk-ui/tests/integration/timeline/pagination.rs index a796fc740da..deea9e41927 100644 --- a/crates/matrix-sdk-ui/tests/integration/timeline/pagination.rs +++ b/crates/matrix-sdk-ui/tests/integration/timeline/pagination.rs @@ -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(), ); diff --git a/crates/matrix-sdk-ui/tests/integration/timeline/profiles.rs b/crates/matrix-sdk-ui/tests/integration/timeline/profiles.rs index 8eeeefa7b23..65dc716f477 100644 --- a/crates/matrix-sdk-ui/tests/integration/timeline/profiles.rs +++ b/crates/matrix-sdk-ui/tests/integration/timeline/profiles.rs @@ -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)), - ), - - event_builder.make_state_event( - &CAROL, - &DEFAULT_TEST_ROOM_ID, - BOB.as_str(), - RoomMemberEventContent::new(MembershipState::Leave), - Some(RoomMemberEventContent::new(MembershipState::Join)), - ), + f.member(&ALICE).membership(MembershipState::Join).previous(MembershipState::Invite).into_raw_timeline(), + + f.member(&CAROL).kicked(&BOB).previous(MembershipState::Join).into_raw_timeline(), ] }))) .mount(&server) diff --git a/crates/matrix-sdk-ui/tests/integration/timeline/queue.rs b/crates/matrix-sdk-ui/tests/integration/timeline/queue.rs index d9d27689180..526ce50d8fd 100644 --- a/crates/matrix-sdk-ui/tests/integration/timeline/queue.rs +++ b/crates/matrix-sdk-ui/tests/integration/timeline/queue.rs @@ -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; diff --git a/crates/matrix-sdk-ui/tests/integration/timeline/reactions.rs b/crates/matrix-sdk-ui/tests/integration/timeline/reactions.rs index 30cfef51000..ff2e5bf3386 100644 --- a/crates/matrix-sdk-ui/tests/integration/timeline/reactions.rs +++ b/crates/matrix-sdk-ui/tests/integration/timeline/reactions.rs @@ -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; diff --git a/crates/matrix-sdk-ui/tests/integration/timeline/replies.rs b/crates/matrix-sdk-ui/tests/integration/timeline/replies.rs index e4a953da94c..82450c1a88e 100644 --- a/crates/matrix-sdk-ui/tests/integration/timeline/replies.rs +++ b/crates/matrix-sdk-ui/tests/integration/timeline/replies.rs @@ -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()), ); diff --git a/crates/matrix-sdk-ui/tests/integration/timeline/subscribe.rs b/crates/matrix-sdk-ui/tests/integration/timeline/subscribe.rs index e9ce4540ece..63ae2131364 100644 --- a/crates/matrix-sdk-ui/tests/integration/timeline/subscribe.rs +++ b/crates/matrix-sdk-ui/tests/integration/timeline/subscribe.rs @@ -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; diff --git a/crates/matrix-sdk/src/event_cache/room/mod.rs b/crates/matrix-sdk/src/event_cache/room/mod.rs index 0d76dc63ec2..93a5f42802f 100644 --- a/crates/matrix-sdk/src/event_cache/room/mod.rs +++ b/crates/matrix-sdk/src/event_cache/room/mod.rs @@ -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; diff --git a/crates/matrix-sdk/tests/integration/widget.rs b/crates/matrix-sdk/tests/integration/widget.rs index eeefaa96fbf..2936f01edf2 100644 --- a/crates/matrix-sdk/tests/integration/widget.rs +++ b/crates/matrix-sdk/tests/integration/widget.rs @@ -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; diff --git a/testing/matrix-sdk-test/src/event_builder.rs b/testing/matrix-sdk-test/src/event_builder.rs deleted file mode 100644 index 13a3d8b2365..00000000000 --- a/testing/matrix-sdk-test/src/event_builder.rs +++ /dev/null @@ -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( - &self, - sender: &UserId, - room_id: &RoomId, - state_key: &str, - content: C, - prev_content: Option, - ) -> Raw { - 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( - &self, - sender: &UserId, - content: C, - ) -> Raw { - 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( - &self, - sender: &UserId, - event_id: &EventId, - content: C, - ) -> Raw { - 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( - &self, - sender: &UserId, - content: C, - ) -> Raw { - 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( - &self, - sender: &UserId, - event_id: &EventId, - content: C, - ) -> Raw { - 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( - &self, - sender: &UserId, - state_key: &str, - content: C, - prev_content: Option, - ) -> Raw { - 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( - &self, - sender: &UserId, - state_key: &str, - content: C, - ) -> Raw { - 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, - ) -> 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", - }, - }) - } -} diff --git a/testing/matrix-sdk-test/src/event_factory.rs b/testing/matrix-sdk-test/src/event_factory.rs index 82c25b427aa..e1aa2bfcecb 100644 --- a/testing/matrix-sdk-test/src/event_factory.rs +++ b/testing/matrix-sdk-test/src/event_factory.rs @@ -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 { + #[serde(skip_serializing_if = "Option::is_none")] + prev_content: Option, + #[serde(skip_serializing_if = "Option::is_none")] transaction_id: Option, + #[serde(rename = "m.relations", skip_serializing_if = "Option::is_none")] relations: Option>>, + + #[serde(skip_serializing_if = "Option::is_none")] + redacted_because: Option, +} + +// rustc can't derive Default because C isn't marked as `Default` 🤔 oh well. +impl Default for Unsigned { + fn default() -> Self { + Self { prev_content: None, transaction_id: None, relations: None, redacted_because: None } + } } #[derive(Debug)] @@ -87,7 +121,7 @@ pub struct EventBuilder { redacts: Option, content: C, server_ts: MilliSecondsSinceUnixEpoch, - unsigned: Option, + unsigned: Option>, state_key: Option, } @@ -142,7 +176,7 @@ where } #[inline(always)] - fn construct_json(self, requires_room: bool) -> Raw { + 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(self) -> Raw { - self.construct_json(true) + Raw::new(&self.construct_json(true)).unwrap().cast() } pub fn into_raw_timeline(self) -> Raw { - 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 { - 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) -> EventBuilder { + 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) -> EventBuilder { + 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) -> EventBuilder { + 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, ) -> EventBuilder { - 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 { 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( + &self, + redacter: &UserId, + content: T, + ) -> EventBuilder { + 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( + &self, + redacter: &UserId, + state_key: impl Into, + content: T, + ) -> EventBuilder { + 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, + answers: Vec>, poll_start_id: &EventId, - ) -> EventBuilder { - 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 { + 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, poll_start_id: &EventId, - ) -> EventBuilder { - let poll_end_content = PollEndEventContent::new( - TextContentBlock::plain(content.into()), - poll_start_id.to_owned(), - ); - self.event(poll_end_content) + ) -> EventBuilder { + 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 { 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) -> 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) -> 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) -> 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, + display_name: Option, +} + +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) -> Self { + self.display_name = Some(name.into()); + self + } +} + +impl From for PreviousMembership { + fn from(state: MembershipState) -> Self { + Self::new(state) + } } diff --git a/testing/matrix-sdk-test/src/lib.rs b/testing/matrix-sdk-test/src/lib.rs index 8b80e4ad798..87e52335c3f 100644 --- a/testing/matrix-sdk-test/src/lib.rs +++ b/testing/matrix-sdk-test/src/lib.rs @@ -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"));