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::{
45 super::{Gap, thread::ThreadInfo},
46 DEFAULT_CHUNK_CAPACITY, DynEventCacheStore,
47};
48use crate::read_receipts::ReadReceipts;
49
50pub fn make_test_event(room_id: &RoomId, content: &str) -> TimelineEvent {
55 make_test_event_with_event_id(room_id, content, None)
56}
57
58pub fn make_encrypted_test_event(room_id: &RoomId, session_id: &str) -> TimelineEvent {
61 let device_id = "DEVICEID";
62 let builder = EventFactory::new()
63 .encrypted("", "curve_key", device_id, session_id)
64 .room(room_id)
65 .sender(*ALICE);
66
67 let event = builder.into_raw();
68 let utd_info = UnableToDecryptInfo {
69 session_id: Some(session_id.to_owned()),
70 reason: UnableToDecryptReason::MissingMegolmSession { withheld_code: None },
71 };
72
73 TimelineEvent::from_utd(event, utd_info)
74}
75
76pub fn make_test_event_with_event_id(
78 room_id: &RoomId,
79 content: &str,
80 event_id: Option<&EventId>,
81) -> TimelineEvent {
82 let encryption_info = Arc::new(EncryptionInfo {
83 sender: (*ALICE).into(),
84 sender_device: None,
85 forwarder: None,
86 algorithm_info: AlgorithmInfo::MegolmV1AesSha2 {
87 curve25519_key: "1337".to_owned(),
88 sender_claimed_keys: Default::default(),
89 session_id: Some("mysessionid9".to_owned()),
90 },
91 verification_state: VerificationState::Verified,
92 });
93
94 let mut builder = EventFactory::new().text_msg(content).room(room_id).sender(*ALICE);
95 if let Some(event_id) = event_id {
96 builder = builder.event_id(event_id);
97 }
98 let event = builder.into_raw();
99
100 TimelineEvent::from_decrypted(
101 DecryptedRoomEvent { event, encryption_info, unsigned_encryption_info: None },
102 Some(vec![Action::Notify]),
103 )
104}
105
106#[track_caller]
111pub fn check_test_event(event: &TimelineEvent, text: &str) {
112 let actions = event.push_actions().unwrap();
114 assert_eq!(actions.len(), 1);
115 assert_matches!(&actions[0], Action::Notify);
116
117 assert_matches!(&event.kind, TimelineEventKind::Decrypted(d) => {
119 assert_eq!(d.encryption_info.sender, *ALICE);
121 assert_matches!(&d.encryption_info.algorithm_info, AlgorithmInfo::MegolmV1AesSha2 { curve25519_key, .. } => {
122 assert_eq!(curve25519_key, "1337");
123 });
124
125 let deserialized = d.event.deserialize().unwrap();
127 assert_matches!(deserialized, AnyTimelineEvent::MessageLike(AnyMessageLikeEvent::RoomMessage(msg)) => {
128 assert_eq!(msg.as_original().unwrap().content.body(), text);
129 });
130 });
131}
132
133#[allow(async_fn_in_trait)]
138pub trait EventCacheStoreIntegrationTests {
139 async fn test_handle_updates_and_rebuild_linked_chunk(&self);
142
143 async fn test_linked_chunk_exists_before_referenced(&self);
146
147 async fn test_linked_chunk_allows_same_event_in_room_and_thread(&self);
150
151 async fn test_load_last_chunk(&self);
153
154 async fn test_load_last_chunk_with_a_cycle(&self);
157
158 async fn test_load_previous_chunk(&self);
160
161 async fn test_linked_chunk_incremental_loading(&self);
164
165 async fn test_linked_chunk_remove_chunk(&self);
167
168 async fn test_linked_chunk_replace_item(&self);
170
171 async fn test_linked_chunk_remove_item(&self);
173
174 async fn test_linked_chunk_detach_last_items(&self);
176
177 async fn test_linked_chunk_start_end_reattach_items(&self);
179
180 async fn test_linked_chunk_clear(&self);
182
183 async fn test_linked_chunk_clear_and_reinsert(&self);
185
186 async fn test_rebuild_empty_linked_chunk(&self);
189
190 async fn test_linked_chunk_multiple_rooms(&self);
193
194 async fn test_load_all_chunks_metadata(&self);
196
197 async fn test_load_and_update_thread_info(&self);
199
200 async fn test_clear_all_events(&self);
202
203 async fn test_clear_all_events_for_specific_room(&self);
205
206 async fn test_filter_duplicated_events(&self);
208
209 async fn test_filter_duplicate_events_no_events(&self);
211
212 async fn test_find_event(&self);
214
215 async fn test_find_event_when_event_in_room_and_thread(&self);
218
219 async fn test_find_event_relations(&self);
221
222 async fn test_find_event_relations_when_event_in_room_and_thread(&self);
225
226 async fn test_get_room_events(&self);
228
229 async fn test_get_room_events_filtered(&self);
231
232 async fn test_get_room_events_with_event_in_room_and_thread(&self);
235
236 async fn test_save_event(&self);
238
239 async fn test_save_event_updates_event_in_room_and_thread(&self);
242
243 async fn test_thread_vs_room_linked_chunk(&self);
246}
247
248impl EventCacheStoreIntegrationTests for DynEventCacheStore {
249 async fn test_handle_updates_and_rebuild_linked_chunk(&self) {
250 let room_id = room_id!("!r0:matrix.org");
251 let linked_chunk_id = LinkedChunkId::Room(room_id);
252
253 self.handle_linked_chunk_updates(
254 linked_chunk_id,
255 vec![
256 Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
258 Update::PushItems {
260 at: Position::new(CId::new(0), 0),
261 items: vec![
262 make_test_event(room_id, "hello"),
263 make_test_event(room_id, "world"),
264 ],
265 },
266 Update::NewGapChunk {
268 previous: Some(CId::new(0)),
269 new: CId::new(1),
270 next: None,
271 gap: Gap { token: "parmesan".to_owned() },
272 },
273 Update::NewItemsChunk { previous: Some(CId::new(1)), new: CId::new(2), next: None },
275 Update::PushItems {
277 at: Position::new(CId::new(2), 0),
278 items: vec![make_test_event(room_id, "sup")],
279 },
280 ],
281 )
282 .await
283 .unwrap();
284
285 let lc = lazy_loader::from_all_chunks::<3, _, _>(
287 self.load_all_chunks(linked_chunk_id).await.unwrap(),
288 )
289 .unwrap()
290 .unwrap();
291
292 let mut chunks = lc.chunks();
293
294 {
295 let first = chunks.next().unwrap();
296 assert_eq!(first.identifier(), CId::new(0));
299
300 assert_matches!(first.content(), ChunkContent::Items(events) => {
301 assert_eq!(events.len(), 2);
302 check_test_event(&events[0], "hello");
303 check_test_event(&events[1], "world");
304 });
305 }
306
307 {
308 let second = chunks.next().unwrap();
309 assert_eq!(second.identifier(), CId::new(1));
310
311 assert_matches!(second.content(), ChunkContent::Gap(gap) => {
312 assert_eq!(gap.token, "parmesan");
313 });
314 }
315
316 {
317 let third = chunks.next().unwrap();
318 assert_eq!(third.identifier(), CId::new(2));
319
320 assert_matches!(third.content(), ChunkContent::Items(events) => {
321 assert_eq!(events.len(), 1);
322 check_test_event(&events[0], "sup");
323 });
324 }
325
326 assert!(chunks.next().is_none());
327 }
328
329 async fn test_linked_chunk_exists_before_referenced(&self) {
330 let room_id = *DEFAULT_TEST_ROOM_ID;
331 let linked_chunk_id = LinkedChunkId::Room(room_id);
332
333 self.handle_linked_chunk_updates(
335 linked_chunk_id,
336 vec![Update::NewItemsChunk {
337 previous: Some(CId::new(41)),
338 new: CId::new(42),
339 next: None,
340 }],
341 )
342 .await
343 .unwrap_err();
344
345 self.handle_linked_chunk_updates(
347 linked_chunk_id,
348 vec![Update::NewItemsChunk {
349 previous: None,
350 new: CId::new(42),
351 next: Some(CId::new(43)),
352 }],
353 )
354 .await
355 .unwrap_err();
356
357 self.handle_linked_chunk_updates(
359 linked_chunk_id,
360 vec![Update::NewGapChunk {
361 previous: Some(CId::new(41)),
362 new: CId::new(42),
363 next: None,
364 gap: Gap { token: "gap".to_owned() },
365 }],
366 )
367 .await
368 .unwrap_err();
369
370 self.handle_linked_chunk_updates(
372 linked_chunk_id,
373 vec![Update::NewGapChunk {
374 previous: None,
375 new: CId::new(42),
376 next: Some(CId::new(43)),
377 gap: Gap { token: "gap".to_owned() },
378 }],
379 )
380 .await
381 .unwrap_err();
382 }
383
384 async fn test_linked_chunk_allows_same_event_in_room_and_thread(&self) {
385 let room_id = *DEFAULT_TEST_ROOM_ID;
390 let thread_root = event_id!("$thread_root");
391
392 let event_id = event_id!("$thread_reply");
395 let event = make_test_event_with_event_id(room_id, "thread reply", Some(event_id));
396
397 let room_linked_chunk_id = LinkedChunkId::Room(room_id);
398 let thread_linked_chunk_id = LinkedChunkId::Thread(room_id, thread_root);
399
400 self.handle_linked_chunk_updates(
402 room_linked_chunk_id,
403 vec![
404 Update::NewItemsChunk { previous: None, new: CId::new(1), next: None },
405 Update::PushItems { at: Position::new(CId::new(1), 0), items: vec![event.clone()] },
406 ],
407 )
408 .await
409 .unwrap();
410
411 self.handle_linked_chunk_updates(
413 thread_linked_chunk_id,
414 vec![
415 Update::NewItemsChunk { previous: None, new: CId::new(1), next: None },
416 Update::PushItems { at: Position::new(CId::new(1), 0), items: vec![event] },
417 ],
418 )
419 .await
420 .unwrap();
421
422 let room_chunks = self.load_all_chunks(room_linked_chunk_id).await.unwrap();
424 let thread_chunks = self.load_all_chunks(thread_linked_chunk_id).await.unwrap();
425
426 assert_eq!(room_chunks.len(), 1);
427 assert_eq!(thread_chunks.len(), 1);
428
429 assert_matches!(&room_chunks[0].content, ChunkContent::Items(events) => {
431 assert_eq!(events.len(), 1);
432 assert_eq!(events[0].event_id(), Some(event_id));
433 });
434 assert_matches!(&thread_chunks[0].content, ChunkContent::Items(events) => {
435 assert_eq!(events.len(), 1);
436 assert_eq!(events[0].event_id(), Some(event_id));
437 });
438 }
439
440 async fn test_load_all_chunks_metadata(&self) {
441 let room_id = room_id!("!r0:matrix.org");
442 let linked_chunk_id = LinkedChunkId::Room(room_id);
443
444 self.handle_linked_chunk_updates(
445 linked_chunk_id,
446 vec![
447 Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
449 Update::PushItems {
451 at: Position::new(CId::new(0), 0),
452 items: vec![
453 make_test_event(room_id, "hello"),
454 make_test_event(room_id, "world"),
455 ],
456 },
457 Update::NewGapChunk {
459 previous: Some(CId::new(0)),
460 new: CId::new(1),
461 next: None,
462 gap: Gap { token: "parmesan".to_owned() },
463 },
464 Update::NewItemsChunk { previous: Some(CId::new(1)), new: CId::new(2), next: None },
466 Update::PushItems {
468 at: Position::new(CId::new(2), 0),
469 items: vec![make_test_event(room_id, "sup")],
470 },
471 Update::NewItemsChunk { previous: Some(CId::new(2)), new: CId::new(3), next: None },
473 ],
474 )
475 .await
476 .unwrap();
477
478 let metas = self.load_all_chunks_metadata(linked_chunk_id).await.unwrap();
479 assert_eq!(metas.len(), 4);
480
481 assert_eq!(metas[0].identifier, CId::new(0));
483 assert_eq!(metas[0].previous, None);
484 assert_eq!(metas[0].next, Some(CId::new(1)));
485 assert_eq!(metas[0].num_items, 2);
486
487 assert_eq!(metas[1].identifier, CId::new(1));
489 assert_eq!(metas[1].previous, Some(CId::new(0)));
490 assert_eq!(metas[1].next, Some(CId::new(2)));
491 assert_eq!(metas[1].num_items, 0);
492
493 assert_eq!(metas[2].identifier, CId::new(2));
495 assert_eq!(metas[2].previous, Some(CId::new(1)));
496 assert_eq!(metas[2].next, Some(CId::new(3)));
497 assert_eq!(metas[2].num_items, 1);
498
499 assert_eq!(metas[3].identifier, CId::new(3));
501 assert_eq!(metas[3].previous, Some(CId::new(2)));
502 assert_eq!(metas[3].next, None);
503 assert_eq!(metas[3].num_items, 0);
504 }
505
506 async fn test_load_last_chunk(&self) {
507 let room_id = room_id!("!r0:matrix.org");
508 let linked_chunk_id = LinkedChunkId::Room(room_id);
509 let event = |msg: &str| make_test_event(room_id, msg);
510
511 {
513 let (last_chunk, chunk_identifier_generator) =
514 self.load_last_chunk(linked_chunk_id).await.unwrap();
515
516 assert!(last_chunk.is_none());
517 assert_eq!(chunk_identifier_generator.current(), 0);
518 }
519
520 {
522 self.handle_linked_chunk_updates(
523 linked_chunk_id,
524 vec![
525 Update::NewItemsChunk { previous: None, new: CId::new(42), next: None },
526 Update::PushItems {
527 at: Position::new(CId::new(42), 0),
528 items: vec![event("saucisse de morteau"), event("comté")],
529 },
530 ],
531 )
532 .await
533 .unwrap();
534
535 let (last_chunk, chunk_identifier_generator) =
536 self.load_last_chunk(linked_chunk_id).await.unwrap();
537
538 assert_matches!(last_chunk, Some(last_chunk) => {
539 assert_eq!(last_chunk.identifier, 42);
540 assert!(last_chunk.previous.is_none());
541 assert!(last_chunk.next.is_none());
542 assert_matches!(last_chunk.content, ChunkContent::Items(items) => {
543 assert_eq!(items.len(), 2);
544 check_test_event(&items[0], "saucisse de morteau");
545 check_test_event(&items[1], "comté");
546 });
547 });
548 assert_eq!(chunk_identifier_generator.current(), 42);
549 }
550
551 {
553 self.handle_linked_chunk_updates(
554 linked_chunk_id,
555 vec![
556 Update::NewItemsChunk {
557 previous: Some(CId::new(42)),
558 new: CId::new(7),
559 next: None,
560 },
561 Update::PushItems {
562 at: Position::new(CId::new(7), 0),
563 items: vec![event("fondue"), event("gruyère"), event("mont d'or")],
564 },
565 ],
566 )
567 .await
568 .unwrap();
569
570 let (last_chunk, chunk_identifier_generator) =
571 self.load_last_chunk(linked_chunk_id).await.unwrap();
572
573 assert_matches!(last_chunk, Some(last_chunk) => {
574 assert_eq!(last_chunk.identifier, 7);
575 assert_matches!(last_chunk.previous, Some(previous) => {
576 assert_eq!(previous, 42);
577 });
578 assert!(last_chunk.next.is_none());
579 assert_matches!(last_chunk.content, ChunkContent::Items(items) => {
580 assert_eq!(items.len(), 3);
581 check_test_event(&items[0], "fondue");
582 check_test_event(&items[1], "gruyère");
583 check_test_event(&items[2], "mont d'or");
584 });
585 });
586 assert_eq!(chunk_identifier_generator.current(), 42);
587 }
588 }
589
590 async fn test_load_last_chunk_with_a_cycle(&self) {
591 let room_id = room_id!("!r0:matrix.org");
592 let linked_chunk_id = LinkedChunkId::Room(room_id);
593
594 self.handle_linked_chunk_updates(
595 linked_chunk_id,
596 vec![
597 Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
598 Update::NewItemsChunk {
599 previous: Some(CId::new(0)),
603 new: CId::new(1),
604 next: Some(CId::new(0)),
605 },
606 ],
607 )
608 .await
609 .unwrap();
610
611 self.load_last_chunk(linked_chunk_id).await.unwrap_err();
612 }
613
614 async fn test_load_previous_chunk(&self) {
615 let room_id = room_id!("!r0:matrix.org");
616 let linked_chunk_id = LinkedChunkId::Room(room_id);
617 let event = |msg: &str| make_test_event(room_id, msg);
618
619 {
622 let previous_chunk =
623 self.load_previous_chunk(linked_chunk_id, CId::new(153)).await.unwrap();
624
625 assert!(previous_chunk.is_none());
626 }
627
628 {
631 self.handle_linked_chunk_updates(
632 linked_chunk_id,
633 vec![Update::NewItemsChunk { previous: None, new: CId::new(42), next: None }],
634 )
635 .await
636 .unwrap();
637
638 let previous_chunk =
639 self.load_previous_chunk(linked_chunk_id, CId::new(42)).await.unwrap();
640
641 assert!(previous_chunk.is_none());
642 }
643
644 {
646 self.handle_linked_chunk_updates(
647 linked_chunk_id,
648 vec![
649 Update::NewItemsChunk {
651 previous: None,
652 new: CId::new(7),
653 next: Some(CId::new(42)),
654 },
655 Update::PushItems {
656 at: Position::new(CId::new(7), 0),
657 items: vec![event("brigand du jorat"), event("morbier")],
658 },
659 ],
660 )
661 .await
662 .unwrap();
663
664 let previous_chunk =
665 self.load_previous_chunk(linked_chunk_id, CId::new(42)).await.unwrap();
666
667 assert_matches!(previous_chunk, Some(previous_chunk) => {
668 assert_eq!(previous_chunk.identifier, 7);
669 assert!(previous_chunk.previous.is_none());
670 assert_matches!(previous_chunk.next, Some(next) => {
671 assert_eq!(next, 42);
672 });
673 assert_matches!(previous_chunk.content, ChunkContent::Items(items) => {
674 assert_eq!(items.len(), 2);
675 check_test_event(&items[0], "brigand du jorat");
676 check_test_event(&items[1], "morbier");
677 });
678 });
679 }
680 }
681
682 async fn test_linked_chunk_incremental_loading(&self) {
683 let room_id = room_id!("!r0:matrix.org");
684 let linked_chunk_id = LinkedChunkId::Room(room_id);
685 let event = |msg: &str| make_test_event(room_id, msg);
686
687 {
689 let (last_chunk, chunk_identifier_generator) =
690 self.load_last_chunk(linked_chunk_id).await.unwrap();
691
692 assert!(last_chunk.is_none());
693 assert_eq!(chunk_identifier_generator.current(), 0);
694 }
695
696 self.handle_linked_chunk_updates(
697 linked_chunk_id,
698 vec![
699 Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
701 Update::PushItems {
703 at: Position::new(CId::new(0), 0),
704 items: vec![event("a"), event("b")],
705 },
706 Update::NewGapChunk {
708 previous: Some(CId::new(0)),
709 new: CId::new(1),
710 next: None,
711 gap: Gap { token: "morbier".to_owned() },
712 },
713 Update::NewItemsChunk { previous: Some(CId::new(1)), new: CId::new(2), next: None },
715 Update::PushItems {
717 at: Position::new(CId::new(2), 0),
718 items: vec![event("c"), event("d"), event("e")],
719 },
720 ],
721 )
722 .await
723 .unwrap();
724
725 let mut linked_chunk = {
727 let (last_chunk, chunk_identifier_generator) =
728 self.load_last_chunk(linked_chunk_id).await.unwrap();
729
730 assert_eq!(chunk_identifier_generator.current(), 2);
731
732 let linked_chunk = lazy_loader::from_last_chunk::<DEFAULT_CHUNK_CAPACITY, _, _>(
733 last_chunk,
734 chunk_identifier_generator,
735 )
736 .unwrap() .unwrap(); let mut rchunks = linked_chunk.rchunks();
740
741 assert_matches!(rchunks.next(), Some(chunk) => {
743 assert_eq!(chunk.identifier(), 2);
744 assert_eq!(chunk.lazy_previous(), Some(CId::new(1)));
745
746 assert_matches!(chunk.content(), ChunkContent::Items(events) => {
747 assert_eq!(events.len(), 3);
748 check_test_event(&events[0], "c");
749 check_test_event(&events[1], "d");
750 check_test_event(&events[2], "e");
751 });
752 });
753
754 assert!(rchunks.next().is_none());
755
756 linked_chunk
757 };
758
759 {
761 let first_chunk = linked_chunk.chunks().next().unwrap().identifier();
762 let previous_chunk =
763 self.load_previous_chunk(linked_chunk_id, first_chunk).await.unwrap().unwrap();
764
765 lazy_loader::insert_new_first_chunk(&mut linked_chunk, previous_chunk).unwrap();
766
767 let mut rchunks = linked_chunk.rchunks();
768
769 assert_matches!(rchunks.next(), Some(chunk) => {
771 assert_eq!(chunk.identifier(), 2);
772 assert!(chunk.lazy_previous().is_none());
773
774 assert_matches!(chunk.content(), ChunkContent::Items(events) => {
776 assert_eq!(events.len(), 3);
777 check_test_event(&events[0], "c");
778 check_test_event(&events[1], "d");
779 check_test_event(&events[2], "e");
780 });
781 });
782
783 assert_matches!(rchunks.next(), Some(chunk) => {
785 assert_eq!(chunk.identifier(), 1);
786 assert_eq!(chunk.lazy_previous(), Some(CId::new(0)));
787
788 assert_matches!(chunk.content(), ChunkContent::Gap(gap) => {
789 assert_eq!(gap.token, "morbier");
790 });
791 });
792
793 assert!(rchunks.next().is_none());
794 }
795
796 {
798 let first_chunk = linked_chunk.chunks().next().unwrap().identifier();
799 let previous_chunk =
800 self.load_previous_chunk(linked_chunk_id, first_chunk).await.unwrap().unwrap();
801
802 lazy_loader::insert_new_first_chunk(&mut linked_chunk, previous_chunk).unwrap();
803
804 let mut rchunks = linked_chunk.rchunks();
805
806 assert_matches!(rchunks.next(), Some(chunk) => {
808 assert_eq!(chunk.identifier(), 2);
809 assert!(chunk.lazy_previous().is_none());
810
811 assert_matches!(chunk.content(), ChunkContent::Items(events) => {
813 assert_eq!(events.len(), 3);
814 check_test_event(&events[0], "c");
815 check_test_event(&events[1], "d");
816 check_test_event(&events[2], "e");
817 });
818 });
819
820 assert_matches!(rchunks.next(), Some(chunk) => {
822 assert_eq!(chunk.identifier(), 1);
823 assert!(chunk.lazy_previous().is_none());
824
825 assert_matches!(chunk.content(), ChunkContent::Gap(gap) => {
827 assert_eq!(gap.token, "morbier");
828 });
829 });
830
831 assert_matches!(rchunks.next(), Some(chunk) => {
833 assert_eq!(chunk.identifier(), 0);
834 assert!(chunk.lazy_previous().is_none());
835
836 assert_matches!(chunk.content(), ChunkContent::Items(events) => {
837 assert_eq!(events.len(), 2);
838 check_test_event(&events[0], "a");
839 check_test_event(&events[1], "b");
840 });
841 });
842
843 assert!(rchunks.next().is_none());
844 }
845
846 {
848 let first_chunk = linked_chunk.chunks().next().unwrap().identifier();
849 let previous_chunk =
850 self.load_previous_chunk(linked_chunk_id, first_chunk).await.unwrap();
851
852 assert!(previous_chunk.is_none());
853 }
854
855 {
858 let mut chunks = linked_chunk.chunks();
859
860 assert_matches!(chunks.next(), Some(chunk) => {
862 assert_eq!(chunk.identifier(), 0);
863 assert!(chunk.lazy_previous().is_none());
864
865 assert_matches!(chunk.content(), ChunkContent::Items(events) => {
866 assert_eq!(events.len(), 2);
867 check_test_event(&events[0], "a");
868 check_test_event(&events[1], "b");
869 });
870 });
871
872 assert_matches!(chunks.next(), Some(chunk) => {
874 assert_eq!(chunk.identifier(), 1);
875 assert!(chunk.lazy_previous().is_none());
876
877 assert_matches!(chunk.content(), ChunkContent::Gap(gap) => {
878 assert_eq!(gap.token, "morbier");
879 });
880 });
881
882 assert_matches!(chunks.next(), Some(chunk) => {
884 assert_eq!(chunk.identifier(), 2);
885 assert!(chunk.lazy_previous().is_none());
886
887 assert_matches!(chunk.content(), ChunkContent::Items(events) => {
888 assert_eq!(events.len(), 3);
889 check_test_event(&events[0], "c");
890 check_test_event(&events[1], "d");
891 check_test_event(&events[2], "e");
892 });
893 });
894
895 assert!(chunks.next().is_none());
896 }
897 }
898
899 async fn test_linked_chunk_remove_chunk(&self) {
900 let room_id = &DEFAULT_TEST_ROOM_ID;
901 let linked_chunk_id = LinkedChunkId::Room(room_id);
902
903 self.handle_linked_chunk_updates(
904 linked_chunk_id,
905 vec![
906 Update::NewGapChunk {
907 previous: None,
908 new: CId::new(42),
909 next: None,
910 gap: Gap { token: "raclette".to_owned() },
911 },
912 Update::NewGapChunk {
913 previous: Some(CId::new(42)),
914 new: CId::new(43),
915 next: None,
916 gap: Gap { token: "fondue".to_owned() },
917 },
918 Update::NewGapChunk {
919 previous: Some(CId::new(43)),
920 new: CId::new(44),
921 next: None,
922 gap: Gap { token: "tartiflette".to_owned() },
923 },
924 Update::RemoveChunk(CId::new(43)),
925 ],
926 )
927 .await
928 .unwrap();
929
930 let mut chunks = self.load_all_chunks(linked_chunk_id).await.unwrap();
931
932 assert_eq!(chunks.len(), 2);
933
934 let c = chunks.remove(0);
936 assert_eq!(c.identifier, CId::new(42));
937 assert_eq!(c.previous, None);
938 assert_eq!(c.next, Some(CId::new(44)));
939 assert_matches!(c.content, ChunkContent::Gap(gap) => {
940 assert_eq!(gap.token, "raclette");
941 });
942
943 let c = chunks.remove(0);
944 assert_eq!(c.identifier, CId::new(44));
945 assert_eq!(c.previous, Some(CId::new(42)));
946 assert_eq!(c.next, None);
947 assert_matches!(c.content, ChunkContent::Gap(gap) => {
948 assert_eq!(gap.token, "tartiflette");
949 });
950 }
951
952 async fn test_linked_chunk_replace_item(&self) {
953 let room_id = &DEFAULT_TEST_ROOM_ID;
954 let linked_chunk_id = LinkedChunkId::Room(room_id);
955 let event_id = event_id!("$world");
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_with_event_id(room_id, "world", Some(event_id)),
966 ],
967 },
968 Update::ReplaceItem {
969 at: Position::new(CId::new(42), 1),
970 item: make_test_event_with_event_id(room_id, "yolo", Some(event_id)),
971 },
972 ],
973 )
974 .await
975 .unwrap();
976
977 let mut chunks = self.load_all_chunks(linked_chunk_id).await.unwrap();
978
979 assert_eq!(chunks.len(), 1);
980
981 let c = chunks.remove(0);
982 assert_eq!(c.identifier, CId::new(42));
983 assert_eq!(c.previous, None);
984 assert_eq!(c.next, None);
985 assert_matches!(c.content, ChunkContent::Items(events) => {
986 assert_eq!(events.len(), 2);
987 check_test_event(&events[0], "hello");
988 check_test_event(&events[1], "yolo");
989 });
990 }
991
992 async fn test_linked_chunk_remove_item(&self) {
993 let room_id = *DEFAULT_TEST_ROOM_ID;
994 let linked_chunk_id = LinkedChunkId::Room(room_id);
995
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, "one"),
1004 make_test_event(room_id, "two"),
1005 make_test_event(room_id, "three"),
1006 make_test_event(room_id, "four"),
1007 make_test_event(room_id, "five"),
1008 make_test_event(room_id, "six"),
1009 ],
1010 },
1011 Update::RemoveItem { at: Position::new(CId::new(42), 2) },
1012 Update::PushItems {
1022 at: Position::new(CId::new(42), 5),
1023 items: vec![make_test_event(room_id, "seven")],
1024 },
1025 ],
1026 )
1027 .await
1028 .unwrap();
1029
1030 let mut chunks = self.load_all_chunks(linked_chunk_id).await.unwrap();
1031
1032 assert_eq!(chunks.len(), 1);
1033
1034 let c = chunks.remove(0);
1035 assert_eq!(c.identifier, CId::new(42));
1036 assert_eq!(c.previous, None);
1037 assert_eq!(c.next, None);
1038 assert_matches!(c.content, ChunkContent::Items(events) => {
1039 assert_eq!(events.len(), 6);
1040 check_test_event(&events[0], "one");
1041 check_test_event(&events[1], "two");
1042 check_test_event(&events[2], "four");
1043 check_test_event(&events[3], "five");
1044 check_test_event(&events[4], "six");
1045 check_test_event(&events[5], "seven");
1046 });
1047
1048 let metas = self.load_all_chunks_metadata(linked_chunk_id).await.unwrap();
1050 assert_eq!(metas.len(), 1);
1051 assert_eq!(metas[0].num_items, 6);
1052 }
1053
1054 async fn test_linked_chunk_detach_last_items(&self) {
1055 let room_id = *DEFAULT_TEST_ROOM_ID;
1056 let linked_chunk_id = LinkedChunkId::Room(room_id);
1057
1058 self.handle_linked_chunk_updates(
1059 linked_chunk_id,
1060 vec![
1061 Update::NewItemsChunk { previous: None, new: CId::new(42), next: None },
1062 Update::PushItems {
1063 at: Position::new(CId::new(42), 0),
1064 items: vec![
1065 make_test_event(room_id, "hello"),
1066 make_test_event(room_id, "world"),
1067 make_test_event(room_id, "howdy"),
1068 ],
1069 },
1070 Update::DetachLastItems { at: Position::new(CId::new(42), 1) },
1071 ],
1072 )
1073 .await
1074 .unwrap();
1075
1076 let mut chunks = self.load_all_chunks(linked_chunk_id).await.unwrap();
1077
1078 assert_eq!(chunks.len(), 1);
1079
1080 let c = chunks.remove(0);
1081 assert_eq!(c.identifier, CId::new(42));
1082 assert_eq!(c.previous, None);
1083 assert_eq!(c.next, None);
1084 assert_matches!(c.content, ChunkContent::Items(events) => {
1085 assert_eq!(events.len(), 1);
1086 check_test_event(&events[0], "hello");
1087 });
1088 }
1089
1090 async fn test_linked_chunk_start_end_reattach_items(&self) {
1091 let room_id = *DEFAULT_TEST_ROOM_ID;
1092 let linked_chunk_id = LinkedChunkId::Room(room_id);
1093
1094 self.handle_linked_chunk_updates(
1098 linked_chunk_id,
1099 vec![
1100 Update::NewItemsChunk { previous: None, new: CId::new(42), next: None },
1101 Update::PushItems {
1102 at: Position::new(CId::new(42), 0),
1103 items: vec![
1104 make_test_event(room_id, "hello"),
1105 make_test_event(room_id, "world"),
1106 make_test_event(room_id, "howdy"),
1107 ],
1108 },
1109 Update::StartReattachItems,
1110 Update::EndReattachItems,
1111 ],
1112 )
1113 .await
1114 .unwrap();
1115
1116 let mut chunks = self.load_all_chunks(linked_chunk_id).await.unwrap();
1117
1118 assert_eq!(chunks.len(), 1);
1119
1120 let c = chunks.remove(0);
1121 assert_eq!(c.identifier, CId::new(42));
1122 assert_eq!(c.previous, None);
1123 assert_eq!(c.next, None);
1124 assert_matches!(c.content, ChunkContent::Items(events) => {
1125 assert_eq!(events.len(), 3);
1126 check_test_event(&events[0], "hello");
1127 check_test_event(&events[1], "world");
1128 check_test_event(&events[2], "howdy");
1129 });
1130 }
1131
1132 async fn test_linked_chunk_clear(&self) {
1133 let room_id = *DEFAULT_TEST_ROOM_ID;
1134 let linked_chunk_id = LinkedChunkId::Room(room_id);
1135 let event_0 = make_test_event(room_id, "hello");
1136 let event_1 = make_test_event(room_id, "world");
1137 let event_2 = make_test_event(room_id, "howdy");
1138
1139 self.handle_linked_chunk_updates(
1140 linked_chunk_id,
1141 vec![
1142 Update::NewItemsChunk { previous: None, new: CId::new(42), next: None },
1143 Update::NewGapChunk {
1144 previous: Some(CId::new(42)),
1145 new: CId::new(54),
1146 next: None,
1147 gap: Gap { token: "fondue".to_owned() },
1148 },
1149 Update::PushItems {
1150 at: Position::new(CId::new(42), 0),
1151 items: vec![event_0.clone(), event_1, event_2],
1152 },
1153 Update::Clear,
1154 ],
1155 )
1156 .await
1157 .unwrap();
1158
1159 let chunks = self.load_all_chunks(linked_chunk_id).await.unwrap();
1160 assert!(chunks.is_empty());
1161 }
1162
1163 async fn test_linked_chunk_clear_and_reinsert(&self) {
1164 let room_id = *DEFAULT_TEST_ROOM_ID;
1165 let linked_chunk_id = LinkedChunkId::Room(room_id);
1166 let event_0 = make_test_event(room_id, "hello");
1167 let event_1 = make_test_event(room_id, "world");
1168 let event_2 = make_test_event(room_id, "howdy");
1169
1170 self.handle_linked_chunk_updates(
1171 linked_chunk_id,
1172 vec![
1173 Update::NewItemsChunk { previous: None, new: CId::new(42), next: None },
1174 Update::NewGapChunk {
1175 previous: Some(CId::new(42)),
1176 new: CId::new(54),
1177 next: None,
1178 gap: Gap { token: "fondue".to_owned() },
1179 },
1180 Update::PushItems {
1181 at: Position::new(CId::new(42), 0),
1182 items: vec![event_0.clone(), event_1, event_2],
1183 },
1184 Update::Clear,
1185 ],
1186 )
1187 .await
1188 .unwrap();
1189
1190 let chunks = self.load_all_chunks(linked_chunk_id).await.unwrap();
1191 assert!(chunks.is_empty());
1192
1193 self.handle_linked_chunk_updates(
1195 linked_chunk_id,
1196 vec![
1197 Update::NewItemsChunk { previous: None, new: CId::new(42), next: None },
1198 Update::PushItems { at: Position::new(CId::new(42), 0), items: vec![event_0] },
1199 ],
1200 )
1201 .await
1202 .unwrap();
1203 }
1204
1205 async fn test_rebuild_empty_linked_chunk(&self) {
1206 let linked_chunk = lazy_loader::from_all_chunks::<3, _, _>(
1208 self.load_all_chunks(LinkedChunkId::Room(&DEFAULT_TEST_ROOM_ID)).await.unwrap(),
1209 )
1210 .unwrap();
1211 assert!(linked_chunk.is_none());
1212 }
1213
1214 async fn test_linked_chunk_multiple_rooms(&self) {
1215 let room1 = room_id!("!realcheeselovers:raclette.fr");
1216 let linked_chunk_id1 = LinkedChunkId::Room(room1);
1217 let room2 = room_id!("!realcheeselovers:fondue.ch");
1218 let linked_chunk_id2 = LinkedChunkId::Room(room2);
1219
1220 self.handle_linked_chunk_updates(
1224 linked_chunk_id1,
1225 vec![
1226 Update::NewItemsChunk { previous: None, new: CId::new(42), next: None },
1227 Update::PushItems {
1228 at: Position::new(CId::new(42), 0),
1229 items: vec![
1230 make_test_event(room1, "best cheese is raclette"),
1231 make_test_event(room1, "obviously"),
1232 ],
1233 },
1234 ],
1235 )
1236 .await
1237 .unwrap();
1238
1239 self.handle_linked_chunk_updates(
1240 linked_chunk_id2,
1241 vec![
1242 Update::NewItemsChunk { previous: None, new: CId::new(42), next: None },
1243 Update::PushItems {
1244 at: Position::new(CId::new(42), 0),
1245 items: vec![make_test_event(room1, "beaufort is the best")],
1246 },
1247 ],
1248 )
1249 .await
1250 .unwrap();
1251
1252 let mut chunks_room1 = self.load_all_chunks(linked_chunk_id1).await.unwrap();
1254 assert_eq!(chunks_room1.len(), 1);
1255
1256 let c = chunks_room1.remove(0);
1257 assert_matches!(c.content, ChunkContent::Items(events) => {
1258 assert_eq!(events.len(), 2);
1259 check_test_event(&events[0], "best cheese is raclette");
1260 check_test_event(&events[1], "obviously");
1261 });
1262
1263 let mut chunks_room2 = self.load_all_chunks(linked_chunk_id2).await.unwrap();
1265 assert_eq!(chunks_room2.len(), 1);
1266
1267 let c = chunks_room2.remove(0);
1268 assert_matches!(c.content, ChunkContent::Items(events) => {
1269 assert_eq!(events.len(), 1);
1270 check_test_event(&events[0], "beaufort is the best");
1271 });
1272 }
1273
1274 async fn test_load_and_update_thread_info(&self) {
1275 let room_id = room_id!("!r0");
1276 let thread_id = event_id!("$t0");
1277
1278 let ThreadInfo { read_receipts } = self.load_thread_info(room_id, thread_id).await.unwrap();
1282 let ReadReceipts { num_unread, num_notifications, num_mentions, latest_active, pending } =
1283 read_receipts;
1284 assert_eq!(num_unread, 0);
1285 assert_eq!(num_notifications, 0);
1286 assert_eq!(num_mentions, 0);
1287 assert!(latest_active.is_none());
1288 assert!(pending.is_empty());
1289
1290 let mut thread_info = self.load_thread_info(room_id, thread_id).await.unwrap();
1294 let ThreadInfo { read_receipts } = &thread_info;
1295 let ReadReceipts { num_unread, num_notifications, num_mentions, latest_active, pending } =
1296 read_receipts;
1297 assert_eq!(*num_unread, 0);
1298 assert_eq!(*num_notifications, 0);
1299 assert_eq!(*num_mentions, 0);
1300 assert!(latest_active.is_none());
1301 assert!(pending.is_empty());
1302
1303 thread_info.read_receipts.num_unread = 1;
1305 thread_info.read_receipts.num_notifications = 2;
1306 self.update_thread_info(room_id, thread_id, &thread_info).await.unwrap();
1307
1308 let ThreadInfo { read_receipts } = self.load_thread_info(room_id, thread_id).await.unwrap();
1312 let ReadReceipts { num_unread, num_notifications, num_mentions, latest_active, pending } =
1313 read_receipts;
1314 assert_eq!(num_unread, 1);
1315 assert_eq!(num_notifications, 2);
1316 assert_eq!(num_mentions, 0);
1317 assert!(latest_active.is_none());
1318 assert!(pending.is_empty());
1319 }
1320
1321 async fn test_clear_all_events(&self) {
1322 let linked_chunk_ids = [
1323 LinkedChunkId::Room(room_id!("!r0")),
1324 LinkedChunkId::Thread(room_id!("!r1"), event_id!("$r1_thread_root0")),
1325 LinkedChunkId::PinnedEvents(room_id!("!r2")),
1326 ];
1328
1329 for linked_chunk_id in linked_chunk_ids {
1331 let room_id = linked_chunk_id.room_id();
1332
1333 if let LinkedChunkId::Thread(_, thread_id) = &linked_chunk_id {
1336 self.load_thread_info(room_id, thread_id).await.unwrap();
1337 }
1338
1339 self.handle_linked_chunk_updates(
1340 linked_chunk_id,
1341 vec![
1342 Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
1344 Update::PushItems {
1346 at: Position::new(CId::new(0), 0),
1347 items: vec![
1348 make_test_event(room_id, "foo"),
1349 make_test_event(room_id, "bar"),
1350 make_test_event(room_id, "baz"),
1351 ],
1352 },
1353 ],
1354 )
1355 .await
1356 .unwrap();
1357
1358 assert!(
1360 lazy_loader::from_all_chunks::<3, _, _>(
1361 self.load_all_chunks(linked_chunk_id).await.unwrap()
1362 )
1363 .unwrap()
1364 .is_some()
1365 );
1366
1367 assert_eq!(self.get_room_events(room_id, None, None).await.unwrap().len(), 3);
1369 }
1370
1371 self.clear_all_events(None).await.unwrap();
1373
1374 for linked_chunk_id in linked_chunk_ids {
1376 let room_id = linked_chunk_id.room_id();
1377
1378 assert!(
1380 lazy_loader::from_all_chunks::<3, _, _>(
1381 self.load_all_chunks(linked_chunk_id).await.unwrap()
1382 )
1383 .unwrap()
1384 .is_none()
1385 );
1386
1387 assert!(self.get_room_events(room_id, None, None).await.unwrap().is_empty());
1389 }
1390 }
1391
1392 async fn test_clear_all_events_for_specific_room(&self) {
1393 let linked_chunk_ids_for_room_0 = [
1394 LinkedChunkId::Room(room_id!("!r0")),
1395 LinkedChunkId::Thread(room_id!("!r0"), event_id!("$r0_thread_root")),
1396 LinkedChunkId::PinnedEvents(room_id!("!r0")),
1397 ];
1398 let linked_chunk_ids_for_room_1 = [
1399 LinkedChunkId::Room(room_id!("!r1")),
1400 LinkedChunkId::Thread(room_id!("!r1"), event_id!("$r1_thread_root")),
1401 LinkedChunkId::PinnedEvents(room_id!("!r1")),
1402 ];
1403 let linked_chunk_ids_for_room_2 = [
1404 LinkedChunkId::Room(room_id!("!r2")),
1405 LinkedChunkId::Thread(room_id!("!r2"), event_id!("$r2_thread_root")),
1406 LinkedChunkId::PinnedEvents(room_id!("!r2")),
1407 ];
1408
1409 for linked_chunk_id in linked_chunk_ids_for_room_0
1411 .iter()
1412 .chain(&linked_chunk_ids_for_room_1)
1413 .chain(&linked_chunk_ids_for_room_2)
1414 {
1415 let room_id = linked_chunk_id.room_id();
1416
1417 if let LinkedChunkId::Thread(_, thread_id) = &linked_chunk_id {
1420 self.load_thread_info(room_id, thread_id).await.unwrap();
1421 }
1422
1423 self.handle_linked_chunk_updates(
1424 *linked_chunk_id,
1425 vec![
1426 Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
1428 Update::PushItems {
1430 at: Position::new(CId::new(0), 0),
1431 items: vec![
1432 make_test_event(room_id, "foo"),
1433 make_test_event(room_id, "bar"),
1434 make_test_event(room_id, "baz"),
1435 ],
1436 },
1437 ],
1438 )
1439 .await
1440 .unwrap();
1441
1442 assert!(
1444 lazy_loader::from_all_chunks::<3, _, _>(
1445 self.load_all_chunks(*linked_chunk_id).await.unwrap()
1446 )
1447 .unwrap()
1448 .is_some()
1449 );
1450
1451 assert_eq!(self.get_room_events(room_id, None, None).await.unwrap().len(), 3);
1453 }
1454
1455 self.clear_all_events(Some(linked_chunk_ids_for_room_1[0].room_id())).await.unwrap();
1457
1458 for linked_chunk_id in linked_chunk_ids_for_room_1 {
1460 let room_id = linked_chunk_id.room_id();
1461
1462 assert!(
1464 lazy_loader::from_all_chunks::<3, _, _>(
1465 self.load_all_chunks(linked_chunk_id).await.unwrap()
1466 )
1467 .unwrap()
1468 .is_none()
1469 );
1470
1471 assert!(self.get_room_events(room_id, None, None).await.unwrap().is_empty());
1473 }
1474
1475 for linked_chunk_id in
1477 linked_chunk_ids_for_room_0.iter().chain(&linked_chunk_ids_for_room_2)
1478 {
1479 let room_id = linked_chunk_id.room_id();
1480
1481 assert!(
1483 lazy_loader::from_all_chunks::<3, _, _>(
1484 self.load_all_chunks(*linked_chunk_id).await.unwrap()
1485 )
1486 .unwrap()
1487 .is_some()
1488 );
1489
1490 assert_eq!(self.get_room_events(room_id, None, None).await.unwrap().len(), 3);
1492 }
1493 }
1494
1495 async fn test_filter_duplicated_events(&self) {
1496 let room_id = room_id!("!r0:matrix.org");
1497 let linked_chunk_id = LinkedChunkId::Room(room_id);
1498 let another_room_id = room_id!("!r1:matrix.org");
1499 let another_linked_chunk_id = LinkedChunkId::Room(another_room_id);
1500 let event = |msg: &str| make_test_event(room_id, msg);
1501
1502 let event_comte = event("comté");
1503 let event_brigand = event("brigand du jorat");
1504 let event_raclette = event("raclette");
1505 let event_morbier = event("morbier");
1506 let event_gruyere = event("gruyère");
1507 let event_tome = event("tome");
1508 let event_mont_dor = event("mont d'or");
1509
1510 self.handle_linked_chunk_updates(
1511 linked_chunk_id,
1512 vec![
1513 Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
1514 Update::PushItems {
1515 at: Position::new(CId::new(0), 0),
1516 items: vec![event_comte.clone(), event_brigand.clone()],
1517 },
1518 Update::NewGapChunk {
1519 previous: Some(CId::new(0)),
1520 new: CId::new(1),
1521 next: None,
1522 gap: Gap { token: "brillat-savarin".to_owned() },
1523 },
1524 Update::NewItemsChunk { previous: Some(CId::new(1)), new: CId::new(2), next: None },
1525 Update::PushItems {
1526 at: Position::new(CId::new(2), 0),
1527 items: vec![event_morbier.clone(), event_mont_dor.clone()],
1528 },
1529 ],
1530 )
1531 .await
1532 .unwrap();
1533
1534 self.handle_linked_chunk_updates(
1537 another_linked_chunk_id,
1538 vec![
1539 Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
1540 Update::PushItems {
1541 at: Position::new(CId::new(0), 0),
1542 items: vec![event_tome.clone()],
1543 },
1544 ],
1545 )
1546 .await
1547 .unwrap();
1548
1549 let duplicated_events = BTreeMap::from_iter(
1550 self.filter_duplicated_events(
1551 linked_chunk_id,
1552 vec![
1553 event_comte.event_id().unwrap().to_owned(),
1554 event_raclette.event_id().unwrap().to_owned(),
1555 event_morbier.event_id().unwrap().to_owned(),
1556 event_gruyere.event_id().unwrap().to_owned(),
1557 event_tome.event_id().unwrap().to_owned(),
1558 event_mont_dor.event_id().unwrap().to_owned(),
1559 ],
1560 )
1561 .await
1562 .unwrap(),
1563 );
1564
1565 assert_eq!(duplicated_events.len(), 3);
1566
1567 assert_eq!(
1568 *duplicated_events.get(event_comte.event_id().unwrap()).unwrap(),
1569 Position::new(CId::new(0), 0)
1570 );
1571 assert_eq!(
1572 *duplicated_events.get(event_morbier.event_id().unwrap()).unwrap(),
1573 Position::new(CId::new(2), 0)
1574 );
1575 assert_eq!(
1576 *duplicated_events.get(event_mont_dor.event_id().unwrap()).unwrap(),
1577 Position::new(CId::new(2), 1)
1578 );
1579 }
1580
1581 async fn test_filter_duplicate_events_no_events(&self) {
1582 let room_id = *DEFAULT_TEST_ROOM_ID;
1583 let linked_chunk_id = LinkedChunkId::Room(room_id);
1584 let duplicates = self.filter_duplicated_events(linked_chunk_id, Vec::new()).await.unwrap();
1585 assert!(duplicates.is_empty());
1586 }
1587
1588 async fn test_find_event(&self) {
1589 let room_id = room_id!("!r0:matrix.org");
1590 let another_room_id = room_id!("!r1:matrix.org");
1591 let another_linked_chunk_id = LinkedChunkId::Room(another_room_id);
1592 let event = |msg: &str| make_test_event(room_id, msg);
1593
1594 let event_comte = event("comté");
1595 let event_gruyere = event("gruyère");
1596
1597 self.handle_linked_chunk_updates(
1599 LinkedChunkId::Room(room_id),
1600 vec![
1601 Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
1602 Update::PushItems {
1603 at: Position::new(CId::new(0), 0),
1604 items: vec![event_comte.clone()],
1605 },
1606 ],
1607 )
1608 .await
1609 .unwrap();
1610
1611 self.handle_linked_chunk_updates(
1613 another_linked_chunk_id,
1614 vec![
1615 Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
1616 Update::PushItems {
1617 at: Position::new(CId::new(0), 0),
1618 items: vec![event_gruyere.clone()],
1619 },
1620 ],
1621 )
1622 .await
1623 .unwrap();
1624
1625 let event = self
1627 .find_event(room_id, event_comte.event_id().unwrap())
1628 .await
1629 .expect("failed to query for finding an event")
1630 .expect("failed to find an event");
1631
1632 assert_eq!(event.event_id(), event_comte.event_id());
1633
1634 assert!(
1636 self.find_event(room_id, event_gruyere.event_id().unwrap())
1637 .await
1638 .expect("failed to query for finding an event")
1639 .is_none()
1640 );
1641
1642 self.clear_all_events(None).await.expect("failed to clear all rooms chunks");
1644 assert!(
1645 self.find_event(room_id, event_comte.event_id().unwrap())
1646 .await
1647 .expect("failed to query for finding an event")
1648 .is_none()
1649 );
1650 }
1651
1652 async fn test_find_event_when_event_in_room_and_thread(&self) {
1653 let room_id = *DEFAULT_TEST_ROOM_ID;
1654 let thread_root = event_id!("$thread_root");
1655
1656 let room_event_id = event_id!("$room_event");
1658 let room_event = make_test_event_with_event_id(room_id, "room event", Some(room_event_id));
1659
1660 let thread_event_id = event_id!("$thread_event");
1662 let thread_event =
1663 make_test_event_with_event_id(room_id, "thread event", Some(thread_event_id));
1664
1665 let room_and_thread_event_id = event_id!("$room_and_thread");
1668 let room_and_thread_event = make_test_event_with_event_id(
1669 room_id,
1670 "room and thread",
1671 Some(room_and_thread_event_id),
1672 );
1673
1674 let room_linked_chunk_id = LinkedChunkId::Room(room_id);
1675 let thread_linked_chunk_id = LinkedChunkId::Thread(room_id, thread_root);
1676
1677 self.handle_linked_chunk_updates(
1679 room_linked_chunk_id,
1680 vec![
1681 Update::NewItemsChunk { previous: None, new: CId::new(1), next: None },
1682 Update::PushItems {
1683 at: Position::new(CId::new(1), 0),
1684 items: vec![room_event, room_and_thread_event.clone()],
1685 },
1686 ],
1687 )
1688 .await
1689 .unwrap();
1690
1691 self.handle_linked_chunk_updates(
1693 thread_linked_chunk_id,
1694 vec![
1695 Update::NewItemsChunk { previous: None, new: CId::new(1), next: None },
1696 Update::PushItems {
1697 at: Position::new(CId::new(1), 0),
1698 items: vec![thread_event, room_and_thread_event],
1699 },
1700 ],
1701 )
1702 .await
1703 .unwrap();
1704
1705 assert_matches!(self.find_event(room_id, room_event_id).await, Ok(Some(event)) => {
1707 assert_eq!(event.event_id().unwrap(), room_event_id)
1708 });
1709
1710 assert_matches!(self.find_event(room_id, thread_event_id).await, Ok(Some(event)) => {
1712 assert_eq!(event.event_id().unwrap(), thread_event_id)
1713 });
1714
1715 assert_matches!(self.find_event(room_id, room_and_thread_event_id).await, Ok(Some(event)) => {
1717 assert_eq!(event.event_id().unwrap(), room_and_thread_event_id);
1718 });
1719 }
1720
1721 async fn test_find_event_relations(&self) {
1722 let room_id = room_id!("!r0:matrix.org");
1723 let another_room_id = room_id!("!r1:matrix.org");
1724
1725 let f = EventFactory::new().room(room_id).sender(*ALICE);
1726
1727 let eid1 = event_id!("$event1:matrix.org");
1729 let e1 = f.text_msg("comter").event_id(eid1).into_event();
1730
1731 let edit_eid1 = event_id!("$edit_event1:matrix.org");
1732 let edit_e1 = f
1733 .text_msg("* comté")
1734 .event_id(edit_eid1)
1735 .edit(eid1, RoomMessageEventContentWithoutRelation::text_plain("comté"))
1736 .into_event();
1737
1738 let reaction_eid1 = event_id!("$reaction_event1:matrix.org");
1739 let reaction_e1 = f.reaction(eid1, "👍").event_id(reaction_eid1).into_event();
1740
1741 let eid2 = event_id!("$event2:matrix.org");
1742 let e2 = f.text_msg("galette saucisse").event_id(eid2).into_event();
1743
1744 let f = f.room(another_room_id);
1746
1747 let eid3 = event_id!("$event3:matrix.org");
1748 let e3 = f.text_msg("gruyère").event_id(eid3).into_event();
1749
1750 let reaction_eid3 = event_id!("$reaction_event3:matrix.org");
1751 let reaction_e3 = f.reaction(eid3, "👍").event_id(reaction_eid3).into_event();
1752
1753 self.save_event(room_id, e1).await.unwrap();
1755 self.save_event(room_id, edit_e1).await.unwrap();
1756 self.save_event(room_id, reaction_e1.clone()).await.unwrap();
1757 self.save_event(room_id, e2).await.unwrap();
1758 self.save_event(another_room_id, e3).await.unwrap();
1759 self.save_event(another_room_id, reaction_e3).await.unwrap();
1760
1761 let relations = self.find_event_relations(room_id, eid1, None).await.unwrap();
1763 assert_eq!(relations.len(), 2);
1764 assert!(
1766 relations.iter().any(|(ev, pos)| ev.event_id() == Some(edit_eid1) && pos.is_none())
1767 );
1768 assert!(
1769 relations.iter().any(|(ev, pos)| ev.event_id() == Some(reaction_eid1) && pos.is_none())
1770 );
1771
1772 let relations = self
1774 .find_event_relations(room_id, eid1, Some(&[RelationType::Replacement]))
1775 .await
1776 .unwrap();
1777 assert_eq!(relations.len(), 1);
1778 assert_eq!(relations[0].0.event_id(), Some(edit_eid1));
1779
1780 let relations = self
1781 .find_event_relations(
1782 room_id,
1783 eid1,
1784 Some(&[RelationType::Replacement, RelationType::Annotation]),
1785 )
1786 .await
1787 .unwrap();
1788 assert_eq!(relations.len(), 2);
1789 assert!(relations.iter().any(|r| r.0.event_id() == Some(edit_eid1)));
1790 assert!(relations.iter().any(|r| r.0.event_id() == Some(reaction_eid1)));
1791
1792 let relations = self
1794 .find_event_relations(another_room_id, eid1, Some(&[RelationType::Replacement]))
1795 .await
1796 .unwrap();
1797 assert!(relations.is_empty());
1798
1799 self.handle_linked_chunk_updates(
1804 LinkedChunkId::Room(room_id),
1805 vec![
1806 Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
1807 Update::PushItems { at: Position::new(CId::new(0), 0), items: vec![reaction_e1] },
1808 ],
1809 )
1810 .await
1811 .unwrap();
1812
1813 let relations = self.find_event_relations(room_id, eid1, None).await.unwrap();
1816
1817 assert!(relations.iter().any(|(ev, pos)| {
1819 ev.event_id() == Some(reaction_eid1) && *pos == Some(Position::new(CId::new(0), 0))
1820 }));
1821
1822 assert!(
1824 relations.iter().any(|(ev, pos)| ev.event_id() == Some(edit_eid1) && pos.is_none())
1825 );
1826 }
1827
1828 async fn test_find_event_relations_when_event_in_room_and_thread(&self) {
1829 let room_id = *DEFAULT_TEST_ROOM_ID;
1830 let thread_root = event_id!("$thread_root");
1831
1832 let event_id = event_id!("$event");
1835 let event = make_test_event_with_event_id(room_id, "event", Some(event_id));
1836
1837 let extra_thread_event_id = event_id!("$extra_thread_event");
1840 let extra_thread_event = make_test_event_with_event_id(
1841 room_id,
1842 "extra thread event",
1843 Some(extra_thread_event_id),
1844 );
1845
1846 let room_reaction_id = event_id!("$room_reaction");
1848 let room_reaction = EventFactory::new()
1849 .room(room_id)
1850 .sender(*ALICE)
1851 .reaction(event_id, "room")
1852 .event_id(room_reaction_id)
1853 .into_event();
1854
1855 let thread_reaction_id = event_id!("$thread_reaction");
1857 let thread_reaction = EventFactory::new()
1858 .room(room_id)
1859 .sender(*ALICE)
1860 .reaction(event_id, "thread")
1861 .event_id(thread_reaction_id)
1862 .into_event();
1863
1864 let room_and_thread_reaction_id = event_id!("$room_and_thread_reaction");
1867 let room_and_thread_reaction = EventFactory::new()
1868 .room(room_id)
1869 .sender(*ALICE)
1870 .reaction(event_id, "room and thread")
1871 .event_id(room_and_thread_reaction_id)
1872 .into_event();
1873
1874 let room_linked_chunk_id = LinkedChunkId::Room(room_id);
1875 let thread_linked_chunk_id = LinkedChunkId::Thread(room_id, thread_root);
1876
1877 self.handle_linked_chunk_updates(
1879 room_linked_chunk_id,
1880 vec![
1881 Update::NewItemsChunk { previous: None, new: CId::new(1), next: None },
1882 Update::PushItems {
1883 at: Position::new(CId::new(1), 0),
1884 items: vec![event.clone(), room_reaction, room_and_thread_reaction.clone()],
1885 },
1886 ],
1887 )
1888 .await
1889 .unwrap();
1890
1891 self.handle_linked_chunk_updates(
1893 thread_linked_chunk_id,
1894 vec![
1895 Update::NewItemsChunk { previous: None, new: CId::new(1), next: None },
1896 Update::PushItems {
1897 at: Position::new(CId::new(1), 0),
1898 items: vec![
1899 event.clone(),
1900 extra_thread_event,
1901 thread_reaction,
1902 room_and_thread_reaction,
1903 ],
1904 },
1905 ],
1906 )
1907 .await
1908 .unwrap();
1909
1910 assert_matches!(self.find_event_relations(room_id, event_id, None).await, Ok(relations) => {
1912 assert_eq!(relations.len(), 3);
1913 let room_relation = relations
1916 .iter()
1917 .find(|relation| relation.0.event_id().unwrap() == room_reaction_id)
1918 .unwrap();
1919 assert_matches!(room_relation, (_, Some(position)) => {
1920 assert_eq!(*position, Position::new(CId::new(1), 1));
1921 });
1922
1923 let thread_relation = relations
1926 .iter()
1927 .find(|relation| relation.0.event_id().unwrap() == thread_reaction_id)
1928 .unwrap();
1929 assert_matches!(thread_relation, (_, None));
1930
1931 let room_and_thread_relation = relations
1934 .iter()
1935 .find(|relation| relation.0.event_id().unwrap() == room_and_thread_reaction_id)
1936 .unwrap();
1937 assert_matches!(room_and_thread_relation, (_, Some(position)) => {
1938 assert_eq!(*position, Position::new(CId::new(1), 2));
1939 });
1940 });
1941 }
1942
1943 async fn test_get_room_events(&self) {
1944 let room_id = room_id!("!r0:matrix.org");
1945 let another_room_id = room_id!("!r1:matrix.org");
1946 let linked_chunk_id = LinkedChunkId::Room(room_id);
1947 let another_linked_chunk_id = LinkedChunkId::Room(another_room_id);
1948 let event = |msg: &str| make_test_event(room_id, msg);
1949
1950 let event_comte = event("comté");
1951 let event_gruyere = event("gruyère");
1952 let event_stilton = event("stilton");
1953
1954 self.handle_linked_chunk_updates(
1956 linked_chunk_id,
1957 vec![
1958 Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
1959 Update::PushItems {
1960 at: Position::new(CId::new(0), 0),
1961 items: vec![event_comte.clone(), event_gruyere.clone()],
1962 },
1963 ],
1964 )
1965 .await
1966 .unwrap();
1967
1968 self.handle_linked_chunk_updates(
1970 another_linked_chunk_id,
1971 vec![
1972 Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
1973 Update::PushItems {
1974 at: Position::new(CId::new(0), 0),
1975 items: vec![event_stilton.clone()],
1976 },
1977 ],
1978 )
1979 .await
1980 .unwrap();
1981
1982 let events = self
1984 .get_room_events(room_id, None, None)
1985 .await
1986 .expect("failed to query for room events");
1987
1988 assert_eq!(events.len(), 2);
1989
1990 let got_ids: Vec<_> =
1991 events.into_iter().map(|ev| ev.event_id().map(ToOwned::to_owned)).collect();
1992 let expected_ids = vec![
1993 event_comte.event_id().map(ToOwned::to_owned),
1994 event_gruyere.event_id().map(ToOwned::to_owned),
1995 ];
1996
1997 for expected in expected_ids {
1998 assert!(
1999 got_ids.contains(&expected),
2000 "Expected event {expected:?} not in got events: {got_ids:?}."
2001 );
2002 }
2003 }
2004
2005 async fn test_get_room_events_filtered(&self) {
2006 macro_rules! assert_expected_events {
2007 ($events:expr, [$($item:expr),* $(,)?]) => {{
2008 let got_ids: BTreeSet<_> = $events.into_iter().map(|ev| ev.event_id().map(ToOwned::to_owned)).flatten().collect();
2009 let expected_ids = BTreeSet::from([$($item.event_id().unwrap().to_owned()),*]);
2010
2011 assert_eq!(got_ids, expected_ids);
2012 }};
2013 }
2014
2015 let room_id = room_id!("!r0:matrix.org");
2016 let linked_chunk_id = LinkedChunkId::Room(room_id);
2017 let another_room_id = room_id!("!r1:matrix.org");
2018 let another_linked_chunk_id = LinkedChunkId::Room(another_room_id);
2019
2020 let event = |session_id: &str| make_encrypted_test_event(room_id, session_id);
2021
2022 let first_event = event("session_1");
2023 let second_event = event("session_2");
2024 let third_event = event("session_3");
2025 let fourth_event = make_test_event(room_id, "It's a secret to everybody");
2026
2027 self.handle_linked_chunk_updates(
2029 linked_chunk_id,
2030 vec![
2031 Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
2032 Update::PushItems {
2033 at: Position::new(CId::new(0), 0),
2034 items: vec![first_event.clone(), second_event.clone(), fourth_event.clone()],
2035 },
2036 ],
2037 )
2038 .await
2039 .unwrap();
2040
2041 self.handle_linked_chunk_updates(
2043 another_linked_chunk_id,
2044 vec![
2045 Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
2046 Update::PushItems {
2047 at: Position::new(CId::new(0), 0),
2048 items: vec![third_event.clone()],
2049 },
2050 ],
2051 )
2052 .await
2053 .unwrap();
2054
2055 let events = self
2057 .get_room_events(room_id, Some("m.room.encrypted"), None)
2058 .await
2059 .expect("failed to query for room events");
2060
2061 assert_eq!(events.len(), 2);
2062 assert_expected_events!(events, [first_event, second_event]);
2063
2064 let events = self
2067 .get_room_events(room_id, Some("m.room.encrypted"), Some("session_1"))
2068 .await
2069 .expect("failed to query for room events");
2070
2071 assert_eq!(events.len(), 1);
2072 assert_expected_events!(events, [first_event]);
2073 }
2074
2075 async fn test_get_room_events_with_event_in_room_and_thread(&self) {
2076 let room_id = *DEFAULT_TEST_ROOM_ID;
2077 let thread_root = event_id!("$thread_root");
2078
2079 let room_event_id = event_id!("$room_event");
2081 let room_event = make_test_event_with_event_id(room_id, "room event", Some(room_event_id));
2082
2083 let thread_event_id = event_id!("$thread_event");
2087 let thread_event =
2088 make_test_event_with_event_id(room_id, "thread event", Some(thread_event_id));
2089
2090 let room_and_thread_event_id = event_id!("$room_and_thread");
2093 let room_and_thread_event = make_test_event_with_event_id(
2094 room_id,
2095 "room and thread",
2096 Some(room_and_thread_event_id),
2097 );
2098
2099 let room_linked_chunk_id = LinkedChunkId::Room(room_id);
2100 let thread_linked_chunk_id = LinkedChunkId::Thread(room_id, thread_root);
2101
2102 self.handle_linked_chunk_updates(
2104 room_linked_chunk_id,
2105 vec![
2106 Update::NewItemsChunk { previous: None, new: CId::new(1), next: None },
2107 Update::PushItems {
2108 at: Position::new(CId::new(1), 0),
2109 items: vec![room_event, room_and_thread_event.clone()],
2110 },
2111 ],
2112 )
2113 .await
2114 .unwrap();
2115
2116 self.handle_linked_chunk_updates(
2118 thread_linked_chunk_id,
2119 vec![
2120 Update::NewItemsChunk { previous: None, new: CId::new(1), next: None },
2121 Update::PushItems {
2122 at: Position::new(CId::new(1), 0),
2123 items: vec![thread_event, room_and_thread_event],
2124 },
2125 ],
2126 )
2127 .await
2128 .unwrap();
2129
2130 let expected_event_ids =
2133 BTreeSet::from([room_event_id, thread_event_id, room_and_thread_event_id]);
2134 assert_matches!(self.get_room_events(room_id, None, None).await, Ok(events) => {
2135 assert_eq!(events.len(), 3);
2136 assert!(events.iter().all(|event| {
2137 expected_event_ids.contains(event.event_id().unwrap())
2138 }));
2139 });
2140 }
2141
2142 async fn test_save_event(&self) {
2143 let room_id = room_id!("!r0:matrix.org");
2144 let another_room_id = room_id!("!r1:matrix.org");
2145
2146 let event = |msg: &str| make_test_event(room_id, msg);
2147 let event_comte = event("comté");
2148 let event_gruyere = event("gruyère");
2149
2150 self.save_event(room_id, event_comte.clone()).await.unwrap();
2152
2153 self.save_event(another_room_id, event_gruyere.clone()).await.unwrap();
2155
2156 let event = self
2158 .find_event(room_id, event_comte.event_id().unwrap())
2159 .await
2160 .expect("failed to query for finding an event")
2161 .expect("failed to find an event");
2162 assert_eq!(event.event_id(), event_comte.event_id());
2163
2164 let event = self
2165 .find_event(another_room_id, event_gruyere.event_id().unwrap())
2166 .await
2167 .expect("failed to query for finding an event")
2168 .expect("failed to find an event");
2169 assert_eq!(event.event_id(), event_gruyere.event_id());
2170
2171 assert!(
2173 self.find_event(another_room_id, event_comte.event_id().unwrap())
2174 .await
2175 .expect("failed to query for finding an event")
2176 .is_none()
2177 );
2178 assert!(
2179 self.find_event(room_id, event_gruyere.event_id().unwrap())
2180 .await
2181 .expect("failed to query for finding an event")
2182 .is_none()
2183 );
2184 }
2185
2186 async fn test_save_event_updates_event_in_room_and_thread(&self) {
2187 let room_id = *DEFAULT_TEST_ROOM_ID;
2188 let thread_root = event_id!("$thread_root");
2189
2190 let event_id = event_id!("$event");
2193 let event = make_test_event_with_event_id(room_id, "event", Some(event_id));
2194
2195 let room_linked_chunk_id = LinkedChunkId::Room(room_id);
2196 let thread_linked_chunk_id = LinkedChunkId::Thread(room_id, thread_root);
2197
2198 self.handle_linked_chunk_updates(
2200 room_linked_chunk_id,
2201 vec![
2202 Update::NewItemsChunk { previous: None, new: CId::new(1), next: None },
2203 Update::PushItems { at: Position::new(CId::new(1), 0), items: vec![event.clone()] },
2204 ],
2205 )
2206 .await
2207 .unwrap();
2208
2209 self.handle_linked_chunk_updates(
2211 thread_linked_chunk_id,
2212 vec![
2213 Update::NewItemsChunk { previous: None, new: CId::new(1), next: None },
2214 Update::PushItems { at: Position::new(CId::new(1), 0), items: vec![event.clone()] },
2215 ],
2216 )
2217 .await
2218 .unwrap();
2219
2220 let updated_content = "updated content";
2223 let updated = make_test_event_with_event_id(room_id, updated_content, Some(event_id));
2224 self.save_event(room_id, updated).await.unwrap();
2225
2226 let room_chunks = self.load_all_chunks(room_linked_chunk_id).await.unwrap();
2228 let thread_chunks = self.load_all_chunks(thread_linked_chunk_id).await.unwrap();
2229
2230 assert_eq!(room_chunks.len(), 1);
2231 assert_eq!(thread_chunks.len(), 1);
2232
2233 assert_matches!(&room_chunks[0].content, ChunkContent::Items(events) => {
2235 assert_eq!(events.len(), 1);
2236 assert_eq!(events[0].event_id(), Some(event_id));
2237 check_test_event(&events[0], updated_content);
2238 });
2239 assert_matches!(&thread_chunks[0].content, ChunkContent::Items(events) => {
2240 assert_eq!(events.len(), 1);
2241 assert_eq!(events[0].event_id(), Some(event_id));
2242 check_test_event(&events[0], updated_content);
2243 });
2244 }
2245
2246 async fn test_thread_vs_room_linked_chunk(&self) {
2247 let room_id = room_id!("!r0:matrix.org");
2248
2249 let event = |msg: &str| make_test_event(room_id, msg);
2250
2251 let thread1_ev = event("comté");
2252 let thread2_ev = event("gruyère");
2253 let thread2_ev2 = event("beaufort");
2254 let room_ev = event("brillat savarin triple crème");
2255
2256 let thread_root1 = event("thread1");
2257 let thread_root2 = event("thread2");
2258
2259 self.handle_linked_chunk_updates(
2261 LinkedChunkId::Thread(room_id, thread_root1.event_id().unwrap()),
2262 vec![
2263 Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
2264 Update::PushItems {
2265 at: Position::new(CId::new(0), 0),
2266 items: vec![thread1_ev.clone()],
2267 },
2268 ],
2269 )
2270 .await
2271 .unwrap();
2272
2273 self.handle_linked_chunk_updates(
2275 LinkedChunkId::Thread(room_id, thread_root2.event_id().unwrap()),
2276 vec![
2277 Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
2278 Update::PushItems {
2279 at: Position::new(CId::new(0), 0),
2280 items: vec![thread2_ev.clone(), thread2_ev2.clone()],
2281 },
2282 ],
2283 )
2284 .await
2285 .unwrap();
2286
2287 self.handle_linked_chunk_updates(
2289 LinkedChunkId::Room(room_id),
2290 vec![
2291 Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
2292 Update::PushItems {
2293 at: Position::new(CId::new(0), 0),
2294 items: vec![room_ev.clone()],
2295 },
2296 ],
2297 )
2298 .await
2299 .unwrap();
2300
2301 self.find_event(room_id, thread2_ev.event_id().unwrap())
2303 .await
2304 .expect("failed to query for finding an event")
2305 .expect("failed to find thread1_ev");
2306
2307 self.find_event(room_id, thread2_ev.event_id().unwrap())
2308 .await
2309 .expect("failed to query for finding an event")
2310 .expect("failed to find thread2_ev");
2311
2312 self.find_event(room_id, thread2_ev2.event_id().unwrap())
2313 .await
2314 .expect("failed to query for finding an event")
2315 .expect("failed to find thread2_ev2");
2316
2317 self.find_event(room_id, room_ev.event_id().unwrap())
2318 .await
2319 .expect("failed to query for finding an event")
2320 .expect("failed to find room_ev");
2321
2322 let dups = self
2324 .filter_duplicated_events(
2325 LinkedChunkId::Thread(room_id, thread_root1.event_id().unwrap()),
2326 vec![
2327 thread1_ev.event_id().unwrap().to_owned(),
2328 room_ev.event_id().unwrap().to_owned(),
2329 ],
2330 )
2331 .await
2332 .unwrap();
2333 assert_eq!(dups.len(), 1);
2334 assert_eq!(dups[0].0, thread1_ev.event_id().unwrap());
2335
2336 let all_chunks = self
2338 .load_all_chunks(LinkedChunkId::Thread(room_id, thread_root2.event_id().unwrap()))
2339 .await
2340 .unwrap();
2341 assert_eq!(all_chunks.len(), 1);
2342 assert_eq!(all_chunks[0].identifier, CId::new(0));
2343 assert_let!(ChunkContent::Items(observed_items) = all_chunks[0].content.clone());
2344 assert_eq!(observed_items.len(), 2);
2345 assert_eq!(observed_items[0].event_id(), thread2_ev.event_id());
2346 assert_eq!(observed_items[1].event_id(), thread2_ev2.event_id());
2347
2348 let metas = self
2351 .load_all_chunks_metadata(LinkedChunkId::Thread(
2352 room_id,
2353 thread_root2.event_id().unwrap(),
2354 ))
2355 .await
2356 .unwrap();
2357 assert_eq!(metas.len(), 1);
2358 assert_eq!(metas[0].identifier, CId::new(0));
2359 assert_eq!(metas[0].num_items, 2);
2360
2361 let (last_chunk, _chunk_identifier_generator) = self
2363 .load_last_chunk(LinkedChunkId::Thread(room_id, thread_root1.event_id().unwrap()))
2364 .await
2365 .unwrap();
2366 let last_chunk = last_chunk.unwrap();
2367 assert_eq!(last_chunk.identifier, CId::new(0));
2368 assert_let!(ChunkContent::Items(observed_items) = last_chunk.content);
2369 assert_eq!(observed_items.len(), 1);
2370 assert_eq!(observed_items[0].event_id(), thread1_ev.event_id());
2371 }
2372}
2373
2374#[allow(unused_macros, unused_extern_crates)]
2402#[macro_export]
2403macro_rules! event_cache_store_integration_tests {
2404 () => {
2405 mod event_cache_store_integration_tests {
2406 use matrix_sdk_test::async_test;
2407 use $crate::event_cache::store::{
2408 EventCacheStoreIntegrationTests, IntoEventCacheStore,
2409 };
2410
2411 use super::get_event_cache_store;
2412
2413 #[async_test]
2414 async fn test_handle_updates_and_rebuild_linked_chunk() {
2415 let event_cache_store =
2416 get_event_cache_store().await.unwrap().into_event_cache_store();
2417 event_cache_store.test_handle_updates_and_rebuild_linked_chunk().await;
2418 }
2419
2420 #[async_test]
2421 async fn test_linked_chunk_exists_before_referenced() {
2422 let event_cache_store =
2423 get_event_cache_store().await.unwrap().into_event_cache_store();
2424 event_cache_store.test_linked_chunk_exists_before_referenced().await;
2425 }
2426
2427 #[async_test]
2428 async fn test_linked_chunk_allow_same_event_in_room_and_thread() {
2429 let event_cache_store =
2430 get_event_cache_store().await.unwrap().into_event_cache_store();
2431 event_cache_store.test_linked_chunk_allows_same_event_in_room_and_thread().await;
2432 }
2433
2434 #[async_test]
2435 async fn test_load_last_chunk() {
2436 let event_cache_store =
2437 get_event_cache_store().await.unwrap().into_event_cache_store();
2438 event_cache_store.test_load_last_chunk().await;
2439 }
2440
2441 #[async_test]
2442 async fn test_load_last_chunk_with_a_cycle() {
2443 let event_cache_store =
2444 get_event_cache_store().await.unwrap().into_event_cache_store();
2445 event_cache_store.test_load_last_chunk_with_a_cycle().await;
2446 }
2447
2448 #[async_test]
2449 async fn test_load_previous_chunk() {
2450 let event_cache_store =
2451 get_event_cache_store().await.unwrap().into_event_cache_store();
2452 event_cache_store.test_load_previous_chunk().await;
2453 }
2454
2455 #[async_test]
2456 async fn test_linked_chunk_incremental_loading() {
2457 let event_cache_store =
2458 get_event_cache_store().await.unwrap().into_event_cache_store();
2459 event_cache_store.test_linked_chunk_incremental_loading().await;
2460 }
2461
2462 #[async_test]
2463 async fn test_linked_chunk_remove_chunk() {
2464 let event_cache_store =
2465 get_event_cache_store().await.unwrap().into_event_cache_store();
2466 event_cache_store.test_linked_chunk_remove_chunk().await;
2467 }
2468
2469 #[async_test]
2470 async fn test_linked_chunk_replace_item() {
2471 let event_cache_store =
2472 get_event_cache_store().await.unwrap().into_event_cache_store();
2473 event_cache_store.test_linked_chunk_replace_item().await;
2474 }
2475
2476 #[async_test]
2477 async fn test_linked_chunk_remove_item() {
2478 let event_cache_store =
2479 get_event_cache_store().await.unwrap().into_event_cache_store();
2480 event_cache_store.test_linked_chunk_remove_item().await;
2481 }
2482
2483 #[async_test]
2484 async fn test_linked_chunk_detach_last_items() {
2485 let event_cache_store =
2486 get_event_cache_store().await.unwrap().into_event_cache_store();
2487 event_cache_store.test_linked_chunk_detach_last_items().await;
2488 }
2489
2490 #[async_test]
2491 async fn test_linked_chunk_start_end_reattach_items() {
2492 let event_cache_store =
2493 get_event_cache_store().await.unwrap().into_event_cache_store();
2494 event_cache_store.test_linked_chunk_start_end_reattach_items().await;
2495 }
2496
2497 #[async_test]
2498 async fn test_linked_chunk_clear() {
2499 let event_cache_store =
2500 get_event_cache_store().await.unwrap().into_event_cache_store();
2501 event_cache_store.test_linked_chunk_clear().await;
2502 }
2503
2504 #[async_test]
2505 async fn test_linked_chunk_clear_and_reinsert() {
2506 let event_cache_store =
2507 get_event_cache_store().await.unwrap().into_event_cache_store();
2508 event_cache_store.test_linked_chunk_clear_and_reinsert().await;
2509 }
2510
2511 #[async_test]
2512 async fn test_rebuild_empty_linked_chunk() {
2513 let event_cache_store =
2514 get_event_cache_store().await.unwrap().into_event_cache_store();
2515 event_cache_store.test_rebuild_empty_linked_chunk().await;
2516 }
2517
2518 #[async_test]
2519 async fn test_linked_chunk_multiple_rooms() {
2520 let event_cache_store =
2521 get_event_cache_store().await.unwrap().into_event_cache_store();
2522 event_cache_store.test_linked_chunk_multiple_rooms().await;
2523 }
2524
2525 #[async_test]
2526 async fn test_load_all_chunks_metadata() {
2527 let event_cache_store =
2528 get_event_cache_store().await.unwrap().into_event_cache_store();
2529 event_cache_store.test_load_all_chunks_metadata().await;
2530 }
2531
2532 #[async_test]
2533 async fn test_load_and_update_thread_info() {
2534 let event_cache_store =
2535 get_event_cache_store().await.unwrap().into_event_cache_store();
2536 event_cache_store.test_load_and_update_thread_info().await;
2537 }
2538
2539 #[async_test]
2540 async fn test_clear_all_events() {
2541 let event_cache_store =
2542 get_event_cache_store().await.unwrap().into_event_cache_store();
2543 event_cache_store.test_clear_all_events().await;
2544 }
2545
2546 #[async_test]
2547 async fn test_clear_all_events_for_specific_room() {
2548 let event_cache_store =
2549 get_event_cache_store().await.unwrap().into_event_cache_store();
2550 event_cache_store.test_clear_all_events_for_specific_room().await;
2551 }
2552
2553 #[async_test]
2554 async fn test_filter_duplicated_events() {
2555 let event_cache_store =
2556 get_event_cache_store().await.unwrap().into_event_cache_store();
2557 event_cache_store.test_filter_duplicated_events().await;
2558 }
2559
2560 #[async_test]
2561 async fn test_filter_duplicate_events_no_events() {
2562 let event_cache_store =
2563 get_event_cache_store().await.unwrap().into_event_cache_store();
2564 event_cache_store.test_filter_duplicate_events_no_events().await;
2565 }
2566
2567 #[async_test]
2568 async fn test_find_event() {
2569 let event_cache_store =
2570 get_event_cache_store().await.unwrap().into_event_cache_store();
2571 event_cache_store.test_find_event().await;
2572 }
2573
2574 #[async_test]
2575 async fn test_find_event_when_event_in_room_and_thread() {
2576 let event_cache_store =
2577 get_event_cache_store().await.unwrap().into_event_cache_store();
2578 event_cache_store.test_find_event_when_event_in_room_and_thread().await;
2579 }
2580
2581 #[async_test]
2582 async fn test_find_event_relations() {
2583 let event_cache_store =
2584 get_event_cache_store().await.unwrap().into_event_cache_store();
2585 event_cache_store.test_find_event_relations().await;
2586 }
2587
2588 #[async_test]
2589 async fn test_find_event_relations_when_event_in_room_and_thread() {
2590 let event_cache_store =
2591 get_event_cache_store().await.unwrap().into_event_cache_store();
2592 event_cache_store.test_find_event_relations_when_event_in_room_and_thread().await;
2593 }
2594
2595 #[async_test]
2596 async fn test_get_room_events() {
2597 let event_cache_store =
2598 get_event_cache_store().await.unwrap().into_event_cache_store();
2599 event_cache_store.test_get_room_events().await;
2600 }
2601
2602 #[async_test]
2603 async fn test_get_room_events_filtered() {
2604 let event_cache_store =
2605 get_event_cache_store().await.unwrap().into_event_cache_store();
2606 event_cache_store.test_get_room_events_filtered().await;
2607 }
2608
2609 #[async_test]
2610 async fn test_get_room_events_with_event_in_room_and_thread() {
2611 let event_cache_store =
2612 get_event_cache_store().await.unwrap().into_event_cache_store();
2613 event_cache_store.test_get_room_events_with_event_in_room_and_thread().await;
2614 }
2615
2616 #[async_test]
2617 async fn test_save_event() {
2618 let event_cache_store =
2619 get_event_cache_store().await.unwrap().into_event_cache_store();
2620 event_cache_store.test_save_event().await;
2621 }
2622
2623 #[async_test]
2624 async fn test_save_event_updates_event_in_room_and_thread() {
2625 let event_cache_store =
2626 get_event_cache_store().await.unwrap().into_event_cache_store();
2627 event_cache_store.test_save_event_updates_event_in_room_and_thread().await;
2628 }
2629
2630 #[async_test]
2631 async fn test_thread_vs_room_linked_chunk() {
2632 let event_cache_store =
2633 get_event_cache_store().await.unwrap().into_event_cache_store();
2634 event_cache_store.test_thread_vs_room_linked_chunk().await;
2635 }
2636 }
2637 };
2638}
2639
2640#[allow(unused_macros)]
2643#[macro_export]
2644macro_rules! event_cache_store_integration_tests_time {
2645 () => {
2646 mod event_cache_store_integration_tests_time {
2647 use std::time::Duration;
2648
2649 #[cfg(all(target_family = "wasm", target_os = "unknown"))]
2650 use gloo_timers::future::sleep;
2651 use matrix_sdk_test::async_test;
2652 #[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
2653 use tokio::time::sleep;
2654 use $crate::event_cache::store::IntoEventCacheStore;
2655
2656 use super::get_event_cache_store;
2657
2658 #[async_test]
2659 async fn test_lease_locks() {
2660 let store = get_event_cache_store().await.unwrap().into_event_cache_store();
2661
2662 let acquired0 = store.try_take_leased_lock(0, "key", "alice").await.unwrap();
2663 assert_eq!(acquired0, Some(1)); let acquired2 = store.try_take_leased_lock(300, "key", "alice").await.unwrap();
2667 assert_eq!(acquired2, Some(1)); let acquired3 = store.try_take_leased_lock(300, "key", "alice").await.unwrap();
2671 assert_eq!(acquired3, Some(1)); let acquired4 = store.try_take_leased_lock(300, "key", "bob").await.unwrap();
2675 assert!(acquired4.is_none()); let acquired5 = store.try_take_leased_lock(300, "key", "bob").await.unwrap();
2679 assert!(acquired5.is_none()); sleep(Duration::from_millis(50)).await;
2683
2684 let acquired55 = store.try_take_leased_lock(300, "key", "bob").await.unwrap();
2686 assert!(acquired55.is_none()); sleep(Duration::from_millis(250)).await;
2690
2691 let acquired6 = store.try_take_leased_lock(0, "key", "bob").await.unwrap();
2693 assert_eq!(acquired6, Some(2)); sleep(Duration::from_millis(1)).await;
2696
2697 let acquired7 = store.try_take_leased_lock(0, "key", "alice").await.unwrap();
2699 assert_eq!(acquired7, Some(3)); sleep(Duration::from_millis(1)).await;
2702
2703 let acquired8 = store.try_take_leased_lock(300, "key", "bob").await.unwrap();
2705 assert_eq!(acquired8, Some(4)); let acquired9 = store.try_take_leased_lock(300, "key", "alice").await.unwrap();
2709 assert!(acquired9.is_none()); let acquired10 = store.try_take_leased_lock(300, "key", "bob").await.unwrap();
2713 assert_eq!(acquired10, Some(4)); }
2715 }
2716 };
2717}