diff --git a/crates/matrix-sdk-base/src/event_cache/store/integration_tests.rs b/crates/matrix-sdk-base/src/event_cache/store/integration_tests.rs index e656c8cd1..e6f2ab015 100644 --- a/crates/matrix-sdk-base/src/event_cache/store/integration_tests.rs +++ b/crates/matrix-sdk-base/src/event_cache/store/integration_tests.rs @@ -17,6 +17,7 @@ use std::{collections::BTreeMap, sync::Arc}; use assert_matches::assert_matches; +use assert_matches2::assert_let; use matrix_sdk_common::{ deserialized_responses::{ AlgorithmInfo, DecryptedRoomEvent, EncryptionInfo, TimelineEvent, TimelineEventKind, @@ -154,6 +155,10 @@ pub trait EventCacheStoreIntegrationTests { /// Test that saving an event works as expected. async fn test_save_event(&self); + + /// Test multiple things related to distinguishing a thread linked chunk + /// from a room linked chunk. + async fn test_thread_vs_room_linked_chunk(&self); } impl EventCacheStoreIntegrationTests for DynEventCacheStore { @@ -1153,6 +1158,139 @@ impl EventCacheStoreIntegrationTests for DynEventCacheStore { .is_none() ); } + + async fn test_thread_vs_room_linked_chunk(&self) { + let room_id = room_id!("!r0:matrix.org"); + + let event = |msg: &str| make_test_event(room_id, msg); + + let thread1_ev = event("comté"); + let thread2_ev = event("gruyère"); + let thread2_ev2 = event("beaufort"); + let room_ev = event("brillat savarin triple crème"); + + let thread_root1 = event("thread1"); + let thread_root2 = event("thread2"); + + // Add one event in a thread linked chunk. + self.handle_linked_chunk_updates( + LinkedChunkId::Thread(room_id, thread_root1.event_id().unwrap().as_ref()), + vec![ + Update::NewItemsChunk { previous: None, new: CId::new(0), next: None }, + Update::PushItems { + at: Position::new(CId::new(0), 0), + items: vec![thread1_ev.clone()], + }, + ], + ) + .await + .unwrap(); + + // Add one event in another thread linked chunk (same room). + self.handle_linked_chunk_updates( + LinkedChunkId::Thread(room_id, thread_root2.event_id().unwrap().as_ref()), + vec![ + Update::NewItemsChunk { previous: None, new: CId::new(0), next: None }, + Update::PushItems { + at: Position::new(CId::new(0), 0), + items: vec![thread2_ev.clone(), thread2_ev2.clone()], + }, + ], + ) + .await + .unwrap(); + + // Add another event to the room linked chunk. + self.handle_linked_chunk_updates( + LinkedChunkId::Room(room_id), + vec![ + Update::NewItemsChunk { previous: None, new: CId::new(0), next: None }, + Update::PushItems { + at: Position::new(CId::new(0), 0), + items: vec![room_ev.clone()], + }, + ], + ) + .await + .unwrap(); + + // All the events can be found with `find_event()` for the room. + self.find_event(room_id, thread2_ev.event_id().unwrap().as_ref()) + .await + .expect("failed to query for finding an event") + .expect("failed to find thread1_ev"); + + self.find_event(room_id, thread2_ev.event_id().unwrap().as_ref()) + .await + .expect("failed to query for finding an event") + .expect("failed to find thread2_ev"); + + self.find_event(room_id, thread2_ev2.event_id().unwrap().as_ref()) + .await + .expect("failed to query for finding an event") + .expect("failed to find thread2_ev2"); + + self.find_event(room_id, room_ev.event_id().unwrap().as_ref()) + .await + .expect("failed to query for finding an event") + .expect("failed to find room_ev"); + + // Finding duplicates operates based on the linked chunk id. + let dups = self + .filter_duplicated_events( + LinkedChunkId::Thread(room_id, thread_root1.event_id().unwrap().as_ref()), + vec![ + thread1_ev.event_id().unwrap().to_owned(), + room_ev.event_id().unwrap().to_owned(), + ], + ) + .await + .unwrap(); + assert_eq!(dups.len(), 1); + assert_eq!(dups[0].0, thread1_ev.event_id().unwrap()); + + // Loading all chunks operates based on the linked chunk id. + let all_chunks = self + .load_all_chunks(LinkedChunkId::Thread( + room_id, + thread_root2.event_id().unwrap().as_ref(), + )) + .await + .unwrap(); + assert_eq!(all_chunks.len(), 1); + assert_eq!(all_chunks[0].identifier, CId::new(0)); + assert_let!(ChunkContent::Items(observed_items) = all_chunks[0].content.clone()); + assert_eq!(observed_items.len(), 2); + assert_eq!(observed_items[0].event_id(), thread2_ev.event_id()); + assert_eq!(observed_items[1].event_id(), thread2_ev2.event_id()); + + // Loading the metadata of all chunks operates based on the linked chunk + // id. + let metas = self + .load_all_chunks_metadata(LinkedChunkId::Thread( + room_id, + thread_root2.event_id().unwrap().as_ref(), + )) + .await + .unwrap(); + assert_eq!(metas.len(), 1); + assert_eq!(metas[0].identifier, CId::new(0)); + assert_eq!(metas[0].num_items, 2); + + // Loading the last chunk operates based on the linked chunk id. + let (last_chunk, _chunk_identifier_generator) = self + .load_last_chunk(LinkedChunkId::Thread( + room_id, + thread_root1.event_id().unwrap().as_ref(), + )) + .await + .unwrap(); + let last_chunk = last_chunk.unwrap(); + assert_eq!(last_chunk.identifier, CId::new(0)); + assert_let!(ChunkContent::Items(observed_items) = last_chunk.content); + assert_eq!(observed_items.len(), 1); + assert_eq!(observed_items[0].event_id(), thread1_ev.event_id()); + } } /// Macro building to allow your `EventCacheStore` implementation to run the @@ -1277,6 +1415,13 @@ macro_rules! event_cache_store_integration_tests { get_event_cache_store().await.unwrap().into_event_cache_store(); event_cache_store.test_save_event().await; } + + #[async_test] + async fn test_thread_vs_room_linked_chunk() { + let event_cache_store = + get_event_cache_store().await.unwrap().into_event_cache_store(); + event_cache_store.test_thread_vs_room_linked_chunk().await; + } } }; } diff --git a/crates/matrix-sdk-base/src/event_cache/store/traits.rs b/crates/matrix-sdk-base/src/event_cache/store/traits.rs index a4e5376a2..84f46ac71 100644 --- a/crates/matrix-sdk-base/src/event_cache/store/traits.rs +++ b/crates/matrix-sdk-base/src/event_cache/store/traits.rs @@ -128,6 +128,9 @@ pub trait EventCacheStore: AsyncTraitDeps { ) -> Result, Self::Error>; /// Find an event by its ID in a room. + /// + /// This method must return events saved either in any linked chunks, *or* + /// events saved "out-of-band" with the [`Self::save_event`] method. async fn find_event( &self, room_id: &RoomId, @@ -147,6 +150,9 @@ pub trait EventCacheStore: AsyncTraitDeps { /// /// An additional filter can be provided to only retrieve related events for /// a certain relationship. + /// + /// This method must return events saved either in any linked chunks, *or* + /// events saved "out-of-band" with the [`Self::save_event`] method. async fn find_event_relations( &self, room_id: &RoomId,