Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

test(event cache): rely less on EventCache::add_initial_events() #4372

Merged
merged 8 commits into from
Dec 4, 2024
108 changes: 44 additions & 64 deletions crates/matrix-sdk-ui/tests/integration/timeline/edit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@ use assert_matches2::assert_let;
use eyeball_im::VectorDiff;
use futures_util::{FutureExt, StreamExt};
use matrix_sdk::{
config::SyncSettings, room::edit::EditedContent, test_utils::logged_in_client_with_server,
config::SyncSettings,
room::edit::EditedContent,
test_utils::{logged_in_client_with_server, mocks::MatrixMockServer},
Client,
};
use matrix_sdk_test::{
Expand All @@ -46,7 +48,7 @@ use ruma::{
MessageType, RoomMessageEventContent, RoomMessageEventContentWithoutRelation,
TextMessageEventContent,
},
AnyMessageLikeEventContent, AnyTimelineEvent,
AnyMessageLikeEventContent, AnyStateEvent, AnyTimelineEvent,
},
owned_event_id, room_id,
serde::Raw,
Expand All @@ -57,7 +59,7 @@ use stream_assert::assert_next_matches;
use tokio::{task::yield_now, time::sleep};
use wiremock::{
matchers::{header, method, path_regex},
Mock, MockServer, ResponseTemplate,
Mock, ResponseTemplate,
};

