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_push_items(&self);
170
171 async fn test_linked_chunk_replace_item(&self);
173
174 async fn test_linked_chunk_remove_item(&self);
176
177 async fn test_linked_chunk_detach_last_items(&self);
179
180 async fn test_linked_chunk_start_end_reattach_items(&self);
182
183 async fn test_linked_chunk_clear(&self);
185
186 async fn test_linked_chunk_clear_and_reinsert(&self);
188
189 async fn test_rebuild_empty_linked_chunk(&self);
192
193 async fn test_linked_chunk_multiple_rooms(&self);
196
197 async fn test_load_all_chunks_metadata(&self);
199
200 async fn test_load_and_update_thread_info(&self);
202
203 async fn test_clear_all_events(&self);
205
206 async fn test_clear_all_events_for_specific_room(&self);
208
209 async fn test_filter_duplicated_events(&self);
211
212 async fn test_filter_duplicate_events_no_events(&self);
214
215 async fn test_find_event(&self);
217
218 async fn test_find_event_when_event_in_room_and_thread(&self);
221
222 async fn test_find_event_relations(&self);
224
225 async fn test_find_event_relations_when_event_in_room_and_thread(&self);
228
229 async fn test_get_room_events(&self);
231
232 async fn test_get_room_events_filtered(&self);
234
235 async fn test_get_room_events_with_event_in_room_and_thread(&self);
238
239 async fn test_save_event(&self);
241
242 async fn test_save_event_updates_event_in_room_and_thread(&self);
245
246 async fn test_thread_vs_room_linked_chunk(&self);
249}
250
251impl EventCacheStoreIntegrationTests for DynEventCacheStore {
252 async fn test_handle_updates_and_rebuild_linked_chunk(&self) {
253 let room_id = room_id!("!r0:matrix.org");
254 let linked_chunk_id = LinkedChunkId::Room(room_id);
255
256 self.handle_linked_chunk_updates(
257 linked_chunk_id,
258 vec![
259 Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
261 Update::PushItems {
263 at: Position::new(CId::new(0), 0),
264 items: vec![
265 make_test_event(room_id, "hello"),
266 make_test_event(room_id, "world"),
267 ],
268 },
269 Update::NewGapChunk {
271 previous: Some(CId::new(0)),
272 new: CId::new(1),
273 next: None,
274 gap: Gap { token: "parmesan".to_owned() },
275 },
276 Update::NewItemsChunk { previous: Some(CId::new(1)), new: CId::new(2), next: None },
278 Update::PushItems {
280 at: Position::new(CId::new(2), 0),
281 items: vec![make_test_event(room_id, "sup")],
282 },
283 ],
284 )
285 .await
286 .unwrap();
287
288 let lc = lazy_loader::from_all_chunks::<3, _, _>(
290 self.load_all_chunks(linked_chunk_id).await.unwrap(),
291 )
292 .unwrap()
293 .unwrap();
294
295 let mut chunks = lc.chunks();
296
297 {
298 let first = chunks.next().unwrap();
299 assert_eq!(first.identifier(), CId::new(0));
302
303 assert_matches!(first.content(), ChunkContent::Items(events) => {
304 assert_eq!(events.len(), 2);
305 check_test_event(&events[0], "hello");
306 check_test_event(&events[1], "world");
307 });
308 }
309
310 {
311 let second = chunks.next().unwrap();
312 assert_eq!(second.identifier(), CId::new(1));
313
314 assert_matches!(second.content(), ChunkContent::Gap(gap) => {
315 assert_eq!(gap.token, "parmesan");
316 });
317 }
318
319 {
320 let third = chunks.next().unwrap();
321 assert_eq!(third.identifier(), CId::new(2));
322
323 assert_matches!(third.content(), ChunkContent::Items(events) => {
324 assert_eq!(events.len(), 1);
325 check_test_event(&events[0], "sup");
326 });
327 }
328
329 assert!(chunks.next().is_none());
330 }
331
332 async fn test_linked_chunk_exists_before_referenced(&self) {
333 let room_id = *DEFAULT_TEST_ROOM_ID;
334 let linked_chunk_id = LinkedChunkId::Room(room_id);
335
336 self.handle_linked_chunk_updates(
338 linked_chunk_id,
339 vec![Update::NewItemsChunk {
340 previous: Some(CId::new(41)),
341 new: CId::new(42),
342 next: None,
343 }],
344 )
345 .await
346 .unwrap_err();
347
348 self.handle_linked_chunk_updates(
350 linked_chunk_id,
351 vec![Update::NewItemsChunk {
352 previous: None,
353 new: CId::new(42),
354 next: Some(CId::new(43)),
355 }],
356 )
357 .await
358 .unwrap_err();
359
360 self.handle_linked_chunk_updates(
362 linked_chunk_id,
363 vec![Update::NewGapChunk {
364 previous: Some(CId::new(41)),
365 new: CId::new(42),
366 next: None,
367 gap: Gap { token: "gap".to_owned() },
368 }],
369 )
370 .await
371 .unwrap_err();
372
373 self.handle_linked_chunk_updates(
375 linked_chunk_id,
376 vec![Update::NewGapChunk {
377 previous: None,
378 new: CId::new(42),
379 next: Some(CId::new(43)),
380 gap: Gap { token: "gap".to_owned() },
381 }],
382 )
383 .await
384 .unwrap_err();
385 }
386
387 async fn test_linked_chunk_allows_same_event_in_room_and_thread(&self) {
388 let room_id = *DEFAULT_TEST_ROOM_ID;
393 let thread_root = event_id!("$thread_root");
394
395 let event_id = event_id!("$thread_reply");
398 let event = make_test_event_with_event_id(room_id, "thread reply", Some(event_id));
399
400 let room_linked_chunk_id = LinkedChunkId::Room(room_id);
401 let thread_linked_chunk_id = LinkedChunkId::Thread(room_id, thread_root);
402
403 self.handle_linked_chunk_updates(
405 room_linked_chunk_id,
406 vec![
407 Update::NewItemsChunk { previous: None, new: CId::new(1), next: None },
408 Update::PushItems { at: Position::new(CId::new(1), 0), items: vec![event.clone()] },
409 ],
410 )
411 .await
412 .unwrap();
413
414 self.handle_linked_chunk_updates(
416 thread_linked_chunk_id,
417 vec![
418 Update::NewItemsChunk { previous: None, new: CId::new(1), next: None },
419 Update::PushItems { at: Position::new(CId::new(1), 0), items: vec![event] },
420 ],
421 )
422 .await
423 .unwrap();
424
425 let room_chunks = self.load_all_chunks(room_linked_chunk_id).await.unwrap();
427 let thread_chunks = self.load_all_chunks(thread_linked_chunk_id).await.unwrap();
428
429 assert_eq!(room_chunks.len(), 1);
430 assert_eq!(thread_chunks.len(), 1);
431
432 assert_matches!(&room_chunks[0].content, ChunkContent::Items(events) => {
434 assert_eq!(events.len(), 1);
435 assert_eq!(events[0].event_id(), Some(event_id));
436 });
437 assert_matches!(&thread_chunks[0].content, ChunkContent::Items(events) => {
438 assert_eq!(events.len(), 1);
439 assert_eq!(events[0].event_id(), Some(event_id));
440 });
441 }
442
443 async fn test_load_all_chunks_metadata(&self) {
444 let room_id = room_id!("!r0:matrix.org");
445 let linked_chunk_id = LinkedChunkId::Room(room_id);
446
447 self.handle_linked_chunk_updates(
448 linked_chunk_id,
449 vec![
450 Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
452 Update::PushItems {
454 at: Position::new(CId::new(0), 0),
455 items: vec![
456 make_test_event(room_id, "hello"),
457 make_test_event(room_id, "world"),
458 ],
459 },
460 Update::NewGapChunk {
462 previous: Some(CId::new(0)),
463 new: CId::new(1),
464 next: None,
465 gap: Gap { token: "parmesan".to_owned() },
466 },
467 Update::NewItemsChunk { previous: Some(CId::new(1)), new: CId::new(2), next: None },
469 Update::PushItems {
471 at: Position::new(CId::new(2), 0),
472 items: vec![make_test_event(room_id, "sup")],
473 },
474 Update::NewItemsChunk { previous: Some(CId::new(2)), new: CId::new(3), next: None },
476 ],
477 )
478 .await
479 .unwrap();
480
481 let metas = self.load_all_chunks_metadata(linked_chunk_id).await.unwrap();
482 assert_eq!(metas.len(), 4);
483
484 assert_eq!(metas[0].identifier, CId::new(0));
486 assert_eq!(metas[0].previous, None);
487 assert_eq!(metas[0].next, Some(CId::new(1)));
488 assert_eq!(metas[0].num_items, 2);
489
490 assert_eq!(metas[1].identifier, CId::new(1));
492 assert_eq!(metas[1].previous, Some(CId::new(0)));
493 assert_eq!(metas[1].next, Some(CId::new(2)));
494 assert_eq!(metas[1].num_items, 0);
495
496 assert_eq!(metas[2].identifier, CId::new(2));
498 assert_eq!(metas[2].previous, Some(CId::new(1)));
499 assert_eq!(metas[2].next, Some(CId::new(3)));
500 assert_eq!(metas[2].num_items, 1);
501
502 assert_eq!(metas[3].identifier, CId::new(3));
504 assert_eq!(metas[3].previous, Some(CId::new(2)));
505 assert_eq!(metas[3].next, None);
506 assert_eq!(metas[3].num_items, 0);
507 }
508
509 async fn test_load_last_chunk(&self) {
510 let room_id = room_id!("!r0:matrix.org");
511 let linked_chunk_id = LinkedChunkId::Room(room_id);
512 let event = |msg: &str| make_test_event(room_id, msg);
513
514 {
516 let (last_chunk, chunk_identifier_generator) =
517 self.load_last_chunk(linked_chunk_id).await.unwrap();
518
519 assert!(last_chunk.is_none());
520 assert_eq!(chunk_identifier_generator.current(), 0);
521 }
522
523 {
525 self.handle_linked_chunk_updates(
526 linked_chunk_id,
527 vec![
528 Update::NewItemsChunk { previous: None, new: CId::new(42), next: None },
529 Update::PushItems {
530 at: Position::new(CId::new(42), 0),
531 items: vec![event("saucisse de morteau"), event("comté")],
532 },
533 ],
534 )
535 .await
536 .unwrap();
537
538 let (last_chunk, chunk_identifier_generator) =
539 self.load_last_chunk(linked_chunk_id).await.unwrap();
540
541 assert_matches!(last_chunk, Some(last_chunk) => {
542 assert_eq!(last_chunk.identifier, 42);
543 assert!(last_chunk.previous.is_none());
544 assert!(last_chunk.next.is_none());
545 assert_matches!(last_chunk.content, ChunkContent::Items(items) => {
546 assert_eq!(items.len(), 2);
547 check_test_event(&items[0], "saucisse de morteau");
548 check_test_event(&items[1], "comté");
549 });
550 });
551 assert_eq!(chunk_identifier_generator.current(), 42);
552 }
553
554 {
556 self.handle_linked_chunk_updates(
557 linked_chunk_id,
558 vec![
559 Update::NewItemsChunk {
560 previous: Some(CId::new(42)),
561 new: CId::new(7),
562 next: None,
563 },
564 Update::PushItems {
565 at: Position::new(CId::new(7), 0),
566 items: vec![event("fondue"), event("gruyère"), event("mont d'or")],
567 },
568 ],
569 )
570 .await
571 .unwrap();
572
573 let (last_chunk, chunk_identifier_generator) =
574 self.load_last_chunk(linked_chunk_id).await.unwrap();
575
576 assert_matches!(last_chunk, Some(last_chunk) => {
577 assert_eq!(last_chunk.identifier, 7);
578 assert_matches!(last_chunk.previous, Some(previous) => {
579 assert_eq!(previous, 42);
580 });
581 assert!(last_chunk.next.is_none());
582 assert_matches!(last_chunk.content, ChunkContent::Items(items) => {
583 assert_eq!(items.len(), 3);
584 check_test_event(&items[0], "fondue");
585 check_test_event(&items[1], "gruyère");
586 check_test_event(&items[2], "mont d'or");
587 });
588 });
589 assert_eq!(chunk_identifier_generator.current(), 42);
590 }
591 }
592
593 async fn test_load_last_chunk_with_a_cycle(&self) {
594 let room_id = room_id!("!r0:matrix.org");
595 let linked_chunk_id = LinkedChunkId::Room(room_id);
596
597 self.handle_linked_chunk_updates(
598 linked_chunk_id,
599 vec![
600 Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
601 Update::NewItemsChunk {
602 previous: Some(CId::new(0)),
606 new: CId::new(1),
607 next: Some(CId::new(0)),
608 },
609 ],
610 )
611 .await
612 .unwrap();
613
614 self.load_last_chunk(linked_chunk_id).await.unwrap_err();
615 }
616
617 async fn test_load_previous_chunk(&self) {
618 let room_id = room_id!("!r0:matrix.org");
619 let linked_chunk_id = LinkedChunkId::Room(room_id);
620 let event = |msg: &str| make_test_event(room_id, msg);
621
622 {
625 let previous_chunk =
626 self.load_previous_chunk(linked_chunk_id, CId::new(153)).await.unwrap();
627
628 assert!(previous_chunk.is_none());
629 }
630
631 {
634 self.handle_linked_chunk_updates(
635 linked_chunk_id,
636 vec![Update::NewItemsChunk { previous: None, new: CId::new(42), next: None }],
637 )
638 .await
639 .unwrap();
640
641 let previous_chunk =
642 self.load_previous_chunk(linked_chunk_id, CId::new(42)).await.unwrap();
643
644 assert!(previous_chunk.is_none());
645 }
646
647 {
649 self.handle_linked_chunk_updates(
650 linked_chunk_id,
651 vec![
652 Update::NewItemsChunk {
654 previous: None,
655 new: CId::new(7),
656 next: Some(CId::new(42)),
657 },
658 Update::PushItems {
659 at: Position::new(CId::new(7), 0),
660 items: vec![event("brigand du jorat"), event("morbier")],
661 },
662 ],
663 )
664 .await
665 .unwrap();
666
667 let previous_chunk =
668 self.load_previous_chunk(linked_chunk_id, CId::new(42)).await.unwrap();
669
670 assert_matches!(previous_chunk, Some(previous_chunk) => {
671 assert_eq!(previous_chunk.identifier, 7);
672 assert!(previous_chunk.previous.is_none());
673 assert_matches!(previous_chunk.next, Some(next) => {
674 assert_eq!(next, 42);
675 });
676 assert_matches!(previous_chunk.content, ChunkContent::Items(items) => {
677 assert_eq!(items.len(), 2);
678 check_test_event(&items[0], "brigand du jorat");
679 check_test_event(&items[1], "morbier");
680 });
681 });
682 }
683 }
684
685 async fn test_linked_chunk_incremental_loading(&self) {
686 let room_id = room_id!("!r0:matrix.org");
687 let linked_chunk_id = LinkedChunkId::Room(room_id);
688 let event = |msg: &str| make_test_event(room_id, msg);
689
690 {
692 let (last_chunk, chunk_identifier_generator) =
693 self.load_last_chunk(linked_chunk_id).await.unwrap();
694
695 assert!(last_chunk.is_none());
696 assert_eq!(chunk_identifier_generator.current(), 0);
697 }
698
699 self.handle_linked_chunk_updates(
700 linked_chunk_id,
701 vec![
702 Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
704 Update::PushItems {
706 at: Position::new(CId::new(0), 0),
707 items: vec![event("a"), event("b")],
708 },
709 Update::NewGapChunk {
711 previous: Some(CId::new(0)),
712 new: CId::new(1),
713 next: None,
714 gap: Gap { token: "morbier".to_owned() },
715 },
716 Update::NewItemsChunk { previous: Some(CId::new(1)), new: CId::new(2), next: None },
718 Update::PushItems {
720 at: Position::new(CId::new(2), 0),
721 items: vec![event("c"), event("d"), event("e")],
722 },
723 ],
724 )
725 .await
726 .unwrap();
727
728 let mut linked_chunk = {
730 let (last_chunk, chunk_identifier_generator) =
731 self.load_last_chunk(linked_chunk_id).await.unwrap();
732
733 assert_eq!(chunk_identifier_generator.current(), 2);
734
735 let linked_chunk = lazy_loader::from_last_chunk::<DEFAULT_CHUNK_CAPACITY, _, _>(
736 last_chunk,
737 chunk_identifier_generator,
738 )
739 .unwrap() .unwrap(); let mut rchunks = linked_chunk.rchunks();
743
744 assert_matches!(rchunks.next(), Some(chunk) => {
746 assert_eq!(chunk.identifier(), 2);
747 assert_eq!(chunk.lazy_previous(), Some(CId::new(1)));
748
749 assert_matches!(chunk.content(), ChunkContent::Items(events) => {
750 assert_eq!(events.len(), 3);
751 check_test_event(&events[0], "c");
752 check_test_event(&events[1], "d");
753 check_test_event(&events[2], "e");
754 });
755 });
756
757 assert!(rchunks.next().is_none());
758
759 linked_chunk
760 };
761
762 {
764 let first_chunk = linked_chunk.chunks().next().unwrap().identifier();
765 let previous_chunk =
766 self.load_previous_chunk(linked_chunk_id, first_chunk).await.unwrap().unwrap();
767
768 lazy_loader::insert_new_first_chunk(&mut linked_chunk, previous_chunk).unwrap();
769
770 let mut rchunks = linked_chunk.rchunks();
771
772 assert_matches!(rchunks.next(), Some(chunk) => {
774 assert_eq!(chunk.identifier(), 2);
775 assert!(chunk.lazy_previous().is_none());
776
777 assert_matches!(chunk.content(), ChunkContent::Items(events) => {
779 assert_eq!(events.len(), 3);
780 check_test_event(&events[0], "c");
781 check_test_event(&events[1], "d");
782 check_test_event(&events[2], "e");
783 });
784 });
785
786 assert_matches!(rchunks.next(), Some(chunk) => {
788 assert_eq!(chunk.identifier(), 1);
789 assert_eq!(chunk.lazy_previous(), Some(CId::new(0)));
790
791 assert_matches!(chunk.content(), ChunkContent::Gap(gap) => {
792 assert_eq!(gap.token, "morbier");
793 });
794 });
795
796 assert!(rchunks.next().is_none());
797 }
798
799 {
801 let first_chunk = linked_chunk.chunks().next().unwrap().identifier();
802 let previous_chunk =
803 self.load_previous_chunk(linked_chunk_id, first_chunk).await.unwrap().unwrap();
804
805 lazy_loader::insert_new_first_chunk(&mut linked_chunk, previous_chunk).unwrap();
806
807 let mut rchunks = linked_chunk.rchunks();
808
809 assert_matches!(rchunks.next(), Some(chunk) => {
811 assert_eq!(chunk.identifier(), 2);
812 assert!(chunk.lazy_previous().is_none());
813
814 assert_matches!(chunk.content(), ChunkContent::Items(events) => {
816 assert_eq!(events.len(), 3);
817 check_test_event(&events[0], "c");
818 check_test_event(&events[1], "d");
819 check_test_event(&events[2], "e");
820 });
821 });
822
823 assert_matches!(rchunks.next(), Some(chunk) => {
825 assert_eq!(chunk.identifier(), 1);
826 assert!(chunk.lazy_previous().is_none());
827
828 assert_matches!(chunk.content(), ChunkContent::Gap(gap) => {
830 assert_eq!(gap.token, "morbier");
831 });
832 });
833
834 assert_matches!(rchunks.next(), Some(chunk) => {
836 assert_eq!(chunk.identifier(), 0);
837 assert!(chunk.lazy_previous().is_none());
838
839 assert_matches!(chunk.content(), ChunkContent::Items(events) => {
840 assert_eq!(events.len(), 2);
841 check_test_event(&events[0], "a");
842 check_test_event(&events[1], "b");
843 });
844 });
845
846 assert!(rchunks.next().is_none());
847 }
848
849 {
851 let first_chunk = linked_chunk.chunks().next().unwrap().identifier();
852 let previous_chunk =
853 self.load_previous_chunk(linked_chunk_id, first_chunk).await.unwrap();
854
855 assert!(previous_chunk.is_none());
856 }
857
858 {
861 let mut chunks = linked_chunk.chunks();
862
863 assert_matches!(chunks.next(), Some(chunk) => {
865 assert_eq!(chunk.identifier(), 0);
866 assert!(chunk.lazy_previous().is_none());
867
868 assert_matches!(chunk.content(), ChunkContent::Items(events) => {
869 assert_eq!(events.len(), 2);
870 check_test_event(&events[0], "a");
871 check_test_event(&events[1], "b");
872 });
873 });
874
875 assert_matches!(chunks.next(), Some(chunk) => {
877 assert_eq!(chunk.identifier(), 1);
878 assert!(chunk.lazy_previous().is_none());
879
880 assert_matches!(chunk.content(), ChunkContent::Gap(gap) => {
881 assert_eq!(gap.token, "morbier");
882 });
883 });
884
885 assert_matches!(chunks.next(), Some(chunk) => {
887 assert_eq!(chunk.identifier(), 2);
888 assert!(chunk.lazy_previous().is_none());
889
890 assert_matches!(chunk.content(), ChunkContent::Items(events) => {
891 assert_eq!(events.len(), 3);
892 check_test_event(&events[0], "c");
893 check_test_event(&events[1], "d");
894 check_test_event(&events[2], "e");
895 });
896 });
897
898 assert!(chunks.next().is_none());
899 }
900 }
901
902 async fn test_linked_chunk_remove_chunk(&self) {
903 let room_id = &DEFAULT_TEST_ROOM_ID;
904 let linked_chunk_id = LinkedChunkId::Room(room_id);
905
906 self.handle_linked_chunk_updates(
907 linked_chunk_id,
908 vec![
909 Update::NewGapChunk {
910 previous: None,
911 new: CId::new(42),
912 next: None,
913 gap: Gap { token: "raclette".to_owned() },
914 },
915 Update::NewGapChunk {
916 previous: Some(CId::new(42)),
917 new: CId::new(43),
918 next: None,
919 gap: Gap { token: "fondue".to_owned() },
920 },
921 Update::NewGapChunk {
922 previous: Some(CId::new(43)),
923 new: CId::new(44),
924 next: None,
925 gap: Gap { token: "tartiflette".to_owned() },
926 },
927 Update::RemoveChunk(CId::new(43)),
928 ],
929 )
930 .await
931 .unwrap();
932
933 let mut chunks = self.load_all_chunks(linked_chunk_id).await.unwrap();
934
935 assert_eq!(chunks.len(), 2);
936
937 let c = chunks.remove(0);
939 assert_eq!(c.identifier, CId::new(42));
940 assert_eq!(c.previous, None);
941 assert_eq!(c.next, Some(CId::new(44)));
942 assert_matches!(c.content, ChunkContent::Gap(gap) => {
943 assert_eq!(gap.token, "raclette");
944 });
945
946 let c = chunks.remove(0);
947 assert_eq!(c.identifier, CId::new(44));
948 assert_eq!(c.previous, Some(CId::new(42)));
949 assert_eq!(c.next, None);
950 assert_matches!(c.content, ChunkContent::Gap(gap) => {
951 assert_eq!(gap.token, "tartiflette");
952 });
953 }
954
955 async fn test_linked_chunk_push_items(&self) {
956 let room_id = *DEFAULT_TEST_ROOM_ID;
957
958 let linked_chunk_ids = [
960 LinkedChunkId::Room(room_id),
961 LinkedChunkId::Thread(room_id, event_id!("$thread_root")),
962 LinkedChunkId::PinnedEvents(room_id),
963 LinkedChunkId::EventFocused(room_id, event_id!("$focus_root")),
964 ];
965
966 let event_id_a = event_id!("$a");
968 let event_a = make_test_event_with_event_id(room_id, "a", Some(event_id_a));
969 for linked_chunk_id in linked_chunk_ids {
970 self.handle_linked_chunk_updates(
971 linked_chunk_id,
972 vec![
973 Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
974 Update::PushItems {
975 at: Position::new(CId::new(0), 0),
976 items: vec![event_a.clone()],
977 },
978 ],
979 )
980 .await
981 .unwrap();
982
983 let mut chunks = self.load_all_chunks(linked_chunk_id).await.unwrap();
984 assert_eq!(chunks.len(), 1);
985 let chunk = chunks.remove(0);
986 assert_matches!(chunk.content, ChunkContent::Items(events) => {
987 assert_eq!(events.len(), 1);
988 check_test_event(&events[0], "a");
989 });
990 }
991
992 let event_b = make_test_event_with_event_id(room_id, "b", Some(event_id!("$b")));
993 for linked_chunk_id in linked_chunk_ids {
995 self.handle_linked_chunk_updates(
996 linked_chunk_id,
997 vec![Update::PushItems {
998 at: Position::new(CId::new(0), 0),
999 items: vec![event_b.clone()],
1000 }],
1001 )
1002 .await
1003 .expect_err("should fail to push an event to an occupied position");
1004
1005 let mut chunks = self.load_all_chunks(linked_chunk_id).await.unwrap();
1006 assert_eq!(chunks.len(), 1);
1007 let chunk = chunks.remove(0);
1008 assert_matches!(chunk.content, ChunkContent::Items(events) => {
1009 assert_eq!(events.len(), 1);
1010 check_test_event(&events[0], "a");
1011 });
1012 }
1013
1014 for linked_chunk_id in linked_chunk_ids {
1017 self.handle_linked_chunk_updates(
1018 linked_chunk_id,
1019 vec![Update::PushItems {
1020 at: Position::new(CId::new(0), 1),
1021 items: vec![event_b.clone()],
1022 }],
1023 )
1024 .await
1025 .unwrap();
1026
1027 let mut chunks = self.load_all_chunks(linked_chunk_id).await.unwrap();
1028 assert_eq!(chunks.len(), 1);
1029 let chunk = chunks.remove(0);
1030 assert_matches!(chunk.content, ChunkContent::Items(events) => {
1031 assert_eq!(events.len(), 2);
1032 check_test_event(&events[0], "a");
1033 check_test_event(&events[1], "b");
1034 });
1035 }
1036
1037 let updated_event_a = make_test_event_with_event_id(room_id, "updated_a", Some(event_id_a));
1039
1040 for linked_chunk_id in linked_chunk_ids {
1043 self.handle_linked_chunk_updates(
1044 linked_chunk_id,
1045 vec![Update::PushItems {
1046 at: Position::new(CId::new(0), 0),
1047 items: vec![updated_event_a.clone()],
1048 }],
1049 )
1050 .await
1051 .expect_err("should fail to push an event to an occupied position");
1052
1053 let mut chunks = self.load_all_chunks(linked_chunk_id).await.unwrap();
1054 assert_eq!(chunks.len(), 1);
1055 let chunk = chunks.remove(0);
1056 assert_matches!(chunk.content, ChunkContent::Items(events) => {
1057 assert_eq!(events.len(), 2);
1058 check_test_event(&events[0], "a");
1059 check_test_event(&events[1], "b");
1060 });
1061 }
1062
1063 for linked_chunk_id in linked_chunk_ids {
1066 self.handle_linked_chunk_updates(
1067 linked_chunk_id,
1068 vec![Update::PushItems {
1069 at: Position::new(CId::new(0), 1),
1070 items: vec![updated_event_a.clone()],
1071 }],
1072 )
1073 .await
1074 .expect_err("should fail to push an event to an occupied position");
1075
1076 let mut chunks = self.load_all_chunks(linked_chunk_id).await.unwrap();
1077 assert_eq!(chunks.len(), 1);
1078 let chunk = chunks.remove(0);
1079 assert_matches!(chunk.content, ChunkContent::Items(events) => {
1080 assert_eq!(events.len(), 2);
1081 check_test_event(&events[0], "a");
1082 check_test_event(&events[1], "b");
1083 });
1084 }
1085
1086 for linked_chunk_id in linked_chunk_ids {
1089 self.handle_linked_chunk_updates(
1090 linked_chunk_id,
1091 vec![Update::PushItems {
1092 at: Position::new(CId::new(0), 2),
1093 items: vec![updated_event_a.clone()],
1094 }],
1095 )
1096 .await
1097 .expect_err("should fail to push an event that already exists in the linked chunk");
1098
1099 let mut chunks = self.load_all_chunks(linked_chunk_id).await.unwrap();
1100 assert_eq!(chunks.len(), 1);
1101 let chunk = chunks.remove(0);
1102 assert_matches!(chunk.content, ChunkContent::Items(events) => {
1103 assert_eq!(events.len(), 2);
1104 check_test_event(&events[0], "a");
1105 check_test_event(&events[1], "b");
1106 });
1107 }
1108
1109 let other_linked_chunk_id = LinkedChunkId::Room(room_id!("!other_room:localhost"));
1111 self.handle_linked_chunk_updates(
1112 other_linked_chunk_id,
1113 vec![Update::NewItemsChunk { previous: None, new: CId::new(0), next: None }],
1114 )
1115 .await
1116 .unwrap();
1117
1118 self.handle_linked_chunk_updates(
1122 other_linked_chunk_id,
1123 vec![Update::PushItems {
1124 at: Position::new(CId::new(0), 0),
1125 items: vec![updated_event_a.clone()],
1126 }],
1127 )
1128 .await
1129 .unwrap();
1130
1131 let mut chunks = self.load_all_chunks(other_linked_chunk_id).await.unwrap();
1132 assert_eq!(chunks.len(), 1);
1133 let chunk = chunks.remove(0);
1134 assert_matches!(chunk.content, ChunkContent::Items(events) => {
1135 assert_eq!(events.len(), 1);
1136 check_test_event(&events[0], "updated_a");
1137 });
1138
1139 for linked_chunk_id in linked_chunk_ids {
1140 let mut chunks = self.load_all_chunks(linked_chunk_id).await.unwrap();
1141 assert_eq!(chunks.len(), 1);
1142 let chunk = chunks.remove(0);
1143 assert_matches!(chunk.content, ChunkContent::Items(events) => {
1144 assert_eq!(events.len(), 2);
1145 check_test_event(&events[0], "updated_a");
1146 check_test_event(&events[1], "b");
1147 });
1148 }
1149 }
1150
1151 async fn test_linked_chunk_replace_item(&self) {
1152 let room_id = &DEFAULT_TEST_ROOM_ID;
1153
1154 let linked_chunk_ids = [
1157 LinkedChunkId::Room(room_id),
1158 LinkedChunkId::Thread(room_id, event_id!("$thread_root")),
1159 LinkedChunkId::PinnedEvents(room_id),
1160 LinkedChunkId::EventFocused(room_id, event_id!("$focus_root")),
1161 LinkedChunkId::Room(room_id!("!other_room")),
1162 ];
1163
1164 let event_id = event_id!("$world");
1166
1167 for linked_chunk_id in linked_chunk_ids {
1169 self.handle_linked_chunk_updates(
1170 linked_chunk_id,
1171 vec![
1172 Update::NewItemsChunk { previous: None, new: CId::new(42), next: None },
1173 Update::PushItems {
1174 at: Position::new(CId::new(42), 0),
1175 items: vec![
1176 make_test_event(room_id, "hello"),
1177 make_test_event_with_event_id(room_id, "world", Some(event_id)),
1178 ],
1179 },
1180 ],
1181 )
1182 .await
1183 .unwrap();
1184
1185 let mut chunks = self.load_all_chunks(linked_chunk_id).await.unwrap();
1186
1187 assert_eq!(chunks.len(), 1);
1188
1189 let c = chunks.remove(0);
1190 assert_eq!(c.identifier, CId::new(42));
1191 assert_eq!(c.previous, None);
1192 assert_eq!(c.next, None);
1193 assert_matches!(c.content, ChunkContent::Items(events) => {
1194 assert_eq!(events.len(), 2);
1195 check_test_event(&events[0], "hello");
1196 check_test_event(&events[1], "world");
1197 });
1198 }
1199
1200 self.handle_linked_chunk_updates(
1203 linked_chunk_ids[0],
1204 vec![Update::ReplaceItem {
1205 at: Position::new(CId::new(42), 1),
1206 item: make_test_event_with_event_id(room_id, "yolo", Some(event_id)),
1207 }],
1208 )
1209 .await
1210 .unwrap();
1211
1212 for linked_chunk_id in linked_chunk_ids {
1214 let mut chunks = self.load_all_chunks(linked_chunk_id).await.unwrap();
1215
1216 assert_eq!(chunks.len(), 1);
1217
1218 let c = chunks.remove(0);
1219 assert_eq!(c.identifier, CId::new(42));
1220 assert_eq!(c.previous, None);
1221 assert_eq!(c.next, None);
1222 assert_matches!(c.content, ChunkContent::Items(events) => {
1223 assert_eq!(events.len(), 2);
1224 check_test_event(&events[0], "hello");
1225 check_test_event(&events[1], "yolo");
1226 });
1227 }
1228 }
1229
1230 async fn test_linked_chunk_remove_item(&self) {
1231 let room_id = *DEFAULT_TEST_ROOM_ID;
1232 let linked_chunk_id = LinkedChunkId::Room(room_id);
1233
1234 self.handle_linked_chunk_updates(
1235 linked_chunk_id,
1236 vec![
1237 Update::NewItemsChunk { previous: None, new: CId::new(42), next: None },
1238 Update::PushItems {
1239 at: Position::new(CId::new(42), 0),
1240 items: vec![
1241 make_test_event(room_id, "one"),
1242 make_test_event(room_id, "two"),
1243 make_test_event(room_id, "three"),
1244 make_test_event(room_id, "four"),
1245 make_test_event(room_id, "five"),
1246 make_test_event(room_id, "six"),
1247 ],
1248 },
1249 Update::RemoveItem { at: Position::new(CId::new(42), 2) },
1250 Update::PushItems {
1260 at: Position::new(CId::new(42), 5),
1261 items: vec![make_test_event(room_id, "seven")],
1262 },
1263 ],
1264 )
1265 .await
1266 .unwrap();
1267
1268 let mut chunks = self.load_all_chunks(linked_chunk_id).await.unwrap();
1269
1270 assert_eq!(chunks.len(), 1);
1271
1272 let c = chunks.remove(0);
1273 assert_eq!(c.identifier, CId::new(42));
1274 assert_eq!(c.previous, None);
1275 assert_eq!(c.next, None);
1276 assert_matches!(c.content, ChunkContent::Items(events) => {
1277 assert_eq!(events.len(), 6);
1278 check_test_event(&events[0], "one");
1279 check_test_event(&events[1], "two");
1280 check_test_event(&events[2], "four");
1281 check_test_event(&events[3], "five");
1282 check_test_event(&events[4], "six");
1283 check_test_event(&events[5], "seven");
1284 });
1285
1286 let metas = self.load_all_chunks_metadata(linked_chunk_id).await.unwrap();
1288 assert_eq!(metas.len(), 1);
1289 assert_eq!(metas[0].num_items, 6);
1290 }
1291
1292 async fn test_linked_chunk_detach_last_items(&self) {
1293 let room_id = *DEFAULT_TEST_ROOM_ID;
1294 let linked_chunk_id = LinkedChunkId::Room(room_id);
1295
1296 self.handle_linked_chunk_updates(
1297 linked_chunk_id,
1298 vec![
1299 Update::NewItemsChunk { previous: None, new: CId::new(42), next: None },
1300 Update::PushItems {
1301 at: Position::new(CId::new(42), 0),
1302 items: vec![
1303 make_test_event(room_id, "hello"),
1304 make_test_event(room_id, "world"),
1305 make_test_event(room_id, "howdy"),
1306 ],
1307 },
1308 Update::DetachLastItems { at: Position::new(CId::new(42), 1) },
1309 ],
1310 )
1311 .await
1312 .unwrap();
1313
1314 let mut chunks = self.load_all_chunks(linked_chunk_id).await.unwrap();
1315
1316 assert_eq!(chunks.len(), 1);
1317
1318 let c = chunks.remove(0);
1319 assert_eq!(c.identifier, CId::new(42));
1320 assert_eq!(c.previous, None);
1321 assert_eq!(c.next, None);
1322 assert_matches!(c.content, ChunkContent::Items(events) => {
1323 assert_eq!(events.len(), 1);
1324 check_test_event(&events[0], "hello");
1325 });
1326 }
1327
1328 async fn test_linked_chunk_start_end_reattach_items(&self) {
1329 let room_id = *DEFAULT_TEST_ROOM_ID;
1330 let linked_chunk_id = LinkedChunkId::Room(room_id);
1331
1332 self.handle_linked_chunk_updates(
1336 linked_chunk_id,
1337 vec![
1338 Update::NewItemsChunk { previous: None, new: CId::new(42), next: None },
1339 Update::PushItems {
1340 at: Position::new(CId::new(42), 0),
1341 items: vec![
1342 make_test_event(room_id, "hello"),
1343 make_test_event(room_id, "world"),
1344 make_test_event(room_id, "howdy"),
1345 ],
1346 },
1347 Update::StartReattachItems,
1348 Update::EndReattachItems,
1349 ],
1350 )
1351 .await
1352 .unwrap();
1353
1354 let mut chunks = self.load_all_chunks(linked_chunk_id).await.unwrap();
1355
1356 assert_eq!(chunks.len(), 1);
1357
1358 let c = chunks.remove(0);
1359 assert_eq!(c.identifier, CId::new(42));
1360 assert_eq!(c.previous, None);
1361 assert_eq!(c.next, None);
1362 assert_matches!(c.content, ChunkContent::Items(events) => {
1363 assert_eq!(events.len(), 3);
1364 check_test_event(&events[0], "hello");
1365 check_test_event(&events[1], "world");
1366 check_test_event(&events[2], "howdy");
1367 });
1368 }
1369
1370 async fn test_linked_chunk_clear(&self) {
1371 let room_id = *DEFAULT_TEST_ROOM_ID;
1372 let linked_chunk_id = LinkedChunkId::Room(room_id);
1373 let event_0 = make_test_event(room_id, "hello");
1374 let event_1 = make_test_event(room_id, "world");
1375 let event_2 = make_test_event(room_id, "howdy");
1376
1377 self.handle_linked_chunk_updates(
1378 linked_chunk_id,
1379 vec![
1380 Update::NewItemsChunk { previous: None, new: CId::new(42), next: None },
1381 Update::NewGapChunk {
1382 previous: Some(CId::new(42)),
1383 new: CId::new(54),
1384 next: None,
1385 gap: Gap { token: "fondue".to_owned() },
1386 },
1387 Update::PushItems {
1388 at: Position::new(CId::new(42), 0),
1389 items: vec![event_0.clone(), event_1, event_2],
1390 },
1391 Update::Clear,
1392 ],
1393 )
1394 .await
1395 .unwrap();
1396
1397 let chunks = self.load_all_chunks(linked_chunk_id).await.unwrap();
1398 assert!(chunks.is_empty());
1399 }
1400
1401 async fn test_linked_chunk_clear_and_reinsert(&self) {
1402 let room_id = *DEFAULT_TEST_ROOM_ID;
1403 let linked_chunk_id = LinkedChunkId::Room(room_id);
1404 let event_0 = make_test_event(room_id, "hello");
1405 let event_1 = make_test_event(room_id, "world");
1406 let event_2 = make_test_event(room_id, "howdy");
1407
1408 self.handle_linked_chunk_updates(
1409 linked_chunk_id,
1410 vec![
1411 Update::NewItemsChunk { previous: None, new: CId::new(42), next: None },
1412 Update::NewGapChunk {
1413 previous: Some(CId::new(42)),
1414 new: CId::new(54),
1415 next: None,
1416 gap: Gap { token: "fondue".to_owned() },
1417 },
1418 Update::PushItems {
1419 at: Position::new(CId::new(42), 0),
1420 items: vec![event_0.clone(), event_1, event_2],
1421 },
1422 Update::Clear,
1423 ],
1424 )
1425 .await
1426 .unwrap();
1427
1428 let chunks = self.load_all_chunks(linked_chunk_id).await.unwrap();
1429 assert!(chunks.is_empty());
1430
1431 self.handle_linked_chunk_updates(
1433 linked_chunk_id,
1434 vec![
1435 Update::NewItemsChunk { previous: None, new: CId::new(42), next: None },
1436 Update::PushItems { at: Position::new(CId::new(42), 0), items: vec![event_0] },
1437 ],
1438 )
1439 .await
1440 .unwrap();
1441 }
1442
1443 async fn test_rebuild_empty_linked_chunk(&self) {
1444 let linked_chunk = lazy_loader::from_all_chunks::<3, _, _>(
1446 self.load_all_chunks(LinkedChunkId::Room(&DEFAULT_TEST_ROOM_ID)).await.unwrap(),
1447 )
1448 .unwrap();
1449 assert!(linked_chunk.is_none());
1450 }
1451
1452 async fn test_linked_chunk_multiple_rooms(&self) {
1453 let room1 = room_id!("!realcheeselovers:raclette.fr");
1454 let linked_chunk_id1 = LinkedChunkId::Room(room1);
1455 let room2 = room_id!("!realcheeselovers:fondue.ch");
1456 let linked_chunk_id2 = LinkedChunkId::Room(room2);
1457
1458 self.handle_linked_chunk_updates(
1462 linked_chunk_id1,
1463 vec![
1464 Update::NewItemsChunk { previous: None, new: CId::new(42), next: None },
1465 Update::PushItems {
1466 at: Position::new(CId::new(42), 0),
1467 items: vec![
1468 make_test_event(room1, "best cheese is raclette"),
1469 make_test_event(room1, "obviously"),
1470 ],
1471 },
1472 ],
1473 )
1474 .await
1475 .unwrap();
1476
1477 self.handle_linked_chunk_updates(
1478 linked_chunk_id2,
1479 vec![
1480 Update::NewItemsChunk { previous: None, new: CId::new(42), next: None },
1481 Update::PushItems {
1482 at: Position::new(CId::new(42), 0),
1483 items: vec![make_test_event(room1, "beaufort is the best")],
1484 },
1485 ],
1486 )
1487 .await
1488 .unwrap();
1489
1490 let mut chunks_room1 = self.load_all_chunks(linked_chunk_id1).await.unwrap();
1492 assert_eq!(chunks_room1.len(), 1);
1493
1494 let c = chunks_room1.remove(0);
1495 assert_matches!(c.content, ChunkContent::Items(events) => {
1496 assert_eq!(events.len(), 2);
1497 check_test_event(&events[0], "best cheese is raclette");
1498 check_test_event(&events[1], "obviously");
1499 });
1500
1501 let mut chunks_room2 = self.load_all_chunks(linked_chunk_id2).await.unwrap();
1503 assert_eq!(chunks_room2.len(), 1);
1504
1505 let c = chunks_room2.remove(0);
1506 assert_matches!(c.content, ChunkContent::Items(events) => {
1507 assert_eq!(events.len(), 1);
1508 check_test_event(&events[0], "beaufort is the best");
1509 });
1510 }
1511
1512 async fn test_load_and_update_thread_info(&self) {
1513 let room_id = room_id!("!r0");
1514 let thread_id = event_id!("$t0");
1515
1516 let ThreadInfo { read_receipts } = self.load_thread_info(room_id, thread_id).await.unwrap();
1520 let ReadReceipts { num_unread, num_notifications, num_mentions, latest_active, pending } =
1521 read_receipts;
1522 assert_eq!(num_unread, 0);
1523 assert_eq!(num_notifications, 0);
1524 assert_eq!(num_mentions, 0);
1525 assert!(latest_active.is_none());
1526 assert!(pending.is_empty());
1527
1528 let mut thread_info = self.load_thread_info(room_id, thread_id).await.unwrap();
1532 let ThreadInfo { read_receipts } = &thread_info;
1533 let ReadReceipts { num_unread, num_notifications, num_mentions, latest_active, pending } =
1534 read_receipts;
1535 assert_eq!(*num_unread, 0);
1536 assert_eq!(*num_notifications, 0);
1537 assert_eq!(*num_mentions, 0);
1538 assert!(latest_active.is_none());
1539 assert!(pending.is_empty());
1540
1541 thread_info.read_receipts.num_unread = 1;
1543 thread_info.read_receipts.num_notifications = 2;
1544 self.update_thread_info(room_id, thread_id, &thread_info).await.unwrap();
1545
1546 let ThreadInfo { read_receipts } = self.load_thread_info(room_id, thread_id).await.unwrap();
1550 let ReadReceipts { num_unread, num_notifications, num_mentions, latest_active, pending } =
1551 read_receipts;
1552 assert_eq!(num_unread, 1);
1553 assert_eq!(num_notifications, 2);
1554 assert_eq!(num_mentions, 0);
1555 assert!(latest_active.is_none());
1556 assert!(pending.is_empty());
1557 }
1558
1559 async fn test_clear_all_events(&self) {
1560 let linked_chunk_ids = [
1561 LinkedChunkId::Room(room_id!("!r0")),
1562 LinkedChunkId::Thread(room_id!("!r1"), event_id!("$r1_thread_root0")),
1563 LinkedChunkId::PinnedEvents(room_id!("!r2")),
1564 ];
1566
1567 for linked_chunk_id in linked_chunk_ids {
1569 let room_id = linked_chunk_id.room_id();
1570
1571 if let LinkedChunkId::Thread(_, thread_id) = &linked_chunk_id {
1574 self.load_thread_info(room_id, thread_id).await.unwrap();
1575 }
1576
1577 self.handle_linked_chunk_updates(
1578 linked_chunk_id,
1579 vec![
1580 Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
1582 Update::PushItems {
1584 at: Position::new(CId::new(0), 0),
1585 items: vec![
1586 make_test_event(room_id, "foo"),
1587 make_test_event(room_id, "bar"),
1588 make_test_event(room_id, "baz"),
1589 ],
1590 },
1591 ],
1592 )
1593 .await
1594 .unwrap();
1595
1596 assert!(
1598 lazy_loader::from_all_chunks::<3, _, _>(
1599 self.load_all_chunks(linked_chunk_id).await.unwrap()
1600 )
1601 .unwrap()
1602 .is_some()
1603 );
1604
1605 assert_eq!(self.get_room_events(room_id, None, None).await.unwrap().len(), 3);
1607 }
1608
1609 self.clear_all_events(None).await.unwrap();
1611
1612 for linked_chunk_id in linked_chunk_ids {
1614 let room_id = linked_chunk_id.room_id();
1615
1616 assert!(
1618 lazy_loader::from_all_chunks::<3, _, _>(
1619 self.load_all_chunks(linked_chunk_id).await.unwrap()
1620 )
1621 .unwrap()
1622 .is_none()
1623 );
1624
1625 assert!(self.get_room_events(room_id, None, None).await.unwrap().is_empty());
1627 }
1628 }
1629
1630 async fn test_clear_all_events_for_specific_room(&self) {
1631 let linked_chunk_ids_for_room_0 = [
1632 LinkedChunkId::Room(room_id!("!r0")),
1633 LinkedChunkId::Thread(room_id!("!r0"), event_id!("$r0_thread_root")),
1634 LinkedChunkId::PinnedEvents(room_id!("!r0")),
1635 ];
1636 let linked_chunk_ids_for_room_1 = [
1637 LinkedChunkId::Room(room_id!("!r1")),
1638 LinkedChunkId::Thread(room_id!("!r1"), event_id!("$r1_thread_root")),
1639 LinkedChunkId::PinnedEvents(room_id!("!r1")),
1640 ];
1641 let linked_chunk_ids_for_room_2 = [
1642 LinkedChunkId::Room(room_id!("!r2")),
1643 LinkedChunkId::Thread(room_id!("!r2"), event_id!("$r2_thread_root")),
1644 LinkedChunkId::PinnedEvents(room_id!("!r2")),
1645 ];
1646
1647 for linked_chunk_id in linked_chunk_ids_for_room_0
1649 .iter()
1650 .chain(&linked_chunk_ids_for_room_1)
1651 .chain(&linked_chunk_ids_for_room_2)
1652 {
1653 let room_id = linked_chunk_id.room_id();
1654
1655 if let LinkedChunkId::Thread(_, thread_id) = &linked_chunk_id {
1658 self.load_thread_info(room_id, thread_id).await.unwrap();
1659 }
1660
1661 self.handle_linked_chunk_updates(
1662 *linked_chunk_id,
1663 vec![
1664 Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
1666 Update::PushItems {
1668 at: Position::new(CId::new(0), 0),
1669 items: vec![
1670 make_test_event(room_id, "foo"),
1671 make_test_event(room_id, "bar"),
1672 make_test_event(room_id, "baz"),
1673 ],
1674 },
1675 ],
1676 )
1677 .await
1678 .unwrap();
1679
1680 assert!(
1682 lazy_loader::from_all_chunks::<3, _, _>(
1683 self.load_all_chunks(*linked_chunk_id).await.unwrap()
1684 )
1685 .unwrap()
1686 .is_some()
1687 );
1688
1689 assert_eq!(self.get_room_events(room_id, None, None).await.unwrap().len(), 3);
1691 }
1692
1693 self.clear_all_events(Some(linked_chunk_ids_for_room_1[0].room_id())).await.unwrap();
1695
1696 for linked_chunk_id in linked_chunk_ids_for_room_1 {
1698 let room_id = linked_chunk_id.room_id();
1699
1700 assert!(
1702 lazy_loader::from_all_chunks::<3, _, _>(
1703 self.load_all_chunks(linked_chunk_id).await.unwrap()
1704 )
1705 .unwrap()
1706 .is_none()
1707 );
1708
1709 assert!(self.get_room_events(room_id, None, None).await.unwrap().is_empty());
1711 }
1712
1713 for linked_chunk_id in
1715 linked_chunk_ids_for_room_0.iter().chain(&linked_chunk_ids_for_room_2)
1716 {
1717 let room_id = linked_chunk_id.room_id();
1718
1719 assert!(
1721 lazy_loader::from_all_chunks::<3, _, _>(
1722 self.load_all_chunks(*linked_chunk_id).await.unwrap()
1723 )
1724 .unwrap()
1725 .is_some()
1726 );
1727
1728 assert_eq!(self.get_room_events(room_id, None, None).await.unwrap().len(), 3);
1730 }
1731 }
1732
1733 async fn test_filter_duplicated_events(&self) {
1734 let room_id = room_id!("!r0:matrix.org");
1735 let linked_chunk_id = LinkedChunkId::Room(room_id);
1736 let another_room_id = room_id!("!r1:matrix.org");
1737 let another_linked_chunk_id = LinkedChunkId::Room(another_room_id);
1738 let event = |msg: &str| make_test_event(room_id, msg);
1739
1740 let event_comte = event("comté");
1741 let event_brigand = event("brigand du jorat");
1742 let event_raclette = event("raclette");
1743 let event_morbier = event("morbier");
1744 let event_gruyere = event("gruyère");
1745 let event_tome = event("tome");
1746 let event_mont_dor = event("mont d'or");
1747
1748 self.handle_linked_chunk_updates(
1749 linked_chunk_id,
1750 vec![
1751 Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
1752 Update::PushItems {
1753 at: Position::new(CId::new(0), 0),
1754 items: vec![event_comte.clone(), event_brigand.clone()],
1755 },
1756 Update::NewGapChunk {
1757 previous: Some(CId::new(0)),
1758 new: CId::new(1),
1759 next: None,
1760 gap: Gap { token: "brillat-savarin".to_owned() },
1761 },
1762 Update::NewItemsChunk { previous: Some(CId::new(1)), new: CId::new(2), next: None },
1763 Update::PushItems {
1764 at: Position::new(CId::new(2), 0),
1765 items: vec![event_morbier.clone(), event_mont_dor.clone()],
1766 },
1767 ],
1768 )
1769 .await
1770 .unwrap();
1771
1772 self.handle_linked_chunk_updates(
1775 another_linked_chunk_id,
1776 vec![
1777 Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
1778 Update::PushItems {
1779 at: Position::new(CId::new(0), 0),
1780 items: vec![event_tome.clone()],
1781 },
1782 ],
1783 )
1784 .await
1785 .unwrap();
1786
1787 let duplicated_events = BTreeMap::from_iter(
1788 self.filter_duplicated_events(
1789 linked_chunk_id,
1790 vec![
1791 event_comte.event_id().unwrap().to_owned(),
1792 event_raclette.event_id().unwrap().to_owned(),
1793 event_morbier.event_id().unwrap().to_owned(),
1794 event_gruyere.event_id().unwrap().to_owned(),
1795 event_tome.event_id().unwrap().to_owned(),
1796 event_mont_dor.event_id().unwrap().to_owned(),
1797 ],
1798 )
1799 .await
1800 .unwrap(),
1801 );
1802
1803 assert_eq!(duplicated_events.len(), 3);
1804
1805 assert_eq!(
1806 *duplicated_events.get(event_comte.event_id().unwrap()).unwrap(),
1807 Position::new(CId::new(0), 0)
1808 );
1809 assert_eq!(
1810 *duplicated_events.get(event_morbier.event_id().unwrap()).unwrap(),
1811 Position::new(CId::new(2), 0)
1812 );
1813 assert_eq!(
1814 *duplicated_events.get(event_mont_dor.event_id().unwrap()).unwrap(),
1815 Position::new(CId::new(2), 1)
1816 );
1817 }
1818
1819 async fn test_filter_duplicate_events_no_events(&self) {
1820 let room_id = *DEFAULT_TEST_ROOM_ID;
1821 let linked_chunk_id = LinkedChunkId::Room(room_id);
1822 let duplicates = self.filter_duplicated_events(linked_chunk_id, Vec::new()).await.unwrap();
1823 assert!(duplicates.is_empty());
1824 }
1825
1826 async fn test_find_event(&self) {
1827 let room_id = room_id!("!r0:matrix.org");
1828 let another_room_id = room_id!("!r1:matrix.org");
1829 let another_linked_chunk_id = LinkedChunkId::Room(another_room_id);
1830 let event = |msg: &str| make_test_event(room_id, msg);
1831
1832 let event_comte = event("comté");
1833 let event_gruyere = event("gruyère");
1834
1835 self.handle_linked_chunk_updates(
1837 LinkedChunkId::Room(room_id),
1838 vec![
1839 Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
1840 Update::PushItems {
1841 at: Position::new(CId::new(0), 0),
1842 items: vec![event_comte.clone()],
1843 },
1844 ],
1845 )
1846 .await
1847 .unwrap();
1848
1849 self.handle_linked_chunk_updates(
1851 another_linked_chunk_id,
1852 vec![
1853 Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
1854 Update::PushItems {
1855 at: Position::new(CId::new(0), 0),
1856 items: vec![event_gruyere.clone()],
1857 },
1858 ],
1859 )
1860 .await
1861 .unwrap();
1862
1863 let event = self
1865 .find_event(room_id, event_comte.event_id().unwrap())
1866 .await
1867 .expect("failed to query for finding an event")
1868 .expect("failed to find an event");
1869
1870 assert_eq!(event.event_id(), event_comte.event_id());
1871
1872 assert!(
1874 self.find_event(room_id, event_gruyere.event_id().unwrap())
1875 .await
1876 .expect("failed to query for finding an event")
1877 .is_none()
1878 );
1879
1880 self.clear_all_events(None).await.expect("failed to clear all rooms chunks");
1882 assert!(
1883 self.find_event(room_id, event_comte.event_id().unwrap())
1884 .await
1885 .expect("failed to query for finding an event")
1886 .is_none()
1887 );
1888 }
1889
1890 async fn test_find_event_when_event_in_room_and_thread(&self) {
1891 let room_id = *DEFAULT_TEST_ROOM_ID;
1892 let thread_root = event_id!("$thread_root");
1893
1894 let room_event_id = event_id!("$room_event");
1896 let room_event = make_test_event_with_event_id(room_id, "room event", Some(room_event_id));
1897
1898 let thread_event_id = event_id!("$thread_event");
1900 let thread_event =
1901 make_test_event_with_event_id(room_id, "thread event", Some(thread_event_id));
1902
1903 let room_and_thread_event_id = event_id!("$room_and_thread");
1906 let room_and_thread_event = make_test_event_with_event_id(
1907 room_id,
1908 "room and thread",
1909 Some(room_and_thread_event_id),
1910 );
1911
1912 let room_linked_chunk_id = LinkedChunkId::Room(room_id);
1913 let thread_linked_chunk_id = LinkedChunkId::Thread(room_id, thread_root);
1914
1915 self.handle_linked_chunk_updates(
1917 room_linked_chunk_id,
1918 vec![
1919 Update::NewItemsChunk { previous: None, new: CId::new(1), next: None },
1920 Update::PushItems {
1921 at: Position::new(CId::new(1), 0),
1922 items: vec![room_event, room_and_thread_event.clone()],
1923 },
1924 ],
1925 )
1926 .await
1927 .unwrap();
1928
1929 self.handle_linked_chunk_updates(
1931 thread_linked_chunk_id,
1932 vec![
1933 Update::NewItemsChunk { previous: None, new: CId::new(1), next: None },
1934 Update::PushItems {
1935 at: Position::new(CId::new(1), 0),
1936 items: vec![thread_event, room_and_thread_event],
1937 },
1938 ],
1939 )
1940 .await
1941 .unwrap();
1942
1943 assert_matches!(self.find_event(room_id, room_event_id).await, Ok(Some(event)) => {
1945 assert_eq!(event.event_id().unwrap(), room_event_id)
1946 });
1947
1948 assert_matches!(self.find_event(room_id, thread_event_id).await, Ok(Some(event)) => {
1950 assert_eq!(event.event_id().unwrap(), thread_event_id)
1951 });
1952
1953 assert_matches!(self.find_event(room_id, room_and_thread_event_id).await, Ok(Some(event)) => {
1955 assert_eq!(event.event_id().unwrap(), room_and_thread_event_id);
1956 });
1957 }
1958
1959 async fn test_find_event_relations(&self) {
1960 let room_id = room_id!("!r0:matrix.org");
1961 let another_room_id = room_id!("!r1:matrix.org");
1962
1963 let f = EventFactory::new().room(room_id).sender(*ALICE);
1964
1965 let eid1 = event_id!("$event1:matrix.org");
1967 let e1 = f.text_msg("comter").event_id(eid1).into_event();
1968
1969 let edit_eid1 = event_id!("$edit_event1:matrix.org");
1970 let edit_e1 = f
1971 .text_msg("* comté")
1972 .event_id(edit_eid1)
1973 .edit(eid1, RoomMessageEventContentWithoutRelation::text_plain("comté"))
1974 .into_event();
1975
1976 let reaction_eid1 = event_id!("$reaction_event1:matrix.org");
1977 let reaction_e1 = f.reaction(eid1, "👍").event_id(reaction_eid1).into_event();
1978
1979 let eid2 = event_id!("$event2:matrix.org");
1980 let e2 = f.text_msg("galette saucisse").event_id(eid2).into_event();
1981
1982 let f = f.room(another_room_id);
1984
1985 let eid3 = event_id!("$event3:matrix.org");
1986 let e3 = f.text_msg("gruyère").event_id(eid3).into_event();
1987
1988 let reaction_eid3 = event_id!("$reaction_event3:matrix.org");
1989 let reaction_e3 = f.reaction(eid3, "👍").event_id(reaction_eid3).into_event();
1990
1991 self.save_event(room_id, e1).await.unwrap();
1993 self.save_event(room_id, edit_e1).await.unwrap();
1994 self.save_event(room_id, reaction_e1.clone()).await.unwrap();
1995 self.save_event(room_id, e2).await.unwrap();
1996 self.save_event(another_room_id, e3).await.unwrap();
1997 self.save_event(another_room_id, reaction_e3).await.unwrap();
1998
1999 let relations = self.find_event_relations(room_id, eid1, None).await.unwrap();
2001 assert_eq!(relations.len(), 2);
2002 assert!(
2004 relations.iter().any(|(ev, pos)| ev.event_id() == Some(edit_eid1) && pos.is_none())
2005 );
2006 assert!(
2007 relations.iter().any(|(ev, pos)| ev.event_id() == Some(reaction_eid1) && pos.is_none())
2008 );
2009
2010 let relations = self
2012 .find_event_relations(room_id, eid1, Some(&[RelationType::Replacement]))
2013 .await
2014 .unwrap();
2015 assert_eq!(relations.len(), 1);
2016 assert_eq!(relations[0].0.event_id(), Some(edit_eid1));
2017
2018 let relations = self
2019 .find_event_relations(
2020 room_id,
2021 eid1,
2022 Some(&[RelationType::Replacement, RelationType::Annotation]),
2023 )
2024 .await
2025 .unwrap();
2026 assert_eq!(relations.len(), 2);
2027 assert!(relations.iter().any(|r| r.0.event_id() == Some(edit_eid1)));
2028 assert!(relations.iter().any(|r| r.0.event_id() == Some(reaction_eid1)));
2029
2030 let relations = self
2032 .find_event_relations(another_room_id, eid1, Some(&[RelationType::Replacement]))
2033 .await
2034 .unwrap();
2035 assert!(relations.is_empty());
2036
2037 self.handle_linked_chunk_updates(
2042 LinkedChunkId::Room(room_id),
2043 vec![
2044 Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
2045 Update::PushItems { at: Position::new(CId::new(0), 0), items: vec![reaction_e1] },
2046 ],
2047 )
2048 .await
2049 .unwrap();
2050
2051 let relations = self.find_event_relations(room_id, eid1, None).await.unwrap();
2054
2055 assert!(relations.iter().any(|(ev, pos)| {
2057 ev.event_id() == Some(reaction_eid1) && *pos == Some(Position::new(CId::new(0), 0))
2058 }));
2059
2060 assert!(
2062 relations.iter().any(|(ev, pos)| ev.event_id() == Some(edit_eid1) && pos.is_none())
2063 );
2064 }
2065
2066 async fn test_find_event_relations_when_event_in_room_and_thread(&self) {
2067 let room_id = *DEFAULT_TEST_ROOM_ID;
2068 let thread_root = event_id!("$thread_root");
2069
2070 let event_id = event_id!("$event");
2073 let event = make_test_event_with_event_id(room_id, "event", Some(event_id));
2074
2075 let extra_thread_event_id = event_id!("$extra_thread_event");
2078 let extra_thread_event = make_test_event_with_event_id(
2079 room_id,
2080 "extra thread event",
2081 Some(extra_thread_event_id),
2082 );
2083
2084 let room_reaction_id = event_id!("$room_reaction");
2086 let room_reaction = EventFactory::new()
2087 .room(room_id)
2088 .sender(*ALICE)
2089 .reaction(event_id, "room")
2090 .event_id(room_reaction_id)
2091 .into_event();
2092
2093 let thread_reaction_id = event_id!("$thread_reaction");
2095 let thread_reaction = EventFactory::new()
2096 .room(room_id)
2097 .sender(*ALICE)
2098 .reaction(event_id, "thread")
2099 .event_id(thread_reaction_id)
2100 .into_event();
2101
2102 let room_and_thread_reaction_id = event_id!("$room_and_thread_reaction");
2105 let room_and_thread_reaction = EventFactory::new()
2106 .room(room_id)
2107 .sender(*ALICE)
2108 .reaction(event_id, "room and thread")
2109 .event_id(room_and_thread_reaction_id)
2110 .into_event();
2111
2112 let room_linked_chunk_id = LinkedChunkId::Room(room_id);
2113 let thread_linked_chunk_id = LinkedChunkId::Thread(room_id, thread_root);
2114
2115 self.handle_linked_chunk_updates(
2117 room_linked_chunk_id,
2118 vec![
2119 Update::NewItemsChunk { previous: None, new: CId::new(1), next: None },
2120 Update::PushItems {
2121 at: Position::new(CId::new(1), 0),
2122 items: vec![event.clone(), room_reaction, room_and_thread_reaction.clone()],
2123 },
2124 ],
2125 )
2126 .await
2127 .unwrap();
2128
2129 self.handle_linked_chunk_updates(
2131 thread_linked_chunk_id,
2132 vec![
2133 Update::NewItemsChunk { previous: None, new: CId::new(1), next: None },
2134 Update::PushItems {
2135 at: Position::new(CId::new(1), 0),
2136 items: vec![
2137 event.clone(),
2138 extra_thread_event,
2139 thread_reaction,
2140 room_and_thread_reaction,
2141 ],
2142 },
2143 ],
2144 )
2145 .await
2146 .unwrap();
2147
2148 assert_matches!(self.find_event_relations(room_id, event_id, None).await, Ok(relations) => {
2150 assert_eq!(relations.len(), 3);
2151 let room_relation = relations
2154 .iter()
2155 .find(|relation| relation.0.event_id().unwrap() == room_reaction_id)
2156 .unwrap();
2157 assert_matches!(room_relation, (_, Some(position)) => {
2158 assert_eq!(*position, Position::new(CId::new(1), 1));
2159 });
2160
2161 let thread_relation = relations
2164 .iter()
2165 .find(|relation| relation.0.event_id().unwrap() == thread_reaction_id)
2166 .unwrap();
2167 assert_matches!(thread_relation, (_, None));
2168
2169 let room_and_thread_relation = relations
2172 .iter()
2173 .find(|relation| relation.0.event_id().unwrap() == room_and_thread_reaction_id)
2174 .unwrap();
2175 assert_matches!(room_and_thread_relation, (_, Some(position)) => {
2176 assert_eq!(*position, Position::new(CId::new(1), 2));
2177 });
2178 });
2179 }
2180
2181 async fn test_get_room_events(&self) {
2182 let room_id = room_id!("!r0:matrix.org");
2183 let another_room_id = room_id!("!r1:matrix.org");
2184 let linked_chunk_id = LinkedChunkId::Room(room_id);
2185 let another_linked_chunk_id = LinkedChunkId::Room(another_room_id);
2186 let event = |msg: &str| make_test_event(room_id, msg);
2187
2188 let event_comte = event("comté");
2189 let event_gruyere = event("gruyère");
2190 let event_stilton = event("stilton");
2191
2192 self.handle_linked_chunk_updates(
2194 linked_chunk_id,
2195 vec![
2196 Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
2197 Update::PushItems {
2198 at: Position::new(CId::new(0), 0),
2199 items: vec![event_comte.clone(), event_gruyere.clone()],
2200 },
2201 ],
2202 )
2203 .await
2204 .unwrap();
2205
2206 self.handle_linked_chunk_updates(
2208 another_linked_chunk_id,
2209 vec![
2210 Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
2211 Update::PushItems {
2212 at: Position::new(CId::new(0), 0),
2213 items: vec![event_stilton.clone()],
2214 },
2215 ],
2216 )
2217 .await
2218 .unwrap();
2219
2220 let events = self
2222 .get_room_events(room_id, None, None)
2223 .await
2224 .expect("failed to query for room events");
2225
2226 assert_eq!(events.len(), 2);
2227
2228 let got_ids: Vec<_> =
2229 events.into_iter().map(|ev| ev.event_id().map(ToOwned::to_owned)).collect();
2230 let expected_ids = vec![
2231 event_comte.event_id().map(ToOwned::to_owned),
2232 event_gruyere.event_id().map(ToOwned::to_owned),
2233 ];
2234
2235 for expected in expected_ids {
2236 assert!(
2237 got_ids.contains(&expected),
2238 "Expected event {expected:?} not in got events: {got_ids:?}."
2239 );
2240 }
2241 }
2242
2243 async fn test_get_room_events_filtered(&self) {
2244 macro_rules! assert_expected_events {
2245 ($events:expr, [$($item:expr),* $(,)?]) => {{
2246 let got_ids: BTreeSet<_> = $events.into_iter().map(|ev| ev.event_id().map(ToOwned::to_owned)).flatten().collect();
2247 let expected_ids = BTreeSet::from([$($item.event_id().unwrap().to_owned()),*]);
2248
2249 assert_eq!(got_ids, expected_ids);
2250 }};
2251 }
2252
2253 let room_id = room_id!("!r0:matrix.org");
2254 let linked_chunk_id = LinkedChunkId::Room(room_id);
2255 let another_room_id = room_id!("!r1:matrix.org");
2256 let another_linked_chunk_id = LinkedChunkId::Room(another_room_id);
2257
2258 let event = |session_id: &str| make_encrypted_test_event(room_id, session_id);
2259
2260 let first_event = event("session_1");
2261 let second_event = event("session_2");
2262 let third_event = event("session_3");
2263 let fourth_event = make_test_event(room_id, "It's a secret to everybody");
2264
2265 self.handle_linked_chunk_updates(
2267 linked_chunk_id,
2268 vec![
2269 Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
2270 Update::PushItems {
2271 at: Position::new(CId::new(0), 0),
2272 items: vec![first_event.clone(), second_event.clone(), fourth_event.clone()],
2273 },
2274 ],
2275 )
2276 .await
2277 .unwrap();
2278
2279 self.handle_linked_chunk_updates(
2281 another_linked_chunk_id,
2282 vec![
2283 Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
2284 Update::PushItems {
2285 at: Position::new(CId::new(0), 0),
2286 items: vec![third_event.clone()],
2287 },
2288 ],
2289 )
2290 .await
2291 .unwrap();
2292
2293 let events = self
2295 .get_room_events(room_id, Some("m.room.encrypted"), None)
2296 .await
2297 .expect("failed to query for room events");
2298
2299 assert_eq!(events.len(), 2);
2300 assert_expected_events!(events, [first_event, second_event]);
2301
2302 let events = self
2305 .get_room_events(room_id, Some("m.room.encrypted"), Some("session_1"))
2306 .await
2307 .expect("failed to query for room events");
2308
2309 assert_eq!(events.len(), 1);
2310 assert_expected_events!(events, [first_event]);
2311 }
2312
2313 async fn test_get_room_events_with_event_in_room_and_thread(&self) {
2314 let room_id = *DEFAULT_TEST_ROOM_ID;
2315 let thread_root = event_id!("$thread_root");
2316
2317 let room_event_id = event_id!("$room_event");
2319 let room_event = make_test_event_with_event_id(room_id, "room event", Some(room_event_id));
2320
2321 let thread_event_id = event_id!("$thread_event");
2325 let thread_event =
2326 make_test_event_with_event_id(room_id, "thread event", Some(thread_event_id));
2327
2328 let room_and_thread_event_id = event_id!("$room_and_thread");
2331 let room_and_thread_event = make_test_event_with_event_id(
2332 room_id,
2333 "room and thread",
2334 Some(room_and_thread_event_id),
2335 );
2336
2337 let room_linked_chunk_id = LinkedChunkId::Room(room_id);
2338 let thread_linked_chunk_id = LinkedChunkId::Thread(room_id, thread_root);
2339
2340 self.handle_linked_chunk_updates(
2342 room_linked_chunk_id,
2343 vec![
2344 Update::NewItemsChunk { previous: None, new: CId::new(1), next: None },
2345 Update::PushItems {
2346 at: Position::new(CId::new(1), 0),
2347 items: vec![room_event, room_and_thread_event.clone()],
2348 },
2349 ],
2350 )
2351 .await
2352 .unwrap();
2353
2354 self.handle_linked_chunk_updates(
2356 thread_linked_chunk_id,
2357 vec![
2358 Update::NewItemsChunk { previous: None, new: CId::new(1), next: None },
2359 Update::PushItems {
2360 at: Position::new(CId::new(1), 0),
2361 items: vec![thread_event, room_and_thread_event],
2362 },
2363 ],
2364 )
2365 .await
2366 .unwrap();
2367
2368 let expected_event_ids =
2371 BTreeSet::from([room_event_id, thread_event_id, room_and_thread_event_id]);
2372 assert_matches!(self.get_room_events(room_id, None, None).await, Ok(events) => {
2373 assert_eq!(events.len(), 3);
2374 assert!(events.iter().all(|event| {
2375 expected_event_ids.contains(event.event_id().unwrap())
2376 }));
2377 });
2378 }
2379
2380 async fn test_save_event(&self) {
2381 let room_id = room_id!("!r0:matrix.org");
2382 let another_room_id = room_id!("!r1:matrix.org");
2383
2384 let event = |msg: &str| make_test_event(room_id, msg);
2385 let event_comte = event("comté");
2386 let event_gruyere = event("gruyère");
2387
2388 self.save_event(room_id, event_comte.clone()).await.unwrap();
2390
2391 self.save_event(another_room_id, event_gruyere.clone()).await.unwrap();
2393
2394 let event = self
2396 .find_event(room_id, event_comte.event_id().unwrap())
2397 .await
2398 .expect("failed to query for finding an event")
2399 .expect("failed to find an event");
2400 assert_eq!(event.event_id(), event_comte.event_id());
2401
2402 let event = self
2403 .find_event(another_room_id, event_gruyere.event_id().unwrap())
2404 .await
2405 .expect("failed to query for finding an event")
2406 .expect("failed to find an event");
2407 assert_eq!(event.event_id(), event_gruyere.event_id());
2408
2409 assert!(
2411 self.find_event(another_room_id, event_comte.event_id().unwrap())
2412 .await
2413 .expect("failed to query for finding an event")
2414 .is_none()
2415 );
2416 assert!(
2417 self.find_event(room_id, event_gruyere.event_id().unwrap())
2418 .await
2419 .expect("failed to query for finding an event")
2420 .is_none()
2421 );
2422 }
2423
2424 async fn test_save_event_updates_event_in_room_and_thread(&self) {
2425 let room_id = *DEFAULT_TEST_ROOM_ID;
2426 let thread_root = event_id!("$thread_root");
2427
2428 let event_id = event_id!("$event");
2431 let event = make_test_event_with_event_id(room_id, "event", Some(event_id));
2432
2433 let room_linked_chunk_id = LinkedChunkId::Room(room_id);
2434 let thread_linked_chunk_id = LinkedChunkId::Thread(room_id, thread_root);
2435
2436 self.handle_linked_chunk_updates(
2438 room_linked_chunk_id,
2439 vec![
2440 Update::NewItemsChunk { previous: None, new: CId::new(1), next: None },
2441 Update::PushItems { at: Position::new(CId::new(1), 0), items: vec![event.clone()] },
2442 ],
2443 )
2444 .await
2445 .unwrap();
2446
2447 self.handle_linked_chunk_updates(
2449 thread_linked_chunk_id,
2450 vec![
2451 Update::NewItemsChunk { previous: None, new: CId::new(1), next: None },
2452 Update::PushItems { at: Position::new(CId::new(1), 0), items: vec![event.clone()] },
2453 ],
2454 )
2455 .await
2456 .unwrap();
2457
2458 let updated_content = "updated content";
2461 let updated = make_test_event_with_event_id(room_id, updated_content, Some(event_id));
2462 self.save_event(room_id, updated).await.unwrap();
2463
2464 let room_chunks = self.load_all_chunks(room_linked_chunk_id).await.unwrap();
2466 let thread_chunks = self.load_all_chunks(thread_linked_chunk_id).await.unwrap();
2467
2468 assert_eq!(room_chunks.len(), 1);
2469 assert_eq!(thread_chunks.len(), 1);
2470
2471 assert_matches!(&room_chunks[0].content, ChunkContent::Items(events) => {
2473 assert_eq!(events.len(), 1);
2474 assert_eq!(events[0].event_id(), Some(event_id));
2475 check_test_event(&events[0], updated_content);
2476 });
2477 assert_matches!(&thread_chunks[0].content, ChunkContent::Items(events) => {
2478 assert_eq!(events.len(), 1);
2479 assert_eq!(events[0].event_id(), Some(event_id));
2480 check_test_event(&events[0], updated_content);
2481 });
2482 }
2483
2484 async fn test_thread_vs_room_linked_chunk(&self) {
2485 let room_id = room_id!("!r0:matrix.org");
2486
2487 let event = |msg: &str| make_test_event(room_id, msg);
2488
2489 let thread1_ev = event("comté");
2490 let thread2_ev = event("gruyère");
2491 let thread2_ev2 = event("beaufort");
2492 let room_ev = event("brillat savarin triple crème");
2493
2494 let thread_root1 = event("thread1");
2495 let thread_root2 = event("thread2");
2496
2497 self.handle_linked_chunk_updates(
2499 LinkedChunkId::Thread(room_id, thread_root1.event_id().unwrap()),
2500 vec![
2501 Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
2502 Update::PushItems {
2503 at: Position::new(CId::new(0), 0),
2504 items: vec![thread1_ev.clone()],
2505 },
2506 ],
2507 )
2508 .await
2509 .unwrap();
2510
2511 self.handle_linked_chunk_updates(
2513 LinkedChunkId::Thread(room_id, thread_root2.event_id().unwrap()),
2514 vec![
2515 Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
2516 Update::PushItems {
2517 at: Position::new(CId::new(0), 0),
2518 items: vec![thread2_ev.clone(), thread2_ev2.clone()],
2519 },
2520 ],
2521 )
2522 .await
2523 .unwrap();
2524
2525 self.handle_linked_chunk_updates(
2527 LinkedChunkId::Room(room_id),
2528 vec![
2529 Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
2530 Update::PushItems {
2531 at: Position::new(CId::new(0), 0),
2532 items: vec![room_ev.clone()],
2533 },
2534 ],
2535 )
2536 .await
2537 .unwrap();
2538
2539 self.find_event(room_id, thread2_ev.event_id().unwrap())
2541 .await
2542 .expect("failed to query for finding an event")
2543 .expect("failed to find thread1_ev");
2544
2545 self.find_event(room_id, thread2_ev.event_id().unwrap())
2546 .await
2547 .expect("failed to query for finding an event")
2548 .expect("failed to find thread2_ev");
2549
2550 self.find_event(room_id, thread2_ev2.event_id().unwrap())
2551 .await
2552 .expect("failed to query for finding an event")
2553 .expect("failed to find thread2_ev2");
2554
2555 self.find_event(room_id, room_ev.event_id().unwrap())
2556 .await
2557 .expect("failed to query for finding an event")
2558 .expect("failed to find room_ev");
2559
2560 let dups = self
2562 .filter_duplicated_events(
2563 LinkedChunkId::Thread(room_id, thread_root1.event_id().unwrap()),
2564 vec![
2565 thread1_ev.event_id().unwrap().to_owned(),
2566 room_ev.event_id().unwrap().to_owned(),
2567 ],
2568 )
2569 .await
2570 .unwrap();
2571 assert_eq!(dups.len(), 1);
2572 assert_eq!(dups[0].0, thread1_ev.event_id().unwrap());
2573
2574 let all_chunks = self
2576 .load_all_chunks(LinkedChunkId::Thread(room_id, thread_root2.event_id().unwrap()))
2577 .await
2578 .unwrap();
2579 assert_eq!(all_chunks.len(), 1);
2580 assert_eq!(all_chunks[0].identifier, CId::new(0));
2581 assert_let!(ChunkContent::Items(observed_items) = all_chunks[0].content.clone());
2582 assert_eq!(observed_items.len(), 2);
2583 assert_eq!(observed_items[0].event_id(), thread2_ev.event_id());
2584 assert_eq!(observed_items[1].event_id(), thread2_ev2.event_id());
2585
2586 let metas = self
2589 .load_all_chunks_metadata(LinkedChunkId::Thread(
2590 room_id,
2591 thread_root2.event_id().unwrap(),
2592 ))
2593 .await
2594 .unwrap();
2595 assert_eq!(metas.len(), 1);
2596 assert_eq!(metas[0].identifier, CId::new(0));
2597 assert_eq!(metas[0].num_items, 2);
2598
2599 let (last_chunk, _chunk_identifier_generator) = self
2601 .load_last_chunk(LinkedChunkId::Thread(room_id, thread_root1.event_id().unwrap()))
2602 .await
2603 .unwrap();
2604 let last_chunk = last_chunk.unwrap();
2605 assert_eq!(last_chunk.identifier, CId::new(0));
2606 assert_let!(ChunkContent::Items(observed_items) = last_chunk.content);
2607 assert_eq!(observed_items.len(), 1);
2608 assert_eq!(observed_items[0].event_id(), thread1_ev.event_id());
2609 }
2610}
2611
2612#[allow(unused_macros, unused_extern_crates)]
2640#[macro_export]
2641macro_rules! event_cache_store_integration_tests {
2642 () => {
2643 mod event_cache_store_integration_tests {
2644 use matrix_sdk_test::async_test;
2645 use $crate::event_cache::store::{
2646 EventCacheStoreIntegrationTests, IntoEventCacheStore,
2647 };
2648
2649 use super::get_event_cache_store;
2650
2651 #[async_test]
2652 async fn test_handle_updates_and_rebuild_linked_chunk() {
2653 let event_cache_store =
2654 get_event_cache_store().await.unwrap().into_event_cache_store();
2655 event_cache_store.test_handle_updates_and_rebuild_linked_chunk().await;
2656 }
2657
2658 #[async_test]
2659 async fn test_linked_chunk_exists_before_referenced() {
2660 let event_cache_store =
2661 get_event_cache_store().await.unwrap().into_event_cache_store();
2662 event_cache_store.test_linked_chunk_exists_before_referenced().await;
2663 }
2664
2665 #[async_test]
2666 async fn test_linked_chunk_allow_same_event_in_room_and_thread() {
2667 let event_cache_store =
2668 get_event_cache_store().await.unwrap().into_event_cache_store();
2669 event_cache_store.test_linked_chunk_allows_same_event_in_room_and_thread().await;
2670 }
2671
2672 #[async_test]
2673 async fn test_load_last_chunk() {
2674 let event_cache_store =
2675 get_event_cache_store().await.unwrap().into_event_cache_store();
2676 event_cache_store.test_load_last_chunk().await;
2677 }
2678
2679 #[async_test]
2680 async fn test_load_last_chunk_with_a_cycle() {
2681 let event_cache_store =
2682 get_event_cache_store().await.unwrap().into_event_cache_store();
2683 event_cache_store.test_load_last_chunk_with_a_cycle().await;
2684 }
2685
2686 #[async_test]
2687 async fn test_load_previous_chunk() {
2688 let event_cache_store =
2689 get_event_cache_store().await.unwrap().into_event_cache_store();
2690 event_cache_store.test_load_previous_chunk().await;
2691 }
2692
2693 #[async_test]
2694 async fn test_linked_chunk_incremental_loading() {
2695 let event_cache_store =
2696 get_event_cache_store().await.unwrap().into_event_cache_store();
2697 event_cache_store.test_linked_chunk_incremental_loading().await;
2698 }
2699
2700 #[async_test]
2701 async fn test_linked_chunk_remove_chunk() {
2702 let event_cache_store =
2703 get_event_cache_store().await.unwrap().into_event_cache_store();
2704 event_cache_store.test_linked_chunk_remove_chunk().await;
2705 }
2706
2707 #[async_test]
2708 async fn test_linked_chunk_push_items() {
2709 let event_cache_store =
2710 get_event_cache_store().await.unwrap().into_event_cache_store();
2711 event_cache_store.test_linked_chunk_push_items().await;
2712 }
2713
2714 #[async_test]
2715 async fn test_linked_chunk_replace_item() {
2716 let event_cache_store =
2717 get_event_cache_store().await.unwrap().into_event_cache_store();
2718 event_cache_store.test_linked_chunk_replace_item().await;
2719 }
2720
2721 #[async_test]
2722 async fn test_linked_chunk_remove_item() {
2723 let event_cache_store =
2724 get_event_cache_store().await.unwrap().into_event_cache_store();
2725 event_cache_store.test_linked_chunk_remove_item().await;
2726 }
2727
2728 #[async_test]
2729 async fn test_linked_chunk_detach_last_items() {
2730 let event_cache_store =
2731 get_event_cache_store().await.unwrap().into_event_cache_store();
2732 event_cache_store.test_linked_chunk_detach_last_items().await;
2733 }
2734
2735 #[async_test]
2736 async fn test_linked_chunk_start_end_reattach_items() {
2737 let event_cache_store =
2738 get_event_cache_store().await.unwrap().into_event_cache_store();
2739 event_cache_store.test_linked_chunk_start_end_reattach_items().await;
2740 }
2741
2742 #[async_test]
2743 async fn test_linked_chunk_clear() {
2744 let event_cache_store =
2745 get_event_cache_store().await.unwrap().into_event_cache_store();
2746 event_cache_store.test_linked_chunk_clear().await;
2747 }
2748
2749 #[async_test]
2750 async fn test_linked_chunk_clear_and_reinsert() {
2751 let event_cache_store =
2752 get_event_cache_store().await.unwrap().into_event_cache_store();
2753 event_cache_store.test_linked_chunk_clear_and_reinsert().await;
2754 }
2755
2756 #[async_test]
2757 async fn test_rebuild_empty_linked_chunk() {
2758 let event_cache_store =
2759 get_event_cache_store().await.unwrap().into_event_cache_store();
2760 event_cache_store.test_rebuild_empty_linked_chunk().await;
2761 }
2762
2763 #[async_test]
2764 async fn test_linked_chunk_multiple_rooms() {
2765 let event_cache_store =
2766 get_event_cache_store().await.unwrap().into_event_cache_store();
2767 event_cache_store.test_linked_chunk_multiple_rooms().await;
2768 }
2769
2770 #[async_test]
2771 async fn test_load_all_chunks_metadata() {
2772 let event_cache_store =
2773 get_event_cache_store().await.unwrap().into_event_cache_store();
2774 event_cache_store.test_load_all_chunks_metadata().await;
2775 }
2776
2777 #[async_test]
2778 async fn test_load_and_update_thread_info() {
2779 let event_cache_store =
2780 get_event_cache_store().await.unwrap().into_event_cache_store();
2781 event_cache_store.test_load_and_update_thread_info().await;
2782 }
2783
2784 #[async_test]
2785 async fn test_clear_all_events() {
2786 let event_cache_store =
2787 get_event_cache_store().await.unwrap().into_event_cache_store();
2788 event_cache_store.test_clear_all_events().await;
2789 }
2790
2791 #[async_test]
2792 async fn test_clear_all_events_for_specific_room() {
2793 let event_cache_store =
2794 get_event_cache_store().await.unwrap().into_event_cache_store();
2795 event_cache_store.test_clear_all_events_for_specific_room().await;
2796 }
2797
2798 #[async_test]
2799 async fn test_filter_duplicated_events() {
2800 let event_cache_store =
2801 get_event_cache_store().await.unwrap().into_event_cache_store();
2802 event_cache_store.test_filter_duplicated_events().await;
2803 }
2804
2805 #[async_test]
2806 async fn test_filter_duplicate_events_no_events() {
2807 let event_cache_store =
2808 get_event_cache_store().await.unwrap().into_event_cache_store();
2809 event_cache_store.test_filter_duplicate_events_no_events().await;
2810 }
2811
2812 #[async_test]
2813 async fn test_find_event() {
2814 let event_cache_store =
2815 get_event_cache_store().await.unwrap().into_event_cache_store();
2816 event_cache_store.test_find_event().await;
2817 }
2818
2819 #[async_test]
2820 async fn test_find_event_when_event_in_room_and_thread() {
2821 let event_cache_store =
2822 get_event_cache_store().await.unwrap().into_event_cache_store();
2823 event_cache_store.test_find_event_when_event_in_room_and_thread().await;
2824 }
2825
2826 #[async_test]
2827 async fn test_find_event_relations() {
2828 let event_cache_store =
2829 get_event_cache_store().await.unwrap().into_event_cache_store();
2830 event_cache_store.test_find_event_relations().await;
2831 }
2832
2833 #[async_test]
2834 async fn test_find_event_relations_when_event_in_room_and_thread() {
2835 let event_cache_store =
2836 get_event_cache_store().await.unwrap().into_event_cache_store();
2837 event_cache_store.test_find_event_relations_when_event_in_room_and_thread().await;
2838 }
2839
2840 #[async_test]
2841 async fn test_get_room_events() {
2842 let event_cache_store =
2843 get_event_cache_store().await.unwrap().into_event_cache_store();
2844 event_cache_store.test_get_room_events().await;
2845 }
2846
2847 #[async_test]
2848 async fn test_get_room_events_filtered() {
2849 let event_cache_store =
2850 get_event_cache_store().await.unwrap().into_event_cache_store();
2851 event_cache_store.test_get_room_events_filtered().await;
2852 }
2853
2854 #[async_test]
2855 async fn test_get_room_events_with_event_in_room_and_thread() {
2856 let event_cache_store =
2857 get_event_cache_store().await.unwrap().into_event_cache_store();
2858 event_cache_store.test_get_room_events_with_event_in_room_and_thread().await;
2859 }
2860
2861 #[async_test]
2862 async fn test_save_event() {
2863 let event_cache_store =
2864 get_event_cache_store().await.unwrap().into_event_cache_store();
2865 event_cache_store.test_save_event().await;
2866 }
2867
2868 #[async_test]
2869 async fn test_save_event_updates_event_in_room_and_thread() {
2870 let event_cache_store =
2871 get_event_cache_store().await.unwrap().into_event_cache_store();
2872 event_cache_store.test_save_event_updates_event_in_room_and_thread().await;
2873 }
2874
2875 #[async_test]
2876 async fn test_thread_vs_room_linked_chunk() {
2877 let event_cache_store =
2878 get_event_cache_store().await.unwrap().into_event_cache_store();
2879 event_cache_store.test_thread_vs_room_linked_chunk().await;
2880 }
2881 }
2882 };
2883}
2884
2885#[allow(unused_macros)]
2888#[macro_export]
2889macro_rules! event_cache_store_integration_tests_time {
2890 () => {
2891 mod event_cache_store_integration_tests_time {
2892 use std::time::Duration;
2893
2894 #[cfg(all(target_family = "wasm", target_os = "unknown"))]
2895 use gloo_timers::future::sleep;
2896 use matrix_sdk_test::async_test;
2897 #[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
2898 use tokio::time::sleep;
2899 use $crate::event_cache::store::IntoEventCacheStore;
2900
2901 use super::get_event_cache_store;
2902
2903 #[async_test]
2904 async fn test_lease_locks() {
2905 let store = get_event_cache_store().await.unwrap().into_event_cache_store();
2906
2907 let acquired0 = store.try_take_leased_lock(0, "key", "alice").await.unwrap();
2908 assert_eq!(acquired0, Some(1)); let acquired2 = store.try_take_leased_lock(300, "key", "alice").await.unwrap();
2912 assert_eq!(acquired2, Some(1)); let acquired3 = store.try_take_leased_lock(300, "key", "alice").await.unwrap();
2916 assert_eq!(acquired3, Some(1)); let acquired4 = store.try_take_leased_lock(300, "key", "bob").await.unwrap();
2920 assert!(acquired4.is_none()); let acquired5 = store.try_take_leased_lock(300, "key", "bob").await.unwrap();
2924 assert!(acquired5.is_none()); sleep(Duration::from_millis(50)).await;
2928
2929 let acquired55 = store.try_take_leased_lock(300, "key", "bob").await.unwrap();
2931 assert!(acquired55.is_none()); sleep(Duration::from_millis(250)).await;
2935
2936 let acquired6 = store.try_take_leased_lock(0, "key", "bob").await.unwrap();
2938 assert_eq!(acquired6, Some(2)); sleep(Duration::from_millis(1)).await;
2941
2942 let acquired7 = store.try_take_leased_lock(0, "key", "alice").await.unwrap();
2944 assert_eq!(acquired7, Some(3)); sleep(Duration::from_millis(1)).await;
2947
2948 let acquired8 = store.try_take_leased_lock(300, "key", "bob").await.unwrap();
2950 assert_eq!(acquired8, Some(4)); let acquired9 = store.try_take_leased_lock(300, "key", "alice").await.unwrap();
2954 assert!(acquired9.is_none()); let acquired10 = store.try_take_leased_lock(300, "key", "bob").await.unwrap();
2958 assert_eq!(acquired10, Some(4)); }
2960 }
2961 };
2962}