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
130 changes: 71 additions & 59 deletions crates/matrix-sdk-ui/tests/integration/timeline/edit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,24 @@
// See the License for the specific language governing permissions and
// limitations under the License.

use std::time::Duration;
use std::{sync::Arc, time::Duration};

use as_variant::as_variant;
use assert_matches::assert_matches;
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::{StoreConfig, SyncSettings},
linked_chunk::{ChunkIdentifier, Update},
room::edit::EditedContent,
test_utils::{logged_in_client_with_server, mocks::MatrixMockServer},
Client,
};
use matrix_sdk_base::event_cache::{
store::{EventCacheStore, MemoryStore},
Gap,
};
use matrix_sdk_test::{
async_test, event_factory::EventFactory, mocks::mock_encryption_state, JoinedRoomBuilder,
SyncResponseBuilder, ALICE, BOB,
Expand All @@ -46,7 +53,7 @@ use ruma::{
MessageType, RoomMessageEventContent, RoomMessageEventContentWithoutRelation,
TextMessageEventContent,
},
AnyMessageLikeEventContent, AnyTimelineEvent,
AnyMessageLikeEventContent, AnyStateEvent, AnyTimelineEvent,
},
owned_event_id, room_id,
serde::Raw,
Expand All @@ -57,7 +64,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 +675,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 server = MatrixMockServer::new().await;
let client = server.client_builder().build().await;

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

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 +693,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 +739,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 +855,77 @@ 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 event_cache_store = Arc::new(MemoryStore::new());
let server = MatrixMockServer::new().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;

mock_encryption_state(&server, false).await;
let client = server
.client_builder()
.store_config(
StoreConfig::new("hodor".to_owned()).event_cache_store(event_cache_store.clone()),
)
.build()
.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()))
ec.enable_storage().unwrap();

event_cache_store
.handle_linked_chunk_updates(
room_id,
vec![
// Maintain the invariant that the first chunk is an items.
Update::NewItemsChunk {
previous: None,
new: ChunkIdentifier::new(0),
next: None,
},
// Mind the gap!
Update::NewGapChunk {
previous: Some(ChunkIdentifier::new(0)),
new: ChunkIdentifier::new(1),
next: None,
gap: Gap { prev_token: "prev-batch-token".to_owned() },
},
],
)
.await
.unwrap();
}

server.sync_joined_room(&client, room_id).await;
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
Loading
Loading