use crate::mock_sync;
Expand Down Expand Up @@ -668,20 +670,14 @@ async fn test_send_edit_poll() {

#[async_test]
async fn test_send_edit_when_timeline_is_clear() {
let room_id = room_id!("!a98sd12bjh:example.org");
let (client, server) = logged_in_client_with_server().await;
let sync_settings = SyncSettings::new().timeout(Duration::from_millis(3000));

let mut sync_builder = SyncResponseBuilder::new();
sync_builder.add_joined_room(JoinedRoomBuilder::new(room_id));
let server = MatrixMockServer::new().await;
let client = server.client_builder().build().await;

mock_sync(&server, sync_builder.build_json_sync_response(), None).await;
let _response = client.sync_once(sync_settings.clone()).await.unwrap();
server.reset().await;
let room_id = room_id!("!a98sd12bjh:example.org");
let room = server.sync_joined_room(&client, room_id).await;

mock_encryption_state(&server, false).await;
server.mock_room_state_encryption().plain().mount().await;

let room = client.get_room(room_id).unwrap();
let timeline = room.timeline().await.unwrap();
let (_, mut timeline_stream) =
timeline.subscribe_filter_map(|item| item.as_event().cloned()).await;
Expand All @@ -692,23 +688,27 @@ async fn test_send_edit_when_timeline_is_clear() {
.sender(client.user_id().unwrap())
.event_id(event_id!("$original_event"))
.into_raw_sync();
sync_builder.add_joined_room(
JoinedRoomBuilder::new(room_id).add_timeline_event(raw_original_event.clone()),
);

mock_sync(&server, sync_builder.build_json_sync_response(), None).await;
let _response = client.sync_once(sync_settings.clone()).await.unwrap();
server.reset().await;
server
.sync_room(
&client,
JoinedRoomBuilder::new(room_id).add_timeline_event(raw_original_event.clone()),
)
.await;

let hello_world_item =
assert_next_matches!(timeline_stream, VectorDiff::PushBack { value } => value);
let hello_world_message = hello_world_item.content().as_message().unwrap();
assert!(!hello_world_message.is_edited());
assert!(hello_world_item.is_editable());

// Clear the event cache (hence the timeline) to make sure the old item does not
// need to be available in it for the edit to work.
client.event_cache().add_initial_events(room_id, vec![], None).await.unwrap();
// Receive a limited (gappy) sync for this room, which will clear the timeline…
//
// TODO: …until the event cache storage is enabled by default, a time where
// we'll be able to get rid of this test entirely (or update its
// expectations).

server.sync_room(&client, JoinedRoomBuilder::new(room_id).set_timeline_limited()).await;
client.event_cache().empty_immutable_cache().await;

yield_now().await;
Expand All @@ -734,8 +734,7 @@ async fn test_send_edit_when_timeline_is_clear() {
// updates, so just wait for a bit before verifying that the endpoint was
// called.
sleep(Duration::from_millis(200)).await;

server.verify().await;
assert!(timeline_stream.next().now_or_never().is_none());
}

#[async_test]
Expand Down Expand Up @@ -851,69 +850,50 @@ async fn test_edit_local_echo_with_unsupported_content() {

struct PendingEditHelper {
client: Client,
server: MockServer,
server: MatrixMockServer,
timeline: Timeline,
sync_builder: SyncResponseBuilder,
sync_settings: SyncSettings,
room_id: OwnedRoomId,
}

impl PendingEditHelper {
async fn new() -> Self {
let room_id = room_id!("!a98sd12bjh:example.org");
let (client, server) = logged_in_client_with_server().await;
let sync_settings = SyncSettings::new().timeout(Duration::from_millis(3000));

let mut sync_builder = SyncResponseBuilder::new();
sync_builder.add_joined_room(JoinedRoomBuilder::new(room_id));
let server = MatrixMockServer::new().await;
let client = server.client_builder().build().await;

mock_sync(&server, sync_builder.build_json_sync_response(), None).await;
let _response = client.sync_once(sync_settings.clone()).await.unwrap();
server.reset().await;
client.event_cache().subscribe().unwrap();

mock_encryption_state(&server, false).await;
// Fill the initial prev-batch token to avoid waiting for it later.
server
.sync_room(
&client,
JoinedRoomBuilder::new(room_id)
.set_timeline_prev_batch("prev-batch-token".to_owned()),
)
.await;

{
// Fill the initial prev-batch token to avoid waiting for it later.
let ec = client.event_cache();
ec.subscribe().unwrap();
ec.add_initial_events(room_id, vec![], Some("prev-batch-token".to_owned()))
.await
.unwrap();
}
server.mock_room_state_encryption().plain().mount().await;

let room = client.get_room(room_id).unwrap();
let timeline = room.timeline().await.unwrap();

Self { client, server, timeline, sync_builder, sync_settings, room_id: room_id.to_owned() }
Self { client, server, timeline, room_id: room_id.to_owned() }
}

async fn handle_sync(&mut self, joined_room_builder: JoinedRoomBuilder) {
self.sync_builder.add_joined_room(joined_room_builder);

mock_sync(&self.server, self.sync_builder.build_json_sync_response(), None).await;
let _response = self.client.sync_once(self.sync_settings.clone()).await.unwrap();

self.server.reset().await;
self.server.sync_room(&self.client, joined_room_builder).await;
}

async fn handle_backpagination(&mut self, events: Vec<Raw<AnyTimelineEvent>>, batch_size: u16) {
Mock::given(method("GET"))
.and(path_regex(r"^/_matrix/client/r0/rooms/.*/messages$"))
.and(header("authorization", "Bearer 1234"))
.respond_with(ResponseTemplate::new(200).set_body_json(json!({
"start": "123",
"end": "yolo",
"chunk": events,
"state": []
})))
.expect(1)
.mount(&self.server)
self.server
.mock_room_messages()
.ok("123".to_owned(), Some("yolo".to_owned()), events, Vec::<Raw<AnyStateEvent>>::new())
.mock_once()
.mount()
.await;

self.timeline.live_paginate_backwards(batch_size).await.unwrap();

self.server.reset().await;
}
}

Expand Down
10 changes: 0 additions & 10 deletions crates/matrix-sdk-ui/tests/integration/timeline/sliding_sync.rs
Original file line number Diff line number Diff line change
Expand Up @@ -279,16 +279,6 @@ async fn timeline_test_helper(
anyhow::anyhow!("Room {room_id} not found in client. Can't provide a timeline for it")
})?;

// TODO: when the event cache handles its own cache, we can remove this.
client
.event_cache()
.add_initial_events(
room_id,
sliding_sync_room.timeline_queue().iter().cloned().collect(),
sliding_sync_room.prev_batch(),
)
.await?;

let timeline = Timeline::builder(&sdk_room).track_read_marker_and_receipts().build().await?;

Ok(timeline.subscribe().await)
Expand Down
5 changes: 5 additions & 0 deletions crates/matrix-sdk/src/event_cache/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,11 @@ impl EventCache {
events: Vec<SyncTimelineEvent>,
prev_batch: Option<String>,
) -> Result<()> {
// If the event cache's storage has been enabled, do nothing.
if self.inner.store.get().is_some() {
return Ok(());
}

let room_cache = self.inner.for_room(room_id).await?;

// We could have received events during a previous sync; remove them all, since
Expand Down
122 changes: 43 additions & 79 deletions crates/matrix-sdk/tests/integration/event_cache.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use matrix_sdk::{
paginator::PaginatorState, BackPaginationOutcome, EventCacheError, RoomEventCacheUpdate,
TimelineHasBeenResetWhilePaginating,
},
test_utils::{assert_event_matches_msg, logged_in_client_with_server},
test_utils::{assert_event_matches_msg, logged_in_client_with_server, mocks::MatrixMockServer},
};
use matrix_sdk_test::{
async_test, event_factory::EventFactory, GlobalAccountDataTestEvent, JoinedRoomBuilder,
Expand Down Expand Up @@ -57,7 +57,7 @@ async fn test_must_explicitly_subscribe() {
}

#[async_test]
async fn test_add_initial_events() {
async fn test_event_cache_receives_events() {
let (client, server) = logged_in_client_with_server().await;

// Immediately subscribe the event cache to sync updates.
Expand Down Expand Up @@ -113,83 +113,43 @@ async fn test_add_initial_events() {
assert_eq!(events.len(), 1);
assert_event_matches_msg(&events[0], "bonjour monde");

// And when I later add initial events to this room,

// XXX: when we get rid of `add_initial_events`, we can keep this test as a
// smoke test for the event cache.
client
.event_cache()
.add_initial_events(room_id, vec![ev_factory.text_msg("new choice!").into_sync()], None)
.await
.unwrap();

// Then I receive an update that the room has been cleared,
let update = timeout(Duration::from_secs(2), subscriber.recv())
.await
.expect("timeout after receiving a sync update")
.expect("should've received a room event cache update");
assert_let!(RoomEventCacheUpdate::Clear = update);

// Before receiving the "initial" event.
let update = timeout(Duration::from_secs(2), subscriber.recv())
.await
.expect("timeout after receiving a sync update")
.expect("should've received a room event cache update");
assert_let!(RoomEventCacheUpdate::AddTimelineEvents { events, .. } = update);
assert_eq!(events.len(), 1);
assert_event_matches_msg(&events[0], "new choice!");

// That's all, folks!
assert!(subscriber.is_empty());
}

#[async_test]
async fn test_ignored_unignored() {
let (client, server) = logged_in_client_with_server().await;
let server = MatrixMockServer::new().await;
let client = server.client_builder().build().await;

// Immediately subscribe the event cache to sync updates.
client.event_cache().subscribe().unwrap();

// If I sync and get informed I've joined The Room, but with no events,
let room_id = room_id!("!omelette:fromage.fr");
let other_room_id = room_id!("!galette:saucisse.bzh");

let mut sync_builder = SyncResponseBuilder::new();
sync_builder
.add_joined_room(JoinedRoomBuilder::new(room_id))
.add_joined_room(JoinedRoomBuilder::new(other_room_id));

mock_sync(&server, sync_builder.build_json_sync_response(), None).await;
client.sync_once(Default::default()).await.unwrap();
server.reset().await;

let dexter = user_id!("@dexter:lab.org");
let ivan = user_id!("@ivan:lab.ch");
let ev_factory = EventFactory::new();

// If I add initial events to a few rooms,
client
.event_cache()
.add_initial_events(
room_id,
vec![
ev_factory.text_msg("hey there").sender(dexter).into_sync(),
ev_factory.text_msg("hoy!").sender(ivan).into_sync(),
],
None,
let f = EventFactory::new();

// Given two known rooms with initial items,
server
.sync_room(
&client,
JoinedRoomBuilder::new(room_id).add_timeline_bulk(vec![
f.text_msg("hey there").sender(dexter).into_raw_sync(),
f.text_msg("hoy!").sender(ivan).into_raw_sync(),
]),
)
.await
.unwrap();
.await;

client
.event_cache()
.add_initial_events(
other_room_id,
vec![ev_factory.text_msg("demat!").sender(ivan).into_sync()],
None,
server
.sync_room(
&client,
JoinedRoomBuilder::new(other_room_id)
.add_timeline_bulk(vec![f.text_msg("demat!").sender(ivan).into_raw_sync()]),
)
.await
.unwrap();
.await;

// And subscribe to the room,
let room = client.get_room(room_id).unwrap();
Expand All @@ -202,17 +162,19 @@ async fn test_ignored_unignored() {
assert_event_matches_msg(&events[1], "hoy!");

// And after receiving a new ignored list,
sync_builder.add_global_account_data_event(GlobalAccountDataTestEvent::Custom(json!({
"content": {
"ignored_users": {
dexter: {}
}
},
"type": "m.ignored_user_list",
})));
mock_sync(&server, sync_builder.build_json_sync_response(), None).await;
client.sync_once(Default::default()).await.unwrap();
server.reset().await;
server
.mock_sync()
.ok_and_run(&client, |sync_builder| {
sync_builder.add_global_account_data_event(GlobalAccountDataTestEvent::Custom(json!({
"content": {
"ignored_users": {
dexter: {}
}
},
"type": "m.ignored_user_list",
})));
})
.await;

// It does receive one update,
let update = timeout(Duration::from_secs(2), subscriber.recv())
Expand All @@ -224,13 +186,15 @@ async fn test_ignored_unignored() {
assert_matches!(update, RoomEventCacheUpdate::Clear);

// Receiving new events still works.
sync_builder.add_joined_room(
JoinedRoomBuilder::new(room_id)
.add_timeline_event(ev_factory.text_msg("i don't like this dexter").sender(ivan)),
);
mock_sync(&server, sync_builder.build_json_sync_response(), None).await;
client.sync_once(Default::default()).await.unwrap();
server.reset().await;
server
.mock_sync()
.ok_and_run(&client, |sync_builder| {
sync_builder.add_joined_room(
JoinedRoomBuilder::new(room_id)
.add_timeline_event(f.text_msg("i don't like this dexter").sender(ivan)),
);
})
.await;

// We do receive one update,
let update = timeout(Duration::from_secs(2), subscriber.recv())
Expand Down
Loading