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 DEFAULT_TEST_ROOM_ID, User,
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(User::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(User::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: User::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, User::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_find_events_before_timestamp(&self);
294
295 async fn test_get_room_events(&self);
297
298 async fn test_get_room_events_filtered(&self);
300
301 async fn test_get_room_events_with_event_in_room_and_thread(&self);
304
305 async fn test_save_event(&self);
307
308 async fn test_save_event_updates_event_in_room_and_thread(&self);
311
312 async fn test_thread_vs_room_linked_chunk(&self);
315}
316
317impl EventCacheStoreIntegrationTests for DynEventCacheStore {
318 async fn test_handle_updates_and_rebuild_linked_chunk(&self) {
319 let room_id = room_id!("!r0:matrix.org");
320 let linked_chunk_id = LinkedChunkId::Room(room_id);
321
322 self.handle_linked_chunk_updates(
323 linked_chunk_id,
324 vec![
325 Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
327 Update::PushItems {
329 at: Position::new(CId::new(0), 0),
330 items: vec![
331 make_test_event(room_id, "hello"),
332 make_test_event(room_id, "world"),
333 ],
334 },
335 Update::NewGapChunk {
337 previous: Some(CId::new(0)),
338 new: CId::new(1),
339 next: None,
340 gap: Gap { token: "parmesan".to_owned() },
341 },
342 Update::NewItemsChunk { previous: Some(CId::new(1)), new: CId::new(2), next: None },
344 Update::PushItems {
346 at: Position::new(CId::new(2), 0),
347 items: vec![make_test_event(room_id, "sup")],
348 },
349 ],
350 )
351 .await
352 .unwrap();
353
354 let lc = lazy_loader::from_all_chunks::<3, _, _>(
356 self.load_all_chunks(linked_chunk_id).await.unwrap(),
357 )
358 .unwrap()
359 .unwrap();
360
361 let mut chunks = lc.chunks();
362
363 {
364 let first = chunks.next().unwrap();
365 assert_eq!(first.identifier(), CId::new(0));
368
369 assert_matches!(first.content(), ChunkContent::Items(events) => {
370 assert_eq!(events.len(), 2);
371 check_test_event(&events[0], "hello");
372 check_test_event(&events[1], "world");
373 });
374 }
375
376 {
377 let second = chunks.next().unwrap();
378 assert_eq!(second.identifier(), CId::new(1));
379
380 assert_matches!(second.content(), ChunkContent::Gap(gap) => {
381 assert_eq!(gap.token, "parmesan");
382 });
383 }
384
385 {
386 let third = chunks.next().unwrap();
387 assert_eq!(third.identifier(), CId::new(2));
388
389 assert_matches!(third.content(), ChunkContent::Items(events) => {
390 assert_eq!(events.len(), 1);
391 check_test_event(&events[0], "sup");
392 });
393 }
394
395 assert!(chunks.next().is_none());
396 }
397
398 async fn test_linked_chunk_exists_before_referenced(&self) {
399 let room_id = *DEFAULT_TEST_ROOM_ID;
400 let linked_chunk_id = LinkedChunkId::Room(room_id);
401
402 self.handle_linked_chunk_updates(
404 linked_chunk_id,
405 vec![Update::NewItemsChunk {
406 previous: Some(CId::new(41)),
407 new: CId::new(42),
408 next: None,
409 }],
410 )
411 .await
412 .unwrap_err();
413
414 self.handle_linked_chunk_updates(
416 linked_chunk_id,
417 vec![Update::NewItemsChunk {
418 previous: None,
419 new: CId::new(42),
420 next: Some(CId::new(43)),
421 }],
422 )
423 .await
424 .unwrap_err();
425
426 self.handle_linked_chunk_updates(
428 linked_chunk_id,
429 vec![Update::NewGapChunk {
430 previous: Some(CId::new(41)),
431 new: CId::new(42),
432 next: None,
433 gap: Gap { token: "gap".to_owned() },
434 }],
435 )
436 .await
437 .unwrap_err();
438
439 self.handle_linked_chunk_updates(
441 linked_chunk_id,
442 vec![Update::NewGapChunk {
443 previous: None,
444 new: CId::new(42),
445 next: Some(CId::new(43)),
446 gap: Gap { token: "gap".to_owned() },
447 }],
448 )
449 .await
450 .unwrap_err();
451 }
452
453 async fn test_linked_chunk_allows_same_event_in_room_and_thread(&self) {
454 let room_id = *DEFAULT_TEST_ROOM_ID;
460 let thread_root = event_id!("$thread_root");
461
462 let event_id = event_id!("$thread_reply");
465 let event = make_test_event_with_event_id(room_id, "thread reply", Some(event_id));
466
467 let room_linked_chunk_id = LinkedChunkId::Room(room_id);
468 let thread_linked_chunk_id = LinkedChunkId::Thread(room_id, thread_root);
469
470 self.handle_linked_chunk_updates(
472 room_linked_chunk_id,
473 vec![
474 Update::NewItemsChunk { previous: None, new: CId::new(1), next: None },
475 Update::PushItems { at: Position::new(CId::new(1), 0), items: vec![event.clone()] },
476 ],
477 )
478 .await
479 .unwrap();
480
481 self.handle_linked_chunk_updates(
483 thread_linked_chunk_id,
484 vec![
485 Update::NewItemsChunk { previous: None, new: CId::new(1), next: None },
486 Update::PushItems { at: Position::new(CId::new(1), 0), items: vec![event] },
487 ],
488 )
489 .await
490 .unwrap();
491
492 let room_chunks = self.load_all_chunks(room_linked_chunk_id).await.unwrap();
495 let thread_chunks = self.load_all_chunks(thread_linked_chunk_id).await.unwrap();
496
497 assert_eq!(room_chunks.len(), 1);
498 assert_eq!(thread_chunks.len(), 1);
499
500 assert_matches!(&room_chunks[0].content, ChunkContent::Items(events) => {
502 assert_eq!(events.len(), 1);
503 assert_eq!(events[0].event_id(), Some(event_id));
504 });
505 assert_matches!(&thread_chunks[0].content, ChunkContent::Items(events) => {
506 assert_eq!(events.len(), 1);
507 assert_eq!(events[0].event_id(), Some(event_id));
508 });
509 }
510
511 async fn test_load_all_chunks_metadata(&self) {
512 let room_id = room_id!("!r0:matrix.org");
513 let linked_chunk_id = LinkedChunkId::Room(room_id);
514
515 self.handle_linked_chunk_updates(
516 linked_chunk_id,
517 vec![
518 Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
520 Update::PushItems {
522 at: Position::new(CId::new(0), 0),
523 items: vec![
524 make_test_event(room_id, "hello"),
525 make_test_event(room_id, "world"),
526 ],
527 },
528 Update::NewGapChunk {
530 previous: Some(CId::new(0)),
531 new: CId::new(1),
532 next: None,
533 gap: Gap { token: "parmesan".to_owned() },
534 },
535 Update::NewItemsChunk { previous: Some(CId::new(1)), new: CId::new(2), next: None },
537 Update::PushItems {
539 at: Position::new(CId::new(2), 0),
540 items: vec![make_test_event(room_id, "sup")],
541 },
542 Update::NewItemsChunk { previous: Some(CId::new(2)), new: CId::new(3), next: None },
544 ],
545 )
546 .await
547 .unwrap();
548
549 let metas = self.load_all_chunks_metadata(linked_chunk_id).await.unwrap();
550 assert_eq!(metas.len(), 4);
551
552 assert_eq!(metas[0].identifier, CId::new(0));
554 assert_eq!(metas[0].previous, None);
555 assert_eq!(metas[0].next, Some(CId::new(1)));
556 assert_eq!(metas[0].num_items, 2);
557
558 assert_eq!(metas[1].identifier, CId::new(1));
560 assert_eq!(metas[1].previous, Some(CId::new(0)));
561 assert_eq!(metas[1].next, Some(CId::new(2)));
562 assert_eq!(metas[1].num_items, 0);
563
564 assert_eq!(metas[2].identifier, CId::new(2));
566 assert_eq!(metas[2].previous, Some(CId::new(1)));
567 assert_eq!(metas[2].next, Some(CId::new(3)));
568 assert_eq!(metas[2].num_items, 1);
569
570 assert_eq!(metas[3].identifier, CId::new(3));
572 assert_eq!(metas[3].previous, Some(CId::new(2)));
573 assert_eq!(metas[3].next, None);
574 assert_eq!(metas[3].num_items, 0);
575 }
576
577 async fn test_load_last_chunk(&self) {
578 let room_id = room_id!("!r0:matrix.org");
579 let linked_chunk_id = LinkedChunkId::Room(room_id);
580 let event = |msg: &str| make_test_event(room_id, msg);
581
582 {
584 let (last_chunk, chunk_identifier_generator) =
585 self.load_last_chunk(linked_chunk_id).await.unwrap();
586
587 assert!(last_chunk.is_none());
588 assert_eq!(chunk_identifier_generator.current(), 0);
589 }
590
591 {
593 self.handle_linked_chunk_updates(
594 linked_chunk_id,
595 vec![
596 Update::NewItemsChunk { previous: None, new: CId::new(42), next: None },
597 Update::PushItems {
598 at: Position::new(CId::new(42), 0),
599 items: vec![event("saucisse de morteau"), event("comté")],
600 },
601 ],
602 )
603 .await
604 .unwrap();
605
606 let (last_chunk, chunk_identifier_generator) =
607 self.load_last_chunk(linked_chunk_id).await.unwrap();
608
609 assert_matches!(last_chunk, Some(last_chunk) => {
610 assert_eq!(last_chunk.identifier, 42);
611 assert!(last_chunk.previous.is_none());
612 assert!(last_chunk.next.is_none());
613 assert_matches!(last_chunk.content, ChunkContent::Items(items) => {
614 assert_eq!(items.len(), 2);
615 check_test_event(&items[0], "saucisse de morteau");
616 check_test_event(&items[1], "comté");
617 });
618 });
619 assert_eq!(chunk_identifier_generator.current(), 42);
620 }
621
622 {
624 self.handle_linked_chunk_updates(
625 linked_chunk_id,
626 vec![
627 Update::NewItemsChunk {
628 previous: Some(CId::new(42)),
629 new: CId::new(7),
630 next: None,
631 },
632 Update::PushItems {
633 at: Position::new(CId::new(7), 0),
634 items: vec![event("fondue"), event("gruyère"), event("mont d'or")],
635 },
636 ],
637 )
638 .await
639 .unwrap();
640
641 let (last_chunk, chunk_identifier_generator) =
642 self.load_last_chunk(linked_chunk_id).await.unwrap();
643
644 assert_matches!(last_chunk, Some(last_chunk) => {
645 assert_eq!(last_chunk.identifier, 7);
646 assert_matches!(last_chunk.previous, Some(previous) => {
647 assert_eq!(previous, 42);
648 });
649 assert!(last_chunk.next.is_none());
650 assert_matches!(last_chunk.content, ChunkContent::Items(items) => {
651 assert_eq!(items.len(), 3);
652 check_test_event(&items[0], "fondue");
653 check_test_event(&items[1], "gruyère");
654 check_test_event(&items[2], "mont d'or");
655 });
656 });
657 assert_eq!(chunk_identifier_generator.current(), 42);
658 }
659 }
660
661 async fn test_load_last_chunk_with_a_cycle(&self) {
662 let room_id = room_id!("!r0:matrix.org");
663 let linked_chunk_id = LinkedChunkId::Room(room_id);
664
665 self.handle_linked_chunk_updates(
666 linked_chunk_id,
667 vec![
668 Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
669 Update::NewItemsChunk {
670 previous: Some(CId::new(0)),
675 new: CId::new(1),
676 next: Some(CId::new(0)),
677 },
678 ],
679 )
680 .await
681 .unwrap();
682
683 self.load_last_chunk(linked_chunk_id).await.unwrap_err();
684 }
685
686 async fn test_load_previous_chunk(&self) {
687 let room_id = room_id!("!r0:matrix.org");
688 let linked_chunk_id = LinkedChunkId::Room(room_id);
689 let event = |msg: &str| make_test_event(room_id, msg);
690
691 {
694 let previous_chunk =
695 self.load_previous_chunk(linked_chunk_id, CId::new(153)).await.unwrap();
696
697 assert!(previous_chunk.is_none());
698 }
699
700 {
703 self.handle_linked_chunk_updates(
704 linked_chunk_id,
705 vec![Update::NewItemsChunk { previous: None, new: CId::new(42), next: None }],
706 )
707 .await
708 .unwrap();
709
710 let previous_chunk =
711 self.load_previous_chunk(linked_chunk_id, CId::new(42)).await.unwrap();
712
713 assert!(previous_chunk.is_none());
714 }
715
716 {
718 self.handle_linked_chunk_updates(
719 linked_chunk_id,
720 vec![
721 Update::NewItemsChunk {
723 previous: None,
724 new: CId::new(7),
725 next: Some(CId::new(42)),
726 },
727 Update::PushItems {
728 at: Position::new(CId::new(7), 0),
729 items: vec![event("brigand du jorat"), event("morbier")],
730 },
731 ],
732 )
733 .await
734 .unwrap();
735
736 let previous_chunk =
737 self.load_previous_chunk(linked_chunk_id, CId::new(42)).await.unwrap();
738
739 assert_matches!(previous_chunk, Some(previous_chunk) => {
740 assert_eq!(previous_chunk.identifier, 7);
741 assert!(previous_chunk.previous.is_none());
742 assert_matches!(previous_chunk.next, Some(next) => {
743 assert_eq!(next, 42);
744 });
745 assert_matches!(previous_chunk.content, ChunkContent::Items(items) => {
746 assert_eq!(items.len(), 2);
747 check_test_event(&items[0], "brigand du jorat");
748 check_test_event(&items[1], "morbier");
749 });
750 });
751 }
752 }
753
754 async fn test_linked_chunk_incremental_loading(&self) {
755 let room_id = room_id!("!r0:matrix.org");
756 let linked_chunk_id = LinkedChunkId::Room(room_id);
757 let event = |msg: &str| make_test_event(room_id, msg);
758
759 {
761 let (last_chunk, chunk_identifier_generator) =
762 self.load_last_chunk(linked_chunk_id).await.unwrap();
763
764 assert!(last_chunk.is_none());
765 assert_eq!(chunk_identifier_generator.current(), 0);
766 }
767
768 self.handle_linked_chunk_updates(
769 linked_chunk_id,
770 vec![
771 Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
773 Update::PushItems {
775 at: Position::new(CId::new(0), 0),
776 items: vec![event("a"), event("b")],
777 },
778 Update::NewGapChunk {
780 previous: Some(CId::new(0)),
781 new: CId::new(1),
782 next: None,
783 gap: Gap { token: "morbier".to_owned() },
784 },
785 Update::NewItemsChunk { previous: Some(CId::new(1)), new: CId::new(2), next: None },
787 Update::PushItems {
789 at: Position::new(CId::new(2), 0),
790 items: vec![event("c"), event("d"), event("e")],
791 },
792 ],
793 )
794 .await
795 .unwrap();
796
797 let mut linked_chunk = {
799 let (last_chunk, chunk_identifier_generator) =
800 self.load_last_chunk(linked_chunk_id).await.unwrap();
801
802 assert_eq!(chunk_identifier_generator.current(), 2);
803
804 let linked_chunk = lazy_loader::from_last_chunk::<DEFAULT_CHUNK_CAPACITY, _, _>(
805 last_chunk,
806 chunk_identifier_generator,
807 )
808 .unwrap() .unwrap(); let mut rchunks = linked_chunk.rchunks();
812
813 assert_matches!(rchunks.next(), Some(chunk) => {
815 assert_eq!(chunk.identifier(), 2);
816 assert_eq!(chunk.lazy_previous(), Some(CId::new(1)));
817
818 assert_matches!(chunk.content(), ChunkContent::Items(events) => {
819 assert_eq!(events.len(), 3);
820 check_test_event(&events[0], "c");
821 check_test_event(&events[1], "d");
822 check_test_event(&events[2], "e");
823 });
824 });
825
826 assert!(rchunks.next().is_none());
827
828 linked_chunk
829 };
830
831 {
833 let first_chunk = linked_chunk.chunks().next().unwrap().identifier();
834 let previous_chunk =
835 self.load_previous_chunk(linked_chunk_id, first_chunk).await.unwrap().unwrap();
836
837 lazy_loader::insert_new_first_chunk(&mut linked_chunk, previous_chunk).unwrap();
838
839 let mut rchunks = linked_chunk.rchunks();
840
841 assert_matches!(rchunks.next(), Some(chunk) => {
843 assert_eq!(chunk.identifier(), 2);
844 assert!(chunk.lazy_previous().is_none());
845
846 assert_matches!(chunk.content(), ChunkContent::Items(events) => {
848 assert_eq!(events.len(), 3);
849 check_test_event(&events[0], "c");
850 check_test_event(&events[1], "d");
851 check_test_event(&events[2], "e");
852 });
853 });
854
855 assert_matches!(rchunks.next(), Some(chunk) => {
857 assert_eq!(chunk.identifier(), 1);
858 assert_eq!(chunk.lazy_previous(), Some(CId::new(0)));
859
860 assert_matches!(chunk.content(), ChunkContent::Gap(gap) => {
861 assert_eq!(gap.token, "morbier");
862 });
863 });
864
865 assert!(rchunks.next().is_none());
866 }
867
868 {
870 let first_chunk = linked_chunk.chunks().next().unwrap().identifier();
871 let previous_chunk =
872 self.load_previous_chunk(linked_chunk_id, first_chunk).await.unwrap().unwrap();
873
874 lazy_loader::insert_new_first_chunk(&mut linked_chunk, previous_chunk).unwrap();
875
876 let mut rchunks = linked_chunk.rchunks();
877
878 assert_matches!(rchunks.next(), Some(chunk) => {
880 assert_eq!(chunk.identifier(), 2);
881 assert!(chunk.lazy_previous().is_none());
882
883 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_matches!(rchunks.next(), Some(chunk) => {
894 assert_eq!(chunk.identifier(), 1);
895 assert!(chunk.lazy_previous().is_none());
896
897 assert_matches!(chunk.content(), ChunkContent::Gap(gap) => {
899 assert_eq!(gap.token, "morbier");
900 });
901 });
902
903 assert_matches!(rchunks.next(), Some(chunk) => {
905 assert_eq!(chunk.identifier(), 0);
906 assert!(chunk.lazy_previous().is_none());
907
908 assert_matches!(chunk.content(), ChunkContent::Items(events) => {
909 assert_eq!(events.len(), 2);
910 check_test_event(&events[0], "a");
911 check_test_event(&events[1], "b");
912 });
913 });
914
915 assert!(rchunks.next().is_none());
916 }
917
918 {
920 let first_chunk = linked_chunk.chunks().next().unwrap().identifier();
921 let previous_chunk =
922 self.load_previous_chunk(linked_chunk_id, first_chunk).await.unwrap();
923
924 assert!(previous_chunk.is_none());
925 }
926
927 {
930 let mut chunks = linked_chunk.chunks();
931
932 assert_matches!(chunks.next(), Some(chunk) => {
934 assert_eq!(chunk.identifier(), 0);
935 assert!(chunk.lazy_previous().is_none());
936
937 assert_matches!(chunk.content(), ChunkContent::Items(events) => {
938 assert_eq!(events.len(), 2);
939 check_test_event(&events[0], "a");
940 check_test_event(&events[1], "b");
941 });
942 });
943
944 assert_matches!(chunks.next(), Some(chunk) => {
946 assert_eq!(chunk.identifier(), 1);
947 assert!(chunk.lazy_previous().is_none());
948
949 assert_matches!(chunk.content(), ChunkContent::Gap(gap) => {
950 assert_eq!(gap.token, "morbier");
951 });
952 });
953
954 assert_matches!(chunks.next(), Some(chunk) => {
956 assert_eq!(chunk.identifier(), 2);
957 assert!(chunk.lazy_previous().is_none());
958
959 assert_matches!(chunk.content(), ChunkContent::Items(events) => {
960 assert_eq!(events.len(), 3);
961 check_test_event(&events[0], "c");
962 check_test_event(&events[1], "d");
963 check_test_event(&events[2], "e");
964 });
965 });
966
967 assert!(chunks.next().is_none());
968 }
969 }
970
971 async fn test_linked_chunk_remove_chunk(&self) {
972 let room_id = &DEFAULT_TEST_ROOM_ID;
973 let linked_chunk_id = LinkedChunkId::Room(room_id);
974
975 self.handle_linked_chunk_updates(
976 linked_chunk_id,
977 vec![
978 Update::NewGapChunk {
979 previous: None,
980 new: CId::new(42),
981 next: None,
982 gap: Gap { token: "raclette".to_owned() },
983 },
984 Update::NewGapChunk {
985 previous: Some(CId::new(42)),
986 new: CId::new(43),
987 next: None,
988 gap: Gap { token: "fondue".to_owned() },
989 },
990 Update::NewGapChunk {
991 previous: Some(CId::new(43)),
992 new: CId::new(44),
993 next: None,
994 gap: Gap { token: "tartiflette".to_owned() },
995 },
996 Update::RemoveChunk(CId::new(43)),
997 ],
998 )
999 .await
1000 .unwrap();
1001
1002 let mut chunks = self.load_all_chunks(linked_chunk_id).await.unwrap();
1003
1004 assert_eq!(chunks.len(), 2);
1005
1006 let c = chunks.remove(0);
1008 assert_eq!(c.identifier, CId::new(42));
1009 assert_eq!(c.previous, None);
1010 assert_eq!(c.next, Some(CId::new(44)));
1011 assert_matches!(c.content, ChunkContent::Gap(gap) => {
1012 assert_eq!(gap.token, "raclette");
1013 });
1014
1015 let c = chunks.remove(0);
1016 assert_eq!(c.identifier, CId::new(44));
1017 assert_eq!(c.previous, Some(CId::new(42)));
1018 assert_eq!(c.next, None);
1019 assert_matches!(c.content, ChunkContent::Gap(gap) => {
1020 assert_eq!(gap.token, "tartiflette");
1021 });
1022 }
1023
1024 async fn test_linked_chunk_push_items(&self) {
1025 let room_id = *DEFAULT_TEST_ROOM_ID;
1026
1027 let linked_chunk_ids = [
1029 LinkedChunkId::Room(room_id),
1030 LinkedChunkId::Thread(room_id, event_id!("$thread_root")),
1031 LinkedChunkId::PinnedEvents(room_id),
1032 LinkedChunkId::EventFocused(room_id, event_id!("$focus_root")),
1033 ];
1034
1035 let event_id_a = event_id!("$a");
1037 let event_a = make_test_event_with_event_id(room_id, "a", Some(event_id_a));
1038 for linked_chunk_id in linked_chunk_ids {
1039 self.handle_linked_chunk_updates(
1040 linked_chunk_id,
1041 vec![
1042 Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
1043 Update::PushItems {
1044 at: Position::new(CId::new(0), 0),
1045 items: vec![event_a.clone()],
1046 },
1047 ],
1048 )
1049 .await
1050 .unwrap();
1051
1052 let mut chunks = self.load_all_chunks(linked_chunk_id).await.unwrap();
1053 assert_eq!(chunks.len(), 1);
1054 let chunk = chunks.remove(0);
1055 assert_matches!(chunk.content, ChunkContent::Items(events) => {
1056 assert_eq!(events.len(), 1);
1057 check_test_event(&events[0], "a");
1058 });
1059 }
1060
1061 let event_b = make_test_event_with_event_id(room_id, "b", Some(event_id!("$b")));
1062 for linked_chunk_id in linked_chunk_ids {
1065 self.handle_linked_chunk_updates(
1066 linked_chunk_id,
1067 vec![Update::PushItems {
1068 at: Position::new(CId::new(0), 0),
1069 items: vec![event_b.clone()],
1070 }],
1071 )
1072 .await
1073 .expect_err("should fail to push an event to an occupied position");
1074
1075 let mut chunks = self.load_all_chunks(linked_chunk_id).await.unwrap();
1076 assert_eq!(chunks.len(), 1);
1077 let chunk = chunks.remove(0);
1078 assert_matches!(chunk.content, ChunkContent::Items(events) => {
1079 assert_eq!(events.len(), 1);
1080 check_test_event(&events[0], "a");
1081 });
1082 }
1083
1084 for linked_chunk_id in linked_chunk_ids {
1087 self.handle_linked_chunk_updates(
1088 linked_chunk_id,
1089 vec![Update::PushItems {
1090 at: Position::new(CId::new(0), 1),
1091 items: vec![event_b.clone()],
1092 }],
1093 )
1094 .await
1095 .unwrap();
1096
1097 let mut chunks = self.load_all_chunks(linked_chunk_id).await.unwrap();
1098 assert_eq!(chunks.len(), 1);
1099 let chunk = chunks.remove(0);
1100 assert_matches!(chunk.content, ChunkContent::Items(events) => {
1101 assert_eq!(events.len(), 2);
1102 check_test_event(&events[0], "a");
1103 check_test_event(&events[1], "b");
1104 });
1105 }
1106
1107 let updated_event_a = make_test_event_with_event_id(room_id, "updated_a", Some(event_id_a));
1109
1110 for linked_chunk_id in linked_chunk_ids {
1113 self.handle_linked_chunk_updates(
1114 linked_chunk_id,
1115 vec![Update::PushItems {
1116 at: Position::new(CId::new(0), 0),
1117 items: vec![updated_event_a.clone()],
1118 }],
1119 )
1120 .await
1121 .expect_err("should fail to push an event to an occupied position");
1122
1123 let mut chunks = self.load_all_chunks(linked_chunk_id).await.unwrap();
1124 assert_eq!(chunks.len(), 1);
1125 let chunk = chunks.remove(0);
1126 assert_matches!(chunk.content, ChunkContent::Items(events) => {
1127 assert_eq!(events.len(), 2);
1128 check_test_event(&events[0], "a");
1129 check_test_event(&events[1], "b");
1130 });
1131 }
1132
1133 for linked_chunk_id in linked_chunk_ids {
1136 self.handle_linked_chunk_updates(
1137 linked_chunk_id,
1138 vec![Update::PushItems {
1139 at: Position::new(CId::new(0), 1),
1140 items: vec![updated_event_a.clone()],
1141 }],
1142 )
1143 .await
1144 .expect_err("should fail to push an event to an occupied position");
1145
1146 let mut chunks = self.load_all_chunks(linked_chunk_id).await.unwrap();
1147 assert_eq!(chunks.len(), 1);
1148 let chunk = chunks.remove(0);
1149 assert_matches!(chunk.content, ChunkContent::Items(events) => {
1150 assert_eq!(events.len(), 2);
1151 check_test_event(&events[0], "a");
1152 check_test_event(&events[1], "b");
1153 });
1154 }
1155
1156 for linked_chunk_id in linked_chunk_ids {
1159 self.handle_linked_chunk_updates(
1160 linked_chunk_id,
1161 vec![Update::PushItems {
1162 at: Position::new(CId::new(0), 2),
1163 items: vec![updated_event_a.clone()],
1164 }],
1165 )
1166 .await
1167 .expect_err("should fail to push an event that already exists in the linked chunk");
1168
1169 let mut chunks = self.load_all_chunks(linked_chunk_id).await.unwrap();
1170 assert_eq!(chunks.len(), 1);
1171 let chunk = chunks.remove(0);
1172 assert_matches!(chunk.content, ChunkContent::Items(events) => {
1173 assert_eq!(events.len(), 2);
1174 check_test_event(&events[0], "a");
1175 check_test_event(&events[1], "b");
1176 });
1177 }
1178
1179 let other_linked_chunk_id = LinkedChunkId::Room(room_id!("!other_room:localhost"));
1181 self.handle_linked_chunk_updates(
1182 other_linked_chunk_id,
1183 vec![Update::NewItemsChunk { previous: None, new: CId::new(0), next: None }],
1184 )
1185 .await
1186 .unwrap();
1187
1188 self.handle_linked_chunk_updates(
1192 other_linked_chunk_id,
1193 vec![Update::PushItems {
1194 at: Position::new(CId::new(0), 0),
1195 items: vec![updated_event_a.clone()],
1196 }],
1197 )
1198 .await
1199 .unwrap();
1200
1201 let mut chunks = self.load_all_chunks(other_linked_chunk_id).await.unwrap();
1202 assert_eq!(chunks.len(), 1);
1203 let chunk = chunks.remove(0);
1204 assert_matches!(chunk.content, ChunkContent::Items(events) => {
1205 assert_eq!(events.len(), 1);
1206 check_test_event(&events[0], "updated_a");
1207 });
1208
1209 for linked_chunk_id in linked_chunk_ids {
1210 let mut chunks = self.load_all_chunks(linked_chunk_id).await.unwrap();
1211 assert_eq!(chunks.len(), 1);
1212 let chunk = chunks.remove(0);
1213 assert_matches!(chunk.content, ChunkContent::Items(events) => {
1214 assert_eq!(events.len(), 2);
1215 check_test_event(&events[0], "updated_a");
1216 check_test_event(&events[1], "b");
1217 });
1218 }
1219 }
1220
1221 async fn test_linked_chunk_replace_item(&self) {
1222 let room_id = &DEFAULT_TEST_ROOM_ID;
1223
1224 let linked_chunk_ids = [
1227 LinkedChunkId::Room(room_id),
1228 LinkedChunkId::Thread(room_id, event_id!("$thread_root")),
1229 LinkedChunkId::PinnedEvents(room_id),
1230 LinkedChunkId::EventFocused(room_id, event_id!("$focus_root")),
1231 LinkedChunkId::Room(room_id!("!other_room")),
1232 ];
1233
1234 let event_id = event_id!("$world");
1236
1237 for linked_chunk_id in linked_chunk_ids {
1239 self.handle_linked_chunk_updates(
1240 linked_chunk_id,
1241 vec![
1242 Update::NewItemsChunk { previous: None, new: CId::new(42), next: None },
1243 Update::PushItems {
1244 at: Position::new(CId::new(42), 0),
1245 items: vec![
1246 make_test_event(room_id, "hello"),
1247 make_test_event_with_event_id(room_id, "world", Some(event_id)),
1248 ],
1249 },
1250 ],
1251 )
1252 .await
1253 .unwrap();
1254
1255 let mut chunks = self.load_all_chunks(linked_chunk_id).await.unwrap();
1256
1257 assert_eq!(chunks.len(), 1);
1258
1259 let c = chunks.remove(0);
1260 assert_eq!(c.identifier, CId::new(42));
1261 assert_eq!(c.previous, None);
1262 assert_eq!(c.next, None);
1263 assert_matches!(c.content, ChunkContent::Items(events) => {
1264 assert_eq!(events.len(), 2);
1265 check_test_event(&events[0], "hello");
1266 check_test_event(&events[1], "world");
1267 });
1268 }
1269
1270 self.handle_linked_chunk_updates(
1273 linked_chunk_ids[0],
1274 vec![Update::ReplaceItem {
1275 at: Position::new(CId::new(42), 1),
1276 item: make_test_event_with_event_id(room_id, "yolo", Some(event_id)),
1277 }],
1278 )
1279 .await
1280 .unwrap();
1281
1282 for linked_chunk_id in linked_chunk_ids {
1284 let mut chunks = self.load_all_chunks(linked_chunk_id).await.unwrap();
1285
1286 assert_eq!(chunks.len(), 1);
1287
1288 let c = chunks.remove(0);
1289 assert_eq!(c.identifier, CId::new(42));
1290 assert_eq!(c.previous, None);
1291 assert_eq!(c.next, None);
1292 assert_matches!(c.content, ChunkContent::Items(events) => {
1293 assert_eq!(events.len(), 2);
1294 check_test_event(&events[0], "hello");
1295 check_test_event(&events[1], "yolo");
1296 });
1297 }
1298 }
1299
1300 async fn test_linked_chunk_filter_incomplete_events(&self) {
1301 let room_id = &DEFAULT_TEST_ROOM_ID;
1302 let linked_chunk_id = LinkedChunkId::Room(room_id);
1303
1304 let event = make_test_event(room_id, "event");
1305 assert_matches!(event.event_id(), Some(_));
1306 assert_matches!(event.kind.event_type(), Some(_));
1307
1308 let event_without_id = make_test_event_without_event_id(room_id, "event-without-id");
1309 assert_eq!(event_without_id.event_id(), None);
1310 assert_matches!(event.kind.event_type(), Some(_));
1311
1312 let event_without_event_type =
1313 make_test_event_without_event_type(room_id, "event-without-event-type");
1314 assert_matches!(event_without_event_type.event_id(), Some(_));
1315 assert_eq!(event_without_event_type.kind.event_type(), None);
1316
1317 self.handle_linked_chunk_updates(
1318 linked_chunk_id,
1319 vec![
1320 Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
1321 Update::PushItems {
1322 at: Position::new(CId::new(0), 0),
1323 items: vec![event, event_without_id.clone(), event_without_event_type.clone()],
1324 },
1325 ],
1326 )
1327 .await
1328 .unwrap();
1329
1330 let mut chunks = self.load_all_chunks(linked_chunk_id).await.unwrap();
1331 assert_eq!(chunks.len(), 1);
1332 let c = chunks.remove(0);
1333 assert_eq!(c.identifier, CId::new(0));
1334 assert_eq!(c.previous, None);
1335 assert_eq!(c.next, None);
1336 assert_matches!(c.content, ChunkContent::Items(events) => {
1337 assert_eq!(events.len(), 1);
1338 check_test_event(&events[0], "event");
1339 });
1340
1341 for event in [event_without_id, event_without_event_type] {
1342 self.handle_linked_chunk_updates(
1343 linked_chunk_id,
1344 vec![Update::ReplaceItem { at: Position::new(CId::new(0), 0), item: event }],
1345 )
1346 .await
1347 .unwrap();
1348
1349 let mut chunks = self.load_all_chunks(linked_chunk_id).await.unwrap();
1350 assert_eq!(chunks.len(), 1);
1351 let c = chunks.remove(0);
1352 assert_eq!(c.identifier, CId::new(0));
1353 assert_eq!(c.previous, None);
1354 assert_eq!(c.next, None);
1355 assert_matches!(c.content, ChunkContent::Items(events) => {
1356 assert_eq!(events.len(), 1);
1357 check_test_event(&events[0], "event");
1358 });
1359 }
1360 }
1361
1362 async fn test_linked_chunk_remove_item(&self) {
1363 let room_id = *DEFAULT_TEST_ROOM_ID;
1364 let linked_chunk_id = LinkedChunkId::Room(room_id);
1365
1366 self.handle_linked_chunk_updates(
1367 linked_chunk_id,
1368 vec![
1369 Update::NewItemsChunk { previous: None, new: CId::new(42), next: None },
1370 Update::PushItems {
1371 at: Position::new(CId::new(42), 0),
1372 items: vec![
1373 make_test_event(room_id, "one"),
1374 make_test_event(room_id, "two"),
1375 make_test_event(room_id, "three"),
1376 make_test_event(room_id, "four"),
1377 make_test_event(room_id, "five"),
1378 make_test_event(room_id, "six"),
1379 ],
1380 },
1381 Update::RemoveItem { at: Position::new(CId::new(42), 2) },
1382 Update::PushItems {
1394 at: Position::new(CId::new(42), 5),
1395 items: vec![make_test_event(room_id, "seven")],
1396 },
1397 ],
1398 )
1399 .await
1400 .unwrap();
1401
1402 let mut chunks = self.load_all_chunks(linked_chunk_id).await.unwrap();
1403
1404 assert_eq!(chunks.len(), 1);
1405
1406 let c = chunks.remove(0);
1407 assert_eq!(c.identifier, CId::new(42));
1408 assert_eq!(c.previous, None);
1409 assert_eq!(c.next, None);
1410 assert_matches!(c.content, ChunkContent::Items(events) => {
1411 assert_eq!(events.len(), 6);
1412 check_test_event(&events[0], "one");
1413 check_test_event(&events[1], "two");
1414 check_test_event(&events[2], "four");
1415 check_test_event(&events[3], "five");
1416 check_test_event(&events[4], "six");
1417 check_test_event(&events[5], "seven");
1418 });
1419
1420 let metas = self.load_all_chunks_metadata(linked_chunk_id).await.unwrap();
1422 assert_eq!(metas.len(), 1);
1423 assert_eq!(metas[0].num_items, 6);
1424 }
1425
1426 async fn test_linked_chunk_detach_last_items(&self) {
1427 let room_id = *DEFAULT_TEST_ROOM_ID;
1428 let linked_chunk_id = LinkedChunkId::Room(room_id);
1429
1430 self.handle_linked_chunk_updates(
1431 linked_chunk_id,
1432 vec![
1433 Update::NewItemsChunk { previous: None, new: CId::new(42), next: None },
1434 Update::PushItems {
1435 at: Position::new(CId::new(42), 0),
1436 items: vec![
1437 make_test_event(room_id, "hello"),
1438 make_test_event(room_id, "world"),
1439 make_test_event(room_id, "howdy"),
1440 ],
1441 },
1442 Update::DetachLastItems { at: Position::new(CId::new(42), 1) },
1443 ],
1444 )
1445 .await
1446 .unwrap();
1447
1448 let mut chunks = self.load_all_chunks(linked_chunk_id).await.unwrap();
1449
1450 assert_eq!(chunks.len(), 1);
1451
1452 let c = chunks.remove(0);
1453 assert_eq!(c.identifier, CId::new(42));
1454 assert_eq!(c.previous, None);
1455 assert_eq!(c.next, None);
1456 assert_matches!(c.content, ChunkContent::Items(events) => {
1457 assert_eq!(events.len(), 1);
1458 check_test_event(&events[0], "hello");
1459 });
1460 }
1461
1462 async fn test_linked_chunk_start_end_reattach_items(&self) {
1463 let room_id = *DEFAULT_TEST_ROOM_ID;
1464 let linked_chunk_id = LinkedChunkId::Room(room_id);
1465
1466 self.handle_linked_chunk_updates(
1470 linked_chunk_id,
1471 vec![
1472 Update::NewItemsChunk { previous: None, new: CId::new(42), next: None },
1473 Update::PushItems {
1474 at: Position::new(CId::new(42), 0),
1475 items: vec![
1476 make_test_event(room_id, "hello"),
1477 make_test_event(room_id, "world"),
1478 make_test_event(room_id, "howdy"),
1479 ],
1480 },
1481 Update::StartReattachItems,
1482 Update::EndReattachItems,
1483 ],
1484 )
1485 .await
1486 .unwrap();
1487
1488 let mut chunks = self.load_all_chunks(linked_chunk_id).await.unwrap();
1489
1490 assert_eq!(chunks.len(), 1);
1491
1492 let c = chunks.remove(0);
1493 assert_eq!(c.identifier, CId::new(42));
1494 assert_eq!(c.previous, None);
1495 assert_eq!(c.next, None);
1496 assert_matches!(c.content, ChunkContent::Items(events) => {
1497 assert_eq!(events.len(), 3);
1498 check_test_event(&events[0], "hello");
1499 check_test_event(&events[1], "world");
1500 check_test_event(&events[2], "howdy");
1501 });
1502 }
1503
1504 async fn test_linked_chunk_clear(&self) {
1505 let room_id = *DEFAULT_TEST_ROOM_ID;
1506 let linked_chunk_id = LinkedChunkId::Room(room_id);
1507 let event_0 = make_test_event(room_id, "hello");
1508 let event_1 = make_test_event(room_id, "world");
1509 let event_2 = make_test_event(room_id, "howdy");
1510
1511 self.handle_linked_chunk_updates(
1512 linked_chunk_id,
1513 vec![
1514 Update::NewItemsChunk { previous: None, new: CId::new(42), next: None },
1515 Update::NewGapChunk {
1516 previous: Some(CId::new(42)),
1517 new: CId::new(54),
1518 next: None,
1519 gap: Gap { token: "fondue".to_owned() },
1520 },
1521 Update::PushItems {
1522 at: Position::new(CId::new(42), 0),
1523 items: vec![event_0.clone(), event_1, event_2],
1524 },
1525 Update::Clear,
1526 ],
1527 )
1528 .await
1529 .unwrap();
1530
1531 let chunks = self.load_all_chunks(linked_chunk_id).await.unwrap();
1532 assert!(chunks.is_empty());
1533 }
1534
1535 async fn test_linked_chunk_clear_and_reinsert(&self) {
1536 let room_id = *DEFAULT_TEST_ROOM_ID;
1537 let linked_chunk_id = LinkedChunkId::Room(room_id);
1538 let event_0 = make_test_event(room_id, "hello");
1539 let event_1 = make_test_event(room_id, "world");
1540 let event_2 = make_test_event(room_id, "howdy");
1541
1542 self.handle_linked_chunk_updates(
1543 linked_chunk_id,
1544 vec![
1545 Update::NewItemsChunk { previous: None, new: CId::new(42), next: None },
1546 Update::NewGapChunk {
1547 previous: Some(CId::new(42)),
1548 new: CId::new(54),
1549 next: None,
1550 gap: Gap { token: "fondue".to_owned() },
1551 },
1552 Update::PushItems {
1553 at: Position::new(CId::new(42), 0),
1554 items: vec![event_0.clone(), event_1, event_2],
1555 },
1556 Update::Clear,
1557 ],
1558 )
1559 .await
1560 .unwrap();
1561
1562 let chunks = self.load_all_chunks(linked_chunk_id).await.unwrap();
1563 assert!(chunks.is_empty());
1564
1565 self.handle_linked_chunk_updates(
1567 linked_chunk_id,
1568 vec![
1569 Update::NewItemsChunk { previous: None, new: CId::new(42), next: None },
1570 Update::PushItems { at: Position::new(CId::new(42), 0), items: vec![event_0] },
1571 ],
1572 )
1573 .await
1574 .unwrap();
1575 }
1576
1577 async fn test_rebuild_empty_linked_chunk(&self) {
1578 let linked_chunk = lazy_loader::from_all_chunks::<3, _, _>(
1580 self.load_all_chunks(LinkedChunkId::Room(&DEFAULT_TEST_ROOM_ID)).await.unwrap(),
1581 )
1582 .unwrap();
1583 assert!(linked_chunk.is_none());
1584 }
1585
1586 async fn test_linked_chunk_multiple_rooms(&self) {
1587 let room1 = room_id!("!realcheeselovers:raclette.fr");
1588 let linked_chunk_id1 = LinkedChunkId::Room(room1);
1589 let room2 = room_id!("!realcheeselovers:fondue.ch");
1590 let linked_chunk_id2 = LinkedChunkId::Room(room2);
1591
1592 self.handle_linked_chunk_updates(
1596 linked_chunk_id1,
1597 vec![
1598 Update::NewItemsChunk { previous: None, new: CId::new(42), next: None },
1599 Update::PushItems {
1600 at: Position::new(CId::new(42), 0),
1601 items: vec![
1602 make_test_event(room1, "best cheese is raclette"),
1603 make_test_event(room1, "obviously"),
1604 ],
1605 },
1606 ],
1607 )
1608 .await
1609 .unwrap();
1610
1611 self.handle_linked_chunk_updates(
1612 linked_chunk_id2,
1613 vec![
1614 Update::NewItemsChunk { previous: None, new: CId::new(42), next: None },
1615 Update::PushItems {
1616 at: Position::new(CId::new(42), 0),
1617 items: vec![make_test_event(room1, "beaufort is the best")],
1618 },
1619 ],
1620 )
1621 .await
1622 .unwrap();
1623
1624 let mut chunks_room1 = self.load_all_chunks(linked_chunk_id1).await.unwrap();
1626 assert_eq!(chunks_room1.len(), 1);
1627
1628 let c = chunks_room1.remove(0);
1629 assert_matches!(c.content, ChunkContent::Items(events) => {
1630 assert_eq!(events.len(), 2);
1631 check_test_event(&events[0], "best cheese is raclette");
1632 check_test_event(&events[1], "obviously");
1633 });
1634
1635 let mut chunks_room2 = self.load_all_chunks(linked_chunk_id2).await.unwrap();
1637 assert_eq!(chunks_room2.len(), 1);
1638
1639 let c = chunks_room2.remove(0);
1640 assert_matches!(c.content, ChunkContent::Items(events) => {
1641 assert_eq!(events.len(), 1);
1642 check_test_event(&events[0], "beaufort is the best");
1643 });
1644 }
1645
1646 async fn test_load_and_update_thread_info(&self) {
1647 let room_id = room_id!("!r0");
1648 let thread_id = event_id!("$t0");
1649
1650 assert!(self.load_thread_info(room_id, thread_id, false).await.unwrap().is_none());
1654
1655 let ThreadInfo { number_of_replies: _, latest_event: _, read_receipts } =
1660 self.load_thread_info(room_id, thread_id, true).await.unwrap().unwrap();
1661 let ReadReceipts { num_unread, num_notifications, num_mentions, latest_active, pending } =
1662 read_receipts;
1663 assert_eq!(num_unread, 0);
1664 assert_eq!(num_notifications, 0);
1665 assert_eq!(num_mentions, 0);
1666 assert!(latest_active.is_none());
1667 assert!(pending.is_empty());
1668
1669 let mut thread_info =
1674 self.load_thread_info(room_id, thread_id, false).await.unwrap().unwrap();
1675 let ThreadInfo { number_of_replies: _, latest_event: _, read_receipts } = &thread_info;
1676 let ReadReceipts { num_unread, num_notifications, num_mentions, latest_active, pending } =
1677 read_receipts;
1678 assert_eq!(*num_unread, 0);
1679 assert_eq!(*num_notifications, 0);
1680 assert_eq!(*num_mentions, 0);
1681 assert!(latest_active.is_none());
1682 assert!(pending.is_empty());
1683
1684 thread_info.read_receipts.num_unread = 1;
1686 thread_info.read_receipts.num_notifications = 2;
1687 self.update_thread_info(room_id, thread_id, &thread_info).await.unwrap();
1688
1689 let ThreadInfo { number_of_replies: _, latest_event: _, read_receipts } =
1693 self.load_thread_info(room_id, thread_id, true).await.unwrap().unwrap();
1694 let ReadReceipts { num_unread, num_notifications, num_mentions, latest_active, pending } =
1695 read_receipts;
1696 assert_eq!(num_unread, 1);
1697 assert_eq!(num_notifications, 2);
1698 assert_eq!(num_mentions, 0);
1699 assert!(latest_active.is_none());
1700 assert!(pending.is_empty());
1701 }
1702
1703 async fn test_clear_all_events(&self) {
1704 let linked_chunk_ids = [
1705 LinkedChunkId::Room(room_id!("!r0")),
1706 LinkedChunkId::Thread(room_id!("!r1"), event_id!("$r1_thread_root0")),
1707 LinkedChunkId::PinnedEvents(room_id!("!r2")),
1708 ];
1710
1711 for linked_chunk_id in linked_chunk_ids {
1713 let room_id = linked_chunk_id.room_id();
1714
1715 if let LinkedChunkId::Thread(_, thread_id) = &linked_chunk_id {
1718 self.load_thread_info(room_id, thread_id, true).await.unwrap();
1719 }
1720
1721 self.handle_linked_chunk_updates(
1722 linked_chunk_id,
1723 vec![
1724 Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
1726 Update::PushItems {
1728 at: Position::new(CId::new(0), 0),
1729 items: vec![
1730 make_test_event(room_id, "foo"),
1731 make_test_event(room_id, "bar"),
1732 make_test_event(room_id, "baz"),
1733 ],
1734 },
1735 ],
1736 )
1737 .await
1738 .unwrap();
1739
1740 assert!(
1742 lazy_loader::from_all_chunks::<3, _, _>(
1743 self.load_all_chunks(linked_chunk_id).await.unwrap()
1744 )
1745 .unwrap()
1746 .is_some()
1747 );
1748
1749 assert_eq!(self.get_room_events(room_id, None, None).await.unwrap().len(), 3);
1751 }
1752
1753 self.clear_all_events(None).await.unwrap();
1755
1756 for linked_chunk_id in linked_chunk_ids {
1758 let room_id = linked_chunk_id.room_id();
1759
1760 assert!(
1762 lazy_loader::from_all_chunks::<3, _, _>(
1763 self.load_all_chunks(linked_chunk_id).await.unwrap()
1764 )
1765 .unwrap()
1766 .is_none()
1767 );
1768
1769 assert!(self.get_room_events(room_id, None, None).await.unwrap().is_empty());
1771 }
1772 }
1773
1774 async fn test_clear_all_events_for_specific_room(&self) {
1775 let linked_chunk_ids_for_room_0 = [
1776 LinkedChunkId::Room(room_id!("!r0")),
1777 LinkedChunkId::Thread(room_id!("!r0"), event_id!("$r0_thread_root")),
1778 LinkedChunkId::PinnedEvents(room_id!("!r0")),
1779 ];
1780 let linked_chunk_ids_for_room_1 = [
1781 LinkedChunkId::Room(room_id!("!r1")),
1782 LinkedChunkId::Thread(room_id!("!r1"), event_id!("$r1_thread_root")),
1783 LinkedChunkId::PinnedEvents(room_id!("!r1")),
1784 ];
1785 let linked_chunk_ids_for_room_2 = [
1786 LinkedChunkId::Room(room_id!("!r2")),
1787 LinkedChunkId::Thread(room_id!("!r2"), event_id!("$r2_thread_root")),
1788 LinkedChunkId::PinnedEvents(room_id!("!r2")),
1789 ];
1790
1791 for linked_chunk_id in linked_chunk_ids_for_room_0
1793 .iter()
1794 .chain(&linked_chunk_ids_for_room_1)
1795 .chain(&linked_chunk_ids_for_room_2)
1796 {
1797 let room_id = linked_chunk_id.room_id();
1798
1799 if let LinkedChunkId::Thread(_, thread_id) = &linked_chunk_id {
1802 self.load_thread_info(room_id, thread_id, true).await.unwrap();
1803 }
1804
1805 self.handle_linked_chunk_updates(
1806 *linked_chunk_id,
1807 vec![
1808 Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
1810 Update::PushItems {
1812 at: Position::new(CId::new(0), 0),
1813 items: vec![
1814 make_test_event(room_id, "foo"),
1815 make_test_event(room_id, "bar"),
1816 make_test_event(room_id, "baz"),
1817 ],
1818 },
1819 ],
1820 )
1821 .await
1822 .unwrap();
1823
1824 assert!(
1826 lazy_loader::from_all_chunks::<3, _, _>(
1827 self.load_all_chunks(*linked_chunk_id).await.unwrap()
1828 )
1829 .unwrap()
1830 .is_some()
1831 );
1832
1833 assert_eq!(self.get_room_events(room_id, None, None).await.unwrap().len(), 3);
1835 }
1836
1837 self.clear_all_events(Some(linked_chunk_ids_for_room_1[0].room_id())).await.unwrap();
1839
1840 for linked_chunk_id in linked_chunk_ids_for_room_1 {
1842 let room_id = linked_chunk_id.room_id();
1843
1844 assert!(
1846 lazy_loader::from_all_chunks::<3, _, _>(
1847 self.load_all_chunks(linked_chunk_id).await.unwrap()
1848 )
1849 .unwrap()
1850 .is_none()
1851 );
1852
1853 assert!(self.get_room_events(room_id, None, None).await.unwrap().is_empty());
1855 }
1856
1857 for linked_chunk_id in
1859 linked_chunk_ids_for_room_0.iter().chain(&linked_chunk_ids_for_room_2)
1860 {
1861 let room_id = linked_chunk_id.room_id();
1862
1863 assert!(
1865 lazy_loader::from_all_chunks::<3, _, _>(
1866 self.load_all_chunks(*linked_chunk_id).await.unwrap()
1867 )
1868 .unwrap()
1869 .is_some()
1870 );
1871
1872 assert_eq!(self.get_room_events(room_id, None, None).await.unwrap().len(), 3);
1874 }
1875 }
1876
1877 async fn test_filter_duplicated_events(&self) {
1878 let room_id = room_id!("!r0:matrix.org");
1879 let linked_chunk_id = LinkedChunkId::Room(room_id);
1880 let another_room_id = room_id!("!r1:matrix.org");
1881 let another_linked_chunk_id = LinkedChunkId::Room(another_room_id);
1882 let event = |msg: &str| make_test_event(room_id, msg);
1883
1884 let event_comte = event("comté");
1885 let event_brigand = event("brigand du jorat");
1886 let event_raclette = event("raclette");
1887 let event_morbier = event("morbier");
1888 let event_gruyere = event("gruyère");
1889 let event_tome = event("tome");
1890 let event_mont_dor = event("mont d'or");
1891
1892 self.handle_linked_chunk_updates(
1893 linked_chunk_id,
1894 vec![
1895 Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
1896 Update::PushItems {
1897 at: Position::new(CId::new(0), 0),
1898 items: vec![event_comte.clone(), event_brigand.clone()],
1899 },
1900 Update::NewGapChunk {
1901 previous: Some(CId::new(0)),
1902 new: CId::new(1),
1903 next: None,
1904 gap: Gap { token: "brillat-savarin".to_owned() },
1905 },
1906 Update::NewItemsChunk { previous: Some(CId::new(1)), new: CId::new(2), next: None },
1907 Update::PushItems {
1908 at: Position::new(CId::new(2), 0),
1909 items: vec![event_morbier.clone(), event_mont_dor.clone()],
1910 },
1911 ],
1912 )
1913 .await
1914 .unwrap();
1915
1916 self.handle_linked_chunk_updates(
1919 another_linked_chunk_id,
1920 vec![
1921 Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
1922 Update::PushItems {
1923 at: Position::new(CId::new(0), 0),
1924 items: vec![event_tome.clone()],
1925 },
1926 ],
1927 )
1928 .await
1929 .unwrap();
1930
1931 let duplicated_events = BTreeMap::from_iter(
1932 self.filter_duplicated_events(
1933 linked_chunk_id,
1934 vec![
1935 event_comte.event_id().unwrap().to_owned(),
1936 event_raclette.event_id().unwrap().to_owned(),
1937 event_morbier.event_id().unwrap().to_owned(),
1938 event_gruyere.event_id().unwrap().to_owned(),
1939 event_tome.event_id().unwrap().to_owned(),
1940 event_mont_dor.event_id().unwrap().to_owned(),
1941 ],
1942 )
1943 .await
1944 .unwrap(),
1945 );
1946
1947 assert_eq!(duplicated_events.len(), 3);
1948
1949 assert_eq!(
1950 *duplicated_events.get(event_comte.event_id().unwrap()).unwrap(),
1951 Position::new(CId::new(0), 0)
1952 );
1953 assert_eq!(
1954 *duplicated_events.get(event_morbier.event_id().unwrap()).unwrap(),
1955 Position::new(CId::new(2), 0)
1956 );
1957 assert_eq!(
1958 *duplicated_events.get(event_mont_dor.event_id().unwrap()).unwrap(),
1959 Position::new(CId::new(2), 1)
1960 );
1961 }
1962
1963 async fn test_filter_duplicate_events_no_events(&self) {
1964 let room_id = *DEFAULT_TEST_ROOM_ID;
1965 let linked_chunk_id = LinkedChunkId::Room(room_id);
1966 let duplicates = self.filter_duplicated_events(linked_chunk_id, Vec::new()).await.unwrap();
1967 assert!(duplicates.is_empty());
1968 }
1969
1970 async fn test_find_event(&self) {
1971 let room_id = room_id!("!r0:matrix.org");
1972 let another_room_id = room_id!("!r1:matrix.org");
1973 let another_linked_chunk_id = LinkedChunkId::Room(another_room_id);
1974 let event = |msg: &str| make_test_event(room_id, msg);
1975
1976 let event_comte = event("comté");
1977 let event_gruyere = event("gruyère");
1978
1979 self.handle_linked_chunk_updates(
1981 LinkedChunkId::Room(room_id),
1982 vec![
1983 Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
1984 Update::PushItems {
1985 at: Position::new(CId::new(0), 0),
1986 items: vec![event_comte.clone()],
1987 },
1988 ],
1989 )
1990 .await
1991 .unwrap();
1992
1993 self.handle_linked_chunk_updates(
1995 another_linked_chunk_id,
1996 vec![
1997 Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
1998 Update::PushItems {
1999 at: Position::new(CId::new(0), 0),
2000 items: vec![event_gruyere.clone()],
2001 },
2002 ],
2003 )
2004 .await
2005 .unwrap();
2006
2007 let event = self
2009 .find_event(room_id, event_comte.event_id().unwrap())
2010 .await
2011 .expect("failed to query for finding an event")
2012 .expect("failed to find an event");
2013
2014 assert_eq!(event.event_id(), event_comte.event_id());
2015
2016 assert!(
2019 self.find_event(room_id, event_gruyere.event_id().unwrap())
2020 .await
2021 .expect("failed to query for finding an event")
2022 .is_none()
2023 );
2024
2025 self.clear_all_events(None).await.expect("failed to clear all rooms chunks");
2027 assert!(
2028 self.find_event(room_id, event_comte.event_id().unwrap())
2029 .await
2030 .expect("failed to query for finding an event")
2031 .is_none()
2032 );
2033 }
2034
2035 async fn test_find_event_when_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");
2045 let thread_event =
2046 make_test_event_with_event_id(room_id, "thread event", Some(thread_event_id));
2047
2048 let room_and_thread_event_id = event_id!("$room_and_thread");
2051 let room_and_thread_event = make_test_event_with_event_id(
2052 room_id,
2053 "room and thread",
2054 Some(room_and_thread_event_id),
2055 );
2056
2057 let room_linked_chunk_id = LinkedChunkId::Room(room_id);
2058 let thread_linked_chunk_id = LinkedChunkId::Thread(room_id, thread_root);
2059
2060 self.handle_linked_chunk_updates(
2062 room_linked_chunk_id,
2063 vec![
2064 Update::NewItemsChunk { previous: None, new: CId::new(1), next: None },
2065 Update::PushItems {
2066 at: Position::new(CId::new(1), 0),
2067 items: vec![room_event, room_and_thread_event.clone()],
2068 },
2069 ],
2070 )
2071 .await
2072 .unwrap();
2073
2074 self.handle_linked_chunk_updates(
2076 thread_linked_chunk_id,
2077 vec![
2078 Update::NewItemsChunk { previous: None, new: CId::new(1), next: None },
2079 Update::PushItems {
2080 at: Position::new(CId::new(1), 0),
2081 items: vec![thread_event, room_and_thread_event],
2082 },
2083 ],
2084 )
2085 .await
2086 .unwrap();
2087
2088 assert_matches!(self.find_event(room_id, room_event_id).await, Ok(Some(event)) => {
2090 assert_eq!(event.event_id().unwrap(), room_event_id)
2091 });
2092
2093 assert_matches!(self.find_event(room_id, thread_event_id).await, Ok(Some(event)) => {
2095 assert_eq!(event.event_id().unwrap(), thread_event_id)
2096 });
2097
2098 assert_matches!(self.find_event(room_id, room_and_thread_event_id).await, Ok(Some(event)) => {
2100 assert_eq!(event.event_id().unwrap(), room_and_thread_event_id);
2101 });
2102 }
2103
2104 async fn test_find_event_relations(&self) {
2105 let room_id = room_id!("!r0:matrix.org");
2106 let another_room_id = room_id!("!r1:matrix.org");
2107
2108 let f = EventFactory::new().room(room_id).sender(User::Alice);
2109
2110 let eid1 = event_id!("$event1:matrix.org");
2112 let e1 = f.text_msg("comter").event_id(eid1).into_event();
2113
2114 let edit_eid1 = event_id!("$edit_event1:matrix.org");
2115 let edit_e1 = f
2116 .text_msg("* comté")
2117 .event_id(edit_eid1)
2118 .edit(eid1, RoomMessageEventContentWithoutRelation::text_plain("comté"))
2119 .into_event();
2120
2121 let reaction_eid1 = event_id!("$reaction_event1:matrix.org");
2122 let reaction_e1 = f.reaction(eid1, "👍").event_id(reaction_eid1).into_event();
2123
2124 let eid2 = event_id!("$event2:matrix.org");
2125 let e2 = f.text_msg("galette saucisse").event_id(eid2).into_event();
2126
2127 let f = f.room(another_room_id);
2129
2130 let eid3 = event_id!("$event3:matrix.org");
2131 let e3 = f.text_msg("gruyère").event_id(eid3).into_event();
2132
2133 let reaction_eid3 = event_id!("$reaction_event3:matrix.org");
2134 let reaction_e3 = f.reaction(eid3, "👍").event_id(reaction_eid3).into_event();
2135
2136 self.save_event(room_id, e1).await.unwrap();
2138 self.save_event(room_id, edit_e1).await.unwrap();
2139 self.save_event(room_id, reaction_e1.clone()).await.unwrap();
2140 self.save_event(room_id, e2).await.unwrap();
2141 self.save_event(another_room_id, e3).await.unwrap();
2142 self.save_event(another_room_id, reaction_e3).await.unwrap();
2143
2144 let relations = self.find_event_relations(room_id, eid1, None).await.unwrap();
2146 assert_eq!(relations.len(), 2);
2147 assert!(
2149 relations.iter().any(|(ev, pos)| ev.event_id() == Some(edit_eid1) && pos.is_none())
2150 );
2151 assert!(
2152 relations.iter().any(|(ev, pos)| ev.event_id() == Some(reaction_eid1) && pos.is_none())
2153 );
2154
2155 let relations = self
2157 .find_event_relations(room_id, eid1, Some(&[RelationType::Replacement]))
2158 .await
2159 .unwrap();
2160 assert_eq!(relations.len(), 1);
2161 assert_eq!(relations[0].0.event_id(), Some(edit_eid1));
2162
2163 let relations = self
2164 .find_event_relations(
2165 room_id,
2166 eid1,
2167 Some(&[RelationType::Replacement, RelationType::Annotation]),
2168 )
2169 .await
2170 .unwrap();
2171 assert_eq!(relations.len(), 2);
2172 assert!(relations.iter().any(|r| r.0.event_id() == Some(edit_eid1)));
2173 assert!(relations.iter().any(|r| r.0.event_id() == Some(reaction_eid1)));
2174
2175 let relations = self
2177 .find_event_relations(another_room_id, eid1, Some(&[RelationType::Replacement]))
2178 .await
2179 .unwrap();
2180 assert!(relations.is_empty());
2181
2182 self.handle_linked_chunk_updates(
2187 LinkedChunkId::Room(room_id),
2188 vec![
2189 Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
2190 Update::PushItems { at: Position::new(CId::new(0), 0), items: vec![reaction_e1] },
2191 ],
2192 )
2193 .await
2194 .unwrap();
2195
2196 let relations = self.find_event_relations(room_id, eid1, None).await.unwrap();
2199
2200 assert!(relations.iter().any(|(ev, pos)| {
2202 ev.event_id() == Some(reaction_eid1) && *pos == Some(Position::new(CId::new(0), 0))
2203 }));
2204
2205 assert!(
2207 relations.iter().any(|(ev, pos)| ev.event_id() == Some(edit_eid1) && pos.is_none())
2208 );
2209 }
2210
2211 async fn test_find_event_relations_when_event_in_room_and_thread(&self) {
2212 let room_id = *DEFAULT_TEST_ROOM_ID;
2213 let thread_root = event_id!("$thread_root");
2214
2215 let event_id = event_id!("$event");
2218 let event = make_test_event_with_event_id(room_id, "event", Some(event_id));
2219
2220 let extra_thread_event_id = event_id!("$extra_thread_event");
2223 let extra_thread_event = make_test_event_with_event_id(
2224 room_id,
2225 "extra thread event",
2226 Some(extra_thread_event_id),
2227 );
2228
2229 let room_reaction_id = event_id!("$room_reaction");
2231 let room_reaction = EventFactory::new()
2232 .room(room_id)
2233 .sender(User::Alice)
2234 .reaction(event_id, "room")
2235 .event_id(room_reaction_id)
2236 .into_event();
2237
2238 let thread_reaction_id = event_id!("$thread_reaction");
2240 let thread_reaction = EventFactory::new()
2241 .room(room_id)
2242 .sender(User::Alice)
2243 .reaction(event_id, "thread")
2244 .event_id(thread_reaction_id)
2245 .into_event();
2246
2247 let room_and_thread_reaction_id = event_id!("$room_and_thread_reaction");
2250 let room_and_thread_reaction = EventFactory::new()
2251 .room(room_id)
2252 .sender(User::Alice)
2253 .reaction(event_id, "room and thread")
2254 .event_id(room_and_thread_reaction_id)
2255 .into_event();
2256
2257 let room_linked_chunk_id = LinkedChunkId::Room(room_id);
2258 let thread_linked_chunk_id = LinkedChunkId::Thread(room_id, thread_root);
2259
2260 self.handle_linked_chunk_updates(
2262 room_linked_chunk_id,
2263 vec![
2264 Update::NewItemsChunk { previous: None, new: CId::new(1), next: None },
2265 Update::PushItems {
2266 at: Position::new(CId::new(1), 0),
2267 items: vec![event.clone(), room_reaction, room_and_thread_reaction.clone()],
2268 },
2269 ],
2270 )
2271 .await
2272 .unwrap();
2273
2274 self.handle_linked_chunk_updates(
2276 thread_linked_chunk_id,
2277 vec![
2278 Update::NewItemsChunk { previous: None, new: CId::new(1), next: None },
2279 Update::PushItems {
2280 at: Position::new(CId::new(1), 0),
2281 items: vec![
2282 event.clone(),
2283 extra_thread_event,
2284 thread_reaction,
2285 room_and_thread_reaction,
2286 ],
2287 },
2288 ],
2289 )
2290 .await
2291 .unwrap();
2292
2293 assert_matches!(self.find_event_relations(room_id, event_id, None).await, Ok(relations) => {
2295 assert_eq!(relations.len(), 3);
2296 let room_relation = relations
2299 .iter()
2300 .find(|relation| relation.0.event_id().unwrap() == room_reaction_id)
2301 .unwrap();
2302 assert_matches!(room_relation, (_, Some(position)) => {
2303 assert_eq!(*position, Position::new(CId::new(1), 1));
2304 });
2305
2306 let thread_relation = relations
2310 .iter()
2311 .find(|relation| relation.0.event_id().unwrap() == thread_reaction_id)
2312 .unwrap();
2313 assert_matches!(thread_relation, (_, None));
2314
2315 let room_and_thread_relation = relations
2319 .iter()
2320 .find(|relation| relation.0.event_id().unwrap() == room_and_thread_reaction_id)
2321 .unwrap();
2322 assert_matches!(room_and_thread_relation, (_, Some(position)) => {
2323 assert_eq!(*position, Position::new(CId::new(1), 2));
2324 });
2325 });
2326 }
2327
2328 async fn test_find_events_before_timestamp(&self) {
2329 let room_id = room_id!("!r0");
2330 let another_room_id = room_id!("!r1");
2331 let cutoff_ms: u64 = 2000;
2332 let event_factory = EventFactory::new().room(room_id).sender(User::Alice);
2333
2334 let event_old_1 = event_factory.text_msg("old1").server_ts(500).into_event();
2336 let thread_root_id = event_id!("$thread_root");
2337 let thread_root = event_factory
2338 .text_msg("thread root")
2339 .event_id(thread_root_id)
2340 .server_ts(750)
2341 .into_event();
2342 let event_in_thread = event_factory
2343 .text_msg("thread reply")
2344 .event_id(event_id!("$thread_reply"))
2345 .in_thread(thread_root_id, thread_root_id)
2346 .server_ts(1000)
2347 .into_event();
2348 let event_old_2 = event_factory.text_msg("old2").server_ts(1500).into_event();
2349 let event_at_cutoff = event_factory.text_msg("at_cutoff").server_ts(2000).into_event();
2352 let event_new = event_factory.text_msg("new").server_ts(3000).into_event();
2354 let event_other_room = EventFactory::new()
2357 .room(another_room_id)
2358 .sender(User::Alice)
2359 .text_msg("other_room")
2360 .server_ts(500)
2361 .into_event();
2362
2363 self.handle_linked_chunk_updates(
2364 LinkedChunkId::Room(room_id),
2365 vec![
2366 Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
2367 Update::PushItems {
2368 at: Position::new(CId::new(0), 0),
2369 items: vec![
2370 event_old_1.clone(),
2371 thread_root.clone(),
2372 event_old_2.clone(),
2373 event_at_cutoff.clone(),
2374 event_new.clone(),
2375 ],
2376 },
2377 ],
2378 )
2379 .await
2380 .unwrap();
2381
2382 self.handle_linked_chunk_updates(
2383 LinkedChunkId::Thread(room_id, thread_root_id),
2384 vec![
2385 Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
2386 Update::PushItems {
2387 at: Position::new(CId::new(0), 0),
2388 items: vec![thread_root.clone(), event_in_thread.clone()],
2389 },
2390 ],
2391 )
2392 .await
2393 .unwrap();
2394
2395 self.handle_linked_chunk_updates(
2396 LinkedChunkId::Room(another_room_id),
2397 vec![
2398 Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
2399 Update::PushItems {
2400 at: Position::new(CId::new(0), 0),
2401 items: vec![event_other_room.clone()],
2402 },
2403 ],
2404 )
2405 .await
2406 .unwrap();
2407
2408 let event_no_position = event_factory.text_msg("no_position").server_ts(100).into_event();
2411 self.save_event(room_id, event_no_position.clone()).await.unwrap();
2412
2413 let results = self
2414 .find_events_before_timestamp(room_id, cutoff_ms)
2415 .await
2416 .expect("failed to query events before timestamp");
2417
2418 let result_ids: Vec<_> =
2419 results.iter().map(|event| event.event_id().unwrap().to_owned()).collect();
2420 assert_eq!(
2421 result_ids,
2422 vec![
2423 event_no_position.event_id().unwrap().to_owned(),
2424 event_old_1.event_id().unwrap().to_owned(),
2425 thread_root.event_id().unwrap().to_owned(),
2426 event_in_thread.event_id().unwrap().to_owned(),
2427 event_old_2.event_id().unwrap().to_owned(),
2428 ]
2429 );
2430 }
2431
2432 async fn test_get_room_events(&self) {
2433 let room_id = room_id!("!r0:matrix.org");
2434 let another_room_id = room_id!("!r1:matrix.org");
2435 let linked_chunk_id = LinkedChunkId::Room(room_id);
2436 let another_linked_chunk_id = LinkedChunkId::Room(another_room_id);
2437 let event = |msg: &str| make_test_event(room_id, msg);
2438
2439 let event_comte = event("comté");
2440 let event_gruyere = event("gruyère");
2441 let event_stilton = event("stilton");
2442
2443 self.handle_linked_chunk_updates(
2445 linked_chunk_id,
2446 vec![
2447 Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
2448 Update::PushItems {
2449 at: Position::new(CId::new(0), 0),
2450 items: vec![event_comte.clone(), event_gruyere.clone()],
2451 },
2452 ],
2453 )
2454 .await
2455 .unwrap();
2456
2457 self.handle_linked_chunk_updates(
2459 another_linked_chunk_id,
2460 vec![
2461 Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
2462 Update::PushItems {
2463 at: Position::new(CId::new(0), 0),
2464 items: vec![event_stilton.clone()],
2465 },
2466 ],
2467 )
2468 .await
2469 .unwrap();
2470
2471 let events = self
2473 .get_room_events(room_id, None, None)
2474 .await
2475 .expect("failed to query for room events");
2476
2477 assert_eq!(events.len(), 2);
2478
2479 let got_ids: Vec<_> =
2480 events.into_iter().map(|ev| ev.event_id().map(ToOwned::to_owned)).collect();
2481 let expected_ids = vec![
2482 event_comte.event_id().map(ToOwned::to_owned),
2483 event_gruyere.event_id().map(ToOwned::to_owned),
2484 ];
2485
2486 for expected in expected_ids {
2487 assert!(
2488 got_ids.contains(&expected),
2489 "Expected event {expected:?} not in got events: {got_ids:?}."
2490 );
2491 }
2492 }
2493
2494 async fn test_get_room_events_filtered(&self) {
2495 macro_rules! assert_expected_events {
2496 ($events:expr, [$($item:expr),* $(,)?]) => {{
2497 let got_ids: BTreeSet<_> = $events.into_iter().map(|ev| ev.event_id().map(ToOwned::to_owned)).flatten().collect();
2498 let expected_ids = BTreeSet::from([$($item.event_id().unwrap().to_owned()),*]);
2499
2500 assert_eq!(got_ids, expected_ids);
2501 }};
2502 }
2503
2504 let room_id = room_id!("!r0:matrix.org");
2505 let linked_chunk_id = LinkedChunkId::Room(room_id);
2506 let another_room_id = room_id!("!r1:matrix.org");
2507 let another_linked_chunk_id = LinkedChunkId::Room(another_room_id);
2508
2509 let event = |session_id: &str| make_encrypted_test_event(room_id, session_id);
2510
2511 let first_event = event("session_1");
2512 let second_event = event("session_2");
2513 let third_event = event("session_3");
2514 let fourth_event = make_test_event(room_id, "It's a secret to everybody");
2515
2516 self.handle_linked_chunk_updates(
2518 linked_chunk_id,
2519 vec![
2520 Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
2521 Update::PushItems {
2522 at: Position::new(CId::new(0), 0),
2523 items: vec![first_event.clone(), second_event.clone(), fourth_event.clone()],
2524 },
2525 ],
2526 )
2527 .await
2528 .unwrap();
2529
2530 self.handle_linked_chunk_updates(
2532 another_linked_chunk_id,
2533 vec![
2534 Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
2535 Update::PushItems {
2536 at: Position::new(CId::new(0), 0),
2537 items: vec![third_event.clone()],
2538 },
2539 ],
2540 )
2541 .await
2542 .unwrap();
2543
2544 let events = self
2546 .get_room_events(room_id, Some("m.room.encrypted"), None)
2547 .await
2548 .expect("failed to query for room events");
2549
2550 assert_eq!(events.len(), 2);
2551 assert_expected_events!(events, [first_event, second_event]);
2552
2553 let events = self
2556 .get_room_events(room_id, Some("m.room.encrypted"), Some("session_1"))
2557 .await
2558 .expect("failed to query for room events");
2559
2560 assert_eq!(events.len(), 1);
2561 assert_expected_events!(events, [first_event]);
2562 }
2563
2564 async fn test_get_room_events_with_event_in_room_and_thread(&self) {
2565 let room_id = *DEFAULT_TEST_ROOM_ID;
2566 let thread_root = event_id!("$thread_root");
2567
2568 let room_event_id = event_id!("$room_event");
2570 let room_event = make_test_event_with_event_id(room_id, "room event", Some(room_event_id));
2571
2572 let thread_event_id = event_id!("$thread_event");
2576 let thread_event =
2577 make_test_event_with_event_id(room_id, "thread event", Some(thread_event_id));
2578
2579 let room_and_thread_event_id = event_id!("$room_and_thread");
2582 let room_and_thread_event = make_test_event_with_event_id(
2583 room_id,
2584 "room and thread",
2585 Some(room_and_thread_event_id),
2586 );
2587
2588 let room_linked_chunk_id = LinkedChunkId::Room(room_id);
2589 let thread_linked_chunk_id = LinkedChunkId::Thread(room_id, thread_root);
2590
2591 self.handle_linked_chunk_updates(
2593 room_linked_chunk_id,
2594 vec![
2595 Update::NewItemsChunk { previous: None, new: CId::new(1), next: None },
2596 Update::PushItems {
2597 at: Position::new(CId::new(1), 0),
2598 items: vec![room_event, room_and_thread_event.clone()],
2599 },
2600 ],
2601 )
2602 .await
2603 .unwrap();
2604
2605 self.handle_linked_chunk_updates(
2607 thread_linked_chunk_id,
2608 vec![
2609 Update::NewItemsChunk { previous: None, new: CId::new(1), next: None },
2610 Update::PushItems {
2611 at: Position::new(CId::new(1), 0),
2612 items: vec![thread_event, room_and_thread_event],
2613 },
2614 ],
2615 )
2616 .await
2617 .unwrap();
2618
2619 let expected_event_ids =
2622 BTreeSet::from([room_event_id, thread_event_id, room_and_thread_event_id]);
2623 assert_matches!(self.get_room_events(room_id, None, None).await, Ok(events) => {
2624 assert_eq!(events.len(), 3);
2625 assert!(events.iter().all(|event| {
2626 expected_event_ids.contains(event.event_id().unwrap())
2627 }));
2628 });
2629 }
2630
2631 async fn test_save_event(&self) {
2632 let room_id = room_id!("!r0:matrix.org");
2633 let another_room_id = room_id!("!r1:matrix.org");
2634
2635 let event = |msg: &str| make_test_event(room_id, msg);
2636 let event_comte = event("comté");
2637 let event_gruyere = event("gruyère");
2638
2639 self.save_event(room_id, event_comte.clone()).await.unwrap();
2641
2642 self.save_event(another_room_id, event_gruyere.clone()).await.unwrap();
2644
2645 let event = self
2647 .find_event(room_id, event_comte.event_id().unwrap())
2648 .await
2649 .expect("failed to query for finding an event")
2650 .expect("failed to find an event");
2651 assert_eq!(event.event_id(), event_comte.event_id());
2652
2653 let event = self
2654 .find_event(another_room_id, event_gruyere.event_id().unwrap())
2655 .await
2656 .expect("failed to query for finding an event")
2657 .expect("failed to find an event");
2658 assert_eq!(event.event_id(), event_gruyere.event_id());
2659
2660 assert!(
2662 self.find_event(another_room_id, event_comte.event_id().unwrap())
2663 .await
2664 .expect("failed to query for finding an event")
2665 .is_none()
2666 );
2667 assert!(
2668 self.find_event(room_id, event_gruyere.event_id().unwrap())
2669 .await
2670 .expect("failed to query for finding an event")
2671 .is_none()
2672 );
2673 }
2674
2675 async fn test_save_event_updates_event_in_room_and_thread(&self) {
2676 let room_id = *DEFAULT_TEST_ROOM_ID;
2677 let thread_root = event_id!("$thread_root");
2678
2679 let event_id = event_id!("$event");
2682 let event = make_test_event_with_event_id(room_id, "event", Some(event_id));
2683
2684 let room_linked_chunk_id = LinkedChunkId::Room(room_id);
2685 let thread_linked_chunk_id = LinkedChunkId::Thread(room_id, thread_root);
2686
2687 self.handle_linked_chunk_updates(
2689 room_linked_chunk_id,
2690 vec![
2691 Update::NewItemsChunk { previous: None, new: CId::new(1), next: None },
2692 Update::PushItems { at: Position::new(CId::new(1), 0), items: vec![event.clone()] },
2693 ],
2694 )
2695 .await
2696 .unwrap();
2697
2698 self.handle_linked_chunk_updates(
2700 thread_linked_chunk_id,
2701 vec![
2702 Update::NewItemsChunk { previous: None, new: CId::new(1), next: None },
2703 Update::PushItems { at: Position::new(CId::new(1), 0), items: vec![event.clone()] },
2704 ],
2705 )
2706 .await
2707 .unwrap();
2708
2709 let updated_content = "updated content";
2712 let updated = make_test_event_with_event_id(room_id, updated_content, Some(event_id));
2713 self.save_event(room_id, updated).await.unwrap();
2714
2715 let room_chunks = self.load_all_chunks(room_linked_chunk_id).await.unwrap();
2717 let thread_chunks = self.load_all_chunks(thread_linked_chunk_id).await.unwrap();
2718
2719 assert_eq!(room_chunks.len(), 1);
2720 assert_eq!(thread_chunks.len(), 1);
2721
2722 assert_matches!(&room_chunks[0].content, ChunkContent::Items(events) => {
2724 assert_eq!(events.len(), 1);
2725 assert_eq!(events[0].event_id(), Some(event_id));
2726 check_test_event(&events[0], updated_content);
2727 });
2728 assert_matches!(&thread_chunks[0].content, ChunkContent::Items(events) => {
2729 assert_eq!(events.len(), 1);
2730 assert_eq!(events[0].event_id(), Some(event_id));
2731 check_test_event(&events[0], updated_content);
2732 });
2733 }
2734
2735 async fn test_thread_vs_room_linked_chunk(&self) {
2736 let room_id = room_id!("!r0:matrix.org");
2737
2738 let event = |msg: &str| make_test_event(room_id, msg);
2739
2740 let thread1_ev = event("comté");
2741 let thread2_ev = event("gruyère");
2742 let thread2_ev2 = event("beaufort");
2743 let room_ev = event("brillat savarin triple crème");
2744
2745 let thread_root1 = event("thread1");
2746 let thread_root2 = event("thread2");
2747
2748 self.handle_linked_chunk_updates(
2750 LinkedChunkId::Thread(room_id, thread_root1.event_id().unwrap()),
2751 vec![
2752 Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
2753 Update::PushItems {
2754 at: Position::new(CId::new(0), 0),
2755 items: vec![thread1_ev.clone()],
2756 },
2757 ],
2758 )
2759 .await
2760 .unwrap();
2761
2762 self.handle_linked_chunk_updates(
2764 LinkedChunkId::Thread(room_id, thread_root2.event_id().unwrap()),
2765 vec![
2766 Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
2767 Update::PushItems {
2768 at: Position::new(CId::new(0), 0),
2769 items: vec![thread2_ev.clone(), thread2_ev2.clone()],
2770 },
2771 ],
2772 )
2773 .await
2774 .unwrap();
2775
2776 self.handle_linked_chunk_updates(
2778 LinkedChunkId::Room(room_id),
2779 vec![
2780 Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
2781 Update::PushItems {
2782 at: Position::new(CId::new(0), 0),
2783 items: vec![room_ev.clone()],
2784 },
2785 ],
2786 )
2787 .await
2788 .unwrap();
2789
2790 self.find_event(room_id, thread2_ev.event_id().unwrap())
2792 .await
2793 .expect("failed to query for finding an event")
2794 .expect("failed to find thread1_ev");
2795
2796 self.find_event(room_id, thread2_ev.event_id().unwrap())
2797 .await
2798 .expect("failed to query for finding an event")
2799 .expect("failed to find thread2_ev");
2800
2801 self.find_event(room_id, thread2_ev2.event_id().unwrap())
2802 .await
2803 .expect("failed to query for finding an event")
2804 .expect("failed to find thread2_ev2");
2805
2806 self.find_event(room_id, room_ev.event_id().unwrap())
2807 .await
2808 .expect("failed to query for finding an event")
2809 .expect("failed to find room_ev");
2810
2811 let dups = self
2813 .filter_duplicated_events(
2814 LinkedChunkId::Thread(room_id, thread_root1.event_id().unwrap()),
2815 vec![
2816 thread1_ev.event_id().unwrap().to_owned(),
2817 room_ev.event_id().unwrap().to_owned(),
2818 ],
2819 )
2820 .await
2821 .unwrap();
2822 assert_eq!(dups.len(), 1);
2823 assert_eq!(dups[0].0, thread1_ev.event_id().unwrap());
2824
2825 let all_chunks = self
2827 .load_all_chunks(LinkedChunkId::Thread(room_id, thread_root2.event_id().unwrap()))
2828 .await
2829 .unwrap();
2830 assert_eq!(all_chunks.len(), 1);
2831 assert_eq!(all_chunks[0].identifier, CId::new(0));
2832 assert_let!(ChunkContent::Items(observed_items) = all_chunks[0].content.clone());
2833 assert_eq!(observed_items.len(), 2);
2834 assert_eq!(observed_items[0].event_id(), thread2_ev.event_id());
2835 assert_eq!(observed_items[1].event_id(), thread2_ev2.event_id());
2836
2837 let metas = self
2840 .load_all_chunks_metadata(LinkedChunkId::Thread(
2841 room_id,
2842 thread_root2.event_id().unwrap(),
2843 ))
2844 .await
2845 .unwrap();
2846 assert_eq!(metas.len(), 1);
2847 assert_eq!(metas[0].identifier, CId::new(0));
2848 assert_eq!(metas[0].num_items, 2);
2849
2850 let (last_chunk, _chunk_identifier_generator) = self
2852 .load_last_chunk(LinkedChunkId::Thread(room_id, thread_root1.event_id().unwrap()))
2853 .await
2854 .unwrap();
2855 let last_chunk = last_chunk.unwrap();
2856 assert_eq!(last_chunk.identifier, CId::new(0));
2857 assert_let!(ChunkContent::Items(observed_items) = last_chunk.content);
2858 assert_eq!(observed_items.len(), 1);
2859 assert_eq!(observed_items[0].event_id(), thread1_ev.event_id());
2860 }
2861}
2862
2863#[allow(unused_macros, unused_extern_crates)]
2893#[macro_export]
2894macro_rules! event_cache_store_integration_tests {
2895 () => {
2896 mod event_cache_store_integration_tests {
2897 use matrix_sdk_test::async_test;
2898 use $crate::event_cache::store::{
2899 EventCacheStoreIntegrationTests, IntoEventCacheStore,
2900 };
2901
2902 use super::get_event_cache_store;
2903
2904 #[async_test]
2905 async fn test_handle_updates_and_rebuild_linked_chunk() {
2906 let event_cache_store =
2907 get_event_cache_store().await.unwrap().into_event_cache_store();
2908 event_cache_store.test_handle_updates_and_rebuild_linked_chunk().await;
2909 }
2910
2911 #[async_test]
2912 async fn test_linked_chunk_exists_before_referenced() {
2913 let event_cache_store =
2914 get_event_cache_store().await.unwrap().into_event_cache_store();
2915 event_cache_store.test_linked_chunk_exists_before_referenced().await;
2916 }
2917
2918 #[async_test]
2919 async fn test_linked_chunk_allow_same_event_in_room_and_thread() {
2920 let event_cache_store =
2921 get_event_cache_store().await.unwrap().into_event_cache_store();
2922 event_cache_store.test_linked_chunk_allows_same_event_in_room_and_thread().await;
2923 }
2924
2925 #[async_test]
2926 async fn test_load_last_chunk() {
2927 let event_cache_store =
2928 get_event_cache_store().await.unwrap().into_event_cache_store();
2929 event_cache_store.test_load_last_chunk().await;
2930 }
2931
2932 #[async_test]
2933 async fn test_load_last_chunk_with_a_cycle() {
2934 let event_cache_store =
2935 get_event_cache_store().await.unwrap().into_event_cache_store();
2936 event_cache_store.test_load_last_chunk_with_a_cycle().await;
2937 }
2938
2939 #[async_test]
2940 async fn test_load_previous_chunk() {
2941 let event_cache_store =
2942 get_event_cache_store().await.unwrap().into_event_cache_store();
2943 event_cache_store.test_load_previous_chunk().await;
2944 }
2945
2946 #[async_test]
2947 async fn test_linked_chunk_incremental_loading() {
2948 let event_cache_store =
2949 get_event_cache_store().await.unwrap().into_event_cache_store();
2950 event_cache_store.test_linked_chunk_incremental_loading().await;
2951 }
2952
2953 #[async_test]
2954 async fn test_linked_chunk_remove_chunk() {
2955 let event_cache_store =
2956 get_event_cache_store().await.unwrap().into_event_cache_store();
2957 event_cache_store.test_linked_chunk_remove_chunk().await;
2958 }
2959
2960 #[async_test]
2961 async fn test_linked_chunk_push_items() {
2962 let event_cache_store =
2963 get_event_cache_store().await.unwrap().into_event_cache_store();
2964 event_cache_store.test_linked_chunk_push_items().await;
2965 }
2966
2967 #[async_test]
2968 async fn test_linked_chunk_replace_item() {
2969 let event_cache_store =
2970 get_event_cache_store().await.unwrap().into_event_cache_store();
2971 event_cache_store.test_linked_chunk_replace_item().await;
2972 }
2973
2974 #[async_test]
2975 async fn test_linked_chunk_filter_incomplete_events() {
2976 let event_cache_store =
2977 get_event_cache_store().await.unwrap().into_event_cache_store();
2978 event_cache_store.test_linked_chunk_filter_incomplete_events().await;
2979 }
2980
2981 #[async_test]
2982 async fn test_linked_chunk_remove_item() {
2983 let event_cache_store =
2984 get_event_cache_store().await.unwrap().into_event_cache_store();
2985 event_cache_store.test_linked_chunk_remove_item().await;
2986 }
2987
2988 #[async_test]
2989 async fn test_linked_chunk_detach_last_items() {
2990 let event_cache_store =
2991 get_event_cache_store().await.unwrap().into_event_cache_store();
2992 event_cache_store.test_linked_chunk_detach_last_items().await;
2993 }
2994
2995 #[async_test]
2996 async fn test_linked_chunk_start_end_reattach_items() {
2997 let event_cache_store =
2998 get_event_cache_store().await.unwrap().into_event_cache_store();
2999 event_cache_store.test_linked_chunk_start_end_reattach_items().await;
3000 }
3001
3002 #[async_test]
3003 async fn test_linked_chunk_clear() {
3004 let event_cache_store =
3005 get_event_cache_store().await.unwrap().into_event_cache_store();
3006 event_cache_store.test_linked_chunk_clear().await;
3007 }
3008
3009 #[async_test]
3010 async fn test_linked_chunk_clear_and_reinsert() {
3011 let event_cache_store =
3012 get_event_cache_store().await.unwrap().into_event_cache_store();
3013 event_cache_store.test_linked_chunk_clear_and_reinsert().await;
3014 }
3015
3016 #[async_test]
3017 async fn test_rebuild_empty_linked_chunk() {
3018 let event_cache_store =
3019 get_event_cache_store().await.unwrap().into_event_cache_store();
3020 event_cache_store.test_rebuild_empty_linked_chunk().await;
3021 }
3022
3023 #[async_test]
3024 async fn test_linked_chunk_multiple_rooms() {
3025 let event_cache_store =
3026 get_event_cache_store().await.unwrap().into_event_cache_store();
3027 event_cache_store.test_linked_chunk_multiple_rooms().await;
3028 }
3029
3030 #[async_test]
3031 async fn test_load_all_chunks_metadata() {
3032 let event_cache_store =
3033 get_event_cache_store().await.unwrap().into_event_cache_store();
3034 event_cache_store.test_load_all_chunks_metadata().await;
3035 }
3036
3037 #[async_test]
3038 async fn test_load_and_update_thread_info() {
3039 let event_cache_store =
3040 get_event_cache_store().await.unwrap().into_event_cache_store();
3041 event_cache_store.test_load_and_update_thread_info().await;
3042 }
3043
3044 #[async_test]
3045 async fn test_clear_all_events() {
3046 let event_cache_store =
3047 get_event_cache_store().await.unwrap().into_event_cache_store();
3048 event_cache_store.test_clear_all_events().await;
3049 }
3050
3051 #[async_test]
3052 async fn test_clear_all_events_for_specific_room() {
3053 let event_cache_store =
3054 get_event_cache_store().await.unwrap().into_event_cache_store();
3055 event_cache_store.test_clear_all_events_for_specific_room().await;
3056 }
3057
3058 #[async_test]
3059 async fn test_filter_duplicated_events() {
3060 let event_cache_store =
3061 get_event_cache_store().await.unwrap().into_event_cache_store();
3062 event_cache_store.test_filter_duplicated_events().await;
3063 }
3064
3065 #[async_test]
3066 async fn test_filter_duplicate_events_no_events() {
3067 let event_cache_store =
3068 get_event_cache_store().await.unwrap().into_event_cache_store();
3069 event_cache_store.test_filter_duplicate_events_no_events().await;
3070 }
3071
3072 #[async_test]
3073 async fn test_find_event() {
3074 let event_cache_store =
3075 get_event_cache_store().await.unwrap().into_event_cache_store();
3076 event_cache_store.test_find_event().await;
3077 }
3078
3079 #[async_test]
3080 async fn test_find_event_when_event_in_room_and_thread() {
3081 let event_cache_store =
3082 get_event_cache_store().await.unwrap().into_event_cache_store();
3083 event_cache_store.test_find_event_when_event_in_room_and_thread().await;
3084 }
3085
3086 #[async_test]
3087 async fn test_find_event_relations() {
3088 let event_cache_store =
3089 get_event_cache_store().await.unwrap().into_event_cache_store();
3090 event_cache_store.test_find_event_relations().await;
3091 }
3092
3093 #[async_test]
3094 async fn test_find_event_relations_when_event_in_room_and_thread() {
3095 let event_cache_store =
3096 get_event_cache_store().await.unwrap().into_event_cache_store();
3097 event_cache_store.test_find_event_relations_when_event_in_room_and_thread().await;
3098 }
3099
3100 #[async_test]
3101 async fn test_find_events_before_timestamp() {
3102 let event_cache_store =
3103 get_event_cache_store().await.unwrap().into_event_cache_store();
3104 event_cache_store.test_find_events_before_timestamp().await;
3105 }
3106
3107 #[async_test]
3108 async fn test_get_room_events() {
3109 let event_cache_store =
3110 get_event_cache_store().await.unwrap().into_event_cache_store();
3111 event_cache_store.test_get_room_events().await;
3112 }
3113
3114 #[async_test]
3115 async fn test_get_room_events_filtered() {
3116 let event_cache_store =
3117 get_event_cache_store().await.unwrap().into_event_cache_store();
3118 event_cache_store.test_get_room_events_filtered().await;
3119 }
3120
3121 #[async_test]
3122 async fn test_get_room_events_with_event_in_room_and_thread() {
3123 let event_cache_store =
3124 get_event_cache_store().await.unwrap().into_event_cache_store();
3125 event_cache_store.test_get_room_events_with_event_in_room_and_thread().await;
3126 }
3127
3128 #[async_test]
3129 async fn test_save_event() {
3130 let event_cache_store =
3131 get_event_cache_store().await.unwrap().into_event_cache_store();
3132 event_cache_store.test_save_event().await;
3133 }
3134
3135 #[async_test]
3136 async fn test_save_event_updates_event_in_room_and_thread() {
3137 let event_cache_store =
3138 get_event_cache_store().await.unwrap().into_event_cache_store();
3139 event_cache_store.test_save_event_updates_event_in_room_and_thread().await;
3140 }
3141
3142 #[async_test]
3143 async fn test_thread_vs_room_linked_chunk() {
3144 let event_cache_store =
3145 get_event_cache_store().await.unwrap().into_event_cache_store();
3146 event_cache_store.test_thread_vs_room_linked_chunk().await;
3147 }
3148 }
3149 };
3150}
3151
3152#[allow(unused_macros)]
3155#[macro_export]
3156macro_rules! event_cache_store_integration_tests_time {
3157 () => {
3158 mod event_cache_store_integration_tests_time {
3159 use std::time::Duration;
3160
3161 #[cfg(all(target_family = "wasm", target_os = "unknown"))]
3162 use gloo_timers::future::sleep;
3163 use matrix_sdk_test::async_test;
3164 #[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
3165 use tokio::time::sleep;
3166 use $crate::event_cache::store::IntoEventCacheStore;
3167
3168 use super::get_event_cache_store;
3169
3170 #[async_test]
3171 async fn test_lease_locks() {
3172 let store = get_event_cache_store().await.unwrap().into_event_cache_store();
3173
3174 let acquired0 = store.try_take_leased_lock(0, "key", "alice").await.unwrap();
3175 assert_eq!(acquired0, Some(1)); let acquired2 = store.try_take_leased_lock(300, "key", "alice").await.unwrap();
3179 assert_eq!(acquired2, Some(1)); let acquired3 = store.try_take_leased_lock(300, "key", "alice").await.unwrap();
3184 assert_eq!(acquired3, Some(1)); let acquired4 = store.try_take_leased_lock(300, "key", "bob").await.unwrap();
3189 assert!(acquired4.is_none()); let acquired5 = store.try_take_leased_lock(300, "key", "bob").await.unwrap();
3193 assert!(acquired5.is_none()); sleep(Duration::from_millis(50)).await;
3197
3198 let acquired55 = store.try_take_leased_lock(300, "key", "bob").await.unwrap();
3200 assert!(acquired55.is_none()); sleep(Duration::from_millis(250)).await;
3204
3205 let acquired6 = store.try_take_leased_lock(0, "key", "bob").await.unwrap();
3207 assert_eq!(acquired6, Some(2)); sleep(Duration::from_millis(1)).await;
3210
3211 let acquired7 = store.try_take_leased_lock(0, "key", "alice").await.unwrap();
3213 assert_eq!(acquired7, Some(3)); sleep(Duration::from_millis(1)).await;
3216
3217 let acquired8 = store.try_take_leased_lock(300, "key", "bob").await.unwrap();
3219 assert_eq!(acquired8, Some(4)); let acquired9 = store.try_take_leased_lock(300, "key", "alice").await.unwrap();
3223 assert!(acquired9.is_none()); let acquired10 = store.try_take_leased_lock(300, "key", "bob").await.unwrap();
3227 assert_eq!(acquired10, Some(4)); }
3229 }
3230 };
3231}