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