matrix_sdk_base/event_cache/store/
integration_tests.rs

1// Copyright 2024 The Matrix.org Foundation C.I.C.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! Trait and macro of integration tests for `EventCacheStore` implementations.
16
17use std::{
18    collections::{BTreeMap, BTreeSet},
19    sync::Arc,
20};
21
22use assert_matches::assert_matches;
23use assert_matches2::assert_let;
24use matrix_sdk_common::{
25    deserialized_responses::{
26        AlgorithmInfo, DecryptedRoomEvent, EncryptionInfo, TimelineEvent, TimelineEventKind,
27        UnableToDecryptInfo, UnableToDecryptReason, VerificationState,
28    },
29    linked_chunk::{
30        ChunkContent, ChunkIdentifier as CId, LinkedChunkId, Position, Update, lazy_loader,
31    },
32};
33use matrix_sdk_test::{ALICE, DEFAULT_TEST_ROOM_ID, event_factory::EventFactory};
34use ruma::{
35    EventId, RoomId, event_id,
36    events::{
37        AnyMessageLikeEvent, AnyTimelineEvent, relation::RelationType,
38        room::message::RoomMessageEventContentWithoutRelation,
39    },
40    push::Action,
41    room_id,
42};
43
44use super::DynEventCacheStore;
45use crate::event_cache::{Gap, store::DEFAULT_CHUNK_CAPACITY};
46
47/// Create a test event with all data filled, for testing that linked chunk
48/// correctly stores event data.
49///
50/// Keep in sync with [`check_test_event`].
51pub fn make_test_event(room_id: &RoomId, content: &str) -> TimelineEvent {
52    make_test_event_with_event_id(room_id, content, None)
53}
54
55/// Create a `m.room.encrypted` test event with all data filled, for testing
56/// that linked chunk correctly stores event data for encrypted events.
57pub fn make_encrypted_test_event(room_id: &RoomId, session_id: &str) -> TimelineEvent {
58    let device_id = "DEVICEID";
59    let builder = EventFactory::new()
60        .encrypted("", "curve_key", device_id, session_id)
61        .room(room_id)
62        .sender(*ALICE);
63
64    let event = builder.into_raw();
65    let utd_info = UnableToDecryptInfo {
66        session_id: Some(session_id.to_owned()),
67        reason: UnableToDecryptReason::MissingMegolmSession { withheld_code: None },
68    };
69
70    TimelineEvent::from_utd(event, utd_info)
71}
72
73/// Same as [`make_test_event`], with an extra event id.
74pub fn make_test_event_with_event_id(
75    room_id: &RoomId,
76    content: &str,
77    event_id: Option<&EventId>,
78) -> TimelineEvent {
79    let encryption_info = Arc::new(EncryptionInfo {
80        sender: (*ALICE).into(),
81        sender_device: None,
82        algorithm_info: AlgorithmInfo::MegolmV1AesSha2 {
83            curve25519_key: "1337".to_owned(),
84            sender_claimed_keys: Default::default(),
85            session_id: Some("mysessionid9".to_owned()),
86        },
87        verification_state: VerificationState::Verified,
88    });
89
90    let mut builder = EventFactory::new().text_msg(content).room(room_id).sender(*ALICE);
91    if let Some(event_id) = event_id {
92        builder = builder.event_id(event_id);
93    }
94    let event = builder.into_raw();
95
96    TimelineEvent::from_decrypted(
97        DecryptedRoomEvent { event, encryption_info, unsigned_encryption_info: None },
98        Some(vec![Action::Notify]),
99    )
100}
101
102/// Check that an event created with [`make_test_event`] contains the expected
103/// data.
104///
105/// Keep in sync with [`make_test_event`].
106#[track_caller]
107pub fn check_test_event(event: &TimelineEvent, text: &str) {
108    // Check push actions.
109    let actions = event.push_actions().unwrap();
110    assert_eq!(actions.len(), 1);
111    assert_matches!(&actions[0], Action::Notify);
112
113    // Check content.
114    assert_matches!(&event.kind, TimelineEventKind::Decrypted(d) => {
115        // Check encryption fields.
116        assert_eq!(d.encryption_info.sender, *ALICE);
117        assert_matches!(&d.encryption_info.algorithm_info, AlgorithmInfo::MegolmV1AesSha2 { curve25519_key, .. } => {
118            assert_eq!(curve25519_key, "1337");
119        });
120
121        // Check event.
122        let deserialized = d.event.deserialize().unwrap();
123        assert_matches!(deserialized, AnyTimelineEvent::MessageLike(AnyMessageLikeEvent::RoomMessage(msg)) => {
124            assert_eq!(msg.as_original().unwrap().content.body(), text);
125        });
126    });
127}
128
129/// `EventCacheStore` integration tests.
130///
131/// This trait is not meant to be used directly, but will be used with the
132/// `event_cache_store_integration_tests!` macro.
133#[allow(async_fn_in_trait)]
134pub trait EventCacheStoreIntegrationTests {
135    /// Test handling updates to a linked chunk and reloading these updates from
136    /// the store.
137    async fn test_handle_updates_and_rebuild_linked_chunk(&self);
138
139    /// Test loading a linked chunk incrementally (chunk by chunk) from the
140    /// store.
141    async fn test_linked_chunk_incremental_loading(&self);
142
143    /// Test that rebuilding a linked chunk from an empty store doesn't return
144    /// anything.
145    async fn test_rebuild_empty_linked_chunk(&self);
146
147    /// Test that loading a linked chunk's metadata works as intended.
148    async fn test_load_all_chunks_metadata(&self);
149
150    /// Test that clear all the rooms' linked chunks works.
151    async fn test_clear_all_linked_chunks(&self);
152
153    /// Test that removing a room from storage empties all associated data.
154    async fn test_remove_room(&self);
155
156    /// Test that filtering duplicated events works as expected.
157    async fn test_filter_duplicated_events(&self);
158
159    /// Test that an event can be found or not.
160    async fn test_find_event(&self);
161
162    /// Test that finding event relations works as expected.
163    async fn test_find_event_relations(&self);
164
165    /// Test that getting all events in a room works as expected.
166    async fn test_get_room_events(&self);
167
168    /// Test that getting events in a room of a certain type works as expected.
169    async fn test_get_room_events_filtered(&self);
170
171    /// Test that saving an event works as expected.
172    async fn test_save_event(&self);
173
174    /// Test multiple things related to distinguishing a thread linked chunk
175    /// from a room linked chunk.
176    async fn test_thread_vs_room_linked_chunk(&self);
177}
178
179impl EventCacheStoreIntegrationTests for DynEventCacheStore {
180    async fn test_handle_updates_and_rebuild_linked_chunk(&self) {
181        let room_id = room_id!("!r0:matrix.org");
182        let linked_chunk_id = LinkedChunkId::Room(room_id);
183
184        self.handle_linked_chunk_updates(
185            linked_chunk_id,
186            vec![
187                // new chunk
188                Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
189                // new items on 0
190                Update::PushItems {
191                    at: Position::new(CId::new(0), 0),
192                    items: vec![
193                        make_test_event(room_id, "hello"),
194                        make_test_event(room_id, "world"),
195                    ],
196                },
197                // a gap chunk
198                Update::NewGapChunk {
199                    previous: Some(CId::new(0)),
200                    new: CId::new(1),
201                    next: None,
202                    gap: Gap { prev_token: "parmesan".to_owned() },
203                },
204                // another items chunk
205                Update::NewItemsChunk { previous: Some(CId::new(1)), new: CId::new(2), next: None },
206                // new items on 2
207                Update::PushItems {
208                    at: Position::new(CId::new(2), 0),
209                    items: vec![make_test_event(room_id, "sup")],
210                },
211            ],
212        )
213        .await
214        .unwrap();
215
216        // The linked chunk is correctly reloaded.
217        let lc = lazy_loader::from_all_chunks::<3, _, _>(
218            self.load_all_chunks(linked_chunk_id).await.unwrap(),
219        )
220        .unwrap()
221        .unwrap();
222
223        let mut chunks = lc.chunks();
224
225        {
226            let first = chunks.next().unwrap();
227            // Note: we can't assert the previous/next chunks, as these fields and their
228            // getters are private.
229            assert_eq!(first.identifier(), CId::new(0));
230
231            assert_matches!(first.content(), ChunkContent::Items(events) => {
232                assert_eq!(events.len(), 2);
233                check_test_event(&events[0], "hello");
234                check_test_event(&events[1], "world");
235            });
236        }
237
238        {
239            let second = chunks.next().unwrap();
240            assert_eq!(second.identifier(), CId::new(1));
241
242            assert_matches!(second.content(), ChunkContent::Gap(gap) => {
243                assert_eq!(gap.prev_token, "parmesan");
244            });
245        }
246
247        {
248            let third = chunks.next().unwrap();
249            assert_eq!(third.identifier(), CId::new(2));
250
251            assert_matches!(third.content(), ChunkContent::Items(events) => {
252                assert_eq!(events.len(), 1);
253                check_test_event(&events[0], "sup");
254            });
255        }
256
257        assert!(chunks.next().is_none());
258    }
259
260    async fn test_load_all_chunks_metadata(&self) {
261        let room_id = room_id!("!r0:matrix.org");
262        let linked_chunk_id = LinkedChunkId::Room(room_id);
263
264        self.handle_linked_chunk_updates(
265            linked_chunk_id,
266            vec![
267                // new chunk
268                Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
269                // new items on 0
270                Update::PushItems {
271                    at: Position::new(CId::new(0), 0),
272                    items: vec![
273                        make_test_event(room_id, "hello"),
274                        make_test_event(room_id, "world"),
275                    ],
276                },
277                // a gap chunk
278                Update::NewGapChunk {
279                    previous: Some(CId::new(0)),
280                    new: CId::new(1),
281                    next: None,
282                    gap: Gap { prev_token: "parmesan".to_owned() },
283                },
284                // another items chunk
285                Update::NewItemsChunk { previous: Some(CId::new(1)), new: CId::new(2), next: None },
286                // new items on 2
287                Update::PushItems {
288                    at: Position::new(CId::new(2), 0),
289                    items: vec![make_test_event(room_id, "sup")],
290                },
291                // and an empty items chunk to finish
292                Update::NewItemsChunk { previous: Some(CId::new(2)), new: CId::new(3), next: None },
293            ],
294        )
295        .await
296        .unwrap();
297
298        let metas = self.load_all_chunks_metadata(linked_chunk_id).await.unwrap();
299        assert_eq!(metas.len(), 4);
300
301        // The first chunk has two items.
302        assert_eq!(metas[0].identifier, CId::new(0));
303        assert_eq!(metas[0].previous, None);
304        assert_eq!(metas[0].next, Some(CId::new(1)));
305        assert_eq!(metas[0].num_items, 2);
306
307        // The second chunk is a gap, so it has 0 items.
308        assert_eq!(metas[1].identifier, CId::new(1));
309        assert_eq!(metas[1].previous, Some(CId::new(0)));
310        assert_eq!(metas[1].next, Some(CId::new(2)));
311        assert_eq!(metas[1].num_items, 0);
312
313        // The third event chunk has one item.
314        assert_eq!(metas[2].identifier, CId::new(2));
315        assert_eq!(metas[2].previous, Some(CId::new(1)));
316        assert_eq!(metas[2].next, Some(CId::new(3)));
317        assert_eq!(metas[2].num_items, 1);
318
319        // The final event chunk is empty.
320        assert_eq!(metas[3].identifier, CId::new(3));
321        assert_eq!(metas[3].previous, Some(CId::new(2)));
322        assert_eq!(metas[3].next, None);
323        assert_eq!(metas[3].num_items, 0);
324    }
325
326    async fn test_linked_chunk_incremental_loading(&self) {
327        let room_id = room_id!("!r0:matrix.org");
328        let linked_chunk_id = LinkedChunkId::Room(room_id);
329        let event = |msg: &str| make_test_event(room_id, msg);
330
331        // Load the last chunk, but none exists yet.
332        {
333            let (last_chunk, chunk_identifier_generator) =
334                self.load_last_chunk(linked_chunk_id).await.unwrap();
335
336            assert!(last_chunk.is_none());
337            assert_eq!(chunk_identifier_generator.current(), 0);
338        }
339
340        self.handle_linked_chunk_updates(
341            linked_chunk_id,
342            vec![
343                // new chunk for items
344                Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
345                // new items on 0
346                Update::PushItems {
347                    at: Position::new(CId::new(0), 0),
348                    items: vec![event("a"), event("b")],
349                },
350                // new chunk for a gap
351                Update::NewGapChunk {
352                    previous: Some(CId::new(0)),
353                    new: CId::new(1),
354                    next: None,
355                    gap: Gap { prev_token: "morbier".to_owned() },
356                },
357                // new chunk for items
358                Update::NewItemsChunk { previous: Some(CId::new(1)), new: CId::new(2), next: None },
359                // new items on 2
360                Update::PushItems {
361                    at: Position::new(CId::new(2), 0),
362                    items: vec![event("c"), event("d"), event("e")],
363                },
364            ],
365        )
366        .await
367        .unwrap();
368
369        // Load the last chunk.
370        let mut linked_chunk = {
371            let (last_chunk, chunk_identifier_generator) =
372                self.load_last_chunk(linked_chunk_id).await.unwrap();
373
374            assert_eq!(chunk_identifier_generator.current(), 2);
375
376            let linked_chunk = lazy_loader::from_last_chunk::<DEFAULT_CHUNK_CAPACITY, _, _>(
377                last_chunk,
378                chunk_identifier_generator,
379            )
380            .unwrap() // unwrap the `Result`
381            .unwrap(); // unwrap the `Option`
382
383            let mut rchunks = linked_chunk.rchunks();
384
385            // A unique chunk.
386            assert_matches!(rchunks.next(), Some(chunk) => {
387                assert_eq!(chunk.identifier(), 2);
388                assert_eq!(chunk.lazy_previous(), Some(CId::new(1)));
389
390                assert_matches!(chunk.content(), ChunkContent::Items(events) => {
391                    assert_eq!(events.len(), 3);
392                    check_test_event(&events[0], "c");
393                    check_test_event(&events[1], "d");
394                    check_test_event(&events[2], "e");
395                });
396            });
397
398            assert!(rchunks.next().is_none());
399
400            linked_chunk
401        };
402
403        // Load the previous chunk: this is a gap.
404        {
405            let first_chunk = linked_chunk.chunks().next().unwrap().identifier();
406            let previous_chunk =
407                self.load_previous_chunk(linked_chunk_id, first_chunk).await.unwrap().unwrap();
408
409            lazy_loader::insert_new_first_chunk(&mut linked_chunk, previous_chunk).unwrap();
410
411            let mut rchunks = linked_chunk.rchunks();
412
413            // The last chunk.
414            assert_matches!(rchunks.next(), Some(chunk) => {
415                assert_eq!(chunk.identifier(), 2);
416                assert!(chunk.lazy_previous().is_none());
417
418                // Already asserted, but let's be sure nothing breaks.
419                assert_matches!(chunk.content(), ChunkContent::Items(events) => {
420                    assert_eq!(events.len(), 3);
421                    check_test_event(&events[0], "c");
422                    check_test_event(&events[1], "d");
423                    check_test_event(&events[2], "e");
424                });
425            });
426
427            // The new chunk.
428            assert_matches!(rchunks.next(), Some(chunk) => {
429                assert_eq!(chunk.identifier(), 1);
430                assert_eq!(chunk.lazy_previous(), Some(CId::new(0)));
431
432                assert_matches!(chunk.content(), ChunkContent::Gap(gap) => {
433                    assert_eq!(gap.prev_token, "morbier");
434                });
435            });
436
437            assert!(rchunks.next().is_none());
438        }
439
440        // Load the previous chunk: these are items.
441        {
442            let first_chunk = linked_chunk.chunks().next().unwrap().identifier();
443            let previous_chunk =
444                self.load_previous_chunk(linked_chunk_id, first_chunk).await.unwrap().unwrap();
445
446            lazy_loader::insert_new_first_chunk(&mut linked_chunk, previous_chunk).unwrap();
447
448            let mut rchunks = linked_chunk.rchunks();
449
450            // The last chunk.
451            assert_matches!(rchunks.next(), Some(chunk) => {
452                assert_eq!(chunk.identifier(), 2);
453                assert!(chunk.lazy_previous().is_none());
454
455                // Already asserted, but let's be sure nothing breaks.
456                assert_matches!(chunk.content(), ChunkContent::Items(events) => {
457                    assert_eq!(events.len(), 3);
458                    check_test_event(&events[0], "c");
459                    check_test_event(&events[1], "d");
460                    check_test_event(&events[2], "e");
461                });
462            });
463
464            // Its previous chunk.
465            assert_matches!(rchunks.next(), Some(chunk) => {
466                assert_eq!(chunk.identifier(), 1);
467                assert!(chunk.lazy_previous().is_none());
468
469                // Already asserted, but let's be sure nothing breaks.
470                assert_matches!(chunk.content(), ChunkContent::Gap(gap) => {
471                    assert_eq!(gap.prev_token, "morbier");
472                });
473            });
474
475            // The new chunk.
476            assert_matches!(rchunks.next(), Some(chunk) => {
477                assert_eq!(chunk.identifier(), 0);
478                assert!(chunk.lazy_previous().is_none());
479
480                assert_matches!(chunk.content(), ChunkContent::Items(events) => {
481                    assert_eq!(events.len(), 2);
482                    check_test_event(&events[0], "a");
483                    check_test_event(&events[1], "b");
484                });
485            });
486
487            assert!(rchunks.next().is_none());
488        }
489
490        // Load the previous chunk: there is none.
491        {
492            let first_chunk = linked_chunk.chunks().next().unwrap().identifier();
493            let previous_chunk =
494                self.load_previous_chunk(linked_chunk_id, first_chunk).await.unwrap();
495
496            assert!(previous_chunk.is_none());
497        }
498
499        // One last check: a round of assert by using the forwards chunk iterator
500        // instead of the backwards chunk iterator.
501        {
502            let mut chunks = linked_chunk.chunks();
503
504            // The first chunk.
505            assert_matches!(chunks.next(), Some(chunk) => {
506                assert_eq!(chunk.identifier(), 0);
507                assert!(chunk.lazy_previous().is_none());
508
509                assert_matches!(chunk.content(), ChunkContent::Items(events) => {
510                    assert_eq!(events.len(), 2);
511                    check_test_event(&events[0], "a");
512                    check_test_event(&events[1], "b");
513                });
514            });
515
516            // The second chunk.
517            assert_matches!(chunks.next(), Some(chunk) => {
518                assert_eq!(chunk.identifier(), 1);
519                assert!(chunk.lazy_previous().is_none());
520
521                assert_matches!(chunk.content(), ChunkContent::Gap(gap) => {
522                    assert_eq!(gap.prev_token, "morbier");
523                });
524            });
525
526            // The third and last chunk.
527            assert_matches!(chunks.next(), Some(chunk) => {
528                assert_eq!(chunk.identifier(), 2);
529                assert!(chunk.lazy_previous().is_none());
530
531                assert_matches!(chunk.content(), ChunkContent::Items(events) => {
532                    assert_eq!(events.len(), 3);
533                    check_test_event(&events[0], "c");
534                    check_test_event(&events[1], "d");
535                    check_test_event(&events[2], "e");
536                });
537            });
538
539            assert!(chunks.next().is_none());
540        }
541    }
542
543    async fn test_rebuild_empty_linked_chunk(&self) {
544        // When I rebuild a linked chunk from an empty store, it's empty.
545        let linked_chunk = lazy_loader::from_all_chunks::<3, _, _>(
546            self.load_all_chunks(LinkedChunkId::Room(&DEFAULT_TEST_ROOM_ID)).await.unwrap(),
547        )
548        .unwrap();
549        assert!(linked_chunk.is_none());
550    }
551
552    async fn test_clear_all_linked_chunks(&self) {
553        let r0 = room_id!("!r0:matrix.org");
554        let linked_chunk_id0 = LinkedChunkId::Room(r0);
555        let r1 = room_id!("!r1:matrix.org");
556        let linked_chunk_id1 = LinkedChunkId::Room(r1);
557
558        // Add updates for the first room.
559        self.handle_linked_chunk_updates(
560            linked_chunk_id0,
561            vec![
562                // new chunk
563                Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
564                // new items on 0
565                Update::PushItems {
566                    at: Position::new(CId::new(0), 0),
567                    items: vec![make_test_event(r0, "hello"), make_test_event(r0, "world")],
568                },
569            ],
570        )
571        .await
572        .unwrap();
573
574        // Add updates for the second room.
575        self.handle_linked_chunk_updates(
576            linked_chunk_id1,
577            vec![
578                // Empty items chunk.
579                Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
580                // a gap chunk
581                Update::NewGapChunk {
582                    previous: Some(CId::new(0)),
583                    new: CId::new(1),
584                    next: None,
585                    gap: Gap { prev_token: "bleu d'auvergne".to_owned() },
586                },
587                // another items chunk
588                Update::NewItemsChunk { previous: Some(CId::new(1)), new: CId::new(2), next: None },
589                // new items on 0
590                Update::PushItems {
591                    at: Position::new(CId::new(2), 0),
592                    items: vec![make_test_event(r0, "yummy")],
593                },
594            ],
595        )
596        .await
597        .unwrap();
598
599        // Sanity check: both linked chunks can be reloaded.
600        assert!(
601            lazy_loader::from_all_chunks::<3, _, _>(
602                self.load_all_chunks(linked_chunk_id0).await.unwrap()
603            )
604            .unwrap()
605            .is_some()
606        );
607        assert!(
608            lazy_loader::from_all_chunks::<3, _, _>(
609                self.load_all_chunks(linked_chunk_id1).await.unwrap()
610            )
611            .unwrap()
612            .is_some()
613        );
614
615        // Clear the chunks.
616        self.clear_all_linked_chunks().await.unwrap();
617
618        // Both rooms now have no linked chunk.
619        assert!(
620            lazy_loader::from_all_chunks::<3, _, _>(
621                self.load_all_chunks(linked_chunk_id0).await.unwrap()
622            )
623            .unwrap()
624            .is_none()
625        );
626        assert!(
627            lazy_loader::from_all_chunks::<3, _, _>(
628                self.load_all_chunks(linked_chunk_id1).await.unwrap()
629            )
630            .unwrap()
631            .is_none()
632        );
633    }
634
635    async fn test_remove_room(&self) {
636        let r0 = room_id!("!r0:matrix.org");
637        let linked_chunk_id0 = LinkedChunkId::Room(r0);
638        let r1 = room_id!("!r1:matrix.org");
639        let linked_chunk_id1 = LinkedChunkId::Room(r1);
640
641        // Add updates to the first room.
642        self.handle_linked_chunk_updates(
643            linked_chunk_id0,
644            vec![
645                // new chunk
646                Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
647                // new items on 0
648                Update::PushItems {
649                    at: Position::new(CId::new(0), 0),
650                    items: vec![make_test_event(r0, "hello"), make_test_event(r0, "world")],
651                },
652            ],
653        )
654        .await
655        .unwrap();
656
657        // Add updates to the second room.
658        self.handle_linked_chunk_updates(
659            linked_chunk_id1,
660            vec![
661                // new chunk
662                Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
663                // new items on 0
664                Update::PushItems {
665                    at: Position::new(CId::new(0), 0),
666                    items: vec![make_test_event(r0, "yummy")],
667                },
668            ],
669        )
670        .await
671        .unwrap();
672
673        // Try to remove content from r0.
674        self.remove_room(r0).await.unwrap();
675
676        // Check that r0 doesn't have a linked chunk anymore.
677        let r0_linked_chunk = self.load_all_chunks(linked_chunk_id0).await.unwrap();
678        assert!(r0_linked_chunk.is_empty());
679
680        // Check that r1 is unaffected.
681        let r1_linked_chunk = self.load_all_chunks(linked_chunk_id1).await.unwrap();
682        assert!(!r1_linked_chunk.is_empty());
683    }
684
685    async fn test_filter_duplicated_events(&self) {
686        let room_id = room_id!("!r0:matrix.org");
687        let linked_chunk_id = LinkedChunkId::Room(room_id);
688        let another_room_id = room_id!("!r1:matrix.org");
689        let another_linked_chunk_id = LinkedChunkId::Room(another_room_id);
690        let event = |msg: &str| make_test_event(room_id, msg);
691
692        let event_comte = event("comté");
693        let event_brigand = event("brigand du jorat");
694        let event_raclette = event("raclette");
695        let event_morbier = event("morbier");
696        let event_gruyere = event("gruyère");
697        let event_tome = event("tome");
698        let event_mont_dor = event("mont d'or");
699
700        self.handle_linked_chunk_updates(
701            linked_chunk_id,
702            vec![
703                Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
704                Update::PushItems {
705                    at: Position::new(CId::new(0), 0),
706                    items: vec![event_comte.clone(), event_brigand.clone()],
707                },
708                Update::NewGapChunk {
709                    previous: Some(CId::new(0)),
710                    new: CId::new(1),
711                    next: None,
712                    gap: Gap { prev_token: "brillat-savarin".to_owned() },
713                },
714                Update::NewItemsChunk { previous: Some(CId::new(1)), new: CId::new(2), next: None },
715                Update::PushItems {
716                    at: Position::new(CId::new(2), 0),
717                    items: vec![event_morbier.clone(), event_mont_dor.clone()],
718                },
719            ],
720        )
721        .await
722        .unwrap();
723
724        // Add other events in another room, to ensure filtering take the `room_id` into
725        // account.
726        self.handle_linked_chunk_updates(
727            another_linked_chunk_id,
728            vec![
729                Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
730                Update::PushItems {
731                    at: Position::new(CId::new(0), 0),
732                    items: vec![event_tome.clone()],
733                },
734            ],
735        )
736        .await
737        .unwrap();
738
739        let duplicated_events = BTreeMap::from_iter(
740            self.filter_duplicated_events(
741                linked_chunk_id,
742                vec![
743                    event_comte.event_id().unwrap().to_owned(),
744                    event_raclette.event_id().unwrap().to_owned(),
745                    event_morbier.event_id().unwrap().to_owned(),
746                    event_gruyere.event_id().unwrap().to_owned(),
747                    event_tome.event_id().unwrap().to_owned(),
748                    event_mont_dor.event_id().unwrap().to_owned(),
749                ],
750            )
751            .await
752            .unwrap(),
753        );
754
755        assert_eq!(duplicated_events.len(), 3);
756
757        assert_eq!(
758            *duplicated_events.get(&event_comte.event_id().unwrap()).unwrap(),
759            Position::new(CId::new(0), 0)
760        );
761        assert_eq!(
762            *duplicated_events.get(&event_morbier.event_id().unwrap()).unwrap(),
763            Position::new(CId::new(2), 0)
764        );
765        assert_eq!(
766            *duplicated_events.get(&event_mont_dor.event_id().unwrap()).unwrap(),
767            Position::new(CId::new(2), 1)
768        );
769    }
770
771    async fn test_find_event(&self) {
772        let room_id = room_id!("!r0:matrix.org");
773        let another_room_id = room_id!("!r1:matrix.org");
774        let another_linked_chunk_id = LinkedChunkId::Room(another_room_id);
775        let event = |msg: &str| make_test_event(room_id, msg);
776
777        let event_comte = event("comté");
778        let event_gruyere = event("gruyère");
779
780        // Add one event in one room.
781        self.handle_linked_chunk_updates(
782            LinkedChunkId::Room(room_id),
783            vec![
784                Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
785                Update::PushItems {
786                    at: Position::new(CId::new(0), 0),
787                    items: vec![event_comte.clone()],
788                },
789            ],
790        )
791        .await
792        .unwrap();
793
794        // Add another event in another room.
795        self.handle_linked_chunk_updates(
796            another_linked_chunk_id,
797            vec![
798                Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
799                Update::PushItems {
800                    at: Position::new(CId::new(0), 0),
801                    items: vec![event_gruyere.clone()],
802                },
803            ],
804        )
805        .await
806        .unwrap();
807
808        // Now let's find the event.
809        let event = self
810            .find_event(room_id, event_comte.event_id().unwrap().as_ref())
811            .await
812            .expect("failed to query for finding an event")
813            .expect("failed to find an event");
814
815        assert_eq!(event.event_id(), event_comte.event_id());
816
817        // Now let's try to find an event that exists, but not in the expected room.
818        assert!(
819            self.find_event(room_id, event_gruyere.event_id().unwrap().as_ref())
820                .await
821                .expect("failed to query for finding an event")
822                .is_none()
823        );
824
825        // Clearing the rooms also clears the event's storage.
826        self.clear_all_linked_chunks().await.expect("failed to clear all rooms chunks");
827        assert!(
828            self.find_event(room_id, event_comte.event_id().unwrap().as_ref())
829                .await
830                .expect("failed to query for finding an event")
831                .is_none()
832        );
833    }
834
835    async fn test_find_event_relations(&self) {
836        let room_id = room_id!("!r0:matrix.org");
837        let another_room_id = room_id!("!r1:matrix.org");
838
839        let f = EventFactory::new().room(room_id).sender(*ALICE);
840
841        // Create event and related events for the first room.
842        let eid1 = event_id!("$event1:matrix.org");
843        let e1 = f.text_msg("comter").event_id(eid1).into_event();
844
845        let edit_eid1 = event_id!("$edit_event1:matrix.org");
846        let edit_e1 = f
847            .text_msg("* comté")
848            .event_id(edit_eid1)
849            .edit(eid1, RoomMessageEventContentWithoutRelation::text_plain("comté"))
850            .into_event();
851
852        let reaction_eid1 = event_id!("$reaction_event1:matrix.org");
853        let reaction_e1 = f.reaction(eid1, "👍").event_id(reaction_eid1).into_event();
854
855        let eid2 = event_id!("$event2:matrix.org");
856        let e2 = f.text_msg("galette saucisse").event_id(eid2).into_event();
857
858        // Create events for the second room.
859        let f = f.room(another_room_id);
860
861        let eid3 = event_id!("$event3:matrix.org");
862        let e3 = f.text_msg("gruyère").event_id(eid3).into_event();
863
864        let reaction_eid3 = event_id!("$reaction_event3:matrix.org");
865        let reaction_e3 = f.reaction(eid3, "👍").event_id(reaction_eid3).into_event();
866
867        // Save All The Things!
868        self.save_event(room_id, e1).await.unwrap();
869        self.save_event(room_id, edit_e1).await.unwrap();
870        self.save_event(room_id, reaction_e1.clone()).await.unwrap();
871        self.save_event(room_id, e2).await.unwrap();
872        self.save_event(another_room_id, e3).await.unwrap();
873        self.save_event(another_room_id, reaction_e3).await.unwrap();
874
875        // Finding relations without a filter returns all of them.
876        let relations = self.find_event_relations(room_id, eid1, None).await.unwrap();
877        assert_eq!(relations.len(), 2);
878        // The position is `None` for items outside the linked chunk.
879        assert!(
880            relations
881                .iter()
882                .any(|(ev, pos)| ev.event_id().as_deref() == Some(edit_eid1) && pos.is_none())
883        );
884        assert!(
885            relations
886                .iter()
887                .any(|(ev, pos)| ev.event_id().as_deref() == Some(reaction_eid1) && pos.is_none())
888        );
889
890        // Finding relations with a filter only returns a subset.
891        let relations = self
892            .find_event_relations(room_id, eid1, Some(&[RelationType::Replacement]))
893            .await
894            .unwrap();
895        assert_eq!(relations.len(), 1);
896        assert_eq!(relations[0].0.event_id().as_deref(), Some(edit_eid1));
897
898        let relations = self
899            .find_event_relations(
900                room_id,
901                eid1,
902                Some(&[RelationType::Replacement, RelationType::Annotation]),
903            )
904            .await
905            .unwrap();
906        assert_eq!(relations.len(), 2);
907        assert!(relations.iter().any(|r| r.0.event_id().as_deref() == Some(edit_eid1)));
908        assert!(relations.iter().any(|r| r.0.event_id().as_deref() == Some(reaction_eid1)));
909
910        // We can't find relations using the wrong room.
911        let relations = self
912            .find_event_relations(another_room_id, eid1, Some(&[RelationType::Replacement]))
913            .await
914            .unwrap();
915        assert!(relations.is_empty());
916
917        // But if an event exists in the linked chunk, we may have its position when
918        // it's found as a relationship.
919
920        // Add reaction_e1 to the room's linked chunk.
921        self.handle_linked_chunk_updates(
922            LinkedChunkId::Room(room_id),
923            vec![
924                Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
925                Update::PushItems { at: Position::new(CId::new(0), 0), items: vec![reaction_e1] },
926            ],
927        )
928        .await
929        .unwrap();
930
931        // When looking for aggregations to e1, we should have the position for
932        // reaction_e1.
933        let relations = self.find_event_relations(room_id, eid1, None).await.unwrap();
934
935        // The position is set for `reaction_eid1` now.
936        assert!(relations.iter().any(|(ev, pos)| {
937            ev.event_id().as_deref() == Some(reaction_eid1)
938                && *pos == Some(Position::new(CId::new(0), 0))
939        }));
940
941        // But it's still not set for the other related events.
942        assert!(
943            relations
944                .iter()
945                .any(|(ev, pos)| ev.event_id().as_deref() == Some(edit_eid1) && pos.is_none())
946        );
947    }
948
949    async fn test_get_room_events(&self) {
950        let room_id = room_id!("!r0:matrix.org");
951        let another_room_id = room_id!("!r1:matrix.org");
952        let linked_chunk_id = LinkedChunkId::Room(room_id);
953        let another_linked_chunk_id = LinkedChunkId::Room(another_room_id);
954        let event = |msg: &str| make_test_event(room_id, msg);
955
956        let event_comte = event("comté");
957        let event_gruyere = event("gruyère");
958        let event_stilton = event("stilton");
959
960        // Add one event in one room.
961        self.handle_linked_chunk_updates(
962            linked_chunk_id,
963            vec![
964                Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
965                Update::PushItems {
966                    at: Position::new(CId::new(0), 0),
967                    items: vec![event_comte.clone(), event_gruyere.clone()],
968                },
969            ],
970        )
971        .await
972        .unwrap();
973
974        // Add an event in a different room.
975        self.handle_linked_chunk_updates(
976            another_linked_chunk_id,
977            vec![
978                Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
979                Update::PushItems {
980                    at: Position::new(CId::new(0), 0),
981                    items: vec![event_stilton.clone()],
982                },
983            ],
984        )
985        .await
986        .unwrap();
987
988        // Now let's find the events.
989        let events = self
990            .get_room_events(room_id, None, None)
991            .await
992            .expect("failed to query for room events");
993
994        assert_eq!(events.len(), 2);
995
996        let got_ids: Vec<_> = events.into_iter().map(|ev| ev.event_id()).collect();
997        let expected_ids = vec![event_comte.event_id(), event_gruyere.event_id()];
998
999        for expected in expected_ids {
1000            assert!(
1001                got_ids.contains(&expected),
1002                "Expected event {expected:?} not in got events: {got_ids:?}."
1003            );
1004        }
1005    }
1006
1007    async fn test_get_room_events_filtered(&self) {
1008        macro_rules! assert_expected_events {
1009            ($events:expr, [$($item:expr),* $(,)?]) => {{
1010                let got_ids: BTreeSet<_> = $events.into_iter().map(|ev| ev.event_id().unwrap()).collect();
1011                let expected_ids = BTreeSet::from([$($item.event_id().unwrap()),*]);
1012
1013                assert_eq!(got_ids, expected_ids);
1014            }};
1015        }
1016
1017        let room_id = room_id!("!r0:matrix.org");
1018        let linked_chunk_id = LinkedChunkId::Room(room_id);
1019        let another_room_id = room_id!("!r1:matrix.org");
1020        let another_linked_chunk_id = LinkedChunkId::Room(another_room_id);
1021
1022        let event = |session_id: &str| make_encrypted_test_event(room_id, session_id);
1023
1024        let first_event = event("session_1");
1025        let second_event = event("session_2");
1026        let third_event = event("session_3");
1027        let fourth_event = make_test_event(room_id, "It's a secret to everybody");
1028
1029        // Add one event in one room.
1030        self.handle_linked_chunk_updates(
1031            linked_chunk_id,
1032            vec![
1033                Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
1034                Update::PushItems {
1035                    at: Position::new(CId::new(0), 0),
1036                    items: vec![first_event.clone(), second_event.clone(), fourth_event.clone()],
1037                },
1038            ],
1039        )
1040        .await
1041        .unwrap();
1042
1043        // Add an event in a different room.
1044        self.handle_linked_chunk_updates(
1045            another_linked_chunk_id,
1046            vec![
1047                Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
1048                Update::PushItems {
1049                    at: Position::new(CId::new(0), 0),
1050                    items: vec![third_event.clone()],
1051                },
1052            ],
1053        )
1054        .await
1055        .unwrap();
1056
1057        // Now let's find all the encrypted events of the first room.
1058        let events = self
1059            .get_room_events(room_id, Some("m.room.encrypted"), None)
1060            .await
1061            .expect("failed to query for room events");
1062
1063        assert_eq!(events.len(), 2);
1064        assert_expected_events!(events, [first_event, second_event]);
1065
1066        // Now let's find all the encrypted events which were encrypted using the first
1067        // session ID.
1068        let events = self
1069            .get_room_events(room_id, Some("m.room.encrypted"), Some("session_1"))
1070            .await
1071            .expect("failed to query for room events");
1072
1073        assert_eq!(events.len(), 1);
1074        assert_expected_events!(events, [first_event]);
1075    }
1076
1077    async fn test_save_event(&self) {
1078        let room_id = room_id!("!r0:matrix.org");
1079        let another_room_id = room_id!("!r1:matrix.org");
1080
1081        let event = |msg: &str| make_test_event(room_id, msg);
1082        let event_comte = event("comté");
1083        let event_gruyere = event("gruyère");
1084
1085        // Add one event in one room.
1086        self.save_event(room_id, event_comte.clone()).await.unwrap();
1087
1088        // Add another event in another room.
1089        self.save_event(another_room_id, event_gruyere.clone()).await.unwrap();
1090
1091        // Events can be found, when searched in their own rooms.
1092        let event = self
1093            .find_event(room_id, event_comte.event_id().unwrap().as_ref())
1094            .await
1095            .expect("failed to query for finding an event")
1096            .expect("failed to find an event");
1097        assert_eq!(event.event_id(), event_comte.event_id());
1098
1099        let event = self
1100            .find_event(another_room_id, event_gruyere.event_id().unwrap().as_ref())
1101            .await
1102            .expect("failed to query for finding an event")
1103            .expect("failed to find an event");
1104        assert_eq!(event.event_id(), event_gruyere.event_id());
1105
1106        // But they won't be returned when searching in the wrong room.
1107        assert!(
1108            self.find_event(another_room_id, event_comte.event_id().unwrap().as_ref())
1109                .await
1110                .expect("failed to query for finding an event")
1111                .is_none()
1112        );
1113        assert!(
1114            self.find_event(room_id, event_gruyere.event_id().unwrap().as_ref())
1115                .await
1116                .expect("failed to query for finding an event")
1117                .is_none()
1118        );
1119    }
1120
1121    async fn test_thread_vs_room_linked_chunk(&self) {
1122        let room_id = room_id!("!r0:matrix.org");
1123
1124        let event = |msg: &str| make_test_event(room_id, msg);
1125
1126        let thread1_ev = event("comté");
1127        let thread2_ev = event("gruyère");
1128        let thread2_ev2 = event("beaufort");
1129        let room_ev = event("brillat savarin triple crème");
1130
1131        let thread_root1 = event("thread1");
1132        let thread_root2 = event("thread2");
1133
1134        // Add one event in a thread linked chunk.
1135        self.handle_linked_chunk_updates(
1136            LinkedChunkId::Thread(room_id, thread_root1.event_id().unwrap().as_ref()),
1137            vec![
1138                Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
1139                Update::PushItems {
1140                    at: Position::new(CId::new(0), 0),
1141                    items: vec![thread1_ev.clone()],
1142                },
1143            ],
1144        )
1145        .await
1146        .unwrap();
1147
1148        // Add one event in another thread linked chunk (same room).
1149        self.handle_linked_chunk_updates(
1150            LinkedChunkId::Thread(room_id, thread_root2.event_id().unwrap().as_ref()),
1151            vec![
1152                Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
1153                Update::PushItems {
1154                    at: Position::new(CId::new(0), 0),
1155                    items: vec![thread2_ev.clone(), thread2_ev2.clone()],
1156                },
1157            ],
1158        )
1159        .await
1160        .unwrap();
1161
1162        // Add another event to the room linked chunk.
1163        self.handle_linked_chunk_updates(
1164            LinkedChunkId::Room(room_id),
1165            vec![
1166                Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
1167                Update::PushItems {
1168                    at: Position::new(CId::new(0), 0),
1169                    items: vec![room_ev.clone()],
1170                },
1171            ],
1172        )
1173        .await
1174        .unwrap();
1175
1176        // All the events can be found with `find_event()` for the room.
1177        self.find_event(room_id, thread2_ev.event_id().unwrap().as_ref())
1178            .await
1179            .expect("failed to query for finding an event")
1180            .expect("failed to find thread1_ev");
1181
1182        self.find_event(room_id, thread2_ev.event_id().unwrap().as_ref())
1183            .await
1184            .expect("failed to query for finding an event")
1185            .expect("failed to find thread2_ev");
1186
1187        self.find_event(room_id, thread2_ev2.event_id().unwrap().as_ref())
1188            .await
1189            .expect("failed to query for finding an event")
1190            .expect("failed to find thread2_ev2");
1191
1192        self.find_event(room_id, room_ev.event_id().unwrap().as_ref())
1193            .await
1194            .expect("failed to query for finding an event")
1195            .expect("failed to find room_ev");
1196
1197        // Finding duplicates operates based on the linked chunk id.
1198        let dups = self
1199            .filter_duplicated_events(
1200                LinkedChunkId::Thread(room_id, thread_root1.event_id().unwrap().as_ref()),
1201                vec![
1202                    thread1_ev.event_id().unwrap().to_owned(),
1203                    room_ev.event_id().unwrap().to_owned(),
1204                ],
1205            )
1206            .await
1207            .unwrap();
1208        assert_eq!(dups.len(), 1);
1209        assert_eq!(dups[0].0, thread1_ev.event_id().unwrap());
1210
1211        // Loading all chunks operates based on the linked chunk id.
1212        let all_chunks = self
1213            .load_all_chunks(LinkedChunkId::Thread(
1214                room_id,
1215                thread_root2.event_id().unwrap().as_ref(),
1216            ))
1217            .await
1218            .unwrap();
1219        assert_eq!(all_chunks.len(), 1);
1220        assert_eq!(all_chunks[0].identifier, CId::new(0));
1221        assert_let!(ChunkContent::Items(observed_items) = all_chunks[0].content.clone());
1222        assert_eq!(observed_items.len(), 2);
1223        assert_eq!(observed_items[0].event_id(), thread2_ev.event_id());
1224        assert_eq!(observed_items[1].event_id(), thread2_ev2.event_id());
1225
1226        // Loading the metadata of all chunks operates based on the linked chunk
1227        // id.
1228        let metas = self
1229            .load_all_chunks_metadata(LinkedChunkId::Thread(
1230                room_id,
1231                thread_root2.event_id().unwrap().as_ref(),
1232            ))
1233            .await
1234            .unwrap();
1235        assert_eq!(metas.len(), 1);
1236        assert_eq!(metas[0].identifier, CId::new(0));
1237        assert_eq!(metas[0].num_items, 2);
1238
1239        // Loading the last chunk operates based on the linked chunk id.
1240        let (last_chunk, _chunk_identifier_generator) = self
1241            .load_last_chunk(LinkedChunkId::Thread(
1242                room_id,
1243                thread_root1.event_id().unwrap().as_ref(),
1244            ))
1245            .await
1246            .unwrap();
1247        let last_chunk = last_chunk.unwrap();
1248        assert_eq!(last_chunk.identifier, CId::new(0));
1249        assert_let!(ChunkContent::Items(observed_items) = last_chunk.content);
1250        assert_eq!(observed_items.len(), 1);
1251        assert_eq!(observed_items[0].event_id(), thread1_ev.event_id());
1252    }
1253}
1254
1255/// Macro building to allow your `EventCacheStore` implementation to run the
1256/// entire tests suite locally.
1257///
1258/// You need to provide a `async fn get_event_cache_store() ->
1259/// EventCacheStoreResult<impl EventCacheStore>` providing a fresh event cache
1260/// store on the same level you invoke the macro.
1261///
1262/// ## Usage Example:
1263/// ```no_run
1264/// # use matrix_sdk_base::event_cache::store::{
1265/// #    EventCacheStore,
1266/// #    MemoryStore as MyStore,
1267/// #    Result as EventCacheStoreResult,
1268/// # };
1269///
1270/// #[cfg(test)]
1271/// mod tests {
1272///     use super::{EventCacheStore, EventCacheStoreResult, MyStore};
1273///
1274///     async fn get_event_cache_store()
1275///     -> EventCacheStoreResult<impl EventCacheStore> {
1276///         Ok(MyStore::new())
1277///     }
1278///
1279///     event_cache_store_integration_tests!();
1280/// }
1281/// ```
1282#[allow(unused_macros, unused_extern_crates)]
1283#[macro_export]
1284macro_rules! event_cache_store_integration_tests {
1285    () => {
1286        mod event_cache_store_integration_tests {
1287            use matrix_sdk_test::async_test;
1288            use $crate::event_cache::store::{
1289                EventCacheStoreIntegrationTests, IntoEventCacheStore,
1290            };
1291
1292            use super::get_event_cache_store;
1293
1294            #[async_test]
1295            async fn test_handle_updates_and_rebuild_linked_chunk() {
1296                let event_cache_store =
1297                    get_event_cache_store().await.unwrap().into_event_cache_store();
1298                event_cache_store.test_handle_updates_and_rebuild_linked_chunk().await;
1299            }
1300
1301            #[async_test]
1302            async fn test_linked_chunk_incremental_loading() {
1303                let event_cache_store =
1304                    get_event_cache_store().await.unwrap().into_event_cache_store();
1305                event_cache_store.test_linked_chunk_incremental_loading().await;
1306            }
1307
1308            #[async_test]
1309            async fn test_rebuild_empty_linked_chunk() {
1310                let event_cache_store =
1311                    get_event_cache_store().await.unwrap().into_event_cache_store();
1312                event_cache_store.test_rebuild_empty_linked_chunk().await;
1313            }
1314
1315            #[async_test]
1316            async fn test_load_all_chunks_metadata() {
1317                let event_cache_store =
1318                    get_event_cache_store().await.unwrap().into_event_cache_store();
1319                event_cache_store.test_load_all_chunks_metadata().await;
1320            }
1321
1322            #[async_test]
1323            async fn test_clear_all_linked_chunks() {
1324                let event_cache_store =
1325                    get_event_cache_store().await.unwrap().into_event_cache_store();
1326                event_cache_store.test_clear_all_linked_chunks().await;
1327            }
1328
1329            #[async_test]
1330            async fn test_remove_room() {
1331                let event_cache_store =
1332                    get_event_cache_store().await.unwrap().into_event_cache_store();
1333                event_cache_store.test_remove_room().await;
1334            }
1335
1336            #[async_test]
1337            async fn test_filter_duplicated_events() {
1338                let event_cache_store =
1339                    get_event_cache_store().await.unwrap().into_event_cache_store();
1340                event_cache_store.test_filter_duplicated_events().await;
1341            }
1342
1343            #[async_test]
1344            async fn test_find_event() {
1345                let event_cache_store =
1346                    get_event_cache_store().await.unwrap().into_event_cache_store();
1347                event_cache_store.test_find_event().await;
1348            }
1349
1350            #[async_test]
1351            async fn test_find_event_relations() {
1352                let event_cache_store =
1353                    get_event_cache_store().await.unwrap().into_event_cache_store();
1354                event_cache_store.test_find_event_relations().await;
1355            }
1356
1357            #[async_test]
1358            async fn test_get_room_events() {
1359                let event_cache_store =
1360                    get_event_cache_store().await.unwrap().into_event_cache_store();
1361                event_cache_store.test_get_room_events().await;
1362            }
1363
1364            #[async_test]
1365            async fn test_get_room_events_filtered() {
1366                let event_cache_store =
1367                    get_event_cache_store().await.unwrap().into_event_cache_store();
1368                event_cache_store.test_get_room_events_filtered().await;
1369            }
1370
1371            #[async_test]
1372            async fn test_save_event() {
1373                let event_cache_store =
1374                    get_event_cache_store().await.unwrap().into_event_cache_store();
1375                event_cache_store.test_save_event().await;
1376            }
1377
1378            #[async_test]
1379            async fn test_thread_vs_room_linked_chunk() {
1380                let event_cache_store =
1381                    get_event_cache_store().await.unwrap().into_event_cache_store();
1382                event_cache_store.test_thread_vs_room_linked_chunk().await;
1383            }
1384        }
1385    };
1386}
1387
1388/// Macro generating tests for the event cache store, related to time (mostly
1389/// for the cross-process lock).
1390#[allow(unused_macros)]
1391#[macro_export]
1392macro_rules! event_cache_store_integration_tests_time {
1393    () => {
1394        mod event_cache_store_integration_tests_time {
1395            use std::time::Duration;
1396
1397            #[cfg(all(target_family = "wasm", target_os = "unknown"))]
1398            use gloo_timers::future::sleep;
1399            use matrix_sdk_test::async_test;
1400            #[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
1401            use tokio::time::sleep;
1402            use $crate::event_cache::store::IntoEventCacheStore;
1403
1404            use super::get_event_cache_store;
1405
1406            #[async_test]
1407            async fn test_lease_locks() {
1408                let store = get_event_cache_store().await.unwrap().into_event_cache_store();
1409
1410                let acquired0 = store.try_take_leased_lock(0, "key", "alice").await.unwrap();
1411                assert!(acquired0);
1412
1413                // Should extend the lease automatically (same holder).
1414                let acquired2 = store.try_take_leased_lock(300, "key", "alice").await.unwrap();
1415                assert!(acquired2);
1416
1417                // Should extend the lease automatically (same holder + time is ok).
1418                let acquired3 = store.try_take_leased_lock(300, "key", "alice").await.unwrap();
1419                assert!(acquired3);
1420
1421                // Another attempt at taking the lock should fail, because it's taken.
1422                let acquired4 = store.try_take_leased_lock(300, "key", "bob").await.unwrap();
1423                assert!(!acquired4);
1424
1425                // Even if we insist.
1426                let acquired5 = store.try_take_leased_lock(300, "key", "bob").await.unwrap();
1427                assert!(!acquired5);
1428
1429                // That's a nice test we got here, go take a little nap.
1430                sleep(Duration::from_millis(50)).await;
1431
1432                // Still too early.
1433                let acquired55 = store.try_take_leased_lock(300, "key", "bob").await.unwrap();
1434                assert!(!acquired55);
1435
1436                // Ok you can take another nap then.
1437                sleep(Duration::from_millis(250)).await;
1438
1439                // At some point, we do get the lock.
1440                let acquired6 = store.try_take_leased_lock(0, "key", "bob").await.unwrap();
1441                assert!(acquired6);
1442
1443                sleep(Duration::from_millis(1)).await;
1444
1445                // The other gets it almost immediately too.
1446                let acquired7 = store.try_take_leased_lock(0, "key", "alice").await.unwrap();
1447                assert!(acquired7);
1448
1449                sleep(Duration::from_millis(1)).await;
1450
1451                // But when we take a longer lease...
1452                let acquired8 = store.try_take_leased_lock(300, "key", "bob").await.unwrap();
1453                assert!(acquired8);
1454
1455                // It blocks the other user.
1456                let acquired9 = store.try_take_leased_lock(300, "key", "alice").await.unwrap();
1457                assert!(!acquired9);
1458
1459                // We can hold onto our lease.
1460                let acquired10 = store.try_take_leased_lock(300, "key", "bob").await.unwrap();
1461                assert!(acquired10);
1462            }
1463        }
1464    };
1465}