1use std::{
18 collections::{BTreeMap, BTreeSet},
19 sync::Arc,
20};
21
22use assert_matches::assert_matches;
23use assert_matches2::assert_let;
24use matrix_sdk_common::{
25 deserialized_responses::{
26 AlgorithmInfo, DecryptedRoomEvent, EncryptionInfo, TimelineEvent, TimelineEventKind,
27 UnableToDecryptInfo, UnableToDecryptReason, VerificationState,
28 },
29 linked_chunk::{
30 ChunkContent, ChunkIdentifier as CId, LinkedChunkId, Position, Update, lazy_loader,
31 },
32};
33use matrix_sdk_test::{ALICE, DEFAULT_TEST_ROOM_ID, event_factory::EventFactory};
34use ruma::{
35 EventId, RoomId, event_id,
36 events::{
37 AnyMessageLikeEvent, AnyTimelineEvent, relation::RelationType,
38 room::message::RoomMessageEventContentWithoutRelation,
39 },
40 push::Action,
41 room_id,
42};
43
44use super::DynEventCacheStore;
45use crate::event_cache::{Gap, store::DEFAULT_CHUNK_CAPACITY};
46
47pub fn make_test_event(room_id: &RoomId, content: &str) -> TimelineEvent {
52 make_test_event_with_event_id(room_id, content, None)
53}
54
55pub fn make_encrypted_test_event(room_id: &RoomId, session_id: &str) -> TimelineEvent {
58 let device_id = "DEVICEID";
59 let builder = EventFactory::new()
60 .encrypted("", "curve_key", device_id, session_id)
61 .room(room_id)
62 .sender(*ALICE);
63
64 let event = builder.into_raw();
65 let utd_info = UnableToDecryptInfo {
66 session_id: Some(session_id.to_owned()),
67 reason: UnableToDecryptReason::MissingMegolmSession { withheld_code: None },
68 };
69
70 TimelineEvent::from_utd(event, utd_info)
71}
72
73pub fn make_test_event_with_event_id(
75 room_id: &RoomId,
76 content: &str,
77 event_id: Option<&EventId>,
78) -> TimelineEvent {
79 let encryption_info = Arc::new(EncryptionInfo {
80 sender: (*ALICE).into(),
81 sender_device: None,
82 algorithm_info: AlgorithmInfo::MegolmV1AesSha2 {
83 curve25519_key: "1337".to_owned(),
84 sender_claimed_keys: Default::default(),
85 session_id: Some("mysessionid9".to_owned()),
86 },
87 verification_state: VerificationState::Verified,
88 });
89
90 let mut builder = EventFactory::new().text_msg(content).room(room_id).sender(*ALICE);
91 if let Some(event_id) = event_id {
92 builder = builder.event_id(event_id);
93 }
94 let event = builder.into_raw();
95
96 TimelineEvent::from_decrypted(
97 DecryptedRoomEvent { event, encryption_info, unsigned_encryption_info: None },
98 Some(vec![Action::Notify]),
99 )
100}
101
102#[track_caller]
107pub fn check_test_event(event: &TimelineEvent, text: &str) {
108 let actions = event.push_actions().unwrap();
110 assert_eq!(actions.len(), 1);
111 assert_matches!(&actions[0], Action::Notify);
112
113 assert_matches!(&event.kind, TimelineEventKind::Decrypted(d) => {
115 assert_eq!(d.encryption_info.sender, *ALICE);
117 assert_matches!(&d.encryption_info.algorithm_info, AlgorithmInfo::MegolmV1AesSha2 { curve25519_key, .. } => {
118 assert_eq!(curve25519_key, "1337");
119 });
120
121 let deserialized = d.event.deserialize().unwrap();
123 assert_matches!(deserialized, AnyTimelineEvent::MessageLike(AnyMessageLikeEvent::RoomMessage(msg)) => {
124 assert_eq!(msg.as_original().unwrap().content.body(), text);
125 });
126 });
127}
128
129#[allow(async_fn_in_trait)]
134pub trait EventCacheStoreIntegrationTests {
135 async fn test_handle_updates_and_rebuild_linked_chunk(&self);
138
139 async fn test_linked_chunk_incremental_loading(&self);
142
143 async fn test_rebuild_empty_linked_chunk(&self);
146
147 async fn test_load_all_chunks_metadata(&self);
149
150 async fn test_clear_all_linked_chunks(&self);
152
153 async fn test_remove_room(&self);
155
156 async fn test_filter_duplicated_events(&self);
158
159 async fn test_find_event(&self);
161
162 async fn test_find_event_relations(&self);
164
165 async fn test_get_room_events(&self);
167
168 async fn test_get_room_events_filtered(&self);
170
171 async fn test_save_event(&self);
173
174 async fn test_thread_vs_room_linked_chunk(&self);
177}
178
179impl EventCacheStoreIntegrationTests for DynEventCacheStore {
180 async fn test_handle_updates_and_rebuild_linked_chunk(&self) {
181 let room_id = room_id!("!r0:matrix.org");
182 let linked_chunk_id = LinkedChunkId::Room(room_id);
183
184 self.handle_linked_chunk_updates(
185 linked_chunk_id,
186 vec![
187 Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
189 Update::PushItems {
191 at: Position::new(CId::new(0), 0),
192 items: vec![
193 make_test_event(room_id, "hello"),
194 make_test_event(room_id, "world"),
195 ],
196 },
197 Update::NewGapChunk {
199 previous: Some(CId::new(0)),
200 new: CId::new(1),
201 next: None,
202 gap: Gap { prev_token: "parmesan".to_owned() },
203 },
204 Update::NewItemsChunk { previous: Some(CId::new(1)), new: CId::new(2), next: None },
206 Update::PushItems {
208 at: Position::new(CId::new(2), 0),
209 items: vec![make_test_event(room_id, "sup")],
210 },
211 ],
212 )
213 .await
214 .unwrap();
215
216 let lc = lazy_loader::from_all_chunks::<3, _, _>(
218 self.load_all_chunks(linked_chunk_id).await.unwrap(),
219 )
220 .unwrap()
221 .unwrap();
222
223 let mut chunks = lc.chunks();
224
225 {
226 let first = chunks.next().unwrap();
227 assert_eq!(first.identifier(), CId::new(0));
230
231 assert_matches!(first.content(), ChunkContent::Items(events) => {
232 assert_eq!(events.len(), 2);
233 check_test_event(&events[0], "hello");
234 check_test_event(&events[1], "world");
235 });
236 }
237
238 {
239 let second = chunks.next().unwrap();
240 assert_eq!(second.identifier(), CId::new(1));
241
242 assert_matches!(second.content(), ChunkContent::Gap(gap) => {
243 assert_eq!(gap.prev_token, "parmesan");
244 });
245 }
246
247 {
248 let third = chunks.next().unwrap();
249 assert_eq!(third.identifier(), CId::new(2));
250
251 assert_matches!(third.content(), ChunkContent::Items(events) => {
252 assert_eq!(events.len(), 1);
253 check_test_event(&events[0], "sup");
254 });
255 }
256
257 assert!(chunks.next().is_none());
258 }
259
260 async fn test_load_all_chunks_metadata(&self) {
261 let room_id = room_id!("!r0:matrix.org");
262 let linked_chunk_id = LinkedChunkId::Room(room_id);
263
264 self.handle_linked_chunk_updates(
265 linked_chunk_id,
266 vec![
267 Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
269 Update::PushItems {
271 at: Position::new(CId::new(0), 0),
272 items: vec![
273 make_test_event(room_id, "hello"),
274 make_test_event(room_id, "world"),
275 ],
276 },
277 Update::NewGapChunk {
279 previous: Some(CId::new(0)),
280 new: CId::new(1),
281 next: None,
282 gap: Gap { prev_token: "parmesan".to_owned() },
283 },
284 Update::NewItemsChunk { previous: Some(CId::new(1)), new: CId::new(2), next: None },
286 Update::PushItems {
288 at: Position::new(CId::new(2), 0),
289 items: vec![make_test_event(room_id, "sup")],
290 },
291 Update::NewItemsChunk { previous: Some(CId::new(2)), new: CId::new(3), next: None },
293 ],
294 )
295 .await
296 .unwrap();
297
298 let metas = self.load_all_chunks_metadata(linked_chunk_id).await.unwrap();
299 assert_eq!(metas.len(), 4);
300
301 assert_eq!(metas[0].identifier, CId::new(0));
303 assert_eq!(metas[0].previous, None);
304 assert_eq!(metas[0].next, Some(CId::new(1)));
305 assert_eq!(metas[0].num_items, 2);
306
307 assert_eq!(metas[1].identifier, CId::new(1));
309 assert_eq!(metas[1].previous, Some(CId::new(0)));
310 assert_eq!(metas[1].next, Some(CId::new(2)));
311 assert_eq!(metas[1].num_items, 0);
312
313 assert_eq!(metas[2].identifier, CId::new(2));
315 assert_eq!(metas[2].previous, Some(CId::new(1)));
316 assert_eq!(metas[2].next, Some(CId::new(3)));
317 assert_eq!(metas[2].num_items, 1);
318
319 assert_eq!(metas[3].identifier, CId::new(3));
321 assert_eq!(metas[3].previous, Some(CId::new(2)));
322 assert_eq!(metas[3].next, None);
323 assert_eq!(metas[3].num_items, 0);
324 }
325
326 async fn test_linked_chunk_incremental_loading(&self) {
327 let room_id = room_id!("!r0:matrix.org");
328 let linked_chunk_id = LinkedChunkId::Room(room_id);
329 let event = |msg: &str| make_test_event(room_id, msg);
330
331 {
333 let (last_chunk, chunk_identifier_generator) =
334 self.load_last_chunk(linked_chunk_id).await.unwrap();
335
336 assert!(last_chunk.is_none());
337 assert_eq!(chunk_identifier_generator.current(), 0);
338 }
339
340 self.handle_linked_chunk_updates(
341 linked_chunk_id,
342 vec![
343 Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
345 Update::PushItems {
347 at: Position::new(CId::new(0), 0),
348 items: vec![event("a"), event("b")],
349 },
350 Update::NewGapChunk {
352 previous: Some(CId::new(0)),
353 new: CId::new(1),
354 next: None,
355 gap: Gap { prev_token: "morbier".to_owned() },
356 },
357 Update::NewItemsChunk { previous: Some(CId::new(1)), new: CId::new(2), next: None },
359 Update::PushItems {
361 at: Position::new(CId::new(2), 0),
362 items: vec![event("c"), event("d"), event("e")],
363 },
364 ],
365 )
366 .await
367 .unwrap();
368
369 let mut linked_chunk = {
371 let (last_chunk, chunk_identifier_generator) =
372 self.load_last_chunk(linked_chunk_id).await.unwrap();
373
374 assert_eq!(chunk_identifier_generator.current(), 2);
375
376 let linked_chunk = lazy_loader::from_last_chunk::<DEFAULT_CHUNK_CAPACITY, _, _>(
377 last_chunk,
378 chunk_identifier_generator,
379 )
380 .unwrap() .unwrap(); let mut rchunks = linked_chunk.rchunks();
384
385 assert_matches!(rchunks.next(), Some(chunk) => {
387 assert_eq!(chunk.identifier(), 2);
388 assert_eq!(chunk.lazy_previous(), Some(CId::new(1)));
389
390 assert_matches!(chunk.content(), ChunkContent::Items(events) => {
391 assert_eq!(events.len(), 3);
392 check_test_event(&events[0], "c");
393 check_test_event(&events[1], "d");
394 check_test_event(&events[2], "e");
395 });
396 });
397
398 assert!(rchunks.next().is_none());
399
400 linked_chunk
401 };
402
403 {
405 let first_chunk = linked_chunk.chunks().next().unwrap().identifier();
406 let previous_chunk =
407 self.load_previous_chunk(linked_chunk_id, first_chunk).await.unwrap().unwrap();
408
409 lazy_loader::insert_new_first_chunk(&mut linked_chunk, previous_chunk).unwrap();
410
411 let mut rchunks = linked_chunk.rchunks();
412
413 assert_matches!(rchunks.next(), Some(chunk) => {
415 assert_eq!(chunk.identifier(), 2);
416 assert!(chunk.lazy_previous().is_none());
417
418 assert_matches!(chunk.content(), ChunkContent::Items(events) => {
420 assert_eq!(events.len(), 3);
421 check_test_event(&events[0], "c");
422 check_test_event(&events[1], "d");
423 check_test_event(&events[2], "e");
424 });
425 });
426
427 assert_matches!(rchunks.next(), Some(chunk) => {
429 assert_eq!(chunk.identifier(), 1);
430 assert_eq!(chunk.lazy_previous(), Some(CId::new(0)));
431
432 assert_matches!(chunk.content(), ChunkContent::Gap(gap) => {
433 assert_eq!(gap.prev_token, "morbier");
434 });
435 });
436
437 assert!(rchunks.next().is_none());
438 }
439
440 {
442 let first_chunk = linked_chunk.chunks().next().unwrap().identifier();
443 let previous_chunk =
444 self.load_previous_chunk(linked_chunk_id, first_chunk).await.unwrap().unwrap();
445
446 lazy_loader::insert_new_first_chunk(&mut linked_chunk, previous_chunk).unwrap();
447
448 let mut rchunks = linked_chunk.rchunks();
449
450 assert_matches!(rchunks.next(), Some(chunk) => {
452 assert_eq!(chunk.identifier(), 2);
453 assert!(chunk.lazy_previous().is_none());
454
455 assert_matches!(chunk.content(), ChunkContent::Items(events) => {
457 assert_eq!(events.len(), 3);
458 check_test_event(&events[0], "c");
459 check_test_event(&events[1], "d");
460 check_test_event(&events[2], "e");
461 });
462 });
463
464 assert_matches!(rchunks.next(), Some(chunk) => {
466 assert_eq!(chunk.identifier(), 1);
467 assert!(chunk.lazy_previous().is_none());
468
469 assert_matches!(chunk.content(), ChunkContent::Gap(gap) => {
471 assert_eq!(gap.prev_token, "morbier");
472 });
473 });
474
475 assert_matches!(rchunks.next(), Some(chunk) => {
477 assert_eq!(chunk.identifier(), 0);
478 assert!(chunk.lazy_previous().is_none());
479
480 assert_matches!(chunk.content(), ChunkContent::Items(events) => {
481 assert_eq!(events.len(), 2);
482 check_test_event(&events[0], "a");
483 check_test_event(&events[1], "b");
484 });
485 });
486
487 assert!(rchunks.next().is_none());
488 }
489
490 {
492 let first_chunk = linked_chunk.chunks().next().unwrap().identifier();
493 let previous_chunk =
494 self.load_previous_chunk(linked_chunk_id, first_chunk).await.unwrap();
495
496 assert!(previous_chunk.is_none());
497 }
498
499 {
502 let mut chunks = linked_chunk.chunks();
503
504 assert_matches!(chunks.next(), Some(chunk) => {
506 assert_eq!(chunk.identifier(), 0);
507 assert!(chunk.lazy_previous().is_none());
508
509 assert_matches!(chunk.content(), ChunkContent::Items(events) => {
510 assert_eq!(events.len(), 2);
511 check_test_event(&events[0], "a");
512 check_test_event(&events[1], "b");
513 });
514 });
515
516 assert_matches!(chunks.next(), Some(chunk) => {
518 assert_eq!(chunk.identifier(), 1);
519 assert!(chunk.lazy_previous().is_none());
520
521 assert_matches!(chunk.content(), ChunkContent::Gap(gap) => {
522 assert_eq!(gap.prev_token, "morbier");
523 });
524 });
525
526 assert_matches!(chunks.next(), Some(chunk) => {
528 assert_eq!(chunk.identifier(), 2);
529 assert!(chunk.lazy_previous().is_none());
530
531 assert_matches!(chunk.content(), ChunkContent::Items(events) => {
532 assert_eq!(events.len(), 3);
533 check_test_event(&events[0], "c");
534 check_test_event(&events[1], "d");
535 check_test_event(&events[2], "e");
536 });
537 });
538
539 assert!(chunks.next().is_none());
540 }
541 }
542
543 async fn test_rebuild_empty_linked_chunk(&self) {
544 let linked_chunk = lazy_loader::from_all_chunks::<3, _, _>(
546 self.load_all_chunks(LinkedChunkId::Room(&DEFAULT_TEST_ROOM_ID)).await.unwrap(),
547 )
548 .unwrap();
549 assert!(linked_chunk.is_none());
550 }
551
552 async fn test_clear_all_linked_chunks(&self) {
553 let r0 = room_id!("!r0:matrix.org");
554 let linked_chunk_id0 = LinkedChunkId::Room(r0);
555 let r1 = room_id!("!r1:matrix.org");
556 let linked_chunk_id1 = LinkedChunkId::Room(r1);
557
558 self.handle_linked_chunk_updates(
560 linked_chunk_id0,
561 vec![
562 Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
564 Update::PushItems {
566 at: Position::new(CId::new(0), 0),
567 items: vec![make_test_event(r0, "hello"), make_test_event(r0, "world")],
568 },
569 ],
570 )
571 .await
572 .unwrap();
573
574 self.handle_linked_chunk_updates(
576 linked_chunk_id1,
577 vec![
578 Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
580 Update::NewGapChunk {
582 previous: Some(CId::new(0)),
583 new: CId::new(1),
584 next: None,
585 gap: Gap { prev_token: "bleu d'auvergne".to_owned() },
586 },
587 Update::NewItemsChunk { previous: Some(CId::new(1)), new: CId::new(2), next: None },
589 Update::PushItems {
591 at: Position::new(CId::new(2), 0),
592 items: vec![make_test_event(r0, "yummy")],
593 },
594 ],
595 )
596 .await
597 .unwrap();
598
599 assert!(
601 lazy_loader::from_all_chunks::<3, _, _>(
602 self.load_all_chunks(linked_chunk_id0).await.unwrap()
603 )
604 .unwrap()
605 .is_some()
606 );
607 assert!(
608 lazy_loader::from_all_chunks::<3, _, _>(
609 self.load_all_chunks(linked_chunk_id1).await.unwrap()
610 )
611 .unwrap()
612 .is_some()
613 );
614
615 self.clear_all_linked_chunks().await.unwrap();
617
618 assert!(
620 lazy_loader::from_all_chunks::<3, _, _>(
621 self.load_all_chunks(linked_chunk_id0).await.unwrap()
622 )
623 .unwrap()
624 .is_none()
625 );
626 assert!(
627 lazy_loader::from_all_chunks::<3, _, _>(
628 self.load_all_chunks(linked_chunk_id1).await.unwrap()
629 )
630 .unwrap()
631 .is_none()
632 );
633 }
634
635 async fn test_remove_room(&self) {
636 let r0 = room_id!("!r0:matrix.org");
637 let linked_chunk_id0 = LinkedChunkId::Room(r0);
638 let r1 = room_id!("!r1:matrix.org");
639 let linked_chunk_id1 = LinkedChunkId::Room(r1);
640
641 self.handle_linked_chunk_updates(
643 linked_chunk_id0,
644 vec![
645 Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
647 Update::PushItems {
649 at: Position::new(CId::new(0), 0),
650 items: vec![make_test_event(r0, "hello"), make_test_event(r0, "world")],
651 },
652 ],
653 )
654 .await
655 .unwrap();
656
657 self.handle_linked_chunk_updates(
659 linked_chunk_id1,
660 vec![
661 Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
663 Update::PushItems {
665 at: Position::new(CId::new(0), 0),
666 items: vec![make_test_event(r0, "yummy")],
667 },
668 ],
669 )
670 .await
671 .unwrap();
672
673 self.remove_room(r0).await.unwrap();
675
676 let r0_linked_chunk = self.load_all_chunks(linked_chunk_id0).await.unwrap();
678 assert!(r0_linked_chunk.is_empty());
679
680 let r1_linked_chunk = self.load_all_chunks(linked_chunk_id1).await.unwrap();
682 assert!(!r1_linked_chunk.is_empty());
683 }
684
685 async fn test_filter_duplicated_events(&self) {
686 let room_id = room_id!("!r0:matrix.org");
687 let linked_chunk_id = LinkedChunkId::Room(room_id);
688 let another_room_id = room_id!("!r1:matrix.org");
689 let another_linked_chunk_id = LinkedChunkId::Room(another_room_id);
690 let event = |msg: &str| make_test_event(room_id, msg);
691
692 let event_comte = event("comté");
693 let event_brigand = event("brigand du jorat");
694 let event_raclette = event("raclette");
695 let event_morbier = event("morbier");
696 let event_gruyere = event("gruyère");
697 let event_tome = event("tome");
698 let event_mont_dor = event("mont d'or");
699
700 self.handle_linked_chunk_updates(
701 linked_chunk_id,
702 vec![
703 Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
704 Update::PushItems {
705 at: Position::new(CId::new(0), 0),
706 items: vec![event_comte.clone(), event_brigand.clone()],
707 },
708 Update::NewGapChunk {
709 previous: Some(CId::new(0)),
710 new: CId::new(1),
711 next: None,
712 gap: Gap { prev_token: "brillat-savarin".to_owned() },
713 },
714 Update::NewItemsChunk { previous: Some(CId::new(1)), new: CId::new(2), next: None },
715 Update::PushItems {
716 at: Position::new(CId::new(2), 0),
717 items: vec![event_morbier.clone(), event_mont_dor.clone()],
718 },
719 ],
720 )
721 .await
722 .unwrap();
723
724 self.handle_linked_chunk_updates(
727 another_linked_chunk_id,
728 vec![
729 Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
730 Update::PushItems {
731 at: Position::new(CId::new(0), 0),
732 items: vec![event_tome.clone()],
733 },
734 ],
735 )
736 .await
737 .unwrap();
738
739 let duplicated_events = BTreeMap::from_iter(
740 self.filter_duplicated_events(
741 linked_chunk_id,
742 vec![
743 event_comte.event_id().unwrap().to_owned(),
744 event_raclette.event_id().unwrap().to_owned(),
745 event_morbier.event_id().unwrap().to_owned(),
746 event_gruyere.event_id().unwrap().to_owned(),
747 event_tome.event_id().unwrap().to_owned(),
748 event_mont_dor.event_id().unwrap().to_owned(),
749 ],
750 )
751 .await
752 .unwrap(),
753 );
754
755 assert_eq!(duplicated_events.len(), 3);
756
757 assert_eq!(
758 *duplicated_events.get(&event_comte.event_id().unwrap()).unwrap(),
759 Position::new(CId::new(0), 0)
760 );
761 assert_eq!(
762 *duplicated_events.get(&event_morbier.event_id().unwrap()).unwrap(),
763 Position::new(CId::new(2), 0)
764 );
765 assert_eq!(
766 *duplicated_events.get(&event_mont_dor.event_id().unwrap()).unwrap(),
767 Position::new(CId::new(2), 1)
768 );
769 }
770
771 async fn test_find_event(&self) {
772 let room_id = room_id!("!r0:matrix.org");
773 let another_room_id = room_id!("!r1:matrix.org");
774 let another_linked_chunk_id = LinkedChunkId::Room(another_room_id);
775 let event = |msg: &str| make_test_event(room_id, msg);
776
777 let event_comte = event("comté");
778 let event_gruyere = event("gruyère");
779
780 self.handle_linked_chunk_updates(
782 LinkedChunkId::Room(room_id),
783 vec![
784 Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
785 Update::PushItems {
786 at: Position::new(CId::new(0), 0),
787 items: vec![event_comte.clone()],
788 },
789 ],
790 )
791 .await
792 .unwrap();
793
794 self.handle_linked_chunk_updates(
796 another_linked_chunk_id,
797 vec![
798 Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
799 Update::PushItems {
800 at: Position::new(CId::new(0), 0),
801 items: vec![event_gruyere.clone()],
802 },
803 ],
804 )
805 .await
806 .unwrap();
807
808 let event = self
810 .find_event(room_id, event_comte.event_id().unwrap().as_ref())
811 .await
812 .expect("failed to query for finding an event")
813 .expect("failed to find an event");
814
815 assert_eq!(event.event_id(), event_comte.event_id());
816
817 assert!(
819 self.find_event(room_id, event_gruyere.event_id().unwrap().as_ref())
820 .await
821 .expect("failed to query for finding an event")
822 .is_none()
823 );
824
825 self.clear_all_linked_chunks().await.expect("failed to clear all rooms chunks");
827 assert!(
828 self.find_event(room_id, event_comte.event_id().unwrap().as_ref())
829 .await
830 .expect("failed to query for finding an event")
831 .is_none()
832 );
833 }
834
835 async fn test_find_event_relations(&self) {
836 let room_id = room_id!("!r0:matrix.org");
837 let another_room_id = room_id!("!r1:matrix.org");
838
839 let f = EventFactory::new().room(room_id).sender(*ALICE);
840
841 let eid1 = event_id!("$event1:matrix.org");
843 let e1 = f.text_msg("comter").event_id(eid1).into_event();
844
845 let edit_eid1 = event_id!("$edit_event1:matrix.org");
846 let edit_e1 = f
847 .text_msg("* comté")
848 .event_id(edit_eid1)
849 .edit(eid1, RoomMessageEventContentWithoutRelation::text_plain("comté"))
850 .into_event();
851
852 let reaction_eid1 = event_id!("$reaction_event1:matrix.org");
853 let reaction_e1 = f.reaction(eid1, "👍").event_id(reaction_eid1).into_event();
854
855 let eid2 = event_id!("$event2:matrix.org");
856 let e2 = f.text_msg("galette saucisse").event_id(eid2).into_event();
857
858 let f = f.room(another_room_id);
860
861 let eid3 = event_id!("$event3:matrix.org");
862 let e3 = f.text_msg("gruyère").event_id(eid3).into_event();
863
864 let reaction_eid3 = event_id!("$reaction_event3:matrix.org");
865 let reaction_e3 = f.reaction(eid3, "👍").event_id(reaction_eid3).into_event();
866
867 self.save_event(room_id, e1).await.unwrap();
869 self.save_event(room_id, edit_e1).await.unwrap();
870 self.save_event(room_id, reaction_e1.clone()).await.unwrap();
871 self.save_event(room_id, e2).await.unwrap();
872 self.save_event(another_room_id, e3).await.unwrap();
873 self.save_event(another_room_id, reaction_e3).await.unwrap();
874
875 let relations = self.find_event_relations(room_id, eid1, None).await.unwrap();
877 assert_eq!(relations.len(), 2);
878 assert!(
880 relations
881 .iter()
882 .any(|(ev, pos)| ev.event_id().as_deref() == Some(edit_eid1) && pos.is_none())
883 );
884 assert!(
885 relations
886 .iter()
887 .any(|(ev, pos)| ev.event_id().as_deref() == Some(reaction_eid1) && pos.is_none())
888 );
889
890 let relations = self
892 .find_event_relations(room_id, eid1, Some(&[RelationType::Replacement]))
893 .await
894 .unwrap();
895 assert_eq!(relations.len(), 1);
896 assert_eq!(relations[0].0.event_id().as_deref(), Some(edit_eid1));
897
898 let relations = self
899 .find_event_relations(
900 room_id,
901 eid1,
902 Some(&[RelationType::Replacement, RelationType::Annotation]),
903 )
904 .await
905 .unwrap();
906 assert_eq!(relations.len(), 2);
907 assert!(relations.iter().any(|r| r.0.event_id().as_deref() == Some(edit_eid1)));
908 assert!(relations.iter().any(|r| r.0.event_id().as_deref() == Some(reaction_eid1)));
909
910 let relations = self
912 .find_event_relations(another_room_id, eid1, Some(&[RelationType::Replacement]))
913 .await
914 .unwrap();
915 assert!(relations.is_empty());
916
917 self.handle_linked_chunk_updates(
922 LinkedChunkId::Room(room_id),
923 vec![
924 Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
925 Update::PushItems { at: Position::new(CId::new(0), 0), items: vec![reaction_e1] },
926 ],
927 )
928 .await
929 .unwrap();
930
931 let relations = self.find_event_relations(room_id, eid1, None).await.unwrap();
934
935 assert!(relations.iter().any(|(ev, pos)| {
937 ev.event_id().as_deref() == Some(reaction_eid1)
938 && *pos == Some(Position::new(CId::new(0), 0))
939 }));
940
941 assert!(
943 relations
944 .iter()
945 .any(|(ev, pos)| ev.event_id().as_deref() == Some(edit_eid1) && pos.is_none())
946 );
947 }
948
949 async fn test_get_room_events(&self) {
950 let room_id = room_id!("!r0:matrix.org");
951 let another_room_id = room_id!("!r1:matrix.org");
952 let linked_chunk_id = LinkedChunkId::Room(room_id);
953 let another_linked_chunk_id = LinkedChunkId::Room(another_room_id);
954 let event = |msg: &str| make_test_event(room_id, msg);
955
956 let event_comte = event("comté");
957 let event_gruyere = event("gruyère");
958 let event_stilton = event("stilton");
959
960 self.handle_linked_chunk_updates(
962 linked_chunk_id,
963 vec![
964 Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
965 Update::PushItems {
966 at: Position::new(CId::new(0), 0),
967 items: vec![event_comte.clone(), event_gruyere.clone()],
968 },
969 ],
970 )
971 .await
972 .unwrap();
973
974 self.handle_linked_chunk_updates(
976 another_linked_chunk_id,
977 vec![
978 Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
979 Update::PushItems {
980 at: Position::new(CId::new(0), 0),
981 items: vec![event_stilton.clone()],
982 },
983 ],
984 )
985 .await
986 .unwrap();
987
988 let events = self
990 .get_room_events(room_id, None, None)
991 .await
992 .expect("failed to query for room events");
993
994 assert_eq!(events.len(), 2);
995
996 let got_ids: Vec<_> = events.into_iter().map(|ev| ev.event_id()).collect();
997 let expected_ids = vec![event_comte.event_id(), event_gruyere.event_id()];
998
999 for expected in expected_ids {
1000 assert!(
1001 got_ids.contains(&expected),
1002 "Expected event {expected:?} not in got events: {got_ids:?}."
1003 );
1004 }
1005 }
1006
1007 async fn test_get_room_events_filtered(&self) {
1008 macro_rules! assert_expected_events {
1009 ($events:expr, [$($item:expr),* $(,)?]) => {{
1010 let got_ids: BTreeSet<_> = $events.into_iter().map(|ev| ev.event_id().unwrap()).collect();
1011 let expected_ids = BTreeSet::from([$($item.event_id().unwrap()),*]);
1012
1013 assert_eq!(got_ids, expected_ids);
1014 }};
1015 }
1016
1017 let room_id = room_id!("!r0:matrix.org");
1018 let linked_chunk_id = LinkedChunkId::Room(room_id);
1019 let another_room_id = room_id!("!r1:matrix.org");
1020 let another_linked_chunk_id = LinkedChunkId::Room(another_room_id);
1021
1022 let event = |session_id: &str| make_encrypted_test_event(room_id, session_id);
1023
1024 let first_event = event("session_1");
1025 let second_event = event("session_2");
1026 let third_event = event("session_3");
1027 let fourth_event = make_test_event(room_id, "It's a secret to everybody");
1028
1029 self.handle_linked_chunk_updates(
1031 linked_chunk_id,
1032 vec![
1033 Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
1034 Update::PushItems {
1035 at: Position::new(CId::new(0), 0),
1036 items: vec![first_event.clone(), second_event.clone(), fourth_event.clone()],
1037 },
1038 ],
1039 )
1040 .await
1041 .unwrap();
1042
1043 self.handle_linked_chunk_updates(
1045 another_linked_chunk_id,
1046 vec![
1047 Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
1048 Update::PushItems {
1049 at: Position::new(CId::new(0), 0),
1050 items: vec![third_event.clone()],
1051 },
1052 ],
1053 )
1054 .await
1055 .unwrap();
1056
1057 let events = self
1059 .get_room_events(room_id, Some("m.room.encrypted"), None)
1060 .await
1061 .expect("failed to query for room events");
1062
1063 assert_eq!(events.len(), 2);
1064 assert_expected_events!(events, [first_event, second_event]);
1065
1066 let events = self
1069 .get_room_events(room_id, Some("m.room.encrypted"), Some("session_1"))
1070 .await
1071 .expect("failed to query for room events");
1072
1073 assert_eq!(events.len(), 1);
1074 assert_expected_events!(events, [first_event]);
1075 }
1076
1077 async fn test_save_event(&self) {
1078 let room_id = room_id!("!r0:matrix.org");
1079 let another_room_id = room_id!("!r1:matrix.org");
1080
1081 let event = |msg: &str| make_test_event(room_id, msg);
1082 let event_comte = event("comté");
1083 let event_gruyere = event("gruyère");
1084
1085 self.save_event(room_id, event_comte.clone()).await.unwrap();
1087
1088 self.save_event(another_room_id, event_gruyere.clone()).await.unwrap();
1090
1091 let event = self
1093 .find_event(room_id, event_comte.event_id().unwrap().as_ref())
1094 .await
1095 .expect("failed to query for finding an event")
1096 .expect("failed to find an event");
1097 assert_eq!(event.event_id(), event_comte.event_id());
1098
1099 let event = self
1100 .find_event(another_room_id, event_gruyere.event_id().unwrap().as_ref())
1101 .await
1102 .expect("failed to query for finding an event")
1103 .expect("failed to find an event");
1104 assert_eq!(event.event_id(), event_gruyere.event_id());
1105
1106 assert!(
1108 self.find_event(another_room_id, event_comte.event_id().unwrap().as_ref())
1109 .await
1110 .expect("failed to query for finding an event")
1111 .is_none()
1112 );
1113 assert!(
1114 self.find_event(room_id, event_gruyere.event_id().unwrap().as_ref())
1115 .await
1116 .expect("failed to query for finding an event")
1117 .is_none()
1118 );
1119 }
1120
1121 async fn test_thread_vs_room_linked_chunk(&self) {
1122 let room_id = room_id!("!r0:matrix.org");
1123
1124 let event = |msg: &str| make_test_event(room_id, msg);
1125
1126 let thread1_ev = event("comté");
1127 let thread2_ev = event("gruyère");
1128 let thread2_ev2 = event("beaufort");
1129 let room_ev = event("brillat savarin triple crème");
1130
1131 let thread_root1 = event("thread1");
1132 let thread_root2 = event("thread2");
1133
1134 self.handle_linked_chunk_updates(
1136 LinkedChunkId::Thread(room_id, thread_root1.event_id().unwrap().as_ref()),
1137 vec![
1138 Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
1139 Update::PushItems {
1140 at: Position::new(CId::new(0), 0),
1141 items: vec![thread1_ev.clone()],
1142 },
1143 ],
1144 )
1145 .await
1146 .unwrap();
1147
1148 self.handle_linked_chunk_updates(
1150 LinkedChunkId::Thread(room_id, thread_root2.event_id().unwrap().as_ref()),
1151 vec![
1152 Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
1153 Update::PushItems {
1154 at: Position::new(CId::new(0), 0),
1155 items: vec![thread2_ev.clone(), thread2_ev2.clone()],
1156 },
1157 ],
1158 )
1159 .await
1160 .unwrap();
1161
1162 self.handle_linked_chunk_updates(
1164 LinkedChunkId::Room(room_id),
1165 vec![
1166 Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
1167 Update::PushItems {
1168 at: Position::new(CId::new(0), 0),
1169 items: vec![room_ev.clone()],
1170 },
1171 ],
1172 )
1173 .await
1174 .unwrap();
1175
1176 self.find_event(room_id, thread2_ev.event_id().unwrap().as_ref())
1178 .await
1179 .expect("failed to query for finding an event")
1180 .expect("failed to find thread1_ev");
1181
1182 self.find_event(room_id, thread2_ev.event_id().unwrap().as_ref())
1183 .await
1184 .expect("failed to query for finding an event")
1185 .expect("failed to find thread2_ev");
1186
1187 self.find_event(room_id, thread2_ev2.event_id().unwrap().as_ref())
1188 .await
1189 .expect("failed to query for finding an event")
1190 .expect("failed to find thread2_ev2");
1191
1192 self.find_event(room_id, room_ev.event_id().unwrap().as_ref())
1193 .await
1194 .expect("failed to query for finding an event")
1195 .expect("failed to find room_ev");
1196
1197 let dups = self
1199 .filter_duplicated_events(
1200 LinkedChunkId::Thread(room_id, thread_root1.event_id().unwrap().as_ref()),
1201 vec![
1202 thread1_ev.event_id().unwrap().to_owned(),
1203 room_ev.event_id().unwrap().to_owned(),
1204 ],
1205 )
1206 .await
1207 .unwrap();
1208 assert_eq!(dups.len(), 1);
1209 assert_eq!(dups[0].0, thread1_ev.event_id().unwrap());
1210
1211 let all_chunks = self
1213 .load_all_chunks(LinkedChunkId::Thread(
1214 room_id,
1215 thread_root2.event_id().unwrap().as_ref(),
1216 ))
1217 .await
1218 .unwrap();
1219 assert_eq!(all_chunks.len(), 1);
1220 assert_eq!(all_chunks[0].identifier, CId::new(0));
1221 assert_let!(ChunkContent::Items(observed_items) = all_chunks[0].content.clone());
1222 assert_eq!(observed_items.len(), 2);
1223 assert_eq!(observed_items[0].event_id(), thread2_ev.event_id());
1224 assert_eq!(observed_items[1].event_id(), thread2_ev2.event_id());
1225
1226 let metas = self
1229 .load_all_chunks_metadata(LinkedChunkId::Thread(
1230 room_id,
1231 thread_root2.event_id().unwrap().as_ref(),
1232 ))
1233 .await
1234 .unwrap();
1235 assert_eq!(metas.len(), 1);
1236 assert_eq!(metas[0].identifier, CId::new(0));
1237 assert_eq!(metas[0].num_items, 2);
1238
1239 let (last_chunk, _chunk_identifier_generator) = self
1241 .load_last_chunk(LinkedChunkId::Thread(
1242 room_id,
1243 thread_root1.event_id().unwrap().as_ref(),
1244 ))
1245 .await
1246 .unwrap();
1247 let last_chunk = last_chunk.unwrap();
1248 assert_eq!(last_chunk.identifier, CId::new(0));
1249 assert_let!(ChunkContent::Items(observed_items) = last_chunk.content);
1250 assert_eq!(observed_items.len(), 1);
1251 assert_eq!(observed_items[0].event_id(), thread1_ev.event_id());
1252 }
1253}
1254
1255#[allow(unused_macros, unused_extern_crates)]
1283#[macro_export]
1284macro_rules! event_cache_store_integration_tests {
1285 () => {
1286 mod event_cache_store_integration_tests {
1287 use matrix_sdk_test::async_test;
1288 use $crate::event_cache::store::{
1289 EventCacheStoreIntegrationTests, IntoEventCacheStore,
1290 };
1291
1292 use super::get_event_cache_store;
1293
1294 #[async_test]
1295 async fn test_handle_updates_and_rebuild_linked_chunk() {
1296 let event_cache_store =
1297 get_event_cache_store().await.unwrap().into_event_cache_store();
1298 event_cache_store.test_handle_updates_and_rebuild_linked_chunk().await;
1299 }
1300
1301 #[async_test]
1302 async fn test_linked_chunk_incremental_loading() {
1303 let event_cache_store =
1304 get_event_cache_store().await.unwrap().into_event_cache_store();
1305 event_cache_store.test_linked_chunk_incremental_loading().await;
1306 }
1307
1308 #[async_test]
1309 async fn test_rebuild_empty_linked_chunk() {
1310 let event_cache_store =
1311 get_event_cache_store().await.unwrap().into_event_cache_store();
1312 event_cache_store.test_rebuild_empty_linked_chunk().await;
1313 }
1314
1315 #[async_test]
1316 async fn test_load_all_chunks_metadata() {
1317 let event_cache_store =
1318 get_event_cache_store().await.unwrap().into_event_cache_store();
1319 event_cache_store.test_load_all_chunks_metadata().await;
1320 }
1321
1322 #[async_test]
1323 async fn test_clear_all_linked_chunks() {
1324 let event_cache_store =
1325 get_event_cache_store().await.unwrap().into_event_cache_store();
1326 event_cache_store.test_clear_all_linked_chunks().await;
1327 }
1328
1329 #[async_test]
1330 async fn test_remove_room() {
1331 let event_cache_store =
1332 get_event_cache_store().await.unwrap().into_event_cache_store();
1333 event_cache_store.test_remove_room().await;
1334 }
1335
1336 #[async_test]
1337 async fn test_filter_duplicated_events() {
1338 let event_cache_store =
1339 get_event_cache_store().await.unwrap().into_event_cache_store();
1340 event_cache_store.test_filter_duplicated_events().await;
1341 }
1342
1343 #[async_test]
1344 async fn test_find_event() {
1345 let event_cache_store =
1346 get_event_cache_store().await.unwrap().into_event_cache_store();
1347 event_cache_store.test_find_event().await;
1348 }
1349
1350 #[async_test]
1351 async fn test_find_event_relations() {
1352 let event_cache_store =
1353 get_event_cache_store().await.unwrap().into_event_cache_store();
1354 event_cache_store.test_find_event_relations().await;
1355 }
1356
1357 #[async_test]
1358 async fn test_get_room_events() {
1359 let event_cache_store =
1360 get_event_cache_store().await.unwrap().into_event_cache_store();
1361 event_cache_store.test_get_room_events().await;
1362 }
1363
1364 #[async_test]
1365 async fn test_get_room_events_filtered() {
1366 let event_cache_store =
1367 get_event_cache_store().await.unwrap().into_event_cache_store();
1368 event_cache_store.test_get_room_events_filtered().await;
1369 }
1370
1371 #[async_test]
1372 async fn test_save_event() {
1373 let event_cache_store =
1374 get_event_cache_store().await.unwrap().into_event_cache_store();
1375 event_cache_store.test_save_event().await;
1376 }
1377
1378 #[async_test]
1379 async fn test_thread_vs_room_linked_chunk() {
1380 let event_cache_store =
1381 get_event_cache_store().await.unwrap().into_event_cache_store();
1382 event_cache_store.test_thread_vs_room_linked_chunk().await;
1383 }
1384 }
1385 };
1386}
1387
1388#[allow(unused_macros)]
1391#[macro_export]
1392macro_rules! event_cache_store_integration_tests_time {
1393 () => {
1394 mod event_cache_store_integration_tests_time {
1395 use std::time::Duration;
1396
1397 #[cfg(all(target_family = "wasm", target_os = "unknown"))]
1398 use gloo_timers::future::sleep;
1399 use matrix_sdk_test::async_test;
1400 #[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
1401 use tokio::time::sleep;
1402 use $crate::event_cache::store::IntoEventCacheStore;
1403
1404 use super::get_event_cache_store;
1405
1406 #[async_test]
1407 async fn test_lease_locks() {
1408 let store = get_event_cache_store().await.unwrap().into_event_cache_store();
1409
1410 let acquired0 = store.try_take_leased_lock(0, "key", "alice").await.unwrap();
1411 assert!(acquired0);
1412
1413 let acquired2 = store.try_take_leased_lock(300, "key", "alice").await.unwrap();
1415 assert!(acquired2);
1416
1417 let acquired3 = store.try_take_leased_lock(300, "key", "alice").await.unwrap();
1419 assert!(acquired3);
1420
1421 let acquired4 = store.try_take_leased_lock(300, "key", "bob").await.unwrap();
1423 assert!(!acquired4);
1424
1425 let acquired5 = store.try_take_leased_lock(300, "key", "bob").await.unwrap();
1427 assert!(!acquired5);
1428
1429 sleep(Duration::from_millis(50)).await;
1431
1432 let acquired55 = store.try_take_leased_lock(300, "key", "bob").await.unwrap();
1434 assert!(!acquired55);
1435
1436 sleep(Duration::from_millis(250)).await;
1438
1439 let acquired6 = store.try_take_leased_lock(0, "key", "bob").await.unwrap();
1441 assert!(acquired6);
1442
1443 sleep(Duration::from_millis(1)).await;
1444
1445 let acquired7 = store.try_take_leased_lock(0, "key", "alice").await.unwrap();
1447 assert!(acquired7);
1448
1449 sleep(Duration::from_millis(1)).await;
1450
1451 let acquired8 = store.try_take_leased_lock(300, "key", "bob").await.unwrap();
1453 assert!(acquired8);
1454
1455 let acquired9 = store.try_take_leased_lock(300, "key", "alice").await.unwrap();
1457 assert!(!acquired9);
1458
1459 let acquired10 = store.try_take_leased_lock(300, "key", "bob").await.unwrap();
1461 assert!(acquired10);
1462 }
1463 }
1464 };
1465}