1use 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
47pub fn make_test_event(room_id: &RoomId, content: &str) -> TimelineEvent {
52 make_test_event_with_event_id(room_id, content, None)
53}
54
55pub 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
73pub 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#[track_caller]
108pub fn check_test_event(event: &TimelineEvent, text: &str) {
109 let actions = event.push_actions().unwrap();
111 assert_eq!(actions.len(), 1);
112 assert_matches!(&actions[0], Action::Notify);
113
114 assert_matches!(&event.kind, TimelineEventKind::Decrypted(d) => {
116 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 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#[allow(async_fn_in_trait)]
135pub trait EventCacheStoreIntegrationTests {
136 async fn test_handle_updates_and_rebuild_linked_chunk(&self);
139
140 async fn test_linked_chunk_exists_before_referenced(&self);
143
144 async fn test_load_last_chunk(&self);
146
147 async fn test_load_last_chunk_with_a_cycle(&self);
150
151 async fn test_load_previous_chunk(&self);
153
154 async fn test_linked_chunk_incremental_loading(&self);
157
158 async fn test_linked_chunk_remove_chunk(&self);
160
161 async fn test_linked_chunk_replace_item(&self);
163
164 async fn test_linked_chunk_remove_item(&self);
166
167 async fn test_linked_chunk_detach_last_items(&self);
169
170 async fn test_linked_chunk_start_end_reattach_items(&self);
172
173 async fn test_linked_chunk_clear(&self);
175
176 async fn test_linked_chunk_clear_and_reinsert(&self);
178
179 async fn test_rebuild_empty_linked_chunk(&self);
182
183 async fn test_linked_chunk_multiple_rooms(&self);
186
187 async fn test_load_all_chunks_metadata(&self);
189
190 async fn test_clear_all_linked_chunks(&self);
192
193 async fn test_remove_room(&self);
195
196 async fn test_filter_duplicated_events(&self);
198
199 async fn test_filter_duplicate_events_no_events(&self);
201
202 async fn test_find_event(&self);
204
205 async fn test_find_event_relations(&self);
207
208 async fn test_get_room_events(&self);
210
211 async fn test_get_room_events_filtered(&self);
213
214 async fn test_save_event(&self);
216
217 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 Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
232 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 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 Update::NewItemsChunk { previous: Some(CId::new(1)), new: CId::new(2), next: None },
249 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 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 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 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 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 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 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 Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
367 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 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 Update::NewItemsChunk { previous: Some(CId::new(1)), new: CId::new(2), next: None },
384 Update::PushItems {
386 at: Position::new(CId::new(2), 0),
387 items: vec![make_test_event(room_id, "sup")],
388 },
389 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 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 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 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 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 {
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 {
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 {
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 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 {
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 {
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 {
564 self.handle_linked_chunk_updates(
565 linked_chunk_id,
566 vec![
567 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 {
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 Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
619 Update::PushItems {
621 at: Position::new(CId::new(0), 0),
622 items: vec![event("a"), event("b")],
623 },
624 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 Update::NewItemsChunk { previous: Some(CId::new(1)), new: CId::new(2), next: None },
633 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 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(); let mut rchunks = linked_chunk.rchunks();
658
659 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 {
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 assert_matches!(rchunks.next(), Some(chunk) => {
689 assert_eq!(chunk.identifier(), 2);
690 assert!(chunk.lazy_previous().is_none());
691
692 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 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 {
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 assert_matches!(rchunks.next(), Some(chunk) => {
726 assert_eq!(chunk.identifier(), 2);
727 assert!(chunk.lazy_previous().is_none());
728
729 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 assert_matches!(rchunks.next(), Some(chunk) => {
740 assert_eq!(chunk.identifier(), 1);
741 assert!(chunk.lazy_previous().is_none());
742
743 assert_matches!(chunk.content(), ChunkContent::Gap(gap) => {
745 assert_eq!(gap.prev_token, "morbier");
746 });
747 });
748
749 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 {
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 {
776 let mut chunks = linked_chunk.chunks();
777
778 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 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 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 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) },
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 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 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 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 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 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 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 self.handle_linked_chunk_updates(
1181 linked_chunk_id0,
1182 vec![
1183 Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
1185 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 self.handle_linked_chunk_updates(
1197 linked_chunk_id1,
1198 vec![
1199 Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
1201 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 Update::NewItemsChunk { previous: Some(CId::new(1)), new: CId::new(2), next: None },
1210 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 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 self.clear_all_linked_chunks().await.unwrap();
1238
1239 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 self.handle_linked_chunk_updates(
1264 linked_chunk_id0,
1265 vec![
1266 Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
1268 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 self.handle_linked_chunk_updates(
1280 linked_chunk_id1,
1281 vec![
1282 Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
1284 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 self.remove_room(r0).await.unwrap();
1296
1297 let r0_linked_chunk = self.load_all_chunks(linked_chunk_id0).await.unwrap();
1299 assert!(r0_linked_chunk.is_empty());
1300
1301 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 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 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 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 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 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 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 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 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 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 let relations = self.find_event_relations(room_id, eid1, None).await.unwrap();
1505 assert_eq!(relations.len(), 2);
1506 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 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 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 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 let relations = self.find_event_relations(room_id, eid1, None).await.unwrap();
1562
1563 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 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 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 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 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 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 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 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 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 self.save_event(room_id, event_comte.clone()).await.unwrap();
1715
1716 self.save_event(another_room_id, event_gruyere.clone()).await.unwrap();
1718
1719 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 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 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 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 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 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 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 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 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 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#[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#[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)); let acquired2 = store.try_take_leased_lock(300, "key", "alice").await.unwrap();
2134 assert_eq!(acquired2, Some(1)); let acquired3 = store.try_take_leased_lock(300, "key", "alice").await.unwrap();
2138 assert_eq!(acquired3, Some(1)); let acquired4 = store.try_take_leased_lock(300, "key", "bob").await.unwrap();
2142 assert!(acquired4.is_none()); let acquired5 = store.try_take_leased_lock(300, "key", "bob").await.unwrap();
2146 assert!(acquired5.is_none()); sleep(Duration::from_millis(50)).await;
2150
2151 let acquired55 = store.try_take_leased_lock(300, "key", "bob").await.unwrap();
2153 assert!(acquired55.is_none()); sleep(Duration::from_millis(250)).await;
2157
2158 let acquired6 = store.try_take_leased_lock(0, "key", "bob").await.unwrap();
2160 assert_eq!(acquired6, Some(2)); sleep(Duration::from_millis(1)).await;
2163
2164 let acquired7 = store.try_take_leased_lock(0, "key", "alice").await.unwrap();
2166 assert_eq!(acquired7, Some(3)); sleep(Duration::from_millis(1)).await;
2169
2170 let acquired8 = store.try_take_leased_lock(300, "key", "bob").await.unwrap();
2172 assert_eq!(acquired8, Some(4)); let acquired9 = store.try_take_leased_lock(300, "key", "alice").await.unwrap();
2176 assert!(acquired9.is_none()); let acquired10 = store.try_take_leased_lock(300, "key", "bob").await.unwrap();
2180 assert_eq!(acquired10, Some(4)); }
2182 }
2183 };
2184}