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        forwarder: None,
83        algorithm_info: AlgorithmInfo::MegolmV1AesSha2 {
84            curve25519_key: "1337".to_owned(),
85            sender_claimed_keys: Default::default(),
86            session_id: Some("mysessionid9".to_owned()),
87        },
88        verification_state: VerificationState::Verified,
89    });
90
91    let mut builder = EventFactory::new().text_msg(content).room(room_id).sender(*ALICE);
92    if let Some(event_id) = event_id {
93        builder = builder.event_id(event_id);
94    }
95    let event = builder.into_raw();
96
97    TimelineEvent::from_decrypted(
98        DecryptedRoomEvent { event, encryption_info, unsigned_encryption_info: None },
99        Some(vec![Action::Notify]),
100    )
101}
102
103/// Check that an event created with [`make_test_event`] contains the expected
104/// data.
105///
106/// Keep in sync with [`make_test_event`].
107#[track_caller]
108pub fn check_test_event(event: &TimelineEvent, text: &str) {
109    // Check push actions.
110    let actions = event.push_actions().unwrap();
111    assert_eq!(actions.len(), 1);
112    assert_matches!(&actions[0], Action::Notify);
113
114    // Check content.
115    assert_matches!(&event.kind, TimelineEventKind::Decrypted(d) => {
116        // Check encryption fields.
117        assert_eq!(d.encryption_info.sender, *ALICE);
118        assert_matches!(&d.encryption_info.algorithm_info, AlgorithmInfo::MegolmV1AesSha2 { curve25519_key, .. } => {
119            assert_eq!(curve25519_key, "1337");
120        });
121
122        // Check event.
123        let deserialized = d.event.deserialize().unwrap();
124        assert_matches!(deserialized, AnyTimelineEvent::MessageLike(AnyMessageLikeEvent::RoomMessage(msg)) => {
125            assert_eq!(msg.as_original().unwrap().content.body(), text);
126        });
127    });
128}
129
130/// `EventCacheStore` integration tests.
131///
132/// This trait is not meant to be used directly, but will be used with the
133/// `event_cache_store_integration_tests!` macro.
134#[allow(async_fn_in_trait)]
135pub trait EventCacheStoreIntegrationTests {
136    /// Test handling updates to a linked chunk and reloading these updates from
137    /// the store.
138    async fn test_handle_updates_and_rebuild_linked_chunk(&self);
139
140    /// Test that the next and previous fields only reference chunks that
141    /// already exist in the store.
142    async fn test_linked_chunk_exists_before_referenced(&self);
143
144    /// Test loading the last chunk in a linked chunk from the store.
145    async fn test_load_last_chunk(&self);
146
147    /// Test that cycles are detected when loading the last chunk in a linked
148    /// chunk from the store.
149    async fn test_load_last_chunk_with_a_cycle(&self);
150
151    /// Test loading the previous chunk in a linked chunk from the store.
152    async fn test_load_previous_chunk(&self);
153
154    /// Test loading a linked chunk incrementally (chunk by chunk) from the
155    /// store.
156    async fn test_linked_chunk_incremental_loading(&self);
157
158    /// Test removing a chunk.
159    async fn test_linked_chunk_remove_chunk(&self);
160
161    /// Test replacing an item in a linked chunk.
162    async fn test_linked_chunk_replace_item(&self);
163
164    /// Test remove an item from a linked chunk.
165    async fn test_linked_chunk_remove_item(&self);
166
167    /// Test detaching last items from a linked chunk.
168    async fn test_linked_chunk_detach_last_items(&self);
169
170    /// Test that start reattach and end reattach items does nothing.
171    async fn test_linked_chunk_start_end_reattach_items(&self);
172
173    /// Test clearing a linked chunk.
174    async fn test_linked_chunk_clear(&self);
175
176    /// Test clearing a linked chunk and re-inserting a past event.
177    async fn test_linked_chunk_clear_and_reinsert(&self);
178
179    /// Test that rebuilding a linked chunk from an empty store doesn't return
180    /// anything.
181    async fn test_rebuild_empty_linked_chunk(&self);
182
183    /// Test that linked chunks are only accessible through their enclosing
184    /// room.
185    async fn test_linked_chunk_multiple_rooms(&self);
186
187    /// Test that loading a linked chunk's metadata works as intended.
188    async fn test_load_all_chunks_metadata(&self);
189
190    /// Test that clear all the rooms' linked chunks works.
191    async fn test_clear_all_linked_chunks(&self);
192
193    /// Test that removing a room from storage empties all associated data.
194    async fn test_remove_room(&self);
195
196    /// Test that filtering duplicated events works as expected.
197    async fn test_filter_duplicated_events(&self);
198
199    /// Test that filtering duplicated events works with an empty filter.
200    async fn test_filter_duplicate_events_no_events(&self);
201
202    /// Test that an event can be found or not.
203    async fn test_find_event(&self);
204
205    /// Test that finding event relations works as expected.
206    async fn test_find_event_relations(&self);
207
208    /// Test that getting all events in a room works as expected.
209    async fn test_get_room_events(&self);
210
211    /// Test that getting events in a room of a certain type works as expected.
212    async fn test_get_room_events_filtered(&self);
213
214    /// Test that saving an event works as expected.
215    async fn test_save_event(&self);
216
217    /// Test multiple things related to distinguishing a thread linked chunk
218    /// from a room linked chunk.
219    async fn test_thread_vs_room_linked_chunk(&self);
220}
221
222impl EventCacheStoreIntegrationTests for DynEventCacheStore {
223    async fn test_handle_updates_and_rebuild_linked_chunk(&self) {
224        let room_id = room_id!("!r0:matrix.org");
225        let linked_chunk_id = LinkedChunkId::Room(room_id);
226
227        self.handle_linked_chunk_updates(
228            linked_chunk_id,
229            vec![
230                // new chunk
231                Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
232                // new items on 0
233                Update::PushItems {
234                    at: Position::new(CId::new(0), 0),
235                    items: vec![
236                        make_test_event(room_id, "hello"),
237                        make_test_event(room_id, "world"),
238                    ],
239                },
240                // a gap chunk
241                Update::NewGapChunk {
242                    previous: Some(CId::new(0)),
243                    new: CId::new(1),
244                    next: None,
245                    gap: Gap { prev_token: "parmesan".to_owned() },
246                },
247                // another items chunk
248                Update::NewItemsChunk { previous: Some(CId::new(1)), new: CId::new(2), next: None },
249                // new items on 2
250                Update::PushItems {
251                    at: Position::new(CId::new(2), 0),
252                    items: vec![make_test_event(room_id, "sup")],
253                },
254            ],
255        )
256        .await
257        .unwrap();
258
259        // The linked chunk is correctly reloaded.
260        let lc = lazy_loader::from_all_chunks::<3, _, _>(
261            self.load_all_chunks(linked_chunk_id).await.unwrap(),
262        )
263        .unwrap()
264        .unwrap();
265
266        let mut chunks = lc.chunks();
267
268        {
269            let first = chunks.next().unwrap();
270            // Note: we can't assert the previous/next chunks, as these fields and their
271            // getters are private.
272            assert_eq!(first.identifier(), CId::new(0));
273
274            assert_matches!(first.content(), ChunkContent::Items(events) => {
275                assert_eq!(events.len(), 2);
276                check_test_event(&events[0], "hello");
277                check_test_event(&events[1], "world");
278            });
279        }
280
281        {
282            let second = chunks.next().unwrap();
283            assert_eq!(second.identifier(), CId::new(1));
284
285            assert_matches!(second.content(), ChunkContent::Gap(gap) => {
286                assert_eq!(gap.prev_token, "parmesan");
287            });
288        }
289
290        {
291            let third = chunks.next().unwrap();
292            assert_eq!(third.identifier(), CId::new(2));
293
294            assert_matches!(third.content(), ChunkContent::Items(events) => {
295                assert_eq!(events.len(), 1);
296                check_test_event(&events[0], "sup");
297            });
298        }
299
300        assert!(chunks.next().is_none());
301    }
302
303    async fn test_linked_chunk_exists_before_referenced(&self) {
304        let room_id = *DEFAULT_TEST_ROOM_ID;
305        let linked_chunk_id = LinkedChunkId::Room(room_id);
306
307        // Fails to add the chunk because previous chunk is not in the self
308        self.handle_linked_chunk_updates(
309            linked_chunk_id,
310            vec![Update::NewItemsChunk {
311                previous: Some(CId::new(41)),
312                new: CId::new(42),
313                next: None,
314            }],
315        )
316        .await
317        .unwrap_err();
318
319        // Fails to add the chunk because next chunk is not in the self
320        self.handle_linked_chunk_updates(
321            linked_chunk_id,
322            vec![Update::NewItemsChunk {
323                previous: None,
324                new: CId::new(42),
325                next: Some(CId::new(43)),
326            }],
327        )
328        .await
329        .unwrap_err();
330
331        // Fails to add the chunk because previous chunk is not in the self
332        self.handle_linked_chunk_updates(
333            linked_chunk_id,
334            vec![Update::NewGapChunk {
335                previous: Some(CId::new(41)),
336                new: CId::new(42),
337                next: None,
338                gap: Gap { prev_token: "gap".to_owned() },
339            }],
340        )
341        .await
342        .unwrap_err();
343
344        // Fails to add the chunk because next chunk is not in the self
345        self.handle_linked_chunk_updates(
346            linked_chunk_id,
347            vec![Update::NewGapChunk {
348                previous: None,
349                new: CId::new(42),
350                next: Some(CId::new(43)),
351                gap: Gap { prev_token: "gap".to_owned() },
352            }],
353        )
354        .await
355        .unwrap_err();
356    }
357
358    async fn test_load_all_chunks_metadata(&self) {
359        let room_id = room_id!("!r0:matrix.org");
360        let linked_chunk_id = LinkedChunkId::Room(room_id);
361
362        self.handle_linked_chunk_updates(
363            linked_chunk_id,
364            vec![
365                // new chunk
366                Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
367                // new items on 0
368                Update::PushItems {
369                    at: Position::new(CId::new(0), 0),
370                    items: vec![
371                        make_test_event(room_id, "hello"),
372                        make_test_event(room_id, "world"),
373                    ],
374                },
375                // a gap chunk
376                Update::NewGapChunk {
377                    previous: Some(CId::new(0)),
378                    new: CId::new(1),
379                    next: None,
380                    gap: Gap { prev_token: "parmesan".to_owned() },
381                },
382                // another items chunk
383                Update::NewItemsChunk { previous: Some(CId::new(1)), new: CId::new(2), next: None },
384                // new items on 2
385                Update::PushItems {
386                    at: Position::new(CId::new(2), 0),
387                    items: vec![make_test_event(room_id, "sup")],
388                },
389                // and an empty items chunk to finish
390                Update::NewItemsChunk { previous: Some(CId::new(2)), new: CId::new(3), next: None },
391            ],
392        )
393        .await
394        .unwrap();
395
396        let metas = self.load_all_chunks_metadata(linked_chunk_id).await.unwrap();
397        assert_eq!(metas.len(), 4);
398
399        // The first chunk has two items.
400        assert_eq!(metas[0].identifier, CId::new(0));
401        assert_eq!(metas[0].previous, None);
402        assert_eq!(metas[0].next, Some(CId::new(1)));
403        assert_eq!(metas[0].num_items, 2);
404
405        // The second chunk is a gap, so it has 0 items.
406        assert_eq!(metas[1].identifier, CId::new(1));
407        assert_eq!(metas[1].previous, Some(CId::new(0)));
408        assert_eq!(metas[1].next, Some(CId::new(2)));
409        assert_eq!(metas[1].num_items, 0);
410
411        // The third event chunk has one item.
412        assert_eq!(metas[2].identifier, CId::new(2));
413        assert_eq!(metas[2].previous, Some(CId::new(1)));
414        assert_eq!(metas[2].next, Some(CId::new(3)));
415        assert_eq!(metas[2].num_items, 1);
416
417        // The final event chunk is empty.
418        assert_eq!(metas[3].identifier, CId::new(3));
419        assert_eq!(metas[3].previous, Some(CId::new(2)));
420        assert_eq!(metas[3].next, None);
421        assert_eq!(metas[3].num_items, 0);
422    }
423
424    async fn test_load_last_chunk(&self) {
425        let room_id = room_id!("!r0:matrix.org");
426        let linked_chunk_id = LinkedChunkId::Room(room_id);
427        let event = |msg: &str| make_test_event(room_id, msg);
428
429        // Case #1: no last chunk.
430        {
431            let (last_chunk, chunk_identifier_generator) =
432                self.load_last_chunk(linked_chunk_id).await.unwrap();
433
434            assert!(last_chunk.is_none());
435            assert_eq!(chunk_identifier_generator.current(), 0);
436        }
437
438        // Case #2: only one chunk is present.
439        {
440            self.handle_linked_chunk_updates(
441                linked_chunk_id,
442                vec![
443                    Update::NewItemsChunk { previous: None, new: CId::new(42), next: None },
444                    Update::PushItems {
445                        at: Position::new(CId::new(42), 0),
446                        items: vec![event("saucisse de morteau"), event("comté")],
447                    },
448                ],
449            )
450            .await
451            .unwrap();
452
453            let (last_chunk, chunk_identifier_generator) =
454                self.load_last_chunk(linked_chunk_id).await.unwrap();
455
456            assert_matches!(last_chunk, Some(last_chunk) => {
457                assert_eq!(last_chunk.identifier, 42);
458                assert!(last_chunk.previous.is_none());
459                assert!(last_chunk.next.is_none());
460                assert_matches!(last_chunk.content, ChunkContent::Items(items) => {
461                    assert_eq!(items.len(), 2);
462                    check_test_event(&items[0], "saucisse de morteau");
463                    check_test_event(&items[1], "comté");
464                });
465            });
466            assert_eq!(chunk_identifier_generator.current(), 42);
467        }
468
469        // Case #3: more chunks are present.
470        {
471            self.handle_linked_chunk_updates(
472                linked_chunk_id,
473                vec![
474                    Update::NewItemsChunk {
475                        previous: Some(CId::new(42)),
476                        new: CId::new(7),
477                        next: None,
478                    },
479                    Update::PushItems {
480                        at: Position::new(CId::new(7), 0),
481                        items: vec![event("fondue"), event("gruyère"), event("mont d'or")],
482                    },
483                ],
484            )
485            .await
486            .unwrap();
487
488            let (last_chunk, chunk_identifier_generator) =
489                self.load_last_chunk(linked_chunk_id).await.unwrap();
490
491            assert_matches!(last_chunk, Some(last_chunk) => {
492                assert_eq!(last_chunk.identifier, 7);
493                assert_matches!(last_chunk.previous, Some(previous) => {
494                    assert_eq!(previous, 42);
495                });
496                assert!(last_chunk.next.is_none());
497                assert_matches!(last_chunk.content, ChunkContent::Items(items) => {
498                    assert_eq!(items.len(), 3);
499                    check_test_event(&items[0], "fondue");
500                    check_test_event(&items[1], "gruyère");
501                    check_test_event(&items[2], "mont d'or");
502                });
503            });
504            assert_eq!(chunk_identifier_generator.current(), 42);
505        }
506    }
507
508    async fn test_load_last_chunk_with_a_cycle(&self) {
509        let room_id = room_id!("!r0:matrix.org");
510        let linked_chunk_id = LinkedChunkId::Room(room_id);
511
512        self.handle_linked_chunk_updates(
513            linked_chunk_id,
514            vec![
515                Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
516                Update::NewItemsChunk {
517                    // Because `previous` connects to chunk #0, it will create a cycle.
518                    // Chunk #0 will have a `next` set to chunk #1! Consequently, the last chunk
519                    // **does not exist**. We have to detect this cycle.
520                    previous: Some(CId::new(0)),
521                    new: CId::new(1),
522                    next: Some(CId::new(0)),
523                },
524            ],
525        )
526        .await
527        .unwrap();
528
529        self.load_last_chunk(linked_chunk_id).await.unwrap_err();
530    }
531
532    async fn test_load_previous_chunk(&self) {
533        let room_id = room_id!("!r0:matrix.org");
534        let linked_chunk_id = LinkedChunkId::Room(room_id);
535        let event = |msg: &str| make_test_event(room_id, msg);
536
537        // Case #1: no chunk at all, equivalent to having an nonexistent
538        // `before_chunk_identifier`.
539        {
540            let previous_chunk =
541                self.load_previous_chunk(linked_chunk_id, CId::new(153)).await.unwrap();
542
543            assert!(previous_chunk.is_none());
544        }
545
546        // Case #2: there is one chunk only: we request the previous on this
547        // one, it doesn't exist.
548        {
549            self.handle_linked_chunk_updates(
550                linked_chunk_id,
551                vec![Update::NewItemsChunk { previous: None, new: CId::new(42), next: None }],
552            )
553            .await
554            .unwrap();
555
556            let previous_chunk =
557                self.load_previous_chunk(linked_chunk_id, CId::new(42)).await.unwrap();
558
559            assert!(previous_chunk.is_none());
560        }
561
562        // Case #3: there are two chunks.
563        {
564            self.handle_linked_chunk_updates(
565                linked_chunk_id,
566                vec![
567                    // new chunk before the one that exists.
568                    Update::NewItemsChunk {
569                        previous: None,
570                        new: CId::new(7),
571                        next: Some(CId::new(42)),
572                    },
573                    Update::PushItems {
574                        at: Position::new(CId::new(7), 0),
575                        items: vec![event("brigand du jorat"), event("morbier")],
576                    },
577                ],
578            )
579            .await
580            .unwrap();
581
582            let previous_chunk =
583                self.load_previous_chunk(linked_chunk_id, CId::new(42)).await.unwrap();
584
585            assert_matches!(previous_chunk, Some(previous_chunk) => {
586                assert_eq!(previous_chunk.identifier, 7);
587                assert!(previous_chunk.previous.is_none());
588                assert_matches!(previous_chunk.next, Some(next) => {
589                    assert_eq!(next, 42);
590                });
591                assert_matches!(previous_chunk.content, ChunkContent::Items(items) => {
592                    assert_eq!(items.len(), 2);
593                    check_test_event(&items[0], "brigand du jorat");
594                    check_test_event(&items[1], "morbier");
595                });
596            });
597        }
598    }
599
600    async fn test_linked_chunk_incremental_loading(&self) {
601        let room_id = room_id!("!r0:matrix.org");
602        let linked_chunk_id = LinkedChunkId::Room(room_id);
603        let event = |msg: &str| make_test_event(room_id, msg);
604
605        // Load the last chunk, but none exists yet.
606        {
607            let (last_chunk, chunk_identifier_generator) =
608                self.load_last_chunk(linked_chunk_id).await.unwrap();
609
610            assert!(last_chunk.is_none());
611            assert_eq!(chunk_identifier_generator.current(), 0);
612        }
613
614        self.handle_linked_chunk_updates(
615            linked_chunk_id,
616            vec![
617                // new chunk for items
618                Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
619                // new items on 0
620                Update::PushItems {
621                    at: Position::new(CId::new(0), 0),
622                    items: vec![event("a"), event("b")],
623                },
624                // new chunk for a gap
625                Update::NewGapChunk {
626                    previous: Some(CId::new(0)),
627                    new: CId::new(1),
628                    next: None,
629                    gap: Gap { prev_token: "morbier".to_owned() },
630                },
631                // new chunk for items
632                Update::NewItemsChunk { previous: Some(CId::new(1)), new: CId::new(2), next: None },
633                // new items on 2
634                Update::PushItems {
635                    at: Position::new(CId::new(2), 0),
636                    items: vec![event("c"), event("d"), event("e")],
637                },
638            ],
639        )
640        .await
641        .unwrap();
642
643        // Load the last chunk.
644        let mut linked_chunk = {
645            let (last_chunk, chunk_identifier_generator) =
646                self.load_last_chunk(linked_chunk_id).await.unwrap();
647
648            assert_eq!(chunk_identifier_generator.current(), 2);
649
650            let linked_chunk = lazy_loader::from_last_chunk::<DEFAULT_CHUNK_CAPACITY, _, _>(
651                last_chunk,
652                chunk_identifier_generator,
653            )
654            .unwrap() // unwrap the `Result`
655            .unwrap(); // unwrap the `Option`
656
657            let mut rchunks = linked_chunk.rchunks();
658
659            // A unique chunk.
660            assert_matches!(rchunks.next(), Some(chunk) => {
661                assert_eq!(chunk.identifier(), 2);
662                assert_eq!(chunk.lazy_previous(), Some(CId::new(1)));
663
664                assert_matches!(chunk.content(), ChunkContent::Items(events) => {
665                    assert_eq!(events.len(), 3);
666                    check_test_event(&events[0], "c");
667                    check_test_event(&events[1], "d");
668                    check_test_event(&events[2], "e");
669                });
670            });
671
672            assert!(rchunks.next().is_none());
673
674            linked_chunk
675        };
676
677        // Load the previous chunk: this is a gap.
678        {
679            let first_chunk = linked_chunk.chunks().next().unwrap().identifier();
680            let previous_chunk =
681                self.load_previous_chunk(linked_chunk_id, first_chunk).await.unwrap().unwrap();
682
683            lazy_loader::insert_new_first_chunk(&mut linked_chunk, previous_chunk).unwrap();
684
685            let mut rchunks = linked_chunk.rchunks();
686
687            // The last chunk.
688            assert_matches!(rchunks.next(), Some(chunk) => {
689                assert_eq!(chunk.identifier(), 2);
690                assert!(chunk.lazy_previous().is_none());
691
692                // Already asserted, but let's be sure nothing breaks.
693                assert_matches!(chunk.content(), ChunkContent::Items(events) => {
694                    assert_eq!(events.len(), 3);
695                    check_test_event(&events[0], "c");
696                    check_test_event(&events[1], "d");
697                    check_test_event(&events[2], "e");
698                });
699            });
700
701            // The new chunk.
702            assert_matches!(rchunks.next(), Some(chunk) => {
703                assert_eq!(chunk.identifier(), 1);
704                assert_eq!(chunk.lazy_previous(), Some(CId::new(0)));
705
706                assert_matches!(chunk.content(), ChunkContent::Gap(gap) => {
707                    assert_eq!(gap.prev_token, "morbier");
708                });
709            });
710
711            assert!(rchunks.next().is_none());
712        }
713
714        // Load the previous chunk: these are items.
715        {
716            let first_chunk = linked_chunk.chunks().next().unwrap().identifier();
717            let previous_chunk =
718                self.load_previous_chunk(linked_chunk_id, first_chunk).await.unwrap().unwrap();
719
720            lazy_loader::insert_new_first_chunk(&mut linked_chunk, previous_chunk).unwrap();
721
722            let mut rchunks = linked_chunk.rchunks();
723
724            // The last chunk.
725            assert_matches!(rchunks.next(), Some(chunk) => {
726                assert_eq!(chunk.identifier(), 2);
727                assert!(chunk.lazy_previous().is_none());
728
729                // Already asserted, but let's be sure nothing breaks.
730                assert_matches!(chunk.content(), ChunkContent::Items(events) => {
731                    assert_eq!(events.len(), 3);
732                    check_test_event(&events[0], "c");
733                    check_test_event(&events[1], "d");
734                    check_test_event(&events[2], "e");
735                });
736            });
737
738            // Its previous chunk.
739            assert_matches!(rchunks.next(), Some(chunk) => {
740                assert_eq!(chunk.identifier(), 1);
741                assert!(chunk.lazy_previous().is_none());
742
743                // Already asserted, but let's be sure nothing breaks.
744                assert_matches!(chunk.content(), ChunkContent::Gap(gap) => {
745                    assert_eq!(gap.prev_token, "morbier");
746                });
747            });
748
749            // The new chunk.
750            assert_matches!(rchunks.next(), Some(chunk) => {
751                assert_eq!(chunk.identifier(), 0);
752                assert!(chunk.lazy_previous().is_none());
753
754                assert_matches!(chunk.content(), ChunkContent::Items(events) => {
755                    assert_eq!(events.len(), 2);
756                    check_test_event(&events[0], "a");
757                    check_test_event(&events[1], "b");
758                });
759            });
760
761            assert!(rchunks.next().is_none());
762        }
763
764        // Load the previous chunk: there is none.
765        {
766            let first_chunk = linked_chunk.chunks().next().unwrap().identifier();
767            let previous_chunk =
768                self.load_previous_chunk(linked_chunk_id, first_chunk).await.unwrap();
769
770            assert!(previous_chunk.is_none());
771        }
772
773        // One last check: a round of assert by using the forwards chunk iterator
774        // instead of the backwards chunk iterator.
775        {
776            let mut chunks = linked_chunk.chunks();
777
778            // The first chunk.
779            assert_matches!(chunks.next(), Some(chunk) => {
780                assert_eq!(chunk.identifier(), 0);
781                assert!(chunk.lazy_previous().is_none());
782
783                assert_matches!(chunk.content(), ChunkContent::Items(events) => {
784                    assert_eq!(events.len(), 2);
785                    check_test_event(&events[0], "a");
786                    check_test_event(&events[1], "b");
787                });
788            });
789
790            // The second chunk.
791            assert_matches!(chunks.next(), Some(chunk) => {
792                assert_eq!(chunk.identifier(), 1);
793                assert!(chunk.lazy_previous().is_none());
794
795                assert_matches!(chunk.content(), ChunkContent::Gap(gap) => {
796                    assert_eq!(gap.prev_token, "morbier");
797                });
798            });
799
800            // The third and last chunk.
801            assert_matches!(chunks.next(), Some(chunk) => {
802                assert_eq!(chunk.identifier(), 2);
803                assert!(chunk.lazy_previous().is_none());
804
805                assert_matches!(chunk.content(), ChunkContent::Items(events) => {
806                    assert_eq!(events.len(), 3);
807                    check_test_event(&events[0], "c");
808                    check_test_event(&events[1], "d");
809                    check_test_event(&events[2], "e");
810                });
811            });
812
813            assert!(chunks.next().is_none());
814        }
815    }
816
817    async fn test_linked_chunk_remove_chunk(&self) {
818        let room_id = &DEFAULT_TEST_ROOM_ID;
819        let linked_chunk_id = LinkedChunkId::Room(room_id);
820
821        self.handle_linked_chunk_updates(
822            linked_chunk_id,
823            vec![
824                Update::NewGapChunk {
825                    previous: None,
826                    new: CId::new(42),
827                    next: None,
828                    gap: Gap { prev_token: "raclette".to_owned() },
829                },
830                Update::NewGapChunk {
831                    previous: Some(CId::new(42)),
832                    new: CId::new(43),
833                    next: None,
834                    gap: Gap { prev_token: "fondue".to_owned() },
835                },
836                Update::NewGapChunk {
837                    previous: Some(CId::new(43)),
838                    new: CId::new(44),
839                    next: None,
840                    gap: Gap { prev_token: "tartiflette".to_owned() },
841                },
842                Update::RemoveChunk(CId::new(43)),
843            ],
844        )
845        .await
846        .unwrap();
847
848        let mut chunks = self.load_all_chunks(linked_chunk_id).await.unwrap();
849
850        assert_eq!(chunks.len(), 2);
851
852        // Chunks are ordered from smaller to bigger IDs.
853        let c = chunks.remove(0);
854        assert_eq!(c.identifier, CId::new(42));
855        assert_eq!(c.previous, None);
856        assert_eq!(c.next, Some(CId::new(44)));
857        assert_matches!(c.content, ChunkContent::Gap(gap) => {
858            assert_eq!(gap.prev_token, "raclette");
859        });
860
861        let c = chunks.remove(0);
862        assert_eq!(c.identifier, CId::new(44));
863        assert_eq!(c.previous, Some(CId::new(42)));
864        assert_eq!(c.next, None);
865        assert_matches!(c.content, ChunkContent::Gap(gap) => {
866            assert_eq!(gap.prev_token, "tartiflette");
867        });
868    }
869
870    async fn test_linked_chunk_replace_item(&self) {
871        let room_id = &DEFAULT_TEST_ROOM_ID;
872        let linked_chunk_id = LinkedChunkId::Room(room_id);
873        let event_id = event_id!("$world");
874
875        self.handle_linked_chunk_updates(
876            linked_chunk_id,
877            vec![
878                Update::NewItemsChunk { previous: None, new: CId::new(42), next: None },
879                Update::PushItems {
880                    at: Position::new(CId::new(42), 0),
881                    items: vec![
882                        make_test_event(room_id, "hello"),
883                        make_test_event_with_event_id(room_id, "world", Some(event_id)),
884                    ],
885                },
886                Update::ReplaceItem {
887                    at: Position::new(CId::new(42), 1),
888                    item: make_test_event_with_event_id(room_id, "yolo", Some(event_id)),
889                },
890            ],
891        )
892        .await
893        .unwrap();
894
895        let mut chunks = self.load_all_chunks(linked_chunk_id).await.unwrap();
896
897        assert_eq!(chunks.len(), 1);
898
899        let c = chunks.remove(0);
900        assert_eq!(c.identifier, CId::new(42));
901        assert_eq!(c.previous, None);
902        assert_eq!(c.next, None);
903        assert_matches!(c.content, ChunkContent::Items(events) => {
904            assert_eq!(events.len(), 2);
905            check_test_event(&events[0], "hello");
906            check_test_event(&events[1], "yolo");
907        });
908    }
909
910    async fn test_linked_chunk_remove_item(&self) {
911        let room_id = *DEFAULT_TEST_ROOM_ID;
912        let linked_chunk_id = LinkedChunkId::Room(room_id);
913
914        self.handle_linked_chunk_updates(
915            linked_chunk_id,
916            vec![
917                Update::NewItemsChunk { previous: None, new: CId::new(42), next: None },
918                Update::PushItems {
919                    at: Position::new(CId::new(42), 0),
920                    items: vec![
921                        make_test_event(room_id, "one"),
922                        make_test_event(room_id, "two"),
923                        make_test_event(room_id, "three"),
924                        make_test_event(room_id, "four"),
925                        make_test_event(room_id, "five"),
926                        make_test_event(room_id, "six"),
927                    ],
928                },
929                Update::RemoveItem { at: Position::new(CId::new(42), 2) /* "three" */ },
930            ],
931        )
932        .await
933        .unwrap();
934
935        let mut chunks = self.load_all_chunks(linked_chunk_id).await.unwrap();
936
937        assert_eq!(chunks.len(), 1);
938
939        let c = chunks.remove(0);
940        assert_eq!(c.identifier, CId::new(42));
941        assert_eq!(c.previous, None);
942        assert_eq!(c.next, None);
943        assert_matches!(c.content, ChunkContent::Items(events) => {
944            assert_eq!(events.len(), 5);
945            check_test_event(&events[0], "one");
946            check_test_event(&events[1], "two");
947            check_test_event(&events[2], "four");
948            check_test_event(&events[3], "five");
949            check_test_event(&events[4], "six");
950        });
951    }
952
953    async fn test_linked_chunk_detach_last_items(&self) {
954        let room_id = *DEFAULT_TEST_ROOM_ID;
955        let linked_chunk_id = LinkedChunkId::Room(room_id);
956
957        self.handle_linked_chunk_updates(
958            linked_chunk_id,
959            vec![
960                Update::NewItemsChunk { previous: None, new: CId::new(42), next: None },
961                Update::PushItems {
962                    at: Position::new(CId::new(42), 0),
963                    items: vec![
964                        make_test_event(room_id, "hello"),
965                        make_test_event(room_id, "world"),
966                        make_test_event(room_id, "howdy"),
967                    ],
968                },
969                Update::DetachLastItems { at: Position::new(CId::new(42), 1) },
970            ],
971        )
972        .await
973        .unwrap();
974
975        let mut chunks = self.load_all_chunks(linked_chunk_id).await.unwrap();
976
977        assert_eq!(chunks.len(), 1);
978
979        let c = chunks.remove(0);
980        assert_eq!(c.identifier, CId::new(42));
981        assert_eq!(c.previous, None);
982        assert_eq!(c.next, None);
983        assert_matches!(c.content, ChunkContent::Items(events) => {
984            assert_eq!(events.len(), 1);
985            check_test_event(&events[0], "hello");
986        });
987    }
988
989    async fn test_linked_chunk_start_end_reattach_items(&self) {
990        let room_id = *DEFAULT_TEST_ROOM_ID;
991        let linked_chunk_id = LinkedChunkId::Room(room_id);
992
993        // Same updates and checks as test_linked_chunk_push_items, but with extra
994        // `StartReattachItems` and `EndReattachItems` updates, which must have no
995        // effects.
996        self.handle_linked_chunk_updates(
997            linked_chunk_id,
998            vec![
999                Update::NewItemsChunk { previous: None, new: CId::new(42), next: None },
1000                Update::PushItems {
1001                    at: Position::new(CId::new(42), 0),
1002                    items: vec![
1003                        make_test_event(room_id, "hello"),
1004                        make_test_event(room_id, "world"),
1005                        make_test_event(room_id, "howdy"),
1006                    ],
1007                },
1008                Update::StartReattachItems,
1009                Update::EndReattachItems,
1010            ],
1011        )
1012        .await
1013        .unwrap();
1014
1015        let mut chunks = self.load_all_chunks(linked_chunk_id).await.unwrap();
1016
1017        assert_eq!(chunks.len(), 1);
1018
1019        let c = chunks.remove(0);
1020        assert_eq!(c.identifier, CId::new(42));
1021        assert_eq!(c.previous, None);
1022        assert_eq!(c.next, None);
1023        assert_matches!(c.content, ChunkContent::Items(events) => {
1024            assert_eq!(events.len(), 3);
1025            check_test_event(&events[0], "hello");
1026            check_test_event(&events[1], "world");
1027            check_test_event(&events[2], "howdy");
1028        });
1029    }
1030
1031    async fn test_linked_chunk_clear(&self) {
1032        let room_id = *DEFAULT_TEST_ROOM_ID;
1033        let linked_chunk_id = LinkedChunkId::Room(room_id);
1034        let event_0 = make_test_event(room_id, "hello");
1035        let event_1 = make_test_event(room_id, "world");
1036        let event_2 = make_test_event(room_id, "howdy");
1037
1038        self.handle_linked_chunk_updates(
1039            linked_chunk_id,
1040            vec![
1041                Update::NewItemsChunk { previous: None, new: CId::new(42), next: None },
1042                Update::NewGapChunk {
1043                    previous: Some(CId::new(42)),
1044                    new: CId::new(54),
1045                    next: None,
1046                    gap: Gap { prev_token: "fondue".to_owned() },
1047                },
1048                Update::PushItems {
1049                    at: Position::new(CId::new(42), 0),
1050                    items: vec![event_0.clone(), event_1, event_2],
1051                },
1052                Update::Clear,
1053            ],
1054        )
1055        .await
1056        .unwrap();
1057
1058        let chunks = self.load_all_chunks(linked_chunk_id).await.unwrap();
1059        assert!(chunks.is_empty());
1060    }
1061
1062    async fn test_linked_chunk_clear_and_reinsert(&self) {
1063        let room_id = *DEFAULT_TEST_ROOM_ID;
1064        let linked_chunk_id = LinkedChunkId::Room(room_id);
1065        let event_0 = make_test_event(room_id, "hello");
1066        let event_1 = make_test_event(room_id, "world");
1067        let event_2 = make_test_event(room_id, "howdy");
1068
1069        self.handle_linked_chunk_updates(
1070            linked_chunk_id,
1071            vec![
1072                Update::NewItemsChunk { previous: None, new: CId::new(42), next: None },
1073                Update::NewGapChunk {
1074                    previous: Some(CId::new(42)),
1075                    new: CId::new(54),
1076                    next: None,
1077                    gap: Gap { prev_token: "fondue".to_owned() },
1078                },
1079                Update::PushItems {
1080                    at: Position::new(CId::new(42), 0),
1081                    items: vec![event_0.clone(), event_1, event_2],
1082                },
1083                Update::Clear,
1084            ],
1085        )
1086        .await
1087        .unwrap();
1088
1089        let chunks = self.load_all_chunks(linked_chunk_id).await.unwrap();
1090        assert!(chunks.is_empty());
1091
1092        // It's okay to re-insert a past event.
1093        self.handle_linked_chunk_updates(
1094            linked_chunk_id,
1095            vec![
1096                Update::NewItemsChunk { previous: None, new: CId::new(42), next: None },
1097                Update::PushItems { at: Position::new(CId::new(42), 0), items: vec![event_0] },
1098            ],
1099        )
1100        .await
1101        .unwrap();
1102    }
1103
1104    async fn test_rebuild_empty_linked_chunk(&self) {
1105        // When I rebuild a linked chunk from an empty store, it's empty.
1106        let linked_chunk = lazy_loader::from_all_chunks::<3, _, _>(
1107            self.load_all_chunks(LinkedChunkId::Room(&DEFAULT_TEST_ROOM_ID)).await.unwrap(),
1108        )
1109        .unwrap();
1110        assert!(linked_chunk.is_none());
1111    }
1112
1113    async fn test_linked_chunk_multiple_rooms(&self) {
1114        let room1 = room_id!("!realcheeselovers:raclette.fr");
1115        let linked_chunk_id1 = LinkedChunkId::Room(room1);
1116        let room2 = room_id!("!realcheeselovers:fondue.ch");
1117        let linked_chunk_id2 = LinkedChunkId::Room(room2);
1118
1119        // Check that applying updates to one room doesn't affect the others.
1120        // Use the same chunk identifier in both rooms to battle-test search.
1121
1122        self.handle_linked_chunk_updates(
1123            linked_chunk_id1,
1124            vec![
1125                Update::NewItemsChunk { previous: None, new: CId::new(42), next: None },
1126                Update::PushItems {
1127                    at: Position::new(CId::new(42), 0),
1128                    items: vec![
1129                        make_test_event(room1, "best cheese is raclette"),
1130                        make_test_event(room1, "obviously"),
1131                    ],
1132                },
1133            ],
1134        )
1135        .await
1136        .unwrap();
1137
1138        self.handle_linked_chunk_updates(
1139            linked_chunk_id2,
1140            vec![
1141                Update::NewItemsChunk { previous: None, new: CId::new(42), next: None },
1142                Update::PushItems {
1143                    at: Position::new(CId::new(42), 0),
1144                    items: vec![make_test_event(room1, "beaufort is the best")],
1145                },
1146            ],
1147        )
1148        .await
1149        .unwrap();
1150
1151        // Check chunks from room 1.
1152        let mut chunks_room1 = self.load_all_chunks(linked_chunk_id1).await.unwrap();
1153        assert_eq!(chunks_room1.len(), 1);
1154
1155        let c = chunks_room1.remove(0);
1156        assert_matches!(c.content, ChunkContent::Items(events) => {
1157            assert_eq!(events.len(), 2);
1158            check_test_event(&events[0], "best cheese is raclette");
1159            check_test_event(&events[1], "obviously");
1160        });
1161
1162        // Check chunks from room 2.
1163        let mut chunks_room2 = self.load_all_chunks(linked_chunk_id2).await.unwrap();
1164        assert_eq!(chunks_room2.len(), 1);
1165
1166        let c = chunks_room2.remove(0);
1167        assert_matches!(c.content, ChunkContent::Items(events) => {
1168            assert_eq!(events.len(), 1);
1169            check_test_event(&events[0], "beaufort is the best");
1170        });
1171    }
1172
1173    async fn test_clear_all_linked_chunks(&self) {
1174        let r0 = room_id!("!r0:matrix.org");
1175        let linked_chunk_id0 = LinkedChunkId::Room(r0);
1176        let r1 = room_id!("!r1:matrix.org");
1177        let linked_chunk_id1 = LinkedChunkId::Room(r1);
1178
1179        // Add updates for the first room.
1180        self.handle_linked_chunk_updates(
1181            linked_chunk_id0,
1182            vec![
1183                // new chunk
1184                Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
1185                // new items on 0
1186                Update::PushItems {
1187                    at: Position::new(CId::new(0), 0),
1188                    items: vec![make_test_event(r0, "hello"), make_test_event(r0, "world")],
1189                },
1190            ],
1191        )
1192        .await
1193        .unwrap();
1194
1195        // Add updates for the second room.
1196        self.handle_linked_chunk_updates(
1197            linked_chunk_id1,
1198            vec![
1199                // Empty items chunk.
1200                Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
1201                // a gap chunk
1202                Update::NewGapChunk {
1203                    previous: Some(CId::new(0)),
1204                    new: CId::new(1),
1205                    next: None,
1206                    gap: Gap { prev_token: "bleu d'auvergne".to_owned() },
1207                },
1208                // another items chunk
1209                Update::NewItemsChunk { previous: Some(CId::new(1)), new: CId::new(2), next: None },
1210                // new items on 0
1211                Update::PushItems {
1212                    at: Position::new(CId::new(2), 0),
1213                    items: vec![make_test_event(r0, "yummy")],
1214                },
1215            ],
1216        )
1217        .await
1218        .unwrap();
1219
1220        // Sanity check: both linked chunks can be reloaded.
1221        assert!(
1222            lazy_loader::from_all_chunks::<3, _, _>(
1223                self.load_all_chunks(linked_chunk_id0).await.unwrap()
1224            )
1225            .unwrap()
1226            .is_some()
1227        );
1228        assert!(
1229            lazy_loader::from_all_chunks::<3, _, _>(
1230                self.load_all_chunks(linked_chunk_id1).await.unwrap()
1231            )
1232            .unwrap()
1233            .is_some()
1234        );
1235
1236        // Clear the chunks.
1237        self.clear_all_linked_chunks().await.unwrap();
1238
1239        // Both rooms now have no linked chunk.
1240        assert!(
1241            lazy_loader::from_all_chunks::<3, _, _>(
1242                self.load_all_chunks(linked_chunk_id0).await.unwrap()
1243            )
1244            .unwrap()
1245            .is_none()
1246        );
1247        assert!(
1248            lazy_loader::from_all_chunks::<3, _, _>(
1249                self.load_all_chunks(linked_chunk_id1).await.unwrap()
1250            )
1251            .unwrap()
1252            .is_none()
1253        );
1254    }
1255
1256    async fn test_remove_room(&self) {
1257        let r0 = room_id!("!r0:matrix.org");
1258        let linked_chunk_id0 = LinkedChunkId::Room(r0);
1259        let r1 = room_id!("!r1:matrix.org");
1260        let linked_chunk_id1 = LinkedChunkId::Room(r1);
1261
1262        // Add updates to the first room.
1263        self.handle_linked_chunk_updates(
1264            linked_chunk_id0,
1265            vec![
1266                // new chunk
1267                Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
1268                // new items on 0
1269                Update::PushItems {
1270                    at: Position::new(CId::new(0), 0),
1271                    items: vec![make_test_event(r0, "hello"), make_test_event(r0, "world")],
1272                },
1273            ],
1274        )
1275        .await
1276        .unwrap();
1277
1278        // Add updates to the second room.
1279        self.handle_linked_chunk_updates(
1280            linked_chunk_id1,
1281            vec![
1282                // new chunk
1283                Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
1284                // new items on 0
1285                Update::PushItems {
1286                    at: Position::new(CId::new(0), 0),
1287                    items: vec![make_test_event(r0, "yummy")],
1288                },
1289            ],
1290        )
1291        .await
1292        .unwrap();
1293
1294        // Try to remove content from r0.
1295        self.remove_room(r0).await.unwrap();
1296
1297        // Check that r0 doesn't have a linked chunk anymore.
1298        let r0_linked_chunk = self.load_all_chunks(linked_chunk_id0).await.unwrap();
1299        assert!(r0_linked_chunk.is_empty());
1300
1301        // Check that r1 is unaffected.
1302        let r1_linked_chunk = self.load_all_chunks(linked_chunk_id1).await.unwrap();
1303        assert!(!r1_linked_chunk.is_empty());
1304    }
1305
1306    async fn test_filter_duplicated_events(&self) {
1307        let room_id = room_id!("!r0:matrix.org");
1308        let linked_chunk_id = LinkedChunkId::Room(room_id);
1309        let another_room_id = room_id!("!r1:matrix.org");
1310        let another_linked_chunk_id = LinkedChunkId::Room(another_room_id);
1311        let event = |msg: &str| make_test_event(room_id, msg);
1312
1313        let event_comte = event("comté");
1314        let event_brigand = event("brigand du jorat");
1315        let event_raclette = event("raclette");
1316        let event_morbier = event("morbier");
1317        let event_gruyere = event("gruyère");
1318        let event_tome = event("tome");
1319        let event_mont_dor = event("mont d'or");
1320
1321        self.handle_linked_chunk_updates(
1322            linked_chunk_id,
1323            vec![
1324                Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
1325                Update::PushItems {
1326                    at: Position::new(CId::new(0), 0),
1327                    items: vec![event_comte.clone(), event_brigand.clone()],
1328                },
1329                Update::NewGapChunk {
1330                    previous: Some(CId::new(0)),
1331                    new: CId::new(1),
1332                    next: None,
1333                    gap: Gap { prev_token: "brillat-savarin".to_owned() },
1334                },
1335                Update::NewItemsChunk { previous: Some(CId::new(1)), new: CId::new(2), next: None },
1336                Update::PushItems {
1337                    at: Position::new(CId::new(2), 0),
1338                    items: vec![event_morbier.clone(), event_mont_dor.clone()],
1339                },
1340            ],
1341        )
1342        .await
1343        .unwrap();
1344
1345        // Add other events in another room, to ensure filtering take the `room_id` into
1346        // account.
1347        self.handle_linked_chunk_updates(
1348            another_linked_chunk_id,
1349            vec![
1350                Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
1351                Update::PushItems {
1352                    at: Position::new(CId::new(0), 0),
1353                    items: vec![event_tome.clone()],
1354                },
1355            ],
1356        )
1357        .await
1358        .unwrap();
1359
1360        let duplicated_events = BTreeMap::from_iter(
1361            self.filter_duplicated_events(
1362                linked_chunk_id,
1363                vec![
1364                    event_comte.event_id().unwrap().to_owned(),
1365                    event_raclette.event_id().unwrap().to_owned(),
1366                    event_morbier.event_id().unwrap().to_owned(),
1367                    event_gruyere.event_id().unwrap().to_owned(),
1368                    event_tome.event_id().unwrap().to_owned(),
1369                    event_mont_dor.event_id().unwrap().to_owned(),
1370                ],
1371            )
1372            .await
1373            .unwrap(),
1374        );
1375
1376        assert_eq!(duplicated_events.len(), 3);
1377
1378        assert_eq!(
1379            *duplicated_events.get(&event_comte.event_id().unwrap()).unwrap(),
1380            Position::new(CId::new(0), 0)
1381        );
1382        assert_eq!(
1383            *duplicated_events.get(&event_morbier.event_id().unwrap()).unwrap(),
1384            Position::new(CId::new(2), 0)
1385        );
1386        assert_eq!(
1387            *duplicated_events.get(&event_mont_dor.event_id().unwrap()).unwrap(),
1388            Position::new(CId::new(2), 1)
1389        );
1390    }
1391
1392    async fn test_filter_duplicate_events_no_events(&self) {
1393        let room_id = *DEFAULT_TEST_ROOM_ID;
1394        let linked_chunk_id = LinkedChunkId::Room(room_id);
1395        let duplicates = self.filter_duplicated_events(linked_chunk_id, Vec::new()).await.unwrap();
1396        assert!(duplicates.is_empty());
1397    }
1398
1399    async fn test_find_event(&self) {
1400        let room_id = room_id!("!r0:matrix.org");
1401        let another_room_id = room_id!("!r1:matrix.org");
1402        let another_linked_chunk_id = LinkedChunkId::Room(another_room_id);
1403        let event = |msg: &str| make_test_event(room_id, msg);
1404
1405        let event_comte = event("comté");
1406        let event_gruyere = event("gruyère");
1407
1408        // Add one event in one room.
1409        self.handle_linked_chunk_updates(
1410            LinkedChunkId::Room(room_id),
1411            vec![
1412                Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
1413                Update::PushItems {
1414                    at: Position::new(CId::new(0), 0),
1415                    items: vec![event_comte.clone()],
1416                },
1417            ],
1418        )
1419        .await
1420        .unwrap();
1421
1422        // Add another event in another room.
1423        self.handle_linked_chunk_updates(
1424            another_linked_chunk_id,
1425            vec![
1426                Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
1427                Update::PushItems {
1428                    at: Position::new(CId::new(0), 0),
1429                    items: vec![event_gruyere.clone()],
1430                },
1431            ],
1432        )
1433        .await
1434        .unwrap();
1435
1436        // Now let's find the event.
1437        let event = self
1438            .find_event(room_id, event_comte.event_id().unwrap().as_ref())
1439            .await
1440            .expect("failed to query for finding an event")
1441            .expect("failed to find an event");
1442
1443        assert_eq!(event.event_id(), event_comte.event_id());
1444
1445        // Now let's try to find an event that exists, but not in the expected room.
1446        assert!(
1447            self.find_event(room_id, event_gruyere.event_id().unwrap().as_ref())
1448                .await
1449                .expect("failed to query for finding an event")
1450                .is_none()
1451        );
1452
1453        // Clearing the rooms also clears the event's storage.
1454        self.clear_all_linked_chunks().await.expect("failed to clear all rooms chunks");
1455        assert!(
1456            self.find_event(room_id, event_comte.event_id().unwrap().as_ref())
1457                .await
1458                .expect("failed to query for finding an event")
1459                .is_none()
1460        );
1461    }
1462
1463    async fn test_find_event_relations(&self) {
1464        let room_id = room_id!("!r0:matrix.org");
1465        let another_room_id = room_id!("!r1:matrix.org");
1466
1467        let f = EventFactory::new().room(room_id).sender(*ALICE);
1468
1469        // Create event and related events for the first room.
1470        let eid1 = event_id!("$event1:matrix.org");
1471        let e1 = f.text_msg("comter").event_id(eid1).into_event();
1472
1473        let edit_eid1 = event_id!("$edit_event1:matrix.org");
1474        let edit_e1 = f
1475            .text_msg("* comté")
1476            .event_id(edit_eid1)
1477            .edit(eid1, RoomMessageEventContentWithoutRelation::text_plain("comté"))
1478            .into_event();
1479
1480        let reaction_eid1 = event_id!("$reaction_event1:matrix.org");
1481        let reaction_e1 = f.reaction(eid1, "👍").event_id(reaction_eid1).into_event();
1482
1483        let eid2 = event_id!("$event2:matrix.org");
1484        let e2 = f.text_msg("galette saucisse").event_id(eid2).into_event();
1485
1486        // Create events for the second room.
1487        let f = f.room(another_room_id);
1488
1489        let eid3 = event_id!("$event3:matrix.org");
1490        let e3 = f.text_msg("gruyère").event_id(eid3).into_event();
1491
1492        let reaction_eid3 = event_id!("$reaction_event3:matrix.org");
1493        let reaction_e3 = f.reaction(eid3, "👍").event_id(reaction_eid3).into_event();
1494
1495        // Save All The Things!
1496        self.save_event(room_id, e1).await.unwrap();
1497        self.save_event(room_id, edit_e1).await.unwrap();
1498        self.save_event(room_id, reaction_e1.clone()).await.unwrap();
1499        self.save_event(room_id, e2).await.unwrap();
1500        self.save_event(another_room_id, e3).await.unwrap();
1501        self.save_event(another_room_id, reaction_e3).await.unwrap();
1502
1503        // Finding relations without a filter returns all of them.
1504        let relations = self.find_event_relations(room_id, eid1, None).await.unwrap();
1505        assert_eq!(relations.len(), 2);
1506        // The position is `None` for items outside the linked chunk.
1507        assert!(
1508            relations
1509                .iter()
1510                .any(|(ev, pos)| ev.event_id().as_deref() == Some(edit_eid1) && pos.is_none())
1511        );
1512        assert!(
1513            relations
1514                .iter()
1515                .any(|(ev, pos)| ev.event_id().as_deref() == Some(reaction_eid1) && pos.is_none())
1516        );
1517
1518        // Finding relations with a filter only returns a subset.
1519        let relations = self
1520            .find_event_relations(room_id, eid1, Some(&[RelationType::Replacement]))
1521            .await
1522            .unwrap();
1523        assert_eq!(relations.len(), 1);
1524        assert_eq!(relations[0].0.event_id().as_deref(), Some(edit_eid1));
1525
1526        let relations = self
1527            .find_event_relations(
1528                room_id,
1529                eid1,
1530                Some(&[RelationType::Replacement, RelationType::Annotation]),
1531            )
1532            .await
1533            .unwrap();
1534        assert_eq!(relations.len(), 2);
1535        assert!(relations.iter().any(|r| r.0.event_id().as_deref() == Some(edit_eid1)));
1536        assert!(relations.iter().any(|r| r.0.event_id().as_deref() == Some(reaction_eid1)));
1537
1538        // We can't find relations using the wrong room.
1539        let relations = self
1540            .find_event_relations(another_room_id, eid1, Some(&[RelationType::Replacement]))
1541            .await
1542            .unwrap();
1543        assert!(relations.is_empty());
1544
1545        // But if an event exists in the linked chunk, we may have its position when
1546        // it's found as a relationship.
1547
1548        // Add reaction_e1 to the room's linked chunk.
1549        self.handle_linked_chunk_updates(
1550            LinkedChunkId::Room(room_id),
1551            vec![
1552                Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
1553                Update::PushItems { at: Position::new(CId::new(0), 0), items: vec![reaction_e1] },
1554            ],
1555        )
1556        .await
1557        .unwrap();
1558
1559        // When looking for aggregations to e1, we should have the position for
1560        // reaction_e1.
1561        let relations = self.find_event_relations(room_id, eid1, None).await.unwrap();
1562
1563        // The position is set for `reaction_eid1` now.
1564        assert!(relations.iter().any(|(ev, pos)| {
1565            ev.event_id().as_deref() == Some(reaction_eid1)
1566                && *pos == Some(Position::new(CId::new(0), 0))
1567        }));
1568
1569        // But it's still not set for the other related events.
1570        assert!(
1571            relations
1572                .iter()
1573                .any(|(ev, pos)| ev.event_id().as_deref() == Some(edit_eid1) && pos.is_none())
1574        );
1575    }
1576
1577    async fn test_get_room_events(&self) {
1578        let room_id = room_id!("!r0:matrix.org");
1579        let another_room_id = room_id!("!r1:matrix.org");
1580        let linked_chunk_id = LinkedChunkId::Room(room_id);
1581        let another_linked_chunk_id = LinkedChunkId::Room(another_room_id);
1582        let event = |msg: &str| make_test_event(room_id, msg);
1583
1584        let event_comte = event("comté");
1585        let event_gruyere = event("gruyère");
1586        let event_stilton = event("stilton");
1587
1588        // Add one event in one room.
1589        self.handle_linked_chunk_updates(
1590            linked_chunk_id,
1591            vec![
1592                Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
1593                Update::PushItems {
1594                    at: Position::new(CId::new(0), 0),
1595                    items: vec![event_comte.clone(), event_gruyere.clone()],
1596                },
1597            ],
1598        )
1599        .await
1600        .unwrap();
1601
1602        // Add an event in a different room.
1603        self.handle_linked_chunk_updates(
1604            another_linked_chunk_id,
1605            vec![
1606                Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
1607                Update::PushItems {
1608                    at: Position::new(CId::new(0), 0),
1609                    items: vec![event_stilton.clone()],
1610                },
1611            ],
1612        )
1613        .await
1614        .unwrap();
1615
1616        // Now let's find the events.
1617        let events = self
1618            .get_room_events(room_id, None, None)
1619            .await
1620            .expect("failed to query for room events");
1621
1622        assert_eq!(events.len(), 2);
1623
1624        let got_ids: Vec<_> = events.into_iter().map(|ev| ev.event_id()).collect();
1625        let expected_ids = vec![event_comte.event_id(), event_gruyere.event_id()];
1626
1627        for expected in expected_ids {
1628            assert!(
1629                got_ids.contains(&expected),
1630                "Expected event {expected:?} not in got events: {got_ids:?}."
1631            );
1632        }
1633    }
1634
1635    async fn test_get_room_events_filtered(&self) {
1636        macro_rules! assert_expected_events {
1637            ($events:expr, [$($item:expr),* $(,)?]) => {{
1638                let got_ids: BTreeSet<_> = $events.into_iter().map(|ev| ev.event_id().unwrap()).collect();
1639                let expected_ids = BTreeSet::from([$($item.event_id().unwrap()),*]);
1640
1641                assert_eq!(got_ids, expected_ids);
1642            }};
1643        }
1644
1645        let room_id = room_id!("!r0:matrix.org");
1646        let linked_chunk_id = LinkedChunkId::Room(room_id);
1647        let another_room_id = room_id!("!r1:matrix.org");
1648        let another_linked_chunk_id = LinkedChunkId::Room(another_room_id);
1649
1650        let event = |session_id: &str| make_encrypted_test_event(room_id, session_id);
1651
1652        let first_event = event("session_1");
1653        let second_event = event("session_2");
1654        let third_event = event("session_3");
1655        let fourth_event = make_test_event(room_id, "It's a secret to everybody");
1656
1657        // Add one event in one room.
1658        self.handle_linked_chunk_updates(
1659            linked_chunk_id,
1660            vec![
1661                Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
1662                Update::PushItems {
1663                    at: Position::new(CId::new(0), 0),
1664                    items: vec![first_event.clone(), second_event.clone(), fourth_event.clone()],
1665                },
1666            ],
1667        )
1668        .await
1669        .unwrap();
1670
1671        // Add an event in a different room.
1672        self.handle_linked_chunk_updates(
1673            another_linked_chunk_id,
1674            vec![
1675                Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
1676                Update::PushItems {
1677                    at: Position::new(CId::new(0), 0),
1678                    items: vec![third_event.clone()],
1679                },
1680            ],
1681        )
1682        .await
1683        .unwrap();
1684
1685        // Now let's find all the encrypted events of the first room.
1686        let events = self
1687            .get_room_events(room_id, Some("m.room.encrypted"), None)
1688            .await
1689            .expect("failed to query for room events");
1690
1691        assert_eq!(events.len(), 2);
1692        assert_expected_events!(events, [first_event, second_event]);
1693
1694        // Now let's find all the encrypted events which were encrypted using the first
1695        // session ID.
1696        let events = self
1697            .get_room_events(room_id, Some("m.room.encrypted"), Some("session_1"))
1698            .await
1699            .expect("failed to query for room events");
1700
1701        assert_eq!(events.len(), 1);
1702        assert_expected_events!(events, [first_event]);
1703    }
1704
1705    async fn test_save_event(&self) {
1706        let room_id = room_id!("!r0:matrix.org");
1707        let another_room_id = room_id!("!r1:matrix.org");
1708
1709        let event = |msg: &str| make_test_event(room_id, msg);
1710        let event_comte = event("comté");
1711        let event_gruyere = event("gruyère");
1712
1713        // Add one event in one room.
1714        self.save_event(room_id, event_comte.clone()).await.unwrap();
1715
1716        // Add another event in another room.
1717        self.save_event(another_room_id, event_gruyere.clone()).await.unwrap();
1718
1719        // Events can be found, when searched in their own rooms.
1720        let event = self
1721            .find_event(room_id, event_comte.event_id().unwrap().as_ref())
1722            .await
1723            .expect("failed to query for finding an event")
1724            .expect("failed to find an event");
1725        assert_eq!(event.event_id(), event_comte.event_id());
1726
1727        let event = self
1728            .find_event(another_room_id, event_gruyere.event_id().unwrap().as_ref())
1729            .await
1730            .expect("failed to query for finding an event")
1731            .expect("failed to find an event");
1732        assert_eq!(event.event_id(), event_gruyere.event_id());
1733
1734        // But they won't be returned when searching in the wrong room.
1735        assert!(
1736            self.find_event(another_room_id, event_comte.event_id().unwrap().as_ref())
1737                .await
1738                .expect("failed to query for finding an event")
1739                .is_none()
1740        );
1741        assert!(
1742            self.find_event(room_id, event_gruyere.event_id().unwrap().as_ref())
1743                .await
1744                .expect("failed to query for finding an event")
1745                .is_none()
1746        );
1747    }
1748
1749    async fn test_thread_vs_room_linked_chunk(&self) {
1750        let room_id = room_id!("!r0:matrix.org");
1751
1752        let event = |msg: &str| make_test_event(room_id, msg);
1753
1754        let thread1_ev = event("comté");
1755        let thread2_ev = event("gruyère");
1756        let thread2_ev2 = event("beaufort");
1757        let room_ev = event("brillat savarin triple crème");
1758
1759        let thread_root1 = event("thread1");
1760        let thread_root2 = event("thread2");
1761
1762        // Add one event in a thread linked chunk.
1763        self.handle_linked_chunk_updates(
1764            LinkedChunkId::Thread(room_id, thread_root1.event_id().unwrap().as_ref()),
1765            vec![
1766                Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
1767                Update::PushItems {
1768                    at: Position::new(CId::new(0), 0),
1769                    items: vec![thread1_ev.clone()],
1770                },
1771            ],
1772        )
1773        .await
1774        .unwrap();
1775
1776        // Add one event in another thread linked chunk (same room).
1777        self.handle_linked_chunk_updates(
1778            LinkedChunkId::Thread(room_id, thread_root2.event_id().unwrap().as_ref()),
1779            vec![
1780                Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
1781                Update::PushItems {
1782                    at: Position::new(CId::new(0), 0),
1783                    items: vec![thread2_ev.clone(), thread2_ev2.clone()],
1784                },
1785            ],
1786        )
1787        .await
1788        .unwrap();
1789
1790        // Add another event to the room linked chunk.
1791        self.handle_linked_chunk_updates(
1792            LinkedChunkId::Room(room_id),
1793            vec![
1794                Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
1795                Update::PushItems {
1796                    at: Position::new(CId::new(0), 0),
1797                    items: vec![room_ev.clone()],
1798                },
1799            ],
1800        )
1801        .await
1802        .unwrap();
1803
1804        // All the events can be found with `find_event()` for the room.
1805        self.find_event(room_id, thread2_ev.event_id().unwrap().as_ref())
1806            .await
1807            .expect("failed to query for finding an event")
1808            .expect("failed to find thread1_ev");
1809
1810        self.find_event(room_id, thread2_ev.event_id().unwrap().as_ref())
1811            .await
1812            .expect("failed to query for finding an event")
1813            .expect("failed to find thread2_ev");
1814
1815        self.find_event(room_id, thread2_ev2.event_id().unwrap().as_ref())
1816            .await
1817            .expect("failed to query for finding an event")
1818            .expect("failed to find thread2_ev2");
1819
1820        self.find_event(room_id, room_ev.event_id().unwrap().as_ref())
1821            .await
1822            .expect("failed to query for finding an event")
1823            .expect("failed to find room_ev");
1824
1825        // Finding duplicates operates based on the linked chunk id.
1826        let dups = self
1827            .filter_duplicated_events(
1828                LinkedChunkId::Thread(room_id, thread_root1.event_id().unwrap().as_ref()),
1829                vec![
1830                    thread1_ev.event_id().unwrap().to_owned(),
1831                    room_ev.event_id().unwrap().to_owned(),
1832                ],
1833            )
1834            .await
1835            .unwrap();
1836        assert_eq!(dups.len(), 1);
1837        assert_eq!(dups[0].0, thread1_ev.event_id().unwrap());
1838
1839        // Loading all chunks operates based on the linked chunk id.
1840        let all_chunks = self
1841            .load_all_chunks(LinkedChunkId::Thread(
1842                room_id,
1843                thread_root2.event_id().unwrap().as_ref(),
1844            ))
1845            .await
1846            .unwrap();
1847        assert_eq!(all_chunks.len(), 1);
1848        assert_eq!(all_chunks[0].identifier, CId::new(0));
1849        assert_let!(ChunkContent::Items(observed_items) = all_chunks[0].content.clone());
1850        assert_eq!(observed_items.len(), 2);
1851        assert_eq!(observed_items[0].event_id(), thread2_ev.event_id());
1852        assert_eq!(observed_items[1].event_id(), thread2_ev2.event_id());
1853
1854        // Loading the metadata of all chunks operates based on the linked chunk
1855        // id.
1856        let metas = self
1857            .load_all_chunks_metadata(LinkedChunkId::Thread(
1858                room_id,
1859                thread_root2.event_id().unwrap().as_ref(),
1860            ))
1861            .await
1862            .unwrap();
1863        assert_eq!(metas.len(), 1);
1864        assert_eq!(metas[0].identifier, CId::new(0));
1865        assert_eq!(metas[0].num_items, 2);
1866
1867        // Loading the last chunk operates based on the linked chunk id.
1868        let (last_chunk, _chunk_identifier_generator) = self
1869            .load_last_chunk(LinkedChunkId::Thread(
1870                room_id,
1871                thread_root1.event_id().unwrap().as_ref(),
1872            ))
1873            .await
1874            .unwrap();
1875        let last_chunk = last_chunk.unwrap();
1876        assert_eq!(last_chunk.identifier, CId::new(0));
1877        assert_let!(ChunkContent::Items(observed_items) = last_chunk.content);
1878        assert_eq!(observed_items.len(), 1);
1879        assert_eq!(observed_items[0].event_id(), thread1_ev.event_id());
1880    }
1881}
1882
1883/// Macro building to allow your `EventCacheStore` implementation to run the
1884/// entire tests suite locally.
1885///
1886/// You need to provide a `async fn get_event_cache_store() ->
1887/// EventCacheStoreResult<impl EventCacheStore>` providing a fresh event cache
1888/// store on the same level you invoke the macro.
1889///
1890/// ## Usage Example:
1891/// ```no_run
1892/// # use matrix_sdk_base::event_cache::store::{
1893/// #    EventCacheStore,
1894/// #    MemoryStore as MyStore,
1895/// #    Result as EventCacheStoreResult,
1896/// # };
1897///
1898/// #[cfg(test)]
1899/// mod tests {
1900///     use super::{EventCacheStore, EventCacheStoreResult, MyStore};
1901///
1902///     async fn get_event_cache_store()
1903///     -> EventCacheStoreResult<impl EventCacheStore> {
1904///         Ok(MyStore::new())
1905///     }
1906///
1907///     event_cache_store_integration_tests!();
1908/// }
1909/// ```
1910#[allow(unused_macros, unused_extern_crates)]
1911#[macro_export]
1912macro_rules! event_cache_store_integration_tests {
1913    () => {
1914        mod event_cache_store_integration_tests {
1915            use matrix_sdk_test::async_test;
1916            use $crate::event_cache::store::{
1917                EventCacheStoreIntegrationTests, IntoEventCacheStore,
1918            };
1919
1920            use super::get_event_cache_store;
1921
1922            #[async_test]
1923            async fn test_handle_updates_and_rebuild_linked_chunk() {
1924                let event_cache_store =
1925                    get_event_cache_store().await.unwrap().into_event_cache_store();
1926                event_cache_store.test_handle_updates_and_rebuild_linked_chunk().await;
1927            }
1928
1929            #[async_test]
1930            async fn test_linked_chunk_exists_before_referenced() {
1931                let event_cache_store =
1932                    get_event_cache_store().await.unwrap().into_event_cache_store();
1933                event_cache_store.test_linked_chunk_exists_before_referenced().await;
1934            }
1935
1936            #[async_test]
1937            async fn test_load_last_chunk() {
1938                let event_cache_store =
1939                    get_event_cache_store().await.unwrap().into_event_cache_store();
1940                event_cache_store.test_load_last_chunk().await;
1941            }
1942
1943            #[async_test]
1944            async fn test_load_last_chunk_with_a_cycle() {
1945                let event_cache_store =
1946                    get_event_cache_store().await.unwrap().into_event_cache_store();
1947                event_cache_store.test_load_last_chunk_with_a_cycle().await;
1948            }
1949
1950            #[async_test]
1951            async fn test_load_previous_chunk() {
1952                let event_cache_store =
1953                    get_event_cache_store().await.unwrap().into_event_cache_store();
1954                event_cache_store.test_load_previous_chunk().await;
1955            }
1956
1957            #[async_test]
1958            async fn test_linked_chunk_incremental_loading() {
1959                let event_cache_store =
1960                    get_event_cache_store().await.unwrap().into_event_cache_store();
1961                event_cache_store.test_linked_chunk_incremental_loading().await;
1962            }
1963
1964            #[async_test]
1965            async fn test_linked_chunk_remove_chunk() {
1966                let event_cache_store =
1967                    get_event_cache_store().await.unwrap().into_event_cache_store();
1968                event_cache_store.test_linked_chunk_remove_chunk().await;
1969            }
1970
1971            #[async_test]
1972            async fn test_linked_chunk_replace_item() {
1973                let event_cache_store =
1974                    get_event_cache_store().await.unwrap().into_event_cache_store();
1975                event_cache_store.test_linked_chunk_replace_item().await;
1976            }
1977
1978            #[async_test]
1979            async fn test_linked_chunk_remove_item() {
1980                let event_cache_store =
1981                    get_event_cache_store().await.unwrap().into_event_cache_store();
1982                event_cache_store.test_linked_chunk_remove_item().await;
1983            }
1984
1985            #[async_test]
1986            async fn test_linked_chunk_detach_last_items() {
1987                let event_cache_store =
1988                    get_event_cache_store().await.unwrap().into_event_cache_store();
1989                event_cache_store.test_linked_chunk_detach_last_items().await;
1990            }
1991
1992            #[async_test]
1993            async fn test_linked_chunk_start_end_reattach_items() {
1994                let event_cache_store =
1995                    get_event_cache_store().await.unwrap().into_event_cache_store();
1996                event_cache_store.test_linked_chunk_start_end_reattach_items().await;
1997            }
1998
1999            #[async_test]
2000            async fn test_linked_chunk_clear() {
2001                let event_cache_store =
2002                    get_event_cache_store().await.unwrap().into_event_cache_store();
2003                event_cache_store.test_linked_chunk_clear().await;
2004            }
2005
2006            #[async_test]
2007            async fn test_linked_chunk_clear_and_reinsert() {
2008                let event_cache_store =
2009                    get_event_cache_store().await.unwrap().into_event_cache_store();
2010                event_cache_store.test_linked_chunk_clear_and_reinsert().await;
2011            }
2012
2013            #[async_test]
2014            async fn test_rebuild_empty_linked_chunk() {
2015                let event_cache_store =
2016                    get_event_cache_store().await.unwrap().into_event_cache_store();
2017                event_cache_store.test_rebuild_empty_linked_chunk().await;
2018            }
2019
2020            #[async_test]
2021            async fn test_linked_chunk_multiple_rooms() {
2022                let event_cache_store =
2023                    get_event_cache_store().await.unwrap().into_event_cache_store();
2024                event_cache_store.test_linked_chunk_multiple_rooms().await;
2025            }
2026
2027            #[async_test]
2028            async fn test_load_all_chunks_metadata() {
2029                let event_cache_store =
2030                    get_event_cache_store().await.unwrap().into_event_cache_store();
2031                event_cache_store.test_load_all_chunks_metadata().await;
2032            }
2033
2034            #[async_test]
2035            async fn test_clear_all_linked_chunks() {
2036                let event_cache_store =
2037                    get_event_cache_store().await.unwrap().into_event_cache_store();
2038                event_cache_store.test_clear_all_linked_chunks().await;
2039            }
2040
2041            #[async_test]
2042            async fn test_remove_room() {
2043                let event_cache_store =
2044                    get_event_cache_store().await.unwrap().into_event_cache_store();
2045                event_cache_store.test_remove_room().await;
2046            }
2047
2048            #[async_test]
2049            async fn test_filter_duplicated_events() {
2050                let event_cache_store =
2051                    get_event_cache_store().await.unwrap().into_event_cache_store();
2052                event_cache_store.test_filter_duplicated_events().await;
2053            }
2054
2055            #[async_test]
2056            async fn test_filter_duplicate_events_no_events() {
2057                let event_cache_store =
2058                    get_event_cache_store().await.unwrap().into_event_cache_store();
2059                event_cache_store.test_filter_duplicate_events_no_events().await;
2060            }
2061
2062            #[async_test]
2063            async fn test_find_event() {
2064                let event_cache_store =
2065                    get_event_cache_store().await.unwrap().into_event_cache_store();
2066                event_cache_store.test_find_event().await;
2067            }
2068
2069            #[async_test]
2070            async fn test_find_event_relations() {
2071                let event_cache_store =
2072                    get_event_cache_store().await.unwrap().into_event_cache_store();
2073                event_cache_store.test_find_event_relations().await;
2074            }
2075
2076            #[async_test]
2077            async fn test_get_room_events() {
2078                let event_cache_store =
2079                    get_event_cache_store().await.unwrap().into_event_cache_store();
2080                event_cache_store.test_get_room_events().await;
2081            }
2082
2083            #[async_test]
2084            async fn test_get_room_events_filtered() {
2085                let event_cache_store =
2086                    get_event_cache_store().await.unwrap().into_event_cache_store();
2087                event_cache_store.test_get_room_events_filtered().await;
2088            }
2089
2090            #[async_test]
2091            async fn test_save_event() {
2092                let event_cache_store =
2093                    get_event_cache_store().await.unwrap().into_event_cache_store();
2094                event_cache_store.test_save_event().await;
2095            }
2096
2097            #[async_test]
2098            async fn test_thread_vs_room_linked_chunk() {
2099                let event_cache_store =
2100                    get_event_cache_store().await.unwrap().into_event_cache_store();
2101                event_cache_store.test_thread_vs_room_linked_chunk().await;
2102            }
2103        }
2104    };
2105}
2106
2107/// Macro generating tests for the event cache store, related to time (mostly
2108/// for the cross-process lock).
2109#[allow(unused_macros)]
2110#[macro_export]
2111macro_rules! event_cache_store_integration_tests_time {
2112    () => {
2113        mod event_cache_store_integration_tests_time {
2114            use std::time::Duration;
2115
2116            #[cfg(all(target_family = "wasm", target_os = "unknown"))]
2117            use gloo_timers::future::sleep;
2118            use matrix_sdk_test::async_test;
2119            #[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
2120            use tokio::time::sleep;
2121            use $crate::event_cache::store::IntoEventCacheStore;
2122
2123            use super::get_event_cache_store;
2124
2125            #[async_test]
2126            async fn test_lease_locks() {
2127                let store = get_event_cache_store().await.unwrap().into_event_cache_store();
2128
2129                let acquired0 = store.try_take_leased_lock(0, "key", "alice").await.unwrap();
2130                assert_eq!(acquired0, Some(1)); // first lock generation
2131
2132                // Should extend the lease automatically (same holder).
2133                let acquired2 = store.try_take_leased_lock(300, "key", "alice").await.unwrap();
2134                assert_eq!(acquired2, Some(1)); // same lock generation
2135
2136                // Should extend the lease automatically (same holder + time is ok).
2137                let acquired3 = store.try_take_leased_lock(300, "key", "alice").await.unwrap();
2138                assert_eq!(acquired3, Some(1)); // same lock generation
2139
2140                // Another attempt at taking the lock should fail, because it's taken.
2141                let acquired4 = store.try_take_leased_lock(300, "key", "bob").await.unwrap();
2142                assert!(acquired4.is_none()); // not acquired
2143
2144                // Even if we insist.
2145                let acquired5 = store.try_take_leased_lock(300, "key", "bob").await.unwrap();
2146                assert!(acquired5.is_none()); // not acquired
2147
2148                // That's a nice test we got here, go take a little nap.
2149                sleep(Duration::from_millis(50)).await;
2150
2151                // Still too early.
2152                let acquired55 = store.try_take_leased_lock(300, "key", "bob").await.unwrap();
2153                assert!(acquired55.is_none()); // not acquired
2154
2155                // Ok you can take another nap then.
2156                sleep(Duration::from_millis(250)).await;
2157
2158                // At some point, we do get the lock.
2159                let acquired6 = store.try_take_leased_lock(0, "key", "bob").await.unwrap();
2160                assert_eq!(acquired6, Some(2)); // new lock generation!
2161
2162                sleep(Duration::from_millis(1)).await;
2163
2164                // The other gets it almost immediately too.
2165                let acquired7 = store.try_take_leased_lock(0, "key", "alice").await.unwrap();
2166                assert_eq!(acquired7, Some(3)); // new lock generation!
2167
2168                sleep(Duration::from_millis(1)).await;
2169
2170                // But when we take a longer lease…
2171                let acquired8 = store.try_take_leased_lock(300, "key", "bob").await.unwrap();
2172                assert_eq!(acquired8, Some(4)); // new lock generation!
2173
2174                // It blocks the other user.
2175                let acquired9 = store.try_take_leased_lock(300, "key", "alice").await.unwrap();
2176                assert!(acquired9.is_none()); // not acquired
2177
2178                // We can hold onto our lease.
2179                let acquired10 = store.try_take_leased_lock(300, "key", "bob").await.unwrap();
2180                assert_eq!(acquired10, Some(4)); // same lock generation
2181            }
2182        }
2183    };
2184}