1use std::{
18 collections::{BTreeMap, BTreeSet},
19 sync::Arc,
20};
21
22use assert_matches::assert_matches;
23use matrix_sdk_common::{
24 deserialized_responses::{
25 AlgorithmInfo, DecryptedRoomEvent, EncryptionInfo, TimelineEvent, TimelineEventKind,
26 UnableToDecryptInfo, UnableToDecryptReason, VerificationState,
27 },
28 linked_chunk::{
29 ChunkContent, ChunkIdentifier as CId, LinkedChunkId, Position, Update, lazy_loader,
30 },
31};
32use matrix_sdk_test::{
33 ALICE, DEFAULT_TEST_ROOM_ID,
34 event_factory::{EventBuilder, EventFactory},
35};
36use ruma::{
37 EventId, RoomId, event_id,
38 events::{
39 AnyMessageLikeEvent, AnyTimelineEvent,
40 relation::RelationType,
41 room::message::{RoomMessageEventContent, RoomMessageEventContentWithoutRelation},
42 },
43 push::Action,
44 room_id,
45 serde::Raw,
46};
47use serde_json::{Map, Value};
48use strass::assert_let;
49
50use super::{
51 super::{Gap, thread::ThreadInfo},
52 DEFAULT_CHUNK_CAPACITY, DynEventCacheStore,
53};
54use crate::read_receipts::ReadReceipts;
55
56pub fn make_test_event(room_id: &RoomId, content: &str) -> TimelineEvent {
61 make_test_event_with_event_id(room_id, content, None)
62}
63
64pub fn make_encrypted_test_event(room_id: &RoomId, session_id: &str) -> TimelineEvent {
67 let device_id = "DEVICEID";
68 let builder = EventFactory::new()
69 .encrypted("", "curve_key", device_id, session_id)
70 .room(room_id)
71 .sender(*ALICE);
72
73 let event = builder.into_raw();
74 let utd_info = UnableToDecryptInfo {
75 session_id: Some(session_id.to_owned()),
76 reason: UnableToDecryptReason::MissingMegolmSession { withheld_code: None },
77 };
78
79 TimelineEvent::from_utd(event, utd_info)
80}
81
82pub fn make_test_event_with_event_id(
84 room_id: &RoomId,
85 content: &str,
86 event_id: Option<&EventId>,
87) -> TimelineEvent {
88 let encryption_info = Arc::new(make_encryption_info());
89 let event = make_test_event_builder_with_event_id(room_id, content, event_id).into_raw();
90
91 TimelineEvent::from_decrypted(
92 DecryptedRoomEvent { event, encryption_info, unsigned_encryption_info: None },
93 Some(vec![Action::Notify]),
94 )
95}
96
97pub fn make_test_event_without_event_id(room_id: &RoomId, content: &str) -> TimelineEvent {
99 let encryption_info = Arc::new(make_encryption_info());
100 let event =
101 make_test_event_builder_with_event_id(room_id, content, None).no_event_id().into_raw();
102
103 TimelineEvent::from_decrypted(
104 DecryptedRoomEvent { event, encryption_info, unsigned_encryption_info: None },
105 Some(vec![Action::Notify]),
106 )
107}
108
109pub fn make_test_event_without_event_type(room_id: &RoomId, content: &str) -> TimelineEvent {
111 let encryption_info = Arc::new(make_encryption_info());
112 let raw = make_test_event_builder_with_event_id(room_id, content, None)
113 .into_raw::<AnyTimelineEvent>();
114
115 let mut map = serde_json::from_str::<Map<String, Value>>(raw.into_json().get())
118 .expect("should deserialize raw event");
119 map.remove("type").expect("should have key 'type'");
120 let string = serde_json::to_string(&map).expect("should serialize map");
121 let event = Raw::<AnyTimelineEvent>::from_json_string(string)
122 .expect("should create raw from json string");
123
124 TimelineEvent::from_decrypted(
125 DecryptedRoomEvent { event, encryption_info, unsigned_encryption_info: None },
126 Some(vec![Action::Notify]),
127 )
128}
129
130pub fn make_test_event_builder_with_event_id(
135 room_id: &RoomId,
136 content: &str,
137 event_id: Option<&EventId>,
138) -> EventBuilder<RoomMessageEventContent> {
139 let mut builder = EventFactory::new().text_msg(content).room(room_id).sender(*ALICE);
140 if let Some(event_id) = event_id {
141 builder = builder.event_id(event_id);
142 }
143 builder
144}
145
146pub fn make_encryption_info() -> EncryptionInfo {
151 EncryptionInfo {
152 sender: (*ALICE).into(),
153 sender_device: None,
154 forwarder: None,
155 algorithm_info: AlgorithmInfo::MegolmV1AesSha2 {
156 curve25519_key: "1337".to_owned(),
157 sender_claimed_keys: Default::default(),
158 session_id: Some("mysessionid9".to_owned()),
159 },
160 verification_state: VerificationState::Verified,
161 }
162}
163
164#[track_caller]
169pub fn check_test_event(event: &TimelineEvent, text: &str) {
170 let actions = event.push_actions().unwrap();
172 assert_eq!(actions.len(), 1);
173 assert_matches!(&actions[0], Action::Notify);
174
175 assert_matches!(&event.kind, TimelineEventKind::Decrypted(d) => {
177 assert_eq!(d.encryption_info.sender, *ALICE);
179 assert_matches!(&d.encryption_info.algorithm_info, AlgorithmInfo::MegolmV1AesSha2 { curve25519_key, .. } => {
180 assert_eq!(curve25519_key, "1337");
181 });
182
183 let deserialized = d.event.deserialize().unwrap();
185 assert_matches!(deserialized, AnyTimelineEvent::MessageLike(AnyMessageLikeEvent::RoomMessage(msg)) => {
186 assert_eq!(msg.as_original().unwrap().content.body(), text);
187 });
188 });
189}
190
191#[allow(async_fn_in_trait)]
196pub trait EventCacheStoreIntegrationTests {
197 async fn test_handle_updates_and_rebuild_linked_chunk(&self);
200
201 async fn test_linked_chunk_exists_before_referenced(&self);
204
205 async fn test_linked_chunk_allows_same_event_in_room_and_thread(&self);
208
209 async fn test_load_last_chunk(&self);
211
212 async fn test_load_last_chunk_with_a_cycle(&self);
215
216 async fn test_load_previous_chunk(&self);
218
219 async fn test_linked_chunk_incremental_loading(&self);
222
223 async fn test_linked_chunk_remove_chunk(&self);
225
226 async fn test_linked_chunk_push_items(&self);
228
229 async fn test_linked_chunk_replace_item(&self);
231
232 async fn test_linked_chunk_filter_incomplete_events(&self);
235
236 async fn test_linked_chunk_remove_item(&self);
238
239 async fn test_linked_chunk_detach_last_items(&self);
241
242 async fn test_linked_chunk_start_end_reattach_items(&self);
244
245 async fn test_linked_chunk_clear(&self);
247
248 async fn test_linked_chunk_clear_and_reinsert(&self);
250
251 async fn test_rebuild_empty_linked_chunk(&self);
254
255 async fn test_linked_chunk_multiple_rooms(&self);
258
259 async fn test_load_all_chunks_metadata(&self);
261
262 async fn test_load_and_update_thread_info(&self);
264
265 async fn test_clear_all_events(&self);
267
268 async fn test_clear_all_events_for_specific_room(&self);
270
271 async fn test_filter_duplicated_events(&self);
273
274 async fn test_filter_duplicate_events_no_events(&self);
276
277 async fn test_find_event(&self);
279
280 async fn test_find_event_when_event_in_room_and_thread(&self);
283
284 async fn test_find_event_relations(&self);
286
287 async fn test_find_event_relations_when_event_in_room_and_thread(&self);
290
291 async fn test_get_room_events(&self);
293
294 async fn test_get_room_events_filtered(&self);
296
297 async fn test_get_room_events_with_event_in_room_and_thread(&self);
300
301 async fn test_save_event(&self);
303
304 async fn test_save_event_updates_event_in_room_and_thread(&self);
307
308 async fn test_thread_vs_room_linked_chunk(&self);
311}
312
313impl EventCacheStoreIntegrationTests for DynEventCacheStore {
314 async fn test_handle_updates_and_rebuild_linked_chunk(&self) {
315 let room_id = room_id!("!r0:matrix.org");
316 let linked_chunk_id = LinkedChunkId::Room(room_id);
317
318 self.handle_linked_chunk_updates(
319 linked_chunk_id,
320 vec![
321 Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
323 Update::PushItems {
325 at: Position::new(CId::new(0), 0),
326 items: vec![
327 make_test_event(room_id, "hello"),
328 make_test_event(room_id, "world"),
329 ],
330 },
331 Update::NewGapChunk {
333 previous: Some(CId::new(0)),
334 new: CId::new(1),
335 next: None,
336 gap: Gap { token: "parmesan".to_owned() },
337 },
338 Update::NewItemsChunk { previous: Some(CId::new(1)), new: CId::new(2), next: None },
340 Update::PushItems {
342 at: Position::new(CId::new(2), 0),
343 items: vec![make_test_event(room_id, "sup")],
344 },
345 ],
346 )
347 .await
348 .unwrap();
349
350 let lc = lazy_loader::from_all_chunks::<3, _, _>(
352 self.load_all_chunks(linked_chunk_id).await.unwrap(),
353 )
354 .unwrap()
355 .unwrap();
356
357 let mut chunks = lc.chunks();
358
359 {
360 let first = chunks.next().unwrap();
361 assert_eq!(first.identifier(), CId::new(0));
364
365 assert_matches!(first.content(), ChunkContent::Items(events) => {
366 assert_eq!(events.len(), 2);
367 check_test_event(&events[0], "hello");
368 check_test_event(&events[1], "world");
369 });
370 }
371
372 {
373 let second = chunks.next().unwrap();
374 assert_eq!(second.identifier(), CId::new(1));
375
376 assert_matches!(second.content(), ChunkContent::Gap(gap) => {
377 assert_eq!(gap.token, "parmesan");
378 });
379 }
380
381 {
382 let third = chunks.next().unwrap();
383 assert_eq!(third.identifier(), CId::new(2));
384
385 assert_matches!(third.content(), ChunkContent::Items(events) => {
386 assert_eq!(events.len(), 1);
387 check_test_event(&events[0], "sup");
388 });
389 }
390
391 assert!(chunks.next().is_none());
392 }
393
394 async fn test_linked_chunk_exists_before_referenced(&self) {
395 let room_id = *DEFAULT_TEST_ROOM_ID;
396 let linked_chunk_id = LinkedChunkId::Room(room_id);
397
398 self.handle_linked_chunk_updates(
400 linked_chunk_id,
401 vec![Update::NewItemsChunk {
402 previous: Some(CId::new(41)),
403 new: CId::new(42),
404 next: None,
405 }],
406 )
407 .await
408 .unwrap_err();
409
410 self.handle_linked_chunk_updates(
412 linked_chunk_id,
413 vec![Update::NewItemsChunk {
414 previous: None,
415 new: CId::new(42),
416 next: Some(CId::new(43)),
417 }],
418 )
419 .await
420 .unwrap_err();
421
422 self.handle_linked_chunk_updates(
424 linked_chunk_id,
425 vec![Update::NewGapChunk {
426 previous: Some(CId::new(41)),
427 new: CId::new(42),
428 next: None,
429 gap: Gap { token: "gap".to_owned() },
430 }],
431 )
432 .await
433 .unwrap_err();
434
435 self.handle_linked_chunk_updates(
437 linked_chunk_id,
438 vec![Update::NewGapChunk {
439 previous: None,
440 new: CId::new(42),
441 next: Some(CId::new(43)),
442 gap: Gap { token: "gap".to_owned() },
443 }],
444 )
445 .await
446 .unwrap_err();
447 }
448
449 async fn test_linked_chunk_allows_same_event_in_room_and_thread(&self) {
450 let room_id = *DEFAULT_TEST_ROOM_ID;
456 let thread_root = event_id!("$thread_root");
457
458 let event_id = event_id!("$thread_reply");
461 let event = make_test_event_with_event_id(room_id, "thread reply", Some(event_id));
462
463 let room_linked_chunk_id = LinkedChunkId::Room(room_id);
464 let thread_linked_chunk_id = LinkedChunkId::Thread(room_id, thread_root);
465
466 self.handle_linked_chunk_updates(
468 room_linked_chunk_id,
469 vec![
470 Update::NewItemsChunk { previous: None, new: CId::new(1), next: None },
471 Update::PushItems { at: Position::new(CId::new(1), 0), items: vec![event.clone()] },
472 ],
473 )
474 .await
475 .unwrap();
476
477 self.handle_linked_chunk_updates(
479 thread_linked_chunk_id,
480 vec![
481 Update::NewItemsChunk { previous: None, new: CId::new(1), next: None },
482 Update::PushItems { at: Position::new(CId::new(1), 0), items: vec![event] },
483 ],
484 )
485 .await
486 .unwrap();
487
488 let room_chunks = self.load_all_chunks(room_linked_chunk_id).await.unwrap();
491 let thread_chunks = self.load_all_chunks(thread_linked_chunk_id).await.unwrap();
492
493 assert_eq!(room_chunks.len(), 1);
494 assert_eq!(thread_chunks.len(), 1);
495
496 assert_matches!(&room_chunks[0].content, ChunkContent::Items(events) => {
498 assert_eq!(events.len(), 1);
499 assert_eq!(events[0].event_id(), Some(event_id));
500 });
501 assert_matches!(&thread_chunks[0].content, ChunkContent::Items(events) => {
502 assert_eq!(events.len(), 1);
503 assert_eq!(events[0].event_id(), Some(event_id));
504 });
505 }
506
507 async fn test_load_all_chunks_metadata(&self) {
508 let room_id = room_id!("!r0:matrix.org");
509 let linked_chunk_id = LinkedChunkId::Room(room_id);
510
511 self.handle_linked_chunk_updates(
512 linked_chunk_id,
513 vec![
514 Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
516 Update::PushItems {
518 at: Position::new(CId::new(0), 0),
519 items: vec![
520 make_test_event(room_id, "hello"),
521 make_test_event(room_id, "world"),
522 ],
523 },
524 Update::NewGapChunk {
526 previous: Some(CId::new(0)),
527 new: CId::new(1),
528 next: None,
529 gap: Gap { token: "parmesan".to_owned() },
530 },
531 Update::NewItemsChunk { previous: Some(CId::new(1)), new: CId::new(2), next: None },
533 Update::PushItems {
535 at: Position::new(CId::new(2), 0),
536 items: vec![make_test_event(room_id, "sup")],
537 },
538 Update::NewItemsChunk { previous: Some(CId::new(2)), new: CId::new(3), next: None },
540 ],
541 )
542 .await
543 .unwrap();
544
545 let metas = self.load_all_chunks_metadata(linked_chunk_id).await.unwrap();
546 assert_eq!(metas.len(), 4);
547
548 assert_eq!(metas[0].identifier, CId::new(0));
550 assert_eq!(metas[0].previous, None);
551 assert_eq!(metas[0].next, Some(CId::new(1)));
552 assert_eq!(metas[0].num_items, 2);
553
554 assert_eq!(metas[1].identifier, CId::new(1));
556 assert_eq!(metas[1].previous, Some(CId::new(0)));
557 assert_eq!(metas[1].next, Some(CId::new(2)));
558 assert_eq!(metas[1].num_items, 0);
559
560 assert_eq!(metas[2].identifier, CId::new(2));
562 assert_eq!(metas[2].previous, Some(CId::new(1)));
563 assert_eq!(metas[2].next, Some(CId::new(3)));
564 assert_eq!(metas[2].num_items, 1);
565
566 assert_eq!(metas[3].identifier, CId::new(3));
568 assert_eq!(metas[3].previous, Some(CId::new(2)));
569 assert_eq!(metas[3].next, None);
570 assert_eq!(metas[3].num_items, 0);
571 }
572
573 async fn test_load_last_chunk(&self) {
574 let room_id = room_id!("!r0:matrix.org");
575 let linked_chunk_id = LinkedChunkId::Room(room_id);
576 let event = |msg: &str| make_test_event(room_id, msg);
577
578 {
580 let (last_chunk, chunk_identifier_generator) =
581 self.load_last_chunk(linked_chunk_id).await.unwrap();
582
583 assert!(last_chunk.is_none());
584 assert_eq!(chunk_identifier_generator.current(), 0);
585 }
586
587 {
589 self.handle_linked_chunk_updates(
590 linked_chunk_id,
591 vec![
592 Update::NewItemsChunk { previous: None, new: CId::new(42), next: None },
593 Update::PushItems {
594 at: Position::new(CId::new(42), 0),
595 items: vec![event("saucisse de morteau"), event("comté")],
596 },
597 ],
598 )
599 .await
600 .unwrap();
601
602 let (last_chunk, chunk_identifier_generator) =
603 self.load_last_chunk(linked_chunk_id).await.unwrap();
604
605 assert_matches!(last_chunk, Some(last_chunk) => {
606 assert_eq!(last_chunk.identifier, 42);
607 assert!(last_chunk.previous.is_none());
608 assert!(last_chunk.next.is_none());
609 assert_matches!(last_chunk.content, ChunkContent::Items(items) => {
610 assert_eq!(items.len(), 2);
611 check_test_event(&items[0], "saucisse de morteau");
612 check_test_event(&items[1], "comté");
613 });
614 });
615 assert_eq!(chunk_identifier_generator.current(), 42);
616 }
617
618 {
620 self.handle_linked_chunk_updates(
621 linked_chunk_id,
622 vec![
623 Update::NewItemsChunk {
624 previous: Some(CId::new(42)),
625 new: CId::new(7),
626 next: None,
627 },
628 Update::PushItems {
629 at: Position::new(CId::new(7), 0),
630 items: vec![event("fondue"), event("gruyère"), event("mont d'or")],
631 },
632 ],
633 )
634 .await
635 .unwrap();
636
637 let (last_chunk, chunk_identifier_generator) =
638 self.load_last_chunk(linked_chunk_id).await.unwrap();
639
640 assert_matches!(last_chunk, Some(last_chunk) => {
641 assert_eq!(last_chunk.identifier, 7);
642 assert_matches!(last_chunk.previous, Some(previous) => {
643 assert_eq!(previous, 42);
644 });
645 assert!(last_chunk.next.is_none());
646 assert_matches!(last_chunk.content, ChunkContent::Items(items) => {
647 assert_eq!(items.len(), 3);
648 check_test_event(&items[0], "fondue");
649 check_test_event(&items[1], "gruyère");
650 check_test_event(&items[2], "mont d'or");
651 });
652 });
653 assert_eq!(chunk_identifier_generator.current(), 42);
654 }
655 }
656
657 async fn test_load_last_chunk_with_a_cycle(&self) {
658 let room_id = room_id!("!r0:matrix.org");
659 let linked_chunk_id = LinkedChunkId::Room(room_id);
660
661 self.handle_linked_chunk_updates(
662 linked_chunk_id,
663 vec![
664 Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
665 Update::NewItemsChunk {
666 previous: Some(CId::new(0)),
671 new: CId::new(1),
672 next: Some(CId::new(0)),
673 },
674 ],
675 )
676 .await
677 .unwrap();
678
679 self.load_last_chunk(linked_chunk_id).await.unwrap_err();
680 }
681
682 async fn test_load_previous_chunk(&self) {
683 let room_id = room_id!("!r0:matrix.org");
684 let linked_chunk_id = LinkedChunkId::Room(room_id);
685 let event = |msg: &str| make_test_event(room_id, msg);
686
687 {
690 let previous_chunk =
691 self.load_previous_chunk(linked_chunk_id, CId::new(153)).await.unwrap();
692
693 assert!(previous_chunk.is_none());
694 }
695
696 {
699 self.handle_linked_chunk_updates(
700 linked_chunk_id,
701 vec![Update::NewItemsChunk { previous: None, new: CId::new(42), next: None }],
702 )
703 .await
704 .unwrap();
705
706 let previous_chunk =
707 self.load_previous_chunk(linked_chunk_id, CId::new(42)).await.unwrap();
708
709 assert!(previous_chunk.is_none());
710 }
711
712 {
714 self.handle_linked_chunk_updates(
715 linked_chunk_id,
716 vec![
717 Update::NewItemsChunk {
719 previous: None,
720 new: CId::new(7),
721 next: Some(CId::new(42)),
722 },
723 Update::PushItems {
724 at: Position::new(CId::new(7), 0),
725 items: vec![event("brigand du jorat"), event("morbier")],
726 },
727 ],
728 )
729 .await
730 .unwrap();
731
732 let previous_chunk =
733 self.load_previous_chunk(linked_chunk_id, CId::new(42)).await.unwrap();
734
735 assert_matches!(previous_chunk, Some(previous_chunk) => {
736 assert_eq!(previous_chunk.identifier, 7);
737 assert!(previous_chunk.previous.is_none());
738 assert_matches!(previous_chunk.next, Some(next) => {
739 assert_eq!(next, 42);
740 });
741 assert_matches!(previous_chunk.content, ChunkContent::Items(items) => {
742 assert_eq!(items.len(), 2);
743 check_test_event(&items[0], "brigand du jorat");
744 check_test_event(&items[1], "morbier");
745 });
746 });
747 }
748 }
749
750 async fn test_linked_chunk_incremental_loading(&self) {
751 let room_id = room_id!("!r0:matrix.org");
752 let linked_chunk_id = LinkedChunkId::Room(room_id);
753 let event = |msg: &str| make_test_event(room_id, msg);
754
755 {
757 let (last_chunk, chunk_identifier_generator) =
758 self.load_last_chunk(linked_chunk_id).await.unwrap();
759
760 assert!(last_chunk.is_none());
761 assert_eq!(chunk_identifier_generator.current(), 0);
762 }
763
764 self.handle_linked_chunk_updates(
765 linked_chunk_id,
766 vec![
767 Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
769 Update::PushItems {
771 at: Position::new(CId::new(0), 0),
772 items: vec![event("a"), event("b")],
773 },
774 Update::NewGapChunk {
776 previous: Some(CId::new(0)),
777 new: CId::new(1),
778 next: None,
779 gap: Gap { token: "morbier".to_owned() },
780 },
781 Update::NewItemsChunk { previous: Some(CId::new(1)), new: CId::new(2), next: None },
783 Update::PushItems {
785 at: Position::new(CId::new(2), 0),
786 items: vec![event("c"), event("d"), event("e")],
787 },
788 ],
789 )
790 .await
791 .unwrap();
792
793 let mut linked_chunk = {
795 let (last_chunk, chunk_identifier_generator) =
796 self.load_last_chunk(linked_chunk_id).await.unwrap();
797
798 assert_eq!(chunk_identifier_generator.current(), 2);
799
800 let linked_chunk = lazy_loader::from_last_chunk::<DEFAULT_CHUNK_CAPACITY, _, _>(
801 last_chunk,
802 chunk_identifier_generator,
803 )
804 .unwrap() .unwrap(); let mut rchunks = linked_chunk.rchunks();
808
809 assert_matches!(rchunks.next(), Some(chunk) => {
811 assert_eq!(chunk.identifier(), 2);
812 assert_eq!(chunk.lazy_previous(), Some(CId::new(1)));
813
814 assert_matches!(chunk.content(), ChunkContent::Items(events) => {
815 assert_eq!(events.len(), 3);
816 check_test_event(&events[0], "c");
817 check_test_event(&events[1], "d");
818 check_test_event(&events[2], "e");
819 });
820 });
821
822 assert!(rchunks.next().is_none());
823
824 linked_chunk
825 };
826
827 {
829 let first_chunk = linked_chunk.chunks().next().unwrap().identifier();
830 let previous_chunk =
831 self.load_previous_chunk(linked_chunk_id, first_chunk).await.unwrap().unwrap();
832
833 lazy_loader::insert_new_first_chunk(&mut linked_chunk, previous_chunk).unwrap();
834
835 let mut rchunks = linked_chunk.rchunks();
836
837 assert_matches!(rchunks.next(), Some(chunk) => {
839 assert_eq!(chunk.identifier(), 2);
840 assert!(chunk.lazy_previous().is_none());
841
842 assert_matches!(chunk.content(), ChunkContent::Items(events) => {
844 assert_eq!(events.len(), 3);
845 check_test_event(&events[0], "c");
846 check_test_event(&events[1], "d");
847 check_test_event(&events[2], "e");
848 });
849 });
850
851 assert_matches!(rchunks.next(), Some(chunk) => {
853 assert_eq!(chunk.identifier(), 1);
854 assert_eq!(chunk.lazy_previous(), Some(CId::new(0)));
855
856 assert_matches!(chunk.content(), ChunkContent::Gap(gap) => {
857 assert_eq!(gap.token, "morbier");
858 });
859 });
860
861 assert!(rchunks.next().is_none());
862 }
863
864 {
866 let first_chunk = linked_chunk.chunks().next().unwrap().identifier();
867 let previous_chunk =
868 self.load_previous_chunk(linked_chunk_id, first_chunk).await.unwrap().unwrap();
869
870 lazy_loader::insert_new_first_chunk(&mut linked_chunk, previous_chunk).unwrap();
871
872 let mut rchunks = linked_chunk.rchunks();
873
874 assert_matches!(rchunks.next(), Some(chunk) => {
876 assert_eq!(chunk.identifier(), 2);
877 assert!(chunk.lazy_previous().is_none());
878
879 assert_matches!(chunk.content(), ChunkContent::Items(events) => {
881 assert_eq!(events.len(), 3);
882 check_test_event(&events[0], "c");
883 check_test_event(&events[1], "d");
884 check_test_event(&events[2], "e");
885 });
886 });
887
888 assert_matches!(rchunks.next(), Some(chunk) => {
890 assert_eq!(chunk.identifier(), 1);
891 assert!(chunk.lazy_previous().is_none());
892
893 assert_matches!(chunk.content(), ChunkContent::Gap(gap) => {
895 assert_eq!(gap.token, "morbier");
896 });
897 });
898
899 assert_matches!(rchunks.next(), Some(chunk) => {
901 assert_eq!(chunk.identifier(), 0);
902 assert!(chunk.lazy_previous().is_none());
903
904 assert_matches!(chunk.content(), ChunkContent::Items(events) => {
905 assert_eq!(events.len(), 2);
906 check_test_event(&events[0], "a");
907 check_test_event(&events[1], "b");
908 });
909 });
910
911 assert!(rchunks.next().is_none());
912 }
913
914 {
916 let first_chunk = linked_chunk.chunks().next().unwrap().identifier();
917 let previous_chunk =
918 self.load_previous_chunk(linked_chunk_id, first_chunk).await.unwrap();
919
920 assert!(previous_chunk.is_none());
921 }
922
923 {
926 let mut chunks = linked_chunk.chunks();
927
928 assert_matches!(chunks.next(), Some(chunk) => {
930 assert_eq!(chunk.identifier(), 0);
931 assert!(chunk.lazy_previous().is_none());
932
933 assert_matches!(chunk.content(), ChunkContent::Items(events) => {
934 assert_eq!(events.len(), 2);
935 check_test_event(&events[0], "a");
936 check_test_event(&events[1], "b");
937 });
938 });
939
940 assert_matches!(chunks.next(), Some(chunk) => {
942 assert_eq!(chunk.identifier(), 1);
943 assert!(chunk.lazy_previous().is_none());
944
945 assert_matches!(chunk.content(), ChunkContent::Gap(gap) => {
946 assert_eq!(gap.token, "morbier");
947 });
948 });
949
950 assert_matches!(chunks.next(), Some(chunk) => {
952 assert_eq!(chunk.identifier(), 2);
953 assert!(chunk.lazy_previous().is_none());
954
955 assert_matches!(chunk.content(), ChunkContent::Items(events) => {
956 assert_eq!(events.len(), 3);
957 check_test_event(&events[0], "c");
958 check_test_event(&events[1], "d");
959 check_test_event(&events[2], "e");
960 });
961 });
962
963 assert!(chunks.next().is_none());
964 }
965 }
966
967 async fn test_linked_chunk_remove_chunk(&self) {
968 let room_id = &DEFAULT_TEST_ROOM_ID;
969 let linked_chunk_id = LinkedChunkId::Room(room_id);
970
971 self.handle_linked_chunk_updates(
972 linked_chunk_id,
973 vec![
974 Update::NewGapChunk {
975 previous: None,
976 new: CId::new(42),
977 next: None,
978 gap: Gap { token: "raclette".to_owned() },
979 },
980 Update::NewGapChunk {
981 previous: Some(CId::new(42)),
982 new: CId::new(43),
983 next: None,
984 gap: Gap { token: "fondue".to_owned() },
985 },
986 Update::NewGapChunk {
987 previous: Some(CId::new(43)),
988 new: CId::new(44),
989 next: None,
990 gap: Gap { token: "tartiflette".to_owned() },
991 },
992 Update::RemoveChunk(CId::new(43)),
993 ],
994 )
995 .await
996 .unwrap();
997
998 let mut chunks = self.load_all_chunks(linked_chunk_id).await.unwrap();
999
1000 assert_eq!(chunks.len(), 2);
1001
1002 let c = chunks.remove(0);
1004 assert_eq!(c.identifier, CId::new(42));
1005 assert_eq!(c.previous, None);
1006 assert_eq!(c.next, Some(CId::new(44)));
1007 assert_matches!(c.content, ChunkContent::Gap(gap) => {
1008 assert_eq!(gap.token, "raclette");
1009 });
1010
1011 let c = chunks.remove(0);
1012 assert_eq!(c.identifier, CId::new(44));
1013 assert_eq!(c.previous, Some(CId::new(42)));
1014 assert_eq!(c.next, None);
1015 assert_matches!(c.content, ChunkContent::Gap(gap) => {
1016 assert_eq!(gap.token, "tartiflette");
1017 });
1018 }
1019
1020 async fn test_linked_chunk_push_items(&self) {
1021 let room_id = *DEFAULT_TEST_ROOM_ID;
1022
1023 let linked_chunk_ids = [
1025 LinkedChunkId::Room(room_id),
1026 LinkedChunkId::Thread(room_id, event_id!("$thread_root")),
1027 LinkedChunkId::PinnedEvents(room_id),
1028 LinkedChunkId::EventFocused(room_id, event_id!("$focus_root")),
1029 ];
1030
1031 let event_id_a = event_id!("$a");
1033 let event_a = make_test_event_with_event_id(room_id, "a", Some(event_id_a));
1034 for linked_chunk_id in linked_chunk_ids {
1035 self.handle_linked_chunk_updates(
1036 linked_chunk_id,
1037 vec![
1038 Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
1039 Update::PushItems {
1040 at: Position::new(CId::new(0), 0),
1041 items: vec![event_a.clone()],
1042 },
1043 ],
1044 )
1045 .await
1046 .unwrap();
1047
1048 let mut chunks = self.load_all_chunks(linked_chunk_id).await.unwrap();
1049 assert_eq!(chunks.len(), 1);
1050 let chunk = chunks.remove(0);
1051 assert_matches!(chunk.content, ChunkContent::Items(events) => {
1052 assert_eq!(events.len(), 1);
1053 check_test_event(&events[0], "a");
1054 });
1055 }
1056
1057 let event_b = make_test_event_with_event_id(room_id, "b", Some(event_id!("$b")));
1058 for linked_chunk_id in linked_chunk_ids {
1061 self.handle_linked_chunk_updates(
1062 linked_chunk_id,
1063 vec![Update::PushItems {
1064 at: Position::new(CId::new(0), 0),
1065 items: vec![event_b.clone()],
1066 }],
1067 )
1068 .await
1069 .expect_err("should fail to push an event to an occupied position");
1070
1071 let mut chunks = self.load_all_chunks(linked_chunk_id).await.unwrap();
1072 assert_eq!(chunks.len(), 1);
1073 let chunk = chunks.remove(0);
1074 assert_matches!(chunk.content, ChunkContent::Items(events) => {
1075 assert_eq!(events.len(), 1);
1076 check_test_event(&events[0], "a");
1077 });
1078 }
1079
1080 for linked_chunk_id in linked_chunk_ids {
1083 self.handle_linked_chunk_updates(
1084 linked_chunk_id,
1085 vec![Update::PushItems {
1086 at: Position::new(CId::new(0), 1),
1087 items: vec![event_b.clone()],
1088 }],
1089 )
1090 .await
1091 .unwrap();
1092
1093 let mut chunks = self.load_all_chunks(linked_chunk_id).await.unwrap();
1094 assert_eq!(chunks.len(), 1);
1095 let chunk = chunks.remove(0);
1096 assert_matches!(chunk.content, ChunkContent::Items(events) => {
1097 assert_eq!(events.len(), 2);
1098 check_test_event(&events[0], "a");
1099 check_test_event(&events[1], "b");
1100 });
1101 }
1102
1103 let updated_event_a = make_test_event_with_event_id(room_id, "updated_a", Some(event_id_a));
1105
1106 for linked_chunk_id in linked_chunk_ids {
1109 self.handle_linked_chunk_updates(
1110 linked_chunk_id,
1111 vec![Update::PushItems {
1112 at: Position::new(CId::new(0), 0),
1113 items: vec![updated_event_a.clone()],
1114 }],
1115 )
1116 .await
1117 .expect_err("should fail to push an event to an occupied position");
1118
1119 let mut chunks = self.load_all_chunks(linked_chunk_id).await.unwrap();
1120 assert_eq!(chunks.len(), 1);
1121 let chunk = chunks.remove(0);
1122 assert_matches!(chunk.content, ChunkContent::Items(events) => {
1123 assert_eq!(events.len(), 2);
1124 check_test_event(&events[0], "a");
1125 check_test_event(&events[1], "b");
1126 });
1127 }
1128
1129 for linked_chunk_id in linked_chunk_ids {
1132 self.handle_linked_chunk_updates(
1133 linked_chunk_id,
1134 vec![Update::PushItems {
1135 at: Position::new(CId::new(0), 1),
1136 items: vec![updated_event_a.clone()],
1137 }],
1138 )
1139 .await
1140 .expect_err("should fail to push an event to an occupied position");
1141
1142 let mut chunks = self.load_all_chunks(linked_chunk_id).await.unwrap();
1143 assert_eq!(chunks.len(), 1);
1144 let chunk = chunks.remove(0);
1145 assert_matches!(chunk.content, ChunkContent::Items(events) => {
1146 assert_eq!(events.len(), 2);
1147 check_test_event(&events[0], "a");
1148 check_test_event(&events[1], "b");
1149 });
1150 }
1151
1152 for linked_chunk_id in linked_chunk_ids {
1155 self.handle_linked_chunk_updates(
1156 linked_chunk_id,
1157 vec![Update::PushItems {
1158 at: Position::new(CId::new(0), 2),
1159 items: vec![updated_event_a.clone()],
1160 }],
1161 )
1162 .await
1163 .expect_err("should fail to push an event that already exists in the linked chunk");
1164
1165 let mut chunks = self.load_all_chunks(linked_chunk_id).await.unwrap();
1166 assert_eq!(chunks.len(), 1);
1167 let chunk = chunks.remove(0);
1168 assert_matches!(chunk.content, ChunkContent::Items(events) => {
1169 assert_eq!(events.len(), 2);
1170 check_test_event(&events[0], "a");
1171 check_test_event(&events[1], "b");
1172 });
1173 }
1174
1175 let other_linked_chunk_id = LinkedChunkId::Room(room_id!("!other_room:localhost"));
1177 self.handle_linked_chunk_updates(
1178 other_linked_chunk_id,
1179 vec![Update::NewItemsChunk { previous: None, new: CId::new(0), next: None }],
1180 )
1181 .await
1182 .unwrap();
1183
1184 self.handle_linked_chunk_updates(
1188 other_linked_chunk_id,
1189 vec![Update::PushItems {
1190 at: Position::new(CId::new(0), 0),
1191 items: vec![updated_event_a.clone()],
1192 }],
1193 )
1194 .await
1195 .unwrap();
1196
1197 let mut chunks = self.load_all_chunks(other_linked_chunk_id).await.unwrap();
1198 assert_eq!(chunks.len(), 1);
1199 let chunk = chunks.remove(0);
1200 assert_matches!(chunk.content, ChunkContent::Items(events) => {
1201 assert_eq!(events.len(), 1);
1202 check_test_event(&events[0], "updated_a");
1203 });
1204
1205 for linked_chunk_id in linked_chunk_ids {
1206 let mut chunks = self.load_all_chunks(linked_chunk_id).await.unwrap();
1207 assert_eq!(chunks.len(), 1);
1208 let chunk = chunks.remove(0);
1209 assert_matches!(chunk.content, ChunkContent::Items(events) => {
1210 assert_eq!(events.len(), 2);
1211 check_test_event(&events[0], "updated_a");
1212 check_test_event(&events[1], "b");
1213 });
1214 }
1215 }
1216
1217 async fn test_linked_chunk_replace_item(&self) {
1218 let room_id = &DEFAULT_TEST_ROOM_ID;
1219
1220 let linked_chunk_ids = [
1223 LinkedChunkId::Room(room_id),
1224 LinkedChunkId::Thread(room_id, event_id!("$thread_root")),
1225 LinkedChunkId::PinnedEvents(room_id),
1226 LinkedChunkId::EventFocused(room_id, event_id!("$focus_root")),
1227 LinkedChunkId::Room(room_id!("!other_room")),
1228 ];
1229
1230 let event_id = event_id!("$world");
1232
1233 for linked_chunk_id in linked_chunk_ids {
1235 self.handle_linked_chunk_updates(
1236 linked_chunk_id,
1237 vec![
1238 Update::NewItemsChunk { previous: None, new: CId::new(42), next: None },
1239 Update::PushItems {
1240 at: Position::new(CId::new(42), 0),
1241 items: vec![
1242 make_test_event(room_id, "hello"),
1243 make_test_event_with_event_id(room_id, "world", Some(event_id)),
1244 ],
1245 },
1246 ],
1247 )
1248 .await
1249 .unwrap();
1250
1251 let mut chunks = self.load_all_chunks(linked_chunk_id).await.unwrap();
1252
1253 assert_eq!(chunks.len(), 1);
1254
1255 let c = chunks.remove(0);
1256 assert_eq!(c.identifier, CId::new(42));
1257 assert_eq!(c.previous, None);
1258 assert_eq!(c.next, None);
1259 assert_matches!(c.content, ChunkContent::Items(events) => {
1260 assert_eq!(events.len(), 2);
1261 check_test_event(&events[0], "hello");
1262 check_test_event(&events[1], "world");
1263 });
1264 }
1265
1266 self.handle_linked_chunk_updates(
1269 linked_chunk_ids[0],
1270 vec![Update::ReplaceItem {
1271 at: Position::new(CId::new(42), 1),
1272 item: make_test_event_with_event_id(room_id, "yolo", Some(event_id)),
1273 }],
1274 )
1275 .await
1276 .unwrap();
1277
1278 for linked_chunk_id in linked_chunk_ids {
1280 let mut chunks = self.load_all_chunks(linked_chunk_id).await.unwrap();
1281
1282 assert_eq!(chunks.len(), 1);
1283
1284 let c = chunks.remove(0);
1285 assert_eq!(c.identifier, CId::new(42));
1286 assert_eq!(c.previous, None);
1287 assert_eq!(c.next, None);
1288 assert_matches!(c.content, ChunkContent::Items(events) => {
1289 assert_eq!(events.len(), 2);
1290 check_test_event(&events[0], "hello");
1291 check_test_event(&events[1], "yolo");
1292 });
1293 }
1294 }
1295
1296 async fn test_linked_chunk_filter_incomplete_events(&self) {
1297 let room_id = &DEFAULT_TEST_ROOM_ID;
1298 let linked_chunk_id = LinkedChunkId::Room(room_id);
1299
1300 let event = make_test_event(room_id, "event");
1301 assert_matches!(event.event_id(), Some(_));
1302 assert_matches!(event.kind.event_type(), Some(_));
1303
1304 let event_without_id = make_test_event_without_event_id(room_id, "event-without-id");
1305 assert_eq!(event_without_id.event_id(), None);
1306 assert_matches!(event.kind.event_type(), Some(_));
1307
1308 let event_without_event_type =
1309 make_test_event_without_event_type(room_id, "event-without-event-type");
1310 assert_matches!(event_without_event_type.event_id(), Some(_));
1311 assert_eq!(event_without_event_type.kind.event_type(), None);
1312
1313 self.handle_linked_chunk_updates(
1314 linked_chunk_id,
1315 vec![
1316 Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
1317 Update::PushItems {
1318 at: Position::new(CId::new(0), 0),
1319 items: vec![event, event_without_id.clone(), event_without_event_type.clone()],
1320 },
1321 ],
1322 )
1323 .await
1324 .unwrap();
1325
1326 let mut chunks = self.load_all_chunks(linked_chunk_id).await.unwrap();
1327 assert_eq!(chunks.len(), 1);
1328 let c = chunks.remove(0);
1329 assert_eq!(c.identifier, CId::new(0));
1330 assert_eq!(c.previous, None);
1331 assert_eq!(c.next, None);
1332 assert_matches!(c.content, ChunkContent::Items(events) => {
1333 assert_eq!(events.len(), 1);
1334 check_test_event(&events[0], "event");
1335 });
1336
1337 for event in [event_without_id, event_without_event_type] {
1338 self.handle_linked_chunk_updates(
1339 linked_chunk_id,
1340 vec![Update::ReplaceItem { at: Position::new(CId::new(0), 0), item: event }],
1341 )
1342 .await
1343 .unwrap();
1344
1345 let mut chunks = self.load_all_chunks(linked_chunk_id).await.unwrap();
1346 assert_eq!(chunks.len(), 1);
1347 let c = chunks.remove(0);
1348 assert_eq!(c.identifier, CId::new(0));
1349 assert_eq!(c.previous, None);
1350 assert_eq!(c.next, None);
1351 assert_matches!(c.content, ChunkContent::Items(events) => {
1352 assert_eq!(events.len(), 1);
1353 check_test_event(&events[0], "event");
1354 });
1355 }
1356 }
1357
1358 async fn test_linked_chunk_remove_item(&self) {
1359 let room_id = *DEFAULT_TEST_ROOM_ID;
1360 let linked_chunk_id = LinkedChunkId::Room(room_id);
1361
1362 self.handle_linked_chunk_updates(
1363 linked_chunk_id,
1364 vec![
1365 Update::NewItemsChunk { previous: None, new: CId::new(42), next: None },
1366 Update::PushItems {
1367 at: Position::new(CId::new(42), 0),
1368 items: vec![
1369 make_test_event(room_id, "one"),
1370 make_test_event(room_id, "two"),
1371 make_test_event(room_id, "three"),
1372 make_test_event(room_id, "four"),
1373 make_test_event(room_id, "five"),
1374 make_test_event(room_id, "six"),
1375 ],
1376 },
1377 Update::RemoveItem { at: Position::new(CId::new(42), 2) },
1378 Update::PushItems {
1390 at: Position::new(CId::new(42), 5),
1391 items: vec![make_test_event(room_id, "seven")],
1392 },
1393 ],
1394 )
1395 .await
1396 .unwrap();
1397
1398 let mut chunks = self.load_all_chunks(linked_chunk_id).await.unwrap();
1399
1400 assert_eq!(chunks.len(), 1);
1401
1402 let c = chunks.remove(0);
1403 assert_eq!(c.identifier, CId::new(42));
1404 assert_eq!(c.previous, None);
1405 assert_eq!(c.next, None);
1406 assert_matches!(c.content, ChunkContent::Items(events) => {
1407 assert_eq!(events.len(), 6);
1408 check_test_event(&events[0], "one");
1409 check_test_event(&events[1], "two");
1410 check_test_event(&events[2], "four");
1411 check_test_event(&events[3], "five");
1412 check_test_event(&events[4], "six");
1413 check_test_event(&events[5], "seven");
1414 });
1415
1416 let metas = self.load_all_chunks_metadata(linked_chunk_id).await.unwrap();
1418 assert_eq!(metas.len(), 1);
1419 assert_eq!(metas[0].num_items, 6);
1420 }
1421
1422 async fn test_linked_chunk_detach_last_items(&self) {
1423 let room_id = *DEFAULT_TEST_ROOM_ID;
1424 let linked_chunk_id = LinkedChunkId::Room(room_id);
1425
1426 self.handle_linked_chunk_updates(
1427 linked_chunk_id,
1428 vec![
1429 Update::NewItemsChunk { previous: None, new: CId::new(42), next: None },
1430 Update::PushItems {
1431 at: Position::new(CId::new(42), 0),
1432 items: vec![
1433 make_test_event(room_id, "hello"),
1434 make_test_event(room_id, "world"),
1435 make_test_event(room_id, "howdy"),
1436 ],
1437 },
1438 Update::DetachLastItems { at: Position::new(CId::new(42), 1) },
1439 ],
1440 )
1441 .await
1442 .unwrap();
1443
1444 let mut chunks = self.load_all_chunks(linked_chunk_id).await.unwrap();
1445
1446 assert_eq!(chunks.len(), 1);
1447
1448 let c = chunks.remove(0);
1449 assert_eq!(c.identifier, CId::new(42));
1450 assert_eq!(c.previous, None);
1451 assert_eq!(c.next, None);
1452 assert_matches!(c.content, ChunkContent::Items(events) => {
1453 assert_eq!(events.len(), 1);
1454 check_test_event(&events[0], "hello");
1455 });
1456 }
1457
1458 async fn test_linked_chunk_start_end_reattach_items(&self) {
1459 let room_id = *DEFAULT_TEST_ROOM_ID;
1460 let linked_chunk_id = LinkedChunkId::Room(room_id);
1461
1462 self.handle_linked_chunk_updates(
1466 linked_chunk_id,
1467 vec![
1468 Update::NewItemsChunk { previous: None, new: CId::new(42), next: None },
1469 Update::PushItems {
1470 at: Position::new(CId::new(42), 0),
1471 items: vec![
1472 make_test_event(room_id, "hello"),
1473 make_test_event(room_id, "world"),
1474 make_test_event(room_id, "howdy"),
1475 ],
1476 },
1477 Update::StartReattachItems,
1478 Update::EndReattachItems,
1479 ],
1480 )
1481 .await
1482 .unwrap();
1483
1484 let mut chunks = self.load_all_chunks(linked_chunk_id).await.unwrap();
1485
1486 assert_eq!(chunks.len(), 1);
1487
1488 let c = chunks.remove(0);
1489 assert_eq!(c.identifier, CId::new(42));
1490 assert_eq!(c.previous, None);
1491 assert_eq!(c.next, None);
1492 assert_matches!(c.content, ChunkContent::Items(events) => {
1493 assert_eq!(events.len(), 3);
1494 check_test_event(&events[0], "hello");
1495 check_test_event(&events[1], "world");
1496 check_test_event(&events[2], "howdy");
1497 });
1498 }
1499
1500 async fn test_linked_chunk_clear(&self) {
1501 let room_id = *DEFAULT_TEST_ROOM_ID;
1502 let linked_chunk_id = LinkedChunkId::Room(room_id);
1503 let event_0 = make_test_event(room_id, "hello");
1504 let event_1 = make_test_event(room_id, "world");
1505 let event_2 = make_test_event(room_id, "howdy");
1506
1507 self.handle_linked_chunk_updates(
1508 linked_chunk_id,
1509 vec![
1510 Update::NewItemsChunk { previous: None, new: CId::new(42), next: None },
1511 Update::NewGapChunk {
1512 previous: Some(CId::new(42)),
1513 new: CId::new(54),
1514 next: None,
1515 gap: Gap { token: "fondue".to_owned() },
1516 },
1517 Update::PushItems {
1518 at: Position::new(CId::new(42), 0),
1519 items: vec![event_0.clone(), event_1, event_2],
1520 },
1521 Update::Clear,
1522 ],
1523 )
1524 .await
1525 .unwrap();
1526
1527 let chunks = self.load_all_chunks(linked_chunk_id).await.unwrap();
1528 assert!(chunks.is_empty());
1529 }
1530
1531 async fn test_linked_chunk_clear_and_reinsert(&self) {
1532 let room_id = *DEFAULT_TEST_ROOM_ID;
1533 let linked_chunk_id = LinkedChunkId::Room(room_id);
1534 let event_0 = make_test_event(room_id, "hello");
1535 let event_1 = make_test_event(room_id, "world");
1536 let event_2 = make_test_event(room_id, "howdy");
1537
1538 self.handle_linked_chunk_updates(
1539 linked_chunk_id,
1540 vec![
1541 Update::NewItemsChunk { previous: None, new: CId::new(42), next: None },
1542 Update::NewGapChunk {
1543 previous: Some(CId::new(42)),
1544 new: CId::new(54),
1545 next: None,
1546 gap: Gap { token: "fondue".to_owned() },
1547 },
1548 Update::PushItems {
1549 at: Position::new(CId::new(42), 0),
1550 items: vec![event_0.clone(), event_1, event_2],
1551 },
1552 Update::Clear,
1553 ],
1554 )
1555 .await
1556 .unwrap();
1557
1558 let chunks = self.load_all_chunks(linked_chunk_id).await.unwrap();
1559 assert!(chunks.is_empty());
1560
1561 self.handle_linked_chunk_updates(
1563 linked_chunk_id,
1564 vec![
1565 Update::NewItemsChunk { previous: None, new: CId::new(42), next: None },
1566 Update::PushItems { at: Position::new(CId::new(42), 0), items: vec![event_0] },
1567 ],
1568 )
1569 .await
1570 .unwrap();
1571 }
1572
1573 async fn test_rebuild_empty_linked_chunk(&self) {
1574 let linked_chunk = lazy_loader::from_all_chunks::<3, _, _>(
1576 self.load_all_chunks(LinkedChunkId::Room(&DEFAULT_TEST_ROOM_ID)).await.unwrap(),
1577 )
1578 .unwrap();
1579 assert!(linked_chunk.is_none());
1580 }
1581
1582 async fn test_linked_chunk_multiple_rooms(&self) {
1583 let room1 = room_id!("!realcheeselovers:raclette.fr");
1584 let linked_chunk_id1 = LinkedChunkId::Room(room1);
1585 let room2 = room_id!("!realcheeselovers:fondue.ch");
1586 let linked_chunk_id2 = LinkedChunkId::Room(room2);
1587
1588 self.handle_linked_chunk_updates(
1592 linked_chunk_id1,
1593 vec![
1594 Update::NewItemsChunk { previous: None, new: CId::new(42), next: None },
1595 Update::PushItems {
1596 at: Position::new(CId::new(42), 0),
1597 items: vec![
1598 make_test_event(room1, "best cheese is raclette"),
1599 make_test_event(room1, "obviously"),
1600 ],
1601 },
1602 ],
1603 )
1604 .await
1605 .unwrap();
1606
1607 self.handle_linked_chunk_updates(
1608 linked_chunk_id2,
1609 vec![
1610 Update::NewItemsChunk { previous: None, new: CId::new(42), next: None },
1611 Update::PushItems {
1612 at: Position::new(CId::new(42), 0),
1613 items: vec![make_test_event(room1, "beaufort is the best")],
1614 },
1615 ],
1616 )
1617 .await
1618 .unwrap();
1619
1620 let mut chunks_room1 = self.load_all_chunks(linked_chunk_id1).await.unwrap();
1622 assert_eq!(chunks_room1.len(), 1);
1623
1624 let c = chunks_room1.remove(0);
1625 assert_matches!(c.content, ChunkContent::Items(events) => {
1626 assert_eq!(events.len(), 2);
1627 check_test_event(&events[0], "best cheese is raclette");
1628 check_test_event(&events[1], "obviously");
1629 });
1630
1631 let mut chunks_room2 = self.load_all_chunks(linked_chunk_id2).await.unwrap();
1633 assert_eq!(chunks_room2.len(), 1);
1634
1635 let c = chunks_room2.remove(0);
1636 assert_matches!(c.content, ChunkContent::Items(events) => {
1637 assert_eq!(events.len(), 1);
1638 check_test_event(&events[0], "beaufort is the best");
1639 });
1640 }
1641
1642 async fn test_load_and_update_thread_info(&self) {
1643 let room_id = room_id!("!r0");
1644 let thread_id = event_id!("$t0");
1645
1646 assert!(self.load_thread_info(room_id, thread_id, false).await.unwrap().is_none());
1650
1651 let ThreadInfo { number_of_replies: _, latest_event: _, read_receipts } =
1656 self.load_thread_info(room_id, thread_id, true).await.unwrap().unwrap();
1657 let ReadReceipts { num_unread, num_notifications, num_mentions, latest_active, pending } =
1658 read_receipts;
1659 assert_eq!(num_unread, 0);
1660 assert_eq!(num_notifications, 0);
1661 assert_eq!(num_mentions, 0);
1662 assert!(latest_active.is_none());
1663 assert!(pending.is_empty());
1664
1665 let mut thread_info =
1670 self.load_thread_info(room_id, thread_id, false).await.unwrap().unwrap();
1671 let ThreadInfo { number_of_replies: _, latest_event: _, read_receipts } = &thread_info;
1672 let ReadReceipts { num_unread, num_notifications, num_mentions, latest_active, pending } =
1673 read_receipts;
1674 assert_eq!(*num_unread, 0);
1675 assert_eq!(*num_notifications, 0);
1676 assert_eq!(*num_mentions, 0);
1677 assert!(latest_active.is_none());
1678 assert!(pending.is_empty());
1679
1680 thread_info.read_receipts.num_unread = 1;
1682 thread_info.read_receipts.num_notifications = 2;
1683 self.update_thread_info(room_id, thread_id, &thread_info).await.unwrap();
1684
1685 let ThreadInfo { number_of_replies: _, latest_event: _, read_receipts } =
1689 self.load_thread_info(room_id, thread_id, true).await.unwrap().unwrap();
1690 let ReadReceipts { num_unread, num_notifications, num_mentions, latest_active, pending } =
1691 read_receipts;
1692 assert_eq!(num_unread, 1);
1693 assert_eq!(num_notifications, 2);
1694 assert_eq!(num_mentions, 0);
1695 assert!(latest_active.is_none());
1696 assert!(pending.is_empty());
1697 }
1698
1699 async fn test_clear_all_events(&self) {
1700 let linked_chunk_ids = [
1701 LinkedChunkId::Room(room_id!("!r0")),
1702 LinkedChunkId::Thread(room_id!("!r1"), event_id!("$r1_thread_root0")),
1703 LinkedChunkId::PinnedEvents(room_id!("!r2")),
1704 ];
1706
1707 for linked_chunk_id in linked_chunk_ids {
1709 let room_id = linked_chunk_id.room_id();
1710
1711 if let LinkedChunkId::Thread(_, thread_id) = &linked_chunk_id {
1714 self.load_thread_info(room_id, thread_id, true).await.unwrap();
1715 }
1716
1717 self.handle_linked_chunk_updates(
1718 linked_chunk_id,
1719 vec![
1720 Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
1722 Update::PushItems {
1724 at: Position::new(CId::new(0), 0),
1725 items: vec![
1726 make_test_event(room_id, "foo"),
1727 make_test_event(room_id, "bar"),
1728 make_test_event(room_id, "baz"),
1729 ],
1730 },
1731 ],
1732 )
1733 .await
1734 .unwrap();
1735
1736 assert!(
1738 lazy_loader::from_all_chunks::<3, _, _>(
1739 self.load_all_chunks(linked_chunk_id).await.unwrap()
1740 )
1741 .unwrap()
1742 .is_some()
1743 );
1744
1745 assert_eq!(self.get_room_events(room_id, None, None).await.unwrap().len(), 3);
1747 }
1748
1749 self.clear_all_events(None).await.unwrap();
1751
1752 for linked_chunk_id in linked_chunk_ids {
1754 let room_id = linked_chunk_id.room_id();
1755
1756 assert!(
1758 lazy_loader::from_all_chunks::<3, _, _>(
1759 self.load_all_chunks(linked_chunk_id).await.unwrap()
1760 )
1761 .unwrap()
1762 .is_none()
1763 );
1764
1765 assert!(self.get_room_events(room_id, None, None).await.unwrap().is_empty());
1767 }
1768 }
1769
1770 async fn test_clear_all_events_for_specific_room(&self) {
1771 let linked_chunk_ids_for_room_0 = [
1772 LinkedChunkId::Room(room_id!("!r0")),
1773 LinkedChunkId::Thread(room_id!("!r0"), event_id!("$r0_thread_root")),
1774 LinkedChunkId::PinnedEvents(room_id!("!r0")),
1775 ];
1776 let linked_chunk_ids_for_room_1 = [
1777 LinkedChunkId::Room(room_id!("!r1")),
1778 LinkedChunkId::Thread(room_id!("!r1"), event_id!("$r1_thread_root")),
1779 LinkedChunkId::PinnedEvents(room_id!("!r1")),
1780 ];
1781 let linked_chunk_ids_for_room_2 = [
1782 LinkedChunkId::Room(room_id!("!r2")),
1783 LinkedChunkId::Thread(room_id!("!r2"), event_id!("$r2_thread_root")),
1784 LinkedChunkId::PinnedEvents(room_id!("!r2")),
1785 ];
1786
1787 for linked_chunk_id in linked_chunk_ids_for_room_0
1789 .iter()
1790 .chain(&linked_chunk_ids_for_room_1)
1791 .chain(&linked_chunk_ids_for_room_2)
1792 {
1793 let room_id = linked_chunk_id.room_id();
1794
1795 if let LinkedChunkId::Thread(_, thread_id) = &linked_chunk_id {
1798 self.load_thread_info(room_id, thread_id, true).await.unwrap();
1799 }
1800
1801 self.handle_linked_chunk_updates(
1802 *linked_chunk_id,
1803 vec![
1804 Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
1806 Update::PushItems {
1808 at: Position::new(CId::new(0), 0),
1809 items: vec![
1810 make_test_event(room_id, "foo"),
1811 make_test_event(room_id, "bar"),
1812 make_test_event(room_id, "baz"),
1813 ],
1814 },
1815 ],
1816 )
1817 .await
1818 .unwrap();
1819
1820 assert!(
1822 lazy_loader::from_all_chunks::<3, _, _>(
1823 self.load_all_chunks(*linked_chunk_id).await.unwrap()
1824 )
1825 .unwrap()
1826 .is_some()
1827 );
1828
1829 assert_eq!(self.get_room_events(room_id, None, None).await.unwrap().len(), 3);
1831 }
1832
1833 self.clear_all_events(Some(linked_chunk_ids_for_room_1[0].room_id())).await.unwrap();
1835
1836 for linked_chunk_id in linked_chunk_ids_for_room_1 {
1838 let room_id = linked_chunk_id.room_id();
1839
1840 assert!(
1842 lazy_loader::from_all_chunks::<3, _, _>(
1843 self.load_all_chunks(linked_chunk_id).await.unwrap()
1844 )
1845 .unwrap()
1846 .is_none()
1847 );
1848
1849 assert!(self.get_room_events(room_id, None, None).await.unwrap().is_empty());
1851 }
1852
1853 for linked_chunk_id in
1855 linked_chunk_ids_for_room_0.iter().chain(&linked_chunk_ids_for_room_2)
1856 {
1857 let room_id = linked_chunk_id.room_id();
1858
1859 assert!(
1861 lazy_loader::from_all_chunks::<3, _, _>(
1862 self.load_all_chunks(*linked_chunk_id).await.unwrap()
1863 )
1864 .unwrap()
1865 .is_some()
1866 );
1867
1868 assert_eq!(self.get_room_events(room_id, None, None).await.unwrap().len(), 3);
1870 }
1871 }
1872
1873 async fn test_filter_duplicated_events(&self) {
1874 let room_id = room_id!("!r0:matrix.org");
1875 let linked_chunk_id = LinkedChunkId::Room(room_id);
1876 let another_room_id = room_id!("!r1:matrix.org");
1877 let another_linked_chunk_id = LinkedChunkId::Room(another_room_id);
1878 let event = |msg: &str| make_test_event(room_id, msg);
1879
1880 let event_comte = event("comté");
1881 let event_brigand = event("brigand du jorat");
1882 let event_raclette = event("raclette");
1883 let event_morbier = event("morbier");
1884 let event_gruyere = event("gruyère");
1885 let event_tome = event("tome");
1886 let event_mont_dor = event("mont d'or");
1887
1888 self.handle_linked_chunk_updates(
1889 linked_chunk_id,
1890 vec![
1891 Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
1892 Update::PushItems {
1893 at: Position::new(CId::new(0), 0),
1894 items: vec![event_comte.clone(), event_brigand.clone()],
1895 },
1896 Update::NewGapChunk {
1897 previous: Some(CId::new(0)),
1898 new: CId::new(1),
1899 next: None,
1900 gap: Gap { token: "brillat-savarin".to_owned() },
1901 },
1902 Update::NewItemsChunk { previous: Some(CId::new(1)), new: CId::new(2), next: None },
1903 Update::PushItems {
1904 at: Position::new(CId::new(2), 0),
1905 items: vec![event_morbier.clone(), event_mont_dor.clone()],
1906 },
1907 ],
1908 )
1909 .await
1910 .unwrap();
1911
1912 self.handle_linked_chunk_updates(
1915 another_linked_chunk_id,
1916 vec![
1917 Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
1918 Update::PushItems {
1919 at: Position::new(CId::new(0), 0),
1920 items: vec![event_tome.clone()],
1921 },
1922 ],
1923 )
1924 .await
1925 .unwrap();
1926
1927 let duplicated_events = BTreeMap::from_iter(
1928 self.filter_duplicated_events(
1929 linked_chunk_id,
1930 vec![
1931 event_comte.event_id().unwrap().to_owned(),
1932 event_raclette.event_id().unwrap().to_owned(),
1933 event_morbier.event_id().unwrap().to_owned(),
1934 event_gruyere.event_id().unwrap().to_owned(),
1935 event_tome.event_id().unwrap().to_owned(),
1936 event_mont_dor.event_id().unwrap().to_owned(),
1937 ],
1938 )
1939 .await
1940 .unwrap(),
1941 );
1942
1943 assert_eq!(duplicated_events.len(), 3);
1944
1945 assert_eq!(
1946 *duplicated_events.get(event_comte.event_id().unwrap()).unwrap(),
1947 Position::new(CId::new(0), 0)
1948 );
1949 assert_eq!(
1950 *duplicated_events.get(event_morbier.event_id().unwrap()).unwrap(),
1951 Position::new(CId::new(2), 0)
1952 );
1953 assert_eq!(
1954 *duplicated_events.get(event_mont_dor.event_id().unwrap()).unwrap(),
1955 Position::new(CId::new(2), 1)
1956 );
1957 }
1958
1959 async fn test_filter_duplicate_events_no_events(&self) {
1960 let room_id = *DEFAULT_TEST_ROOM_ID;
1961 let linked_chunk_id = LinkedChunkId::Room(room_id);
1962 let duplicates = self.filter_duplicated_events(linked_chunk_id, Vec::new()).await.unwrap();
1963 assert!(duplicates.is_empty());
1964 }
1965
1966 async fn test_find_event(&self) {
1967 let room_id = room_id!("!r0:matrix.org");
1968 let another_room_id = room_id!("!r1:matrix.org");
1969 let another_linked_chunk_id = LinkedChunkId::Room(another_room_id);
1970 let event = |msg: &str| make_test_event(room_id, msg);
1971
1972 let event_comte = event("comté");
1973 let event_gruyere = event("gruyère");
1974
1975 self.handle_linked_chunk_updates(
1977 LinkedChunkId::Room(room_id),
1978 vec![
1979 Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
1980 Update::PushItems {
1981 at: Position::new(CId::new(0), 0),
1982 items: vec![event_comte.clone()],
1983 },
1984 ],
1985 )
1986 .await
1987 .unwrap();
1988
1989 self.handle_linked_chunk_updates(
1991 another_linked_chunk_id,
1992 vec![
1993 Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
1994 Update::PushItems {
1995 at: Position::new(CId::new(0), 0),
1996 items: vec![event_gruyere.clone()],
1997 },
1998 ],
1999 )
2000 .await
2001 .unwrap();
2002
2003 let event = self
2005 .find_event(room_id, event_comte.event_id().unwrap())
2006 .await
2007 .expect("failed to query for finding an event")
2008 .expect("failed to find an event");
2009
2010 assert_eq!(event.event_id(), event_comte.event_id());
2011
2012 assert!(
2015 self.find_event(room_id, event_gruyere.event_id().unwrap())
2016 .await
2017 .expect("failed to query for finding an event")
2018 .is_none()
2019 );
2020
2021 self.clear_all_events(None).await.expect("failed to clear all rooms chunks");
2023 assert!(
2024 self.find_event(room_id, event_comte.event_id().unwrap())
2025 .await
2026 .expect("failed to query for finding an event")
2027 .is_none()
2028 );
2029 }
2030
2031 async fn test_find_event_when_event_in_room_and_thread(&self) {
2032 let room_id = *DEFAULT_TEST_ROOM_ID;
2033 let thread_root = event_id!("$thread_root");
2034
2035 let room_event_id = event_id!("$room_event");
2037 let room_event = make_test_event_with_event_id(room_id, "room event", Some(room_event_id));
2038
2039 let thread_event_id = event_id!("$thread_event");
2041 let thread_event =
2042 make_test_event_with_event_id(room_id, "thread event", Some(thread_event_id));
2043
2044 let room_and_thread_event_id = event_id!("$room_and_thread");
2047 let room_and_thread_event = make_test_event_with_event_id(
2048 room_id,
2049 "room and thread",
2050 Some(room_and_thread_event_id),
2051 );
2052
2053 let room_linked_chunk_id = LinkedChunkId::Room(room_id);
2054 let thread_linked_chunk_id = LinkedChunkId::Thread(room_id, thread_root);
2055
2056 self.handle_linked_chunk_updates(
2058 room_linked_chunk_id,
2059 vec![
2060 Update::NewItemsChunk { previous: None, new: CId::new(1), next: None },
2061 Update::PushItems {
2062 at: Position::new(CId::new(1), 0),
2063 items: vec![room_event, room_and_thread_event.clone()],
2064 },
2065 ],
2066 )
2067 .await
2068 .unwrap();
2069
2070 self.handle_linked_chunk_updates(
2072 thread_linked_chunk_id,
2073 vec![
2074 Update::NewItemsChunk { previous: None, new: CId::new(1), next: None },
2075 Update::PushItems {
2076 at: Position::new(CId::new(1), 0),
2077 items: vec![thread_event, room_and_thread_event],
2078 },
2079 ],
2080 )
2081 .await
2082 .unwrap();
2083
2084 assert_matches!(self.find_event(room_id, room_event_id).await, Ok(Some(event)) => {
2086 assert_eq!(event.event_id().unwrap(), room_event_id)
2087 });
2088
2089 assert_matches!(self.find_event(room_id, thread_event_id).await, Ok(Some(event)) => {
2091 assert_eq!(event.event_id().unwrap(), thread_event_id)
2092 });
2093
2094 assert_matches!(self.find_event(room_id, room_and_thread_event_id).await, Ok(Some(event)) => {
2096 assert_eq!(event.event_id().unwrap(), room_and_thread_event_id);
2097 });
2098 }
2099
2100 async fn test_find_event_relations(&self) {
2101 let room_id = room_id!("!r0:matrix.org");
2102 let another_room_id = room_id!("!r1:matrix.org");
2103
2104 let f = EventFactory::new().room(room_id).sender(*ALICE);
2105
2106 let eid1 = event_id!("$event1:matrix.org");
2108 let e1 = f.text_msg("comter").event_id(eid1).into_event();
2109
2110 let edit_eid1 = event_id!("$edit_event1:matrix.org");
2111 let edit_e1 = f
2112 .text_msg("* comté")
2113 .event_id(edit_eid1)
2114 .edit(eid1, RoomMessageEventContentWithoutRelation::text_plain("comté"))
2115 .into_event();
2116
2117 let reaction_eid1 = event_id!("$reaction_event1:matrix.org");
2118 let reaction_e1 = f.reaction(eid1, "👍").event_id(reaction_eid1).into_event();
2119
2120 let eid2 = event_id!("$event2:matrix.org");
2121 let e2 = f.text_msg("galette saucisse").event_id(eid2).into_event();
2122
2123 let f = f.room(another_room_id);
2125
2126 let eid3 = event_id!("$event3:matrix.org");
2127 let e3 = f.text_msg("gruyère").event_id(eid3).into_event();
2128
2129 let reaction_eid3 = event_id!("$reaction_event3:matrix.org");
2130 let reaction_e3 = f.reaction(eid3, "👍").event_id(reaction_eid3).into_event();
2131
2132 self.save_event(room_id, e1).await.unwrap();
2134 self.save_event(room_id, edit_e1).await.unwrap();
2135 self.save_event(room_id, reaction_e1.clone()).await.unwrap();
2136 self.save_event(room_id, e2).await.unwrap();
2137 self.save_event(another_room_id, e3).await.unwrap();
2138 self.save_event(another_room_id, reaction_e3).await.unwrap();
2139
2140 let relations = self.find_event_relations(room_id, eid1, None).await.unwrap();
2142 assert_eq!(relations.len(), 2);
2143 assert!(
2145 relations.iter().any(|(ev, pos)| ev.event_id() == Some(edit_eid1) && pos.is_none())
2146 );
2147 assert!(
2148 relations.iter().any(|(ev, pos)| ev.event_id() == Some(reaction_eid1) && pos.is_none())
2149 );
2150
2151 let relations = self
2153 .find_event_relations(room_id, eid1, Some(&[RelationType::Replacement]))
2154 .await
2155 .unwrap();
2156 assert_eq!(relations.len(), 1);
2157 assert_eq!(relations[0].0.event_id(), Some(edit_eid1));
2158
2159 let relations = self
2160 .find_event_relations(
2161 room_id,
2162 eid1,
2163 Some(&[RelationType::Replacement, RelationType::Annotation]),
2164 )
2165 .await
2166 .unwrap();
2167 assert_eq!(relations.len(), 2);
2168 assert!(relations.iter().any(|r| r.0.event_id() == Some(edit_eid1)));
2169 assert!(relations.iter().any(|r| r.0.event_id() == Some(reaction_eid1)));
2170
2171 let relations = self
2173 .find_event_relations(another_room_id, eid1, Some(&[RelationType::Replacement]))
2174 .await
2175 .unwrap();
2176 assert!(relations.is_empty());
2177
2178 self.handle_linked_chunk_updates(
2183 LinkedChunkId::Room(room_id),
2184 vec![
2185 Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
2186 Update::PushItems { at: Position::new(CId::new(0), 0), items: vec![reaction_e1] },
2187 ],
2188 )
2189 .await
2190 .unwrap();
2191
2192 let relations = self.find_event_relations(room_id, eid1, None).await.unwrap();
2195
2196 assert!(relations.iter().any(|(ev, pos)| {
2198 ev.event_id() == Some(reaction_eid1) && *pos == Some(Position::new(CId::new(0), 0))
2199 }));
2200
2201 assert!(
2203 relations.iter().any(|(ev, pos)| ev.event_id() == Some(edit_eid1) && pos.is_none())
2204 );
2205 }
2206
2207 async fn test_find_event_relations_when_event_in_room_and_thread(&self) {
2208 let room_id = *DEFAULT_TEST_ROOM_ID;
2209 let thread_root = event_id!("$thread_root");
2210
2211 let event_id = event_id!("$event");
2214 let event = make_test_event_with_event_id(room_id, "event", Some(event_id));
2215
2216 let extra_thread_event_id = event_id!("$extra_thread_event");
2219 let extra_thread_event = make_test_event_with_event_id(
2220 room_id,
2221 "extra thread event",
2222 Some(extra_thread_event_id),
2223 );
2224
2225 let room_reaction_id = event_id!("$room_reaction");
2227 let room_reaction = EventFactory::new()
2228 .room(room_id)
2229 .sender(*ALICE)
2230 .reaction(event_id, "room")
2231 .event_id(room_reaction_id)
2232 .into_event();
2233
2234 let thread_reaction_id = event_id!("$thread_reaction");
2236 let thread_reaction = EventFactory::new()
2237 .room(room_id)
2238 .sender(*ALICE)
2239 .reaction(event_id, "thread")
2240 .event_id(thread_reaction_id)
2241 .into_event();
2242
2243 let room_and_thread_reaction_id = event_id!("$room_and_thread_reaction");
2246 let room_and_thread_reaction = EventFactory::new()
2247 .room(room_id)
2248 .sender(*ALICE)
2249 .reaction(event_id, "room and thread")
2250 .event_id(room_and_thread_reaction_id)
2251 .into_event();
2252
2253 let room_linked_chunk_id = LinkedChunkId::Room(room_id);
2254 let thread_linked_chunk_id = LinkedChunkId::Thread(room_id, thread_root);
2255
2256 self.handle_linked_chunk_updates(
2258 room_linked_chunk_id,
2259 vec![
2260 Update::NewItemsChunk { previous: None, new: CId::new(1), next: None },
2261 Update::PushItems {
2262 at: Position::new(CId::new(1), 0),
2263 items: vec![event.clone(), room_reaction, room_and_thread_reaction.clone()],
2264 },
2265 ],
2266 )
2267 .await
2268 .unwrap();
2269
2270 self.handle_linked_chunk_updates(
2272 thread_linked_chunk_id,
2273 vec![
2274 Update::NewItemsChunk { previous: None, new: CId::new(1), next: None },
2275 Update::PushItems {
2276 at: Position::new(CId::new(1), 0),
2277 items: vec![
2278 event.clone(),
2279 extra_thread_event,
2280 thread_reaction,
2281 room_and_thread_reaction,
2282 ],
2283 },
2284 ],
2285 )
2286 .await
2287 .unwrap();
2288
2289 assert_matches!(self.find_event_relations(room_id, event_id, None).await, Ok(relations) => {
2291 assert_eq!(relations.len(), 3);
2292 let room_relation = relations
2295 .iter()
2296 .find(|relation| relation.0.event_id().unwrap() == room_reaction_id)
2297 .unwrap();
2298 assert_matches!(room_relation, (_, Some(position)) => {
2299 assert_eq!(*position, Position::new(CId::new(1), 1));
2300 });
2301
2302 let thread_relation = relations
2306 .iter()
2307 .find(|relation| relation.0.event_id().unwrap() == thread_reaction_id)
2308 .unwrap();
2309 assert_matches!(thread_relation, (_, None));
2310
2311 let room_and_thread_relation = relations
2315 .iter()
2316 .find(|relation| relation.0.event_id().unwrap() == room_and_thread_reaction_id)
2317 .unwrap();
2318 assert_matches!(room_and_thread_relation, (_, Some(position)) => {
2319 assert_eq!(*position, Position::new(CId::new(1), 2));
2320 });
2321 });
2322 }
2323
2324 async fn test_get_room_events(&self) {
2325 let room_id = room_id!("!r0:matrix.org");
2326 let another_room_id = room_id!("!r1:matrix.org");
2327 let linked_chunk_id = LinkedChunkId::Room(room_id);
2328 let another_linked_chunk_id = LinkedChunkId::Room(another_room_id);
2329 let event = |msg: &str| make_test_event(room_id, msg);
2330
2331 let event_comte = event("comté");
2332 let event_gruyere = event("gruyère");
2333 let event_stilton = event("stilton");
2334
2335 self.handle_linked_chunk_updates(
2337 linked_chunk_id,
2338 vec![
2339 Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
2340 Update::PushItems {
2341 at: Position::new(CId::new(0), 0),
2342 items: vec![event_comte.clone(), event_gruyere.clone()],
2343 },
2344 ],
2345 )
2346 .await
2347 .unwrap();
2348
2349 self.handle_linked_chunk_updates(
2351 another_linked_chunk_id,
2352 vec![
2353 Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
2354 Update::PushItems {
2355 at: Position::new(CId::new(0), 0),
2356 items: vec![event_stilton.clone()],
2357 },
2358 ],
2359 )
2360 .await
2361 .unwrap();
2362
2363 let events = self
2365 .get_room_events(room_id, None, None)
2366 .await
2367 .expect("failed to query for room events");
2368
2369 assert_eq!(events.len(), 2);
2370
2371 let got_ids: Vec<_> =
2372 events.into_iter().map(|ev| ev.event_id().map(ToOwned::to_owned)).collect();
2373 let expected_ids = vec![
2374 event_comte.event_id().map(ToOwned::to_owned),
2375 event_gruyere.event_id().map(ToOwned::to_owned),
2376 ];
2377
2378 for expected in expected_ids {
2379 assert!(
2380 got_ids.contains(&expected),
2381 "Expected event {expected:?} not in got events: {got_ids:?}."
2382 );
2383 }
2384 }
2385
2386 async fn test_get_room_events_filtered(&self) {
2387 macro_rules! assert_expected_events {
2388 ($events:expr, [$($item:expr),* $(,)?]) => {{
2389 let got_ids: BTreeSet<_> = $events.into_iter().map(|ev| ev.event_id().map(ToOwned::to_owned)).flatten().collect();
2390 let expected_ids = BTreeSet::from([$($item.event_id().unwrap().to_owned()),*]);
2391
2392 assert_eq!(got_ids, expected_ids);
2393 }};
2394 }
2395
2396 let room_id = room_id!("!r0:matrix.org");
2397 let linked_chunk_id = LinkedChunkId::Room(room_id);
2398 let another_room_id = room_id!("!r1:matrix.org");
2399 let another_linked_chunk_id = LinkedChunkId::Room(another_room_id);
2400
2401 let event = |session_id: &str| make_encrypted_test_event(room_id, session_id);
2402
2403 let first_event = event("session_1");
2404 let second_event = event("session_2");
2405 let third_event = event("session_3");
2406 let fourth_event = make_test_event(room_id, "It's a secret to everybody");
2407
2408 self.handle_linked_chunk_updates(
2410 linked_chunk_id,
2411 vec![
2412 Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
2413 Update::PushItems {
2414 at: Position::new(CId::new(0), 0),
2415 items: vec![first_event.clone(), second_event.clone(), fourth_event.clone()],
2416 },
2417 ],
2418 )
2419 .await
2420 .unwrap();
2421
2422 self.handle_linked_chunk_updates(
2424 another_linked_chunk_id,
2425 vec![
2426 Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
2427 Update::PushItems {
2428 at: Position::new(CId::new(0), 0),
2429 items: vec![third_event.clone()],
2430 },
2431 ],
2432 )
2433 .await
2434 .unwrap();
2435
2436 let events = self
2438 .get_room_events(room_id, Some("m.room.encrypted"), None)
2439 .await
2440 .expect("failed to query for room events");
2441
2442 assert_eq!(events.len(), 2);
2443 assert_expected_events!(events, [first_event, second_event]);
2444
2445 let events = self
2448 .get_room_events(room_id, Some("m.room.encrypted"), Some("session_1"))
2449 .await
2450 .expect("failed to query for room events");
2451
2452 assert_eq!(events.len(), 1);
2453 assert_expected_events!(events, [first_event]);
2454 }
2455
2456 async fn test_get_room_events_with_event_in_room_and_thread(&self) {
2457 let room_id = *DEFAULT_TEST_ROOM_ID;
2458 let thread_root = event_id!("$thread_root");
2459
2460 let room_event_id = event_id!("$room_event");
2462 let room_event = make_test_event_with_event_id(room_id, "room event", Some(room_event_id));
2463
2464 let thread_event_id = event_id!("$thread_event");
2468 let thread_event =
2469 make_test_event_with_event_id(room_id, "thread event", Some(thread_event_id));
2470
2471 let room_and_thread_event_id = event_id!("$room_and_thread");
2474 let room_and_thread_event = make_test_event_with_event_id(
2475 room_id,
2476 "room and thread",
2477 Some(room_and_thread_event_id),
2478 );
2479
2480 let room_linked_chunk_id = LinkedChunkId::Room(room_id);
2481 let thread_linked_chunk_id = LinkedChunkId::Thread(room_id, thread_root);
2482
2483 self.handle_linked_chunk_updates(
2485 room_linked_chunk_id,
2486 vec![
2487 Update::NewItemsChunk { previous: None, new: CId::new(1), next: None },
2488 Update::PushItems {
2489 at: Position::new(CId::new(1), 0),
2490 items: vec![room_event, room_and_thread_event.clone()],
2491 },
2492 ],
2493 )
2494 .await
2495 .unwrap();
2496
2497 self.handle_linked_chunk_updates(
2499 thread_linked_chunk_id,
2500 vec![
2501 Update::NewItemsChunk { previous: None, new: CId::new(1), next: None },
2502 Update::PushItems {
2503 at: Position::new(CId::new(1), 0),
2504 items: vec![thread_event, room_and_thread_event],
2505 },
2506 ],
2507 )
2508 .await
2509 .unwrap();
2510
2511 let expected_event_ids =
2514 BTreeSet::from([room_event_id, thread_event_id, room_and_thread_event_id]);
2515 assert_matches!(self.get_room_events(room_id, None, None).await, Ok(events) => {
2516 assert_eq!(events.len(), 3);
2517 assert!(events.iter().all(|event| {
2518 expected_event_ids.contains(event.event_id().unwrap())
2519 }));
2520 });
2521 }
2522
2523 async fn test_save_event(&self) {
2524 let room_id = room_id!("!r0:matrix.org");
2525 let another_room_id = room_id!("!r1:matrix.org");
2526
2527 let event = |msg: &str| make_test_event(room_id, msg);
2528 let event_comte = event("comté");
2529 let event_gruyere = event("gruyère");
2530
2531 self.save_event(room_id, event_comte.clone()).await.unwrap();
2533
2534 self.save_event(another_room_id, event_gruyere.clone()).await.unwrap();
2536
2537 let event = self
2539 .find_event(room_id, event_comte.event_id().unwrap())
2540 .await
2541 .expect("failed to query for finding an event")
2542 .expect("failed to find an event");
2543 assert_eq!(event.event_id(), event_comte.event_id());
2544
2545 let event = self
2546 .find_event(another_room_id, event_gruyere.event_id().unwrap())
2547 .await
2548 .expect("failed to query for finding an event")
2549 .expect("failed to find an event");
2550 assert_eq!(event.event_id(), event_gruyere.event_id());
2551
2552 assert!(
2554 self.find_event(another_room_id, event_comte.event_id().unwrap())
2555 .await
2556 .expect("failed to query for finding an event")
2557 .is_none()
2558 );
2559 assert!(
2560 self.find_event(room_id, event_gruyere.event_id().unwrap())
2561 .await
2562 .expect("failed to query for finding an event")
2563 .is_none()
2564 );
2565 }
2566
2567 async fn test_save_event_updates_event_in_room_and_thread(&self) {
2568 let room_id = *DEFAULT_TEST_ROOM_ID;
2569 let thread_root = event_id!("$thread_root");
2570
2571 let event_id = event_id!("$event");
2574 let event = make_test_event_with_event_id(room_id, "event", Some(event_id));
2575
2576 let room_linked_chunk_id = LinkedChunkId::Room(room_id);
2577 let thread_linked_chunk_id = LinkedChunkId::Thread(room_id, thread_root);
2578
2579 self.handle_linked_chunk_updates(
2581 room_linked_chunk_id,
2582 vec![
2583 Update::NewItemsChunk { previous: None, new: CId::new(1), next: None },
2584 Update::PushItems { at: Position::new(CId::new(1), 0), items: vec![event.clone()] },
2585 ],
2586 )
2587 .await
2588 .unwrap();
2589
2590 self.handle_linked_chunk_updates(
2592 thread_linked_chunk_id,
2593 vec![
2594 Update::NewItemsChunk { previous: None, new: CId::new(1), next: None },
2595 Update::PushItems { at: Position::new(CId::new(1), 0), items: vec![event.clone()] },
2596 ],
2597 )
2598 .await
2599 .unwrap();
2600
2601 let updated_content = "updated content";
2604 let updated = make_test_event_with_event_id(room_id, updated_content, Some(event_id));
2605 self.save_event(room_id, updated).await.unwrap();
2606
2607 let room_chunks = self.load_all_chunks(room_linked_chunk_id).await.unwrap();
2609 let thread_chunks = self.load_all_chunks(thread_linked_chunk_id).await.unwrap();
2610
2611 assert_eq!(room_chunks.len(), 1);
2612 assert_eq!(thread_chunks.len(), 1);
2613
2614 assert_matches!(&room_chunks[0].content, ChunkContent::Items(events) => {
2616 assert_eq!(events.len(), 1);
2617 assert_eq!(events[0].event_id(), Some(event_id));
2618 check_test_event(&events[0], updated_content);
2619 });
2620 assert_matches!(&thread_chunks[0].content, ChunkContent::Items(events) => {
2621 assert_eq!(events.len(), 1);
2622 assert_eq!(events[0].event_id(), Some(event_id));
2623 check_test_event(&events[0], updated_content);
2624 });
2625 }
2626
2627 async fn test_thread_vs_room_linked_chunk(&self) {
2628 let room_id = room_id!("!r0:matrix.org");
2629
2630 let event = |msg: &str| make_test_event(room_id, msg);
2631
2632 let thread1_ev = event("comté");
2633 let thread2_ev = event("gruyère");
2634 let thread2_ev2 = event("beaufort");
2635 let room_ev = event("brillat savarin triple crème");
2636
2637 let thread_root1 = event("thread1");
2638 let thread_root2 = event("thread2");
2639
2640 self.handle_linked_chunk_updates(
2642 LinkedChunkId::Thread(room_id, thread_root1.event_id().unwrap()),
2643 vec![
2644 Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
2645 Update::PushItems {
2646 at: Position::new(CId::new(0), 0),
2647 items: vec![thread1_ev.clone()],
2648 },
2649 ],
2650 )
2651 .await
2652 .unwrap();
2653
2654 self.handle_linked_chunk_updates(
2656 LinkedChunkId::Thread(room_id, thread_root2.event_id().unwrap()),
2657 vec![
2658 Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
2659 Update::PushItems {
2660 at: Position::new(CId::new(0), 0),
2661 items: vec![thread2_ev.clone(), thread2_ev2.clone()],
2662 },
2663 ],
2664 )
2665 .await
2666 .unwrap();
2667
2668 self.handle_linked_chunk_updates(
2670 LinkedChunkId::Room(room_id),
2671 vec![
2672 Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
2673 Update::PushItems {
2674 at: Position::new(CId::new(0), 0),
2675 items: vec![room_ev.clone()],
2676 },
2677 ],
2678 )
2679 .await
2680 .unwrap();
2681
2682 self.find_event(room_id, thread2_ev.event_id().unwrap())
2684 .await
2685 .expect("failed to query for finding an event")
2686 .expect("failed to find thread1_ev");
2687
2688 self.find_event(room_id, thread2_ev.event_id().unwrap())
2689 .await
2690 .expect("failed to query for finding an event")
2691 .expect("failed to find thread2_ev");
2692
2693 self.find_event(room_id, thread2_ev2.event_id().unwrap())
2694 .await
2695 .expect("failed to query for finding an event")
2696 .expect("failed to find thread2_ev2");
2697
2698 self.find_event(room_id, room_ev.event_id().unwrap())
2699 .await
2700 .expect("failed to query for finding an event")
2701 .expect("failed to find room_ev");
2702
2703 let dups = self
2705 .filter_duplicated_events(
2706 LinkedChunkId::Thread(room_id, thread_root1.event_id().unwrap()),
2707 vec![
2708 thread1_ev.event_id().unwrap().to_owned(),
2709 room_ev.event_id().unwrap().to_owned(),
2710 ],
2711 )
2712 .await
2713 .unwrap();
2714 assert_eq!(dups.len(), 1);
2715 assert_eq!(dups[0].0, thread1_ev.event_id().unwrap());
2716
2717 let all_chunks = self
2719 .load_all_chunks(LinkedChunkId::Thread(room_id, thread_root2.event_id().unwrap()))
2720 .await
2721 .unwrap();
2722 assert_eq!(all_chunks.len(), 1);
2723 assert_eq!(all_chunks[0].identifier, CId::new(0));
2724 assert_let!(ChunkContent::Items(observed_items) = all_chunks[0].content.clone());
2725 assert_eq!(observed_items.len(), 2);
2726 assert_eq!(observed_items[0].event_id(), thread2_ev.event_id());
2727 assert_eq!(observed_items[1].event_id(), thread2_ev2.event_id());
2728
2729 let metas = self
2732 .load_all_chunks_metadata(LinkedChunkId::Thread(
2733 room_id,
2734 thread_root2.event_id().unwrap(),
2735 ))
2736 .await
2737 .unwrap();
2738 assert_eq!(metas.len(), 1);
2739 assert_eq!(metas[0].identifier, CId::new(0));
2740 assert_eq!(metas[0].num_items, 2);
2741
2742 let (last_chunk, _chunk_identifier_generator) = self
2744 .load_last_chunk(LinkedChunkId::Thread(room_id, thread_root1.event_id().unwrap()))
2745 .await
2746 .unwrap();
2747 let last_chunk = last_chunk.unwrap();
2748 assert_eq!(last_chunk.identifier, CId::new(0));
2749 assert_let!(ChunkContent::Items(observed_items) = last_chunk.content);
2750 assert_eq!(observed_items.len(), 1);
2751 assert_eq!(observed_items[0].event_id(), thread1_ev.event_id());
2752 }
2753}
2754
2755#[allow(unused_macros, unused_extern_crates)]
2785#[macro_export]
2786macro_rules! event_cache_store_integration_tests {
2787 () => {
2788 mod event_cache_store_integration_tests {
2789 use matrix_sdk_test::async_test;
2790 use $crate::event_cache::store::{
2791 EventCacheStoreIntegrationTests, IntoEventCacheStore,
2792 };
2793
2794 use super::get_event_cache_store;
2795
2796 #[async_test]
2797 async fn test_handle_updates_and_rebuild_linked_chunk() {
2798 let event_cache_store =
2799 get_event_cache_store().await.unwrap().into_event_cache_store();
2800 event_cache_store.test_handle_updates_and_rebuild_linked_chunk().await;
2801 }
2802
2803 #[async_test]
2804 async fn test_linked_chunk_exists_before_referenced() {
2805 let event_cache_store =
2806 get_event_cache_store().await.unwrap().into_event_cache_store();
2807 event_cache_store.test_linked_chunk_exists_before_referenced().await;
2808 }
2809
2810 #[async_test]
2811 async fn test_linked_chunk_allow_same_event_in_room_and_thread() {
2812 let event_cache_store =
2813 get_event_cache_store().await.unwrap().into_event_cache_store();
2814 event_cache_store.test_linked_chunk_allows_same_event_in_room_and_thread().await;
2815 }
2816
2817 #[async_test]
2818 async fn test_load_last_chunk() {
2819 let event_cache_store =
2820 get_event_cache_store().await.unwrap().into_event_cache_store();
2821 event_cache_store.test_load_last_chunk().await;
2822 }
2823
2824 #[async_test]
2825 async fn test_load_last_chunk_with_a_cycle() {
2826 let event_cache_store =
2827 get_event_cache_store().await.unwrap().into_event_cache_store();
2828 event_cache_store.test_load_last_chunk_with_a_cycle().await;
2829 }
2830
2831 #[async_test]
2832 async fn test_load_previous_chunk() {
2833 let event_cache_store =
2834 get_event_cache_store().await.unwrap().into_event_cache_store();
2835 event_cache_store.test_load_previous_chunk().await;
2836 }
2837
2838 #[async_test]
2839 async fn test_linked_chunk_incremental_loading() {
2840 let event_cache_store =
2841 get_event_cache_store().await.unwrap().into_event_cache_store();
2842 event_cache_store.test_linked_chunk_incremental_loading().await;
2843 }
2844
2845 #[async_test]
2846 async fn test_linked_chunk_remove_chunk() {
2847 let event_cache_store =
2848 get_event_cache_store().await.unwrap().into_event_cache_store();
2849 event_cache_store.test_linked_chunk_remove_chunk().await;
2850 }
2851
2852 #[async_test]
2853 async fn test_linked_chunk_push_items() {
2854 let event_cache_store =
2855 get_event_cache_store().await.unwrap().into_event_cache_store();
2856 event_cache_store.test_linked_chunk_push_items().await;
2857 }
2858
2859 #[async_test]
2860 async fn test_linked_chunk_replace_item() {
2861 let event_cache_store =
2862 get_event_cache_store().await.unwrap().into_event_cache_store();
2863 event_cache_store.test_linked_chunk_replace_item().await;
2864 }
2865
2866 #[async_test]
2867 async fn test_linked_chunk_filter_incomplete_events() {
2868 let event_cache_store =
2869 get_event_cache_store().await.unwrap().into_event_cache_store();
2870 event_cache_store.test_linked_chunk_filter_incomplete_events().await;
2871 }
2872
2873 #[async_test]
2874 async fn test_linked_chunk_remove_item() {
2875 let event_cache_store =
2876 get_event_cache_store().await.unwrap().into_event_cache_store();
2877 event_cache_store.test_linked_chunk_remove_item().await;
2878 }
2879
2880 #[async_test]
2881 async fn test_linked_chunk_detach_last_items() {
2882 let event_cache_store =
2883 get_event_cache_store().await.unwrap().into_event_cache_store();
2884 event_cache_store.test_linked_chunk_detach_last_items().await;
2885 }
2886
2887 #[async_test]
2888 async fn test_linked_chunk_start_end_reattach_items() {
2889 let event_cache_store =
2890 get_event_cache_store().await.unwrap().into_event_cache_store();
2891 event_cache_store.test_linked_chunk_start_end_reattach_items().await;
2892 }
2893
2894 #[async_test]
2895 async fn test_linked_chunk_clear() {
2896 let event_cache_store =
2897 get_event_cache_store().await.unwrap().into_event_cache_store();
2898 event_cache_store.test_linked_chunk_clear().await;
2899 }
2900
2901 #[async_test]
2902 async fn test_linked_chunk_clear_and_reinsert() {
2903 let event_cache_store =
2904 get_event_cache_store().await.unwrap().into_event_cache_store();
2905 event_cache_store.test_linked_chunk_clear_and_reinsert().await;
2906 }
2907
2908 #[async_test]
2909 async fn test_rebuild_empty_linked_chunk() {
2910 let event_cache_store =
2911 get_event_cache_store().await.unwrap().into_event_cache_store();
2912 event_cache_store.test_rebuild_empty_linked_chunk().await;
2913 }
2914
2915 #[async_test]
2916 async fn test_linked_chunk_multiple_rooms() {
2917 let event_cache_store =
2918 get_event_cache_store().await.unwrap().into_event_cache_store();
2919 event_cache_store.test_linked_chunk_multiple_rooms().await;
2920 }
2921
2922 #[async_test]
2923 async fn test_load_all_chunks_metadata() {
2924 let event_cache_store =
2925 get_event_cache_store().await.unwrap().into_event_cache_store();
2926 event_cache_store.test_load_all_chunks_metadata().await;
2927 }
2928
2929 #[async_test]
2930 async fn test_load_and_update_thread_info() {
2931 let event_cache_store =
2932 get_event_cache_store().await.unwrap().into_event_cache_store();
2933 event_cache_store.test_load_and_update_thread_info().await;
2934 }
2935
2936 #[async_test]
2937 async fn test_clear_all_events() {
2938 let event_cache_store =
2939 get_event_cache_store().await.unwrap().into_event_cache_store();
2940 event_cache_store.test_clear_all_events().await;
2941 }
2942
2943 #[async_test]
2944 async fn test_clear_all_events_for_specific_room() {
2945 let event_cache_store =
2946 get_event_cache_store().await.unwrap().into_event_cache_store();
2947 event_cache_store.test_clear_all_events_for_specific_room().await;
2948 }
2949
2950 #[async_test]
2951 async fn test_filter_duplicated_events() {
2952 let event_cache_store =
2953 get_event_cache_store().await.unwrap().into_event_cache_store();
2954 event_cache_store.test_filter_duplicated_events().await;
2955 }
2956
2957 #[async_test]
2958 async fn test_filter_duplicate_events_no_events() {
2959 let event_cache_store =
2960 get_event_cache_store().await.unwrap().into_event_cache_store();
2961 event_cache_store.test_filter_duplicate_events_no_events().await;
2962 }
2963
2964 #[async_test]
2965 async fn test_find_event() {
2966 let event_cache_store =
2967 get_event_cache_store().await.unwrap().into_event_cache_store();
2968 event_cache_store.test_find_event().await;
2969 }
2970
2971 #[async_test]
2972 async fn test_find_event_when_event_in_room_and_thread() {
2973 let event_cache_store =
2974 get_event_cache_store().await.unwrap().into_event_cache_store();
2975 event_cache_store.test_find_event_when_event_in_room_and_thread().await;
2976 }
2977
2978 #[async_test]
2979 async fn test_find_event_relations() {
2980 let event_cache_store =
2981 get_event_cache_store().await.unwrap().into_event_cache_store();
2982 event_cache_store.test_find_event_relations().await;
2983 }
2984
2985 #[async_test]
2986 async fn test_find_event_relations_when_event_in_room_and_thread() {
2987 let event_cache_store =
2988 get_event_cache_store().await.unwrap().into_event_cache_store();
2989 event_cache_store.test_find_event_relations_when_event_in_room_and_thread().await;
2990 }
2991
2992 #[async_test]
2993 async fn test_get_room_events() {
2994 let event_cache_store =
2995 get_event_cache_store().await.unwrap().into_event_cache_store();
2996 event_cache_store.test_get_room_events().await;
2997 }
2998
2999 #[async_test]
3000 async fn test_get_room_events_filtered() {
3001 let event_cache_store =
3002 get_event_cache_store().await.unwrap().into_event_cache_store();
3003 event_cache_store.test_get_room_events_filtered().await;
3004 }
3005
3006 #[async_test]
3007 async fn test_get_room_events_with_event_in_room_and_thread() {
3008 let event_cache_store =
3009 get_event_cache_store().await.unwrap().into_event_cache_store();
3010 event_cache_store.test_get_room_events_with_event_in_room_and_thread().await;
3011 }
3012
3013 #[async_test]
3014 async fn test_save_event() {
3015 let event_cache_store =
3016 get_event_cache_store().await.unwrap().into_event_cache_store();
3017 event_cache_store.test_save_event().await;
3018 }
3019
3020 #[async_test]
3021 async fn test_save_event_updates_event_in_room_and_thread() {
3022 let event_cache_store =
3023 get_event_cache_store().await.unwrap().into_event_cache_store();
3024 event_cache_store.test_save_event_updates_event_in_room_and_thread().await;
3025 }
3026
3027 #[async_test]
3028 async fn test_thread_vs_room_linked_chunk() {
3029 let event_cache_store =
3030 get_event_cache_store().await.unwrap().into_event_cache_store();
3031 event_cache_store.test_thread_vs_room_linked_chunk().await;
3032 }
3033 }
3034 };
3035}
3036
3037#[allow(unused_macros)]
3040#[macro_export]
3041macro_rules! event_cache_store_integration_tests_time {
3042 () => {
3043 mod event_cache_store_integration_tests_time {
3044 use std::time::Duration;
3045
3046 #[cfg(all(target_family = "wasm", target_os = "unknown"))]
3047 use gloo_timers::future::sleep;
3048 use matrix_sdk_test::async_test;
3049 #[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
3050 use tokio::time::sleep;
3051 use $crate::event_cache::store::IntoEventCacheStore;
3052
3053 use super::get_event_cache_store;
3054
3055 #[async_test]
3056 async fn test_lease_locks() {
3057 let store = get_event_cache_store().await.unwrap().into_event_cache_store();
3058
3059 let acquired0 = store.try_take_leased_lock(0, "key", "alice").await.unwrap();
3060 assert_eq!(acquired0, Some(1)); let acquired2 = store.try_take_leased_lock(300, "key", "alice").await.unwrap();
3064 assert_eq!(acquired2, Some(1)); let acquired3 = store.try_take_leased_lock(300, "key", "alice").await.unwrap();
3069 assert_eq!(acquired3, Some(1)); let acquired4 = store.try_take_leased_lock(300, "key", "bob").await.unwrap();
3074 assert!(acquired4.is_none()); let acquired5 = store.try_take_leased_lock(300, "key", "bob").await.unwrap();
3078 assert!(acquired5.is_none()); sleep(Duration::from_millis(50)).await;
3082
3083 let acquired55 = store.try_take_leased_lock(300, "key", "bob").await.unwrap();
3085 assert!(acquired55.is_none()); sleep(Duration::from_millis(250)).await;
3089
3090 let acquired6 = store.try_take_leased_lock(0, "key", "bob").await.unwrap();
3092 assert_eq!(acquired6, Some(2)); sleep(Duration::from_millis(1)).await;
3095
3096 let acquired7 = store.try_take_leased_lock(0, "key", "alice").await.unwrap();
3098 assert_eq!(acquired7, Some(3)); sleep(Duration::from_millis(1)).await;
3101
3102 let acquired8 = store.try_take_leased_lock(300, "key", "bob").await.unwrap();
3104 assert_eq!(acquired8, Some(4)); let acquired9 = store.try_take_leased_lock(300, "key", "alice").await.unwrap();
3108 assert!(acquired9.is_none()); let acquired10 = store.try_take_leased_lock(300, "key", "bob").await.unwrap();
3112 assert_eq!(acquired10, Some(4)); }
3114 }
3115 };
3116}