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