1use std::{collections::BTreeMap, sync::Arc};
18
19use assert_matches::assert_matches;
20use assert_matches2::assert_let;
21use matrix_sdk_common::{
22 deserialized_responses::{
23 AlgorithmInfo, DecryptedRoomEvent, EncryptionInfo, TimelineEvent, TimelineEventKind,
24 VerificationState,
25 },
26 linked_chunk::{
27 ChunkContent, ChunkIdentifier as CId, LinkedChunkId, Position, Update, lazy_loader,
28 },
29};
30use matrix_sdk_test::{ALICE, DEFAULT_TEST_ROOM_ID, event_factory::EventFactory};
31use ruma::{
32 EventId, RoomId, event_id,
33 events::{
34 AnyMessageLikeEvent, AnyTimelineEvent, relation::RelationType,
35 room::message::RoomMessageEventContentWithoutRelation,
36 },
37 push::Action,
38 room_id,
39};
40
41use super::DynEventCacheStore;
42use crate::event_cache::{Gap, store::DEFAULT_CHUNK_CAPACITY};
43
44pub fn make_test_event(room_id: &RoomId, content: &str) -> TimelineEvent {
49 make_test_event_with_event_id(room_id, content, None)
50}
51
52pub fn make_test_event_with_event_id(
54 room_id: &RoomId,
55 content: &str,
56 event_id: Option<&EventId>,
57) -> TimelineEvent {
58 let encryption_info = Arc::new(EncryptionInfo {
59 sender: (*ALICE).into(),
60 sender_device: None,
61 algorithm_info: AlgorithmInfo::MegolmV1AesSha2 {
62 curve25519_key: "1337".to_owned(),
63 sender_claimed_keys: Default::default(),
64 session_id: Some("mysessionid9".to_owned()),
65 },
66 verification_state: VerificationState::Verified,
67 });
68
69 let mut builder = EventFactory::new().text_msg(content).room(room_id).sender(*ALICE);
70 if let Some(event_id) = event_id {
71 builder = builder.event_id(event_id);
72 }
73 let event = builder.into_raw();
74
75 TimelineEvent::from_decrypted(
76 DecryptedRoomEvent { event, encryption_info, unsigned_encryption_info: None },
77 Some(vec![Action::Notify]),
78 )
79}
80
81#[track_caller]
86pub fn check_test_event(event: &TimelineEvent, text: &str) {
87 let actions = event.push_actions().unwrap();
89 assert_eq!(actions.len(), 1);
90 assert_matches!(&actions[0], Action::Notify);
91
92 assert_matches!(&event.kind, TimelineEventKind::Decrypted(d) => {
94 assert_eq!(d.encryption_info.sender, *ALICE);
96 assert_matches!(&d.encryption_info.algorithm_info, AlgorithmInfo::MegolmV1AesSha2 { curve25519_key, .. } => {
97 assert_eq!(curve25519_key, "1337");
98 });
99
100 let deserialized = d.event.deserialize().unwrap();
102 assert_matches!(deserialized, AnyTimelineEvent::MessageLike(AnyMessageLikeEvent::RoomMessage(msg)) => {
103 assert_eq!(msg.as_original().unwrap().content.body(), text);
104 });
105 });
106}
107
108#[allow(async_fn_in_trait)]
113pub trait EventCacheStoreIntegrationTests {
114 async fn test_handle_updates_and_rebuild_linked_chunk(&self);
117
118 async fn test_linked_chunk_incremental_loading(&self);
121
122 async fn test_rebuild_empty_linked_chunk(&self);
125
126 async fn test_load_all_chunks_metadata(&self);
128
129 async fn test_clear_all_linked_chunks(&self);
131
132 async fn test_remove_room(&self);
134
135 async fn test_filter_duplicated_events(&self);
137
138 async fn test_find_event(&self);
140
141 async fn test_find_event_relations(&self);
143
144 async fn test_get_room_events(&self);
146
147 async fn test_save_event(&self);
149
150 async fn test_thread_vs_room_linked_chunk(&self);
153}
154
155impl EventCacheStoreIntegrationTests for DynEventCacheStore {
156 async fn test_handle_updates_and_rebuild_linked_chunk(&self) {
157 let room_id = room_id!("!r0:matrix.org");
158 let linked_chunk_id = LinkedChunkId::Room(room_id);
159
160 self.handle_linked_chunk_updates(
161 linked_chunk_id,
162 vec![
163 Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
165 Update::PushItems {
167 at: Position::new(CId::new(0), 0),
168 items: vec![
169 make_test_event(room_id, "hello"),
170 make_test_event(room_id, "world"),
171 ],
172 },
173 Update::NewGapChunk {
175 previous: Some(CId::new(0)),
176 new: CId::new(1),
177 next: None,
178 gap: Gap { prev_token: "parmesan".to_owned() },
179 },
180 Update::NewItemsChunk { previous: Some(CId::new(1)), new: CId::new(2), next: None },
182 Update::PushItems {
184 at: Position::new(CId::new(2), 0),
185 items: vec![make_test_event(room_id, "sup")],
186 },
187 ],
188 )
189 .await
190 .unwrap();
191
192 let lc = lazy_loader::from_all_chunks::<3, _, _>(
194 self.load_all_chunks(linked_chunk_id).await.unwrap(),
195 )
196 .unwrap()
197 .unwrap();
198
199 let mut chunks = lc.chunks();
200
201 {
202 let first = chunks.next().unwrap();
203 assert_eq!(first.identifier(), CId::new(0));
206
207 assert_matches!(first.content(), ChunkContent::Items(events) => {
208 assert_eq!(events.len(), 2);
209 check_test_event(&events[0], "hello");
210 check_test_event(&events[1], "world");
211 });
212 }
213
214 {
215 let second = chunks.next().unwrap();
216 assert_eq!(second.identifier(), CId::new(1));
217
218 assert_matches!(second.content(), ChunkContent::Gap(gap) => {
219 assert_eq!(gap.prev_token, "parmesan");
220 });
221 }
222
223 {
224 let third = chunks.next().unwrap();
225 assert_eq!(third.identifier(), CId::new(2));
226
227 assert_matches!(third.content(), ChunkContent::Items(events) => {
228 assert_eq!(events.len(), 1);
229 check_test_event(&events[0], "sup");
230 });
231 }
232
233 assert!(chunks.next().is_none());
234 }
235
236 async fn test_load_all_chunks_metadata(&self) {
237 let room_id = room_id!("!r0:matrix.org");
238 let linked_chunk_id = LinkedChunkId::Room(room_id);
239
240 self.handle_linked_chunk_updates(
241 linked_chunk_id,
242 vec![
243 Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
245 Update::PushItems {
247 at: Position::new(CId::new(0), 0),
248 items: vec![
249 make_test_event(room_id, "hello"),
250 make_test_event(room_id, "world"),
251 ],
252 },
253 Update::NewGapChunk {
255 previous: Some(CId::new(0)),
256 new: CId::new(1),
257 next: None,
258 gap: Gap { prev_token: "parmesan".to_owned() },
259 },
260 Update::NewItemsChunk { previous: Some(CId::new(1)), new: CId::new(2), next: None },
262 Update::PushItems {
264 at: Position::new(CId::new(2), 0),
265 items: vec![make_test_event(room_id, "sup")],
266 },
267 Update::NewItemsChunk { previous: Some(CId::new(2)), new: CId::new(3), next: None },
269 ],
270 )
271 .await
272 .unwrap();
273
274 let metas = self.load_all_chunks_metadata(linked_chunk_id).await.unwrap();
275 assert_eq!(metas.len(), 4);
276
277 assert_eq!(metas[0].identifier, CId::new(0));
279 assert_eq!(metas[0].previous, None);
280 assert_eq!(metas[0].next, Some(CId::new(1)));
281 assert_eq!(metas[0].num_items, 2);
282
283 assert_eq!(metas[1].identifier, CId::new(1));
285 assert_eq!(metas[1].previous, Some(CId::new(0)));
286 assert_eq!(metas[1].next, Some(CId::new(2)));
287 assert_eq!(metas[1].num_items, 0);
288
289 assert_eq!(metas[2].identifier, CId::new(2));
291 assert_eq!(metas[2].previous, Some(CId::new(1)));
292 assert_eq!(metas[2].next, Some(CId::new(3)));
293 assert_eq!(metas[2].num_items, 1);
294
295 assert_eq!(metas[3].identifier, CId::new(3));
297 assert_eq!(metas[3].previous, Some(CId::new(2)));
298 assert_eq!(metas[3].next, None);
299 assert_eq!(metas[3].num_items, 0);
300 }
301
302 async fn test_linked_chunk_incremental_loading(&self) {
303 let room_id = room_id!("!r0:matrix.org");
304 let linked_chunk_id = LinkedChunkId::Room(room_id);
305 let event = |msg: &str| make_test_event(room_id, msg);
306
307 {
309 let (last_chunk, chunk_identifier_generator) =
310 self.load_last_chunk(linked_chunk_id).await.unwrap();
311
312 assert!(last_chunk.is_none());
313 assert_eq!(chunk_identifier_generator.current(), 0);
314 }
315
316 self.handle_linked_chunk_updates(
317 linked_chunk_id,
318 vec![
319 Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
321 Update::PushItems {
323 at: Position::new(CId::new(0), 0),
324 items: vec![event("a"), event("b")],
325 },
326 Update::NewGapChunk {
328 previous: Some(CId::new(0)),
329 new: CId::new(1),
330 next: None,
331 gap: Gap { prev_token: "morbier".to_owned() },
332 },
333 Update::NewItemsChunk { previous: Some(CId::new(1)), new: CId::new(2), next: None },
335 Update::PushItems {
337 at: Position::new(CId::new(2), 0),
338 items: vec![event("c"), event("d"), event("e")],
339 },
340 ],
341 )
342 .await
343 .unwrap();
344
345 let mut linked_chunk = {
347 let (last_chunk, chunk_identifier_generator) =
348 self.load_last_chunk(linked_chunk_id).await.unwrap();
349
350 assert_eq!(chunk_identifier_generator.current(), 2);
351
352 let linked_chunk = lazy_loader::from_last_chunk::<DEFAULT_CHUNK_CAPACITY, _, _>(
353 last_chunk,
354 chunk_identifier_generator,
355 )
356 .unwrap() .unwrap(); let mut rchunks = linked_chunk.rchunks();
360
361 assert_matches!(rchunks.next(), Some(chunk) => {
363 assert_eq!(chunk.identifier(), 2);
364 assert_eq!(chunk.lazy_previous(), Some(CId::new(1)));
365
366 assert_matches!(chunk.content(), ChunkContent::Items(events) => {
367 assert_eq!(events.len(), 3);
368 check_test_event(&events[0], "c");
369 check_test_event(&events[1], "d");
370 check_test_event(&events[2], "e");
371 });
372 });
373
374 assert!(rchunks.next().is_none());
375
376 linked_chunk
377 };
378
379 {
381 let first_chunk = linked_chunk.chunks().next().unwrap().identifier();
382 let previous_chunk =
383 self.load_previous_chunk(linked_chunk_id, first_chunk).await.unwrap().unwrap();
384
385 lazy_loader::insert_new_first_chunk(&mut linked_chunk, previous_chunk).unwrap();
386
387 let mut rchunks = linked_chunk.rchunks();
388
389 assert_matches!(rchunks.next(), Some(chunk) => {
391 assert_eq!(chunk.identifier(), 2);
392 assert!(chunk.lazy_previous().is_none());
393
394 assert_matches!(chunk.content(), ChunkContent::Items(events) => {
396 assert_eq!(events.len(), 3);
397 check_test_event(&events[0], "c");
398 check_test_event(&events[1], "d");
399 check_test_event(&events[2], "e");
400 });
401 });
402
403 assert_matches!(rchunks.next(), Some(chunk) => {
405 assert_eq!(chunk.identifier(), 1);
406 assert_eq!(chunk.lazy_previous(), Some(CId::new(0)));
407
408 assert_matches!(chunk.content(), ChunkContent::Gap(gap) => {
409 assert_eq!(gap.prev_token, "morbier");
410 });
411 });
412
413 assert!(rchunks.next().is_none());
414 }
415
416 {
418 let first_chunk = linked_chunk.chunks().next().unwrap().identifier();
419 let previous_chunk =
420 self.load_previous_chunk(linked_chunk_id, first_chunk).await.unwrap().unwrap();
421
422 lazy_loader::insert_new_first_chunk(&mut linked_chunk, previous_chunk).unwrap();
423
424 let mut rchunks = linked_chunk.rchunks();
425
426 assert_matches!(rchunks.next(), Some(chunk) => {
428 assert_eq!(chunk.identifier(), 2);
429 assert!(chunk.lazy_previous().is_none());
430
431 assert_matches!(chunk.content(), ChunkContent::Items(events) => {
433 assert_eq!(events.len(), 3);
434 check_test_event(&events[0], "c");
435 check_test_event(&events[1], "d");
436 check_test_event(&events[2], "e");
437 });
438 });
439
440 assert_matches!(rchunks.next(), Some(chunk) => {
442 assert_eq!(chunk.identifier(), 1);
443 assert!(chunk.lazy_previous().is_none());
444
445 assert_matches!(chunk.content(), ChunkContent::Gap(gap) => {
447 assert_eq!(gap.prev_token, "morbier");
448 });
449 });
450
451 assert_matches!(rchunks.next(), Some(chunk) => {
453 assert_eq!(chunk.identifier(), 0);
454 assert!(chunk.lazy_previous().is_none());
455
456 assert_matches!(chunk.content(), ChunkContent::Items(events) => {
457 assert_eq!(events.len(), 2);
458 check_test_event(&events[0], "a");
459 check_test_event(&events[1], "b");
460 });
461 });
462
463 assert!(rchunks.next().is_none());
464 }
465
466 {
468 let first_chunk = linked_chunk.chunks().next().unwrap().identifier();
469 let previous_chunk =
470 self.load_previous_chunk(linked_chunk_id, first_chunk).await.unwrap();
471
472 assert!(previous_chunk.is_none());
473 }
474
475 {
478 let mut chunks = linked_chunk.chunks();
479
480 assert_matches!(chunks.next(), Some(chunk) => {
482 assert_eq!(chunk.identifier(), 0);
483 assert!(chunk.lazy_previous().is_none());
484
485 assert_matches!(chunk.content(), ChunkContent::Items(events) => {
486 assert_eq!(events.len(), 2);
487 check_test_event(&events[0], "a");
488 check_test_event(&events[1], "b");
489 });
490 });
491
492 assert_matches!(chunks.next(), Some(chunk) => {
494 assert_eq!(chunk.identifier(), 1);
495 assert!(chunk.lazy_previous().is_none());
496
497 assert_matches!(chunk.content(), ChunkContent::Gap(gap) => {
498 assert_eq!(gap.prev_token, "morbier");
499 });
500 });
501
502 assert_matches!(chunks.next(), Some(chunk) => {
504 assert_eq!(chunk.identifier(), 2);
505 assert!(chunk.lazy_previous().is_none());
506
507 assert_matches!(chunk.content(), ChunkContent::Items(events) => {
508 assert_eq!(events.len(), 3);
509 check_test_event(&events[0], "c");
510 check_test_event(&events[1], "d");
511 check_test_event(&events[2], "e");
512 });
513 });
514
515 assert!(chunks.next().is_none());
516 }
517 }
518
519 async fn test_rebuild_empty_linked_chunk(&self) {
520 let linked_chunk = lazy_loader::from_all_chunks::<3, _, _>(
522 self.load_all_chunks(LinkedChunkId::Room(&DEFAULT_TEST_ROOM_ID)).await.unwrap(),
523 )
524 .unwrap();
525 assert!(linked_chunk.is_none());
526 }
527
528 async fn test_clear_all_linked_chunks(&self) {
529 let r0 = room_id!("!r0:matrix.org");
530 let linked_chunk_id0 = LinkedChunkId::Room(r0);
531 let r1 = room_id!("!r1:matrix.org");
532 let linked_chunk_id1 = LinkedChunkId::Room(r1);
533
534 self.handle_linked_chunk_updates(
536 linked_chunk_id0,
537 vec![
538 Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
540 Update::PushItems {
542 at: Position::new(CId::new(0), 0),
543 items: vec![make_test_event(r0, "hello"), make_test_event(r0, "world")],
544 },
545 ],
546 )
547 .await
548 .unwrap();
549
550 self.handle_linked_chunk_updates(
552 linked_chunk_id1,
553 vec![
554 Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
556 Update::NewGapChunk {
558 previous: Some(CId::new(0)),
559 new: CId::new(1),
560 next: None,
561 gap: Gap { prev_token: "bleu d'auvergne".to_owned() },
562 },
563 Update::NewItemsChunk { previous: Some(CId::new(1)), new: CId::new(2), next: None },
565 Update::PushItems {
567 at: Position::new(CId::new(2), 0),
568 items: vec![make_test_event(r0, "yummy")],
569 },
570 ],
571 )
572 .await
573 .unwrap();
574
575 assert!(
577 lazy_loader::from_all_chunks::<3, _, _>(
578 self.load_all_chunks(linked_chunk_id0).await.unwrap()
579 )
580 .unwrap()
581 .is_some()
582 );
583 assert!(
584 lazy_loader::from_all_chunks::<3, _, _>(
585 self.load_all_chunks(linked_chunk_id1).await.unwrap()
586 )
587 .unwrap()
588 .is_some()
589 );
590
591 self.clear_all_linked_chunks().await.unwrap();
593
594 assert!(
596 lazy_loader::from_all_chunks::<3, _, _>(
597 self.load_all_chunks(linked_chunk_id0).await.unwrap()
598 )
599 .unwrap()
600 .is_none()
601 );
602 assert!(
603 lazy_loader::from_all_chunks::<3, _, _>(
604 self.load_all_chunks(linked_chunk_id1).await.unwrap()
605 )
606 .unwrap()
607 .is_none()
608 );
609 }
610
611 async fn test_remove_room(&self) {
612 let r0 = room_id!("!r0:matrix.org");
613 let linked_chunk_id0 = LinkedChunkId::Room(r0);
614 let r1 = room_id!("!r1:matrix.org");
615 let linked_chunk_id1 = LinkedChunkId::Room(r1);
616
617 self.handle_linked_chunk_updates(
619 linked_chunk_id0,
620 vec![
621 Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
623 Update::PushItems {
625 at: Position::new(CId::new(0), 0),
626 items: vec![make_test_event(r0, "hello"), make_test_event(r0, "world")],
627 },
628 ],
629 )
630 .await
631 .unwrap();
632
633 self.handle_linked_chunk_updates(
635 linked_chunk_id1,
636 vec![
637 Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
639 Update::PushItems {
641 at: Position::new(CId::new(0), 0),
642 items: vec![make_test_event(r0, "yummy")],
643 },
644 ],
645 )
646 .await
647 .unwrap();
648
649 self.remove_room(r0).await.unwrap();
651
652 let r0_linked_chunk = self.load_all_chunks(linked_chunk_id0).await.unwrap();
654 assert!(r0_linked_chunk.is_empty());
655
656 let r1_linked_chunk = self.load_all_chunks(linked_chunk_id1).await.unwrap();
658 assert!(!r1_linked_chunk.is_empty());
659 }
660
661 async fn test_filter_duplicated_events(&self) {
662 let room_id = room_id!("!r0:matrix.org");
663 let linked_chunk_id = LinkedChunkId::Room(room_id);
664 let another_room_id = room_id!("!r1:matrix.org");
665 let another_linked_chunk_id = LinkedChunkId::Room(another_room_id);
666 let event = |msg: &str| make_test_event(room_id, msg);
667
668 let event_comte = event("comté");
669 let event_brigand = event("brigand du jorat");
670 let event_raclette = event("raclette");
671 let event_morbier = event("morbier");
672 let event_gruyere = event("gruyère");
673 let event_tome = event("tome");
674 let event_mont_dor = event("mont d'or");
675
676 self.handle_linked_chunk_updates(
677 linked_chunk_id,
678 vec![
679 Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
680 Update::PushItems {
681 at: Position::new(CId::new(0), 0),
682 items: vec![event_comte.clone(), event_brigand.clone()],
683 },
684 Update::NewGapChunk {
685 previous: Some(CId::new(0)),
686 new: CId::new(1),
687 next: None,
688 gap: Gap { prev_token: "brillat-savarin".to_owned() },
689 },
690 Update::NewItemsChunk { previous: Some(CId::new(1)), new: CId::new(2), next: None },
691 Update::PushItems {
692 at: Position::new(CId::new(2), 0),
693 items: vec![event_morbier.clone(), event_mont_dor.clone()],
694 },
695 ],
696 )
697 .await
698 .unwrap();
699
700 self.handle_linked_chunk_updates(
703 another_linked_chunk_id,
704 vec![
705 Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
706 Update::PushItems {
707 at: Position::new(CId::new(0), 0),
708 items: vec![event_tome.clone()],
709 },
710 ],
711 )
712 .await
713 .unwrap();
714
715 let duplicated_events = BTreeMap::from_iter(
716 self.filter_duplicated_events(
717 linked_chunk_id,
718 vec![
719 event_comte.event_id().unwrap().to_owned(),
720 event_raclette.event_id().unwrap().to_owned(),
721 event_morbier.event_id().unwrap().to_owned(),
722 event_gruyere.event_id().unwrap().to_owned(),
723 event_tome.event_id().unwrap().to_owned(),
724 event_mont_dor.event_id().unwrap().to_owned(),
725 ],
726 )
727 .await
728 .unwrap(),
729 );
730
731 assert_eq!(duplicated_events.len(), 3);
732
733 assert_eq!(
734 *duplicated_events.get(&event_comte.event_id().unwrap()).unwrap(),
735 Position::new(CId::new(0), 0)
736 );
737 assert_eq!(
738 *duplicated_events.get(&event_morbier.event_id().unwrap()).unwrap(),
739 Position::new(CId::new(2), 0)
740 );
741 assert_eq!(
742 *duplicated_events.get(&event_mont_dor.event_id().unwrap()).unwrap(),
743 Position::new(CId::new(2), 1)
744 );
745 }
746
747 async fn test_find_event(&self) {
748 let room_id = room_id!("!r0:matrix.org");
749 let another_room_id = room_id!("!r1:matrix.org");
750 let another_linked_chunk_id = LinkedChunkId::Room(another_room_id);
751 let event = |msg: &str| make_test_event(room_id, msg);
752
753 let event_comte = event("comté");
754 let event_gruyere = event("gruyère");
755
756 self.handle_linked_chunk_updates(
758 LinkedChunkId::Room(room_id),
759 vec![
760 Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
761 Update::PushItems {
762 at: Position::new(CId::new(0), 0),
763 items: vec![event_comte.clone()],
764 },
765 ],
766 )
767 .await
768 .unwrap();
769
770 self.handle_linked_chunk_updates(
772 another_linked_chunk_id,
773 vec![
774 Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
775 Update::PushItems {
776 at: Position::new(CId::new(0), 0),
777 items: vec![event_gruyere.clone()],
778 },
779 ],
780 )
781 .await
782 .unwrap();
783
784 let event = self
786 .find_event(room_id, event_comte.event_id().unwrap().as_ref())
787 .await
788 .expect("failed to query for finding an event")
789 .expect("failed to find an event");
790
791 assert_eq!(event.event_id(), event_comte.event_id());
792
793 assert!(
795 self.find_event(room_id, event_gruyere.event_id().unwrap().as_ref())
796 .await
797 .expect("failed to query for finding an event")
798 .is_none()
799 );
800
801 self.clear_all_linked_chunks().await.expect("failed to clear all rooms chunks");
803 assert!(
804 self.find_event(room_id, event_comte.event_id().unwrap().as_ref())
805 .await
806 .expect("failed to query for finding an event")
807 .is_none()
808 );
809 }
810
811 async fn test_find_event_relations(&self) {
812 let room_id = room_id!("!r0:matrix.org");
813 let another_room_id = room_id!("!r1:matrix.org");
814
815 let f = EventFactory::new().room(room_id).sender(*ALICE);
816
817 let eid1 = event_id!("$event1:matrix.org");
819 let e1 = f.text_msg("comter").event_id(eid1).into_event();
820
821 let edit_eid1 = event_id!("$edit_event1:matrix.org");
822 let edit_e1 = f
823 .text_msg("* comté")
824 .event_id(edit_eid1)
825 .edit(eid1, RoomMessageEventContentWithoutRelation::text_plain("comté"))
826 .into_event();
827
828 let reaction_eid1 = event_id!("$reaction_event1:matrix.org");
829 let reaction_e1 = f.reaction(eid1, "👍").event_id(reaction_eid1).into_event();
830
831 let eid2 = event_id!("$event2:matrix.org");
832 let e2 = f.text_msg("galette saucisse").event_id(eid2).into_event();
833
834 let f = f.room(another_room_id);
836
837 let eid3 = event_id!("$event3:matrix.org");
838 let e3 = f.text_msg("gruyère").event_id(eid3).into_event();
839
840 let reaction_eid3 = event_id!("$reaction_event3:matrix.org");
841 let reaction_e3 = f.reaction(eid3, "👍").event_id(reaction_eid3).into_event();
842
843 self.save_event(room_id, e1).await.unwrap();
845 self.save_event(room_id, edit_e1).await.unwrap();
846 self.save_event(room_id, reaction_e1.clone()).await.unwrap();
847 self.save_event(room_id, e2).await.unwrap();
848 self.save_event(another_room_id, e3).await.unwrap();
849 self.save_event(another_room_id, reaction_e3).await.unwrap();
850
851 let relations = self.find_event_relations(room_id, eid1, None).await.unwrap();
853 assert_eq!(relations.len(), 2);
854 assert!(
856 relations
857 .iter()
858 .any(|(ev, pos)| ev.event_id().as_deref() == Some(edit_eid1) && pos.is_none())
859 );
860 assert!(
861 relations
862 .iter()
863 .any(|(ev, pos)| ev.event_id().as_deref() == Some(reaction_eid1) && pos.is_none())
864 );
865
866 let relations = self
868 .find_event_relations(room_id, eid1, Some(&[RelationType::Replacement]))
869 .await
870 .unwrap();
871 assert_eq!(relations.len(), 1);
872 assert_eq!(relations[0].0.event_id().as_deref(), Some(edit_eid1));
873
874 let relations = self
875 .find_event_relations(
876 room_id,
877 eid1,
878 Some(&[RelationType::Replacement, RelationType::Annotation]),
879 )
880 .await
881 .unwrap();
882 assert_eq!(relations.len(), 2);
883 assert!(relations.iter().any(|r| r.0.event_id().as_deref() == Some(edit_eid1)));
884 assert!(relations.iter().any(|r| r.0.event_id().as_deref() == Some(reaction_eid1)));
885
886 let relations = self
888 .find_event_relations(another_room_id, eid1, Some(&[RelationType::Replacement]))
889 .await
890 .unwrap();
891 assert!(relations.is_empty());
892
893 self.handle_linked_chunk_updates(
898 LinkedChunkId::Room(room_id),
899 vec![
900 Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
901 Update::PushItems { at: Position::new(CId::new(0), 0), items: vec![reaction_e1] },
902 ],
903 )
904 .await
905 .unwrap();
906
907 let relations = self.find_event_relations(room_id, eid1, None).await.unwrap();
910
911 assert!(relations.iter().any(|(ev, pos)| {
913 ev.event_id().as_deref() == Some(reaction_eid1)
914 && *pos == Some(Position::new(CId::new(0), 0))
915 }));
916
917 assert!(
919 relations
920 .iter()
921 .any(|(ev, pos)| ev.event_id().as_deref() == Some(edit_eid1) && pos.is_none())
922 );
923 }
924
925 async fn test_get_room_events(&self) {
926 let room_id = room_id!("!r0:matrix.org");
927 let another_room_id = room_id!("!r1:matrix.org");
928 let linked_chunk_id = LinkedChunkId::Room(room_id);
929 let another_linked_chunk_id = LinkedChunkId::Room(another_room_id);
930 let event = |msg: &str| make_test_event(room_id, msg);
931
932 let event_comte = event("comté");
933 let event_gruyere = event("gruyère");
934 let event_stilton = event("stilton");
935
936 self.handle_linked_chunk_updates(
938 linked_chunk_id,
939 vec![
940 Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
941 Update::PushItems {
942 at: Position::new(CId::new(0), 0),
943 items: vec![event_comte.clone(), event_gruyere.clone()],
944 },
945 ],
946 )
947 .await
948 .unwrap();
949
950 self.handle_linked_chunk_updates(
952 another_linked_chunk_id,
953 vec![
954 Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
955 Update::PushItems {
956 at: Position::new(CId::new(0), 0),
957 items: vec![event_stilton.clone()],
958 },
959 ],
960 )
961 .await
962 .unwrap();
963
964 let events = self.get_room_events(room_id).await.expect("failed to query for room events");
966
967 assert_eq!(events.len(), 2);
968
969 let got_ids: Vec<_> = events.into_iter().map(|ev| ev.event_id()).collect();
970 let expected_ids = vec![event_comte.event_id(), event_gruyere.event_id()];
971
972 for expected in expected_ids {
973 assert!(
974 got_ids.contains(&expected),
975 "Expected event {expected:?} not in got events: {got_ids:?}."
976 );
977 }
978 }
979
980 async fn test_save_event(&self) {
981 let room_id = room_id!("!r0:matrix.org");
982 let another_room_id = room_id!("!r1:matrix.org");
983
984 let event = |msg: &str| make_test_event(room_id, msg);
985 let event_comte = event("comté");
986 let event_gruyere = event("gruyère");
987
988 self.save_event(room_id, event_comte.clone()).await.unwrap();
990
991 self.save_event(another_room_id, event_gruyere.clone()).await.unwrap();
993
994 let event = self
996 .find_event(room_id, event_comte.event_id().unwrap().as_ref())
997 .await
998 .expect("failed to query for finding an event")
999 .expect("failed to find an event");
1000 assert_eq!(event.event_id(), event_comte.event_id());
1001
1002 let event = self
1003 .find_event(another_room_id, event_gruyere.event_id().unwrap().as_ref())
1004 .await
1005 .expect("failed to query for finding an event")
1006 .expect("failed to find an event");
1007 assert_eq!(event.event_id(), event_gruyere.event_id());
1008
1009 assert!(
1011 self.find_event(another_room_id, event_comte.event_id().unwrap().as_ref())
1012 .await
1013 .expect("failed to query for finding an event")
1014 .is_none()
1015 );
1016 assert!(
1017 self.find_event(room_id, event_gruyere.event_id().unwrap().as_ref())
1018 .await
1019 .expect("failed to query for finding an event")
1020 .is_none()
1021 );
1022 }
1023
1024 async fn test_thread_vs_room_linked_chunk(&self) {
1025 let room_id = room_id!("!r0:matrix.org");
1026
1027 let event = |msg: &str| make_test_event(room_id, msg);
1028
1029 let thread1_ev = event("comté");
1030 let thread2_ev = event("gruyère");
1031 let thread2_ev2 = event("beaufort");
1032 let room_ev = event("brillat savarin triple crème");
1033
1034 let thread_root1 = event("thread1");
1035 let thread_root2 = event("thread2");
1036
1037 self.handle_linked_chunk_updates(
1039 LinkedChunkId::Thread(room_id, thread_root1.event_id().unwrap().as_ref()),
1040 vec![
1041 Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
1042 Update::PushItems {
1043 at: Position::new(CId::new(0), 0),
1044 items: vec![thread1_ev.clone()],
1045 },
1046 ],
1047 )
1048 .await
1049 .unwrap();
1050
1051 self.handle_linked_chunk_updates(
1053 LinkedChunkId::Thread(room_id, thread_root2.event_id().unwrap().as_ref()),
1054 vec![
1055 Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
1056 Update::PushItems {
1057 at: Position::new(CId::new(0), 0),
1058 items: vec![thread2_ev.clone(), thread2_ev2.clone()],
1059 },
1060 ],
1061 )
1062 .await
1063 .unwrap();
1064
1065 self.handle_linked_chunk_updates(
1067 LinkedChunkId::Room(room_id),
1068 vec![
1069 Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
1070 Update::PushItems {
1071 at: Position::new(CId::new(0), 0),
1072 items: vec![room_ev.clone()],
1073 },
1074 ],
1075 )
1076 .await
1077 .unwrap();
1078
1079 self.find_event(room_id, thread2_ev.event_id().unwrap().as_ref())
1081 .await
1082 .expect("failed to query for finding an event")
1083 .expect("failed to find thread1_ev");
1084
1085 self.find_event(room_id, thread2_ev.event_id().unwrap().as_ref())
1086 .await
1087 .expect("failed to query for finding an event")
1088 .expect("failed to find thread2_ev");
1089
1090 self.find_event(room_id, thread2_ev2.event_id().unwrap().as_ref())
1091 .await
1092 .expect("failed to query for finding an event")
1093 .expect("failed to find thread2_ev2");
1094
1095 self.find_event(room_id, room_ev.event_id().unwrap().as_ref())
1096 .await
1097 .expect("failed to query for finding an event")
1098 .expect("failed to find room_ev");
1099
1100 let dups = self
1102 .filter_duplicated_events(
1103 LinkedChunkId::Thread(room_id, thread_root1.event_id().unwrap().as_ref()),
1104 vec![
1105 thread1_ev.event_id().unwrap().to_owned(),
1106 room_ev.event_id().unwrap().to_owned(),
1107 ],
1108 )
1109 .await
1110 .unwrap();
1111 assert_eq!(dups.len(), 1);
1112 assert_eq!(dups[0].0, thread1_ev.event_id().unwrap());
1113
1114 let all_chunks = self
1116 .load_all_chunks(LinkedChunkId::Thread(
1117 room_id,
1118 thread_root2.event_id().unwrap().as_ref(),
1119 ))
1120 .await
1121 .unwrap();
1122 assert_eq!(all_chunks.len(), 1);
1123 assert_eq!(all_chunks[0].identifier, CId::new(0));
1124 assert_let!(ChunkContent::Items(observed_items) = all_chunks[0].content.clone());
1125 assert_eq!(observed_items.len(), 2);
1126 assert_eq!(observed_items[0].event_id(), thread2_ev.event_id());
1127 assert_eq!(observed_items[1].event_id(), thread2_ev2.event_id());
1128
1129 let metas = self
1132 .load_all_chunks_metadata(LinkedChunkId::Thread(
1133 room_id,
1134 thread_root2.event_id().unwrap().as_ref(),
1135 ))
1136 .await
1137 .unwrap();
1138 assert_eq!(metas.len(), 1);
1139 assert_eq!(metas[0].identifier, CId::new(0));
1140 assert_eq!(metas[0].num_items, 2);
1141
1142 let (last_chunk, _chunk_identifier_generator) = self
1144 .load_last_chunk(LinkedChunkId::Thread(
1145 room_id,
1146 thread_root1.event_id().unwrap().as_ref(),
1147 ))
1148 .await
1149 .unwrap();
1150 let last_chunk = last_chunk.unwrap();
1151 assert_eq!(last_chunk.identifier, CId::new(0));
1152 assert_let!(ChunkContent::Items(observed_items) = last_chunk.content);
1153 assert_eq!(observed_items.len(), 1);
1154 assert_eq!(observed_items[0].event_id(), thread1_ev.event_id());
1155 }
1156}
1157
1158#[allow(unused_macros, unused_extern_crates)]
1186#[macro_export]
1187macro_rules! event_cache_store_integration_tests {
1188 () => {
1189 mod event_cache_store_integration_tests {
1190 use matrix_sdk_test::async_test;
1191 use $crate::event_cache::store::{
1192 EventCacheStoreIntegrationTests, IntoEventCacheStore,
1193 };
1194
1195 use super::get_event_cache_store;
1196
1197 #[async_test]
1198 async fn test_handle_updates_and_rebuild_linked_chunk() {
1199 let event_cache_store =
1200 get_event_cache_store().await.unwrap().into_event_cache_store();
1201 event_cache_store.test_handle_updates_and_rebuild_linked_chunk().await;
1202 }
1203
1204 #[async_test]
1205 async fn test_linked_chunk_incremental_loading() {
1206 let event_cache_store =
1207 get_event_cache_store().await.unwrap().into_event_cache_store();
1208 event_cache_store.test_linked_chunk_incremental_loading().await;
1209 }
1210
1211 #[async_test]
1212 async fn test_rebuild_empty_linked_chunk() {
1213 let event_cache_store =
1214 get_event_cache_store().await.unwrap().into_event_cache_store();
1215 event_cache_store.test_rebuild_empty_linked_chunk().await;
1216 }
1217
1218 #[async_test]
1219 async fn test_load_all_chunks_metadata() {
1220 let event_cache_store =
1221 get_event_cache_store().await.unwrap().into_event_cache_store();
1222 event_cache_store.test_load_all_chunks_metadata().await;
1223 }
1224
1225 #[async_test]
1226 async fn test_clear_all_linked_chunks() {
1227 let event_cache_store =
1228 get_event_cache_store().await.unwrap().into_event_cache_store();
1229 event_cache_store.test_clear_all_linked_chunks().await;
1230 }
1231
1232 #[async_test]
1233 async fn test_remove_room() {
1234 let event_cache_store =
1235 get_event_cache_store().await.unwrap().into_event_cache_store();
1236 event_cache_store.test_remove_room().await;
1237 }
1238
1239 #[async_test]
1240 async fn test_filter_duplicated_events() {
1241 let event_cache_store =
1242 get_event_cache_store().await.unwrap().into_event_cache_store();
1243 event_cache_store.test_filter_duplicated_events().await;
1244 }
1245
1246 #[async_test]
1247 async fn test_find_event() {
1248 let event_cache_store =
1249 get_event_cache_store().await.unwrap().into_event_cache_store();
1250 event_cache_store.test_find_event().await;
1251 }
1252
1253 #[async_test]
1254 async fn test_find_event_relations() {
1255 let event_cache_store =
1256 get_event_cache_store().await.unwrap().into_event_cache_store();
1257 event_cache_store.test_find_event_relations().await;
1258 }
1259
1260 #[async_test]
1261 async fn test_get_room_events() {
1262 let event_cache_store =
1263 get_event_cache_store().await.unwrap().into_event_cache_store();
1264 event_cache_store.test_get_room_events().await;
1265 }
1266
1267 #[async_test]
1268 async fn test_save_event() {
1269 let event_cache_store =
1270 get_event_cache_store().await.unwrap().into_event_cache_store();
1271 event_cache_store.test_save_event().await;
1272 }
1273
1274 #[async_test]
1275 async fn test_thread_vs_room_linked_chunk() {
1276 let event_cache_store =
1277 get_event_cache_store().await.unwrap().into_event_cache_store();
1278 event_cache_store.test_thread_vs_room_linked_chunk().await;
1279 }
1280 }
1281 };
1282}
1283
1284#[allow(unused_macros)]
1287#[macro_export]
1288macro_rules! event_cache_store_integration_tests_time {
1289 () => {
1290 mod event_cache_store_integration_tests_time {
1291 use std::time::Duration;
1292
1293 #[cfg(all(target_family = "wasm", target_os = "unknown"))]
1294 use gloo_timers::future::sleep;
1295 use matrix_sdk_test::async_test;
1296 #[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
1297 use tokio::time::sleep;
1298 use $crate::event_cache::store::IntoEventCacheStore;
1299
1300 use super::get_event_cache_store;
1301
1302 #[async_test]
1303 async fn test_lease_locks() {
1304 let store = get_event_cache_store().await.unwrap().into_event_cache_store();
1305
1306 let acquired0 = store.try_take_leased_lock(0, "key", "alice").await.unwrap();
1307 assert!(acquired0);
1308
1309 let acquired2 = store.try_take_leased_lock(300, "key", "alice").await.unwrap();
1311 assert!(acquired2);
1312
1313 let acquired3 = store.try_take_leased_lock(300, "key", "alice").await.unwrap();
1315 assert!(acquired3);
1316
1317 let acquired4 = store.try_take_leased_lock(300, "key", "bob").await.unwrap();
1319 assert!(!acquired4);
1320
1321 let acquired5 = store.try_take_leased_lock(300, "key", "bob").await.unwrap();
1323 assert!(!acquired5);
1324
1325 sleep(Duration::from_millis(50)).await;
1327
1328 let acquired55 = store.try_take_leased_lock(300, "key", "bob").await.unwrap();
1330 assert!(!acquired55);
1331
1332 sleep(Duration::from_millis(250)).await;
1334
1335 let acquired6 = store.try_take_leased_lock(0, "key", "bob").await.unwrap();
1337 assert!(acquired6);
1338
1339 sleep(Duration::from_millis(1)).await;
1340
1341 let acquired7 = store.try_take_leased_lock(0, "key", "alice").await.unwrap();
1343 assert!(acquired7);
1344
1345 sleep(Duration::from_millis(1)).await;
1346
1347 let acquired8 = store.try_take_leased_lock(300, "key", "bob").await.unwrap();
1349 assert!(acquired8);
1350
1351 let acquired9 = store.try_take_leased_lock(300, "key", "alice").await.unwrap();
1353 assert!(!acquired9);
1354
1355 let acquired10 = store.try_take_leased_lock(300, "key", "bob").await.unwrap();
1357 assert!(acquired10);
1358 }
1359 }
1360 };
1361}