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