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,
33 api::client::media::get_content_thumbnail::v3::Method,
34 event_id,
35 events::{
36 AnyMessageLikeEvent, AnyTimelineEvent,
37 relation::RelationType,
38 room::{MediaSource, message::RoomMessageEventContentWithoutRelation},
39 },
40 mxc_uri,
41 push::Action,
42 room_id, uint,
43};
44
45use super::{DynEventCacheStore, media::IgnoreMediaRetentionPolicy};
46use crate::{
47 event_cache::{Gap, store::DEFAULT_CHUNK_CAPACITY},
48 media::{MediaFormat, MediaRequestParameters, MediaThumbnailSettings},
49};
50
51pub fn make_test_event(room_id: &RoomId, content: &str) -> TimelineEvent {
56 make_test_event_with_event_id(room_id, content, None)
57}
58
59pub fn make_test_event_with_event_id(
61 room_id: &RoomId,
62 content: &str,
63 event_id: Option<&EventId>,
64) -> TimelineEvent {
65 let encryption_info = Arc::new(EncryptionInfo {
66 sender: (*ALICE).into(),
67 sender_device: None,
68 algorithm_info: AlgorithmInfo::MegolmV1AesSha2 {
69 curve25519_key: "1337".to_owned(),
70 sender_claimed_keys: Default::default(),
71 session_id: Some("mysessionid9".to_owned()),
72 },
73 verification_state: VerificationState::Verified,
74 });
75
76 let mut builder = EventFactory::new().text_msg(content).room(room_id).sender(*ALICE);
77 if let Some(event_id) = event_id {
78 builder = builder.event_id(event_id);
79 }
80 let event = builder.into_raw();
81
82 TimelineEvent::from_decrypted(
83 DecryptedRoomEvent { event, encryption_info, unsigned_encryption_info: None },
84 Some(vec![Action::Notify]),
85 )
86}
87
88#[track_caller]
93pub fn check_test_event(event: &TimelineEvent, text: &str) {
94 let actions = event.push_actions().unwrap();
96 assert_eq!(actions.len(), 1);
97 assert_matches!(&actions[0], Action::Notify);
98
99 assert_matches!(&event.kind, TimelineEventKind::Decrypted(d) => {
101 assert_eq!(d.encryption_info.sender, *ALICE);
103 assert_matches!(&d.encryption_info.algorithm_info, AlgorithmInfo::MegolmV1AesSha2 { curve25519_key, .. } => {
104 assert_eq!(curve25519_key, "1337");
105 });
106
107 let deserialized = d.event.deserialize().unwrap();
109 assert_matches!(deserialized, AnyTimelineEvent::MessageLike(AnyMessageLikeEvent::RoomMessage(msg)) => {
110 assert_eq!(msg.as_original().unwrap().content.body(), text);
111 });
112 });
113}
114
115#[allow(async_fn_in_trait)]
120pub trait EventCacheStoreIntegrationTests {
121 async fn test_media_content(&self);
123
124 async fn test_replace_media_key(&self);
126
127 async fn test_handle_updates_and_rebuild_linked_chunk(&self);
130
131 async fn test_linked_chunk_incremental_loading(&self);
134
135 async fn test_rebuild_empty_linked_chunk(&self);
138
139 async fn test_load_all_chunks_metadata(&self);
141
142 async fn test_clear_all_linked_chunks(&self);
144
145 async fn test_remove_room(&self);
147
148 async fn test_filter_duplicated_events(&self);
150
151 async fn test_find_event(&self);
153
154 async fn test_find_event_relations(&self);
156
157 async fn test_save_event(&self);
159
160 async fn test_thread_vs_room_linked_chunk(&self);
163}
164
165impl EventCacheStoreIntegrationTests for DynEventCacheStore {
166 async fn test_media_content(&self) {
167 let uri = mxc_uri!("mxc://localhost/media");
168 let request_file = MediaRequestParameters {
169 source: MediaSource::Plain(uri.to_owned()),
170 format: MediaFormat::File,
171 };
172 let request_thumbnail = MediaRequestParameters {
173 source: MediaSource::Plain(uri.to_owned()),
174 format: MediaFormat::Thumbnail(MediaThumbnailSettings::with_method(
175 Method::Crop,
176 uint!(100),
177 uint!(100),
178 )),
179 };
180
181 let other_uri = mxc_uri!("mxc://localhost/media-other");
182 let request_other_file = MediaRequestParameters {
183 source: MediaSource::Plain(other_uri.to_owned()),
184 format: MediaFormat::File,
185 };
186
187 let content: Vec<u8> = "hello".into();
188 let thumbnail_content: Vec<u8> = "world".into();
189 let other_content: Vec<u8> = "foo".into();
190
191 assert!(
193 self.get_media_content(&request_file).await.unwrap().is_none(),
194 "unexpected media found"
195 );
196 assert!(
197 self.get_media_content(&request_thumbnail).await.unwrap().is_none(),
198 "media not found"
199 );
200
201 self.add_media_content(&request_file, content.clone(), IgnoreMediaRetentionPolicy::No)
203 .await
204 .expect("adding media failed");
205
206 assert_eq!(
208 self.get_media_content(&request_file).await.unwrap().as_ref(),
209 Some(&content),
210 "media not found though added"
211 );
212 assert_eq!(
213 self.get_media_content_for_uri(uri).await.unwrap().as_ref(),
214 Some(&content),
215 "media not found by URI though added"
216 );
217
218 self.remove_media_content(&request_file).await.expect("removing media failed");
220
221 assert!(
223 self.get_media_content(&request_file).await.unwrap().is_none(),
224 "media still there after removing"
225 );
226 assert!(
227 self.get_media_content_for_uri(uri).await.unwrap().is_none(),
228 "media still found by URI after removing"
229 );
230
231 self.add_media_content(&request_file, content.clone(), IgnoreMediaRetentionPolicy::No)
233 .await
234 .expect("adding media again failed");
235
236 assert_eq!(
237 self.get_media_content(&request_file).await.unwrap().as_ref(),
238 Some(&content),
239 "media not found after adding again"
240 );
241
242 self.add_media_content(
244 &request_thumbnail,
245 thumbnail_content.clone(),
246 IgnoreMediaRetentionPolicy::No,
247 )
248 .await
249 .expect("adding thumbnail failed");
250
251 assert_eq!(
253 self.get_media_content(&request_thumbnail).await.unwrap().as_ref(),
254 Some(&thumbnail_content),
255 "thumbnail not found"
256 );
257
258 assert!(
260 self.get_media_content_for_uri(uri).await.unwrap().is_some(),
261 "media not found by URI though two where added"
262 );
263
264 self.add_media_content(
266 &request_other_file,
267 other_content.clone(),
268 IgnoreMediaRetentionPolicy::No,
269 )
270 .await
271 .expect("adding other media failed");
272
273 assert_eq!(
275 self.get_media_content(&request_other_file).await.unwrap().as_ref(),
276 Some(&other_content),
277 "other file not found"
278 );
279 assert_eq!(
280 self.get_media_content_for_uri(other_uri).await.unwrap().as_ref(),
281 Some(&other_content),
282 "other file not found by URI"
283 );
284
285 self.remove_media_content_for_uri(uri).await.expect("removing all media for uri failed");
287
288 assert!(
289 self.get_media_content(&request_file).await.unwrap().is_none(),
290 "media wasn't removed"
291 );
292 assert!(
293 self.get_media_content(&request_thumbnail).await.unwrap().is_none(),
294 "thumbnail wasn't removed"
295 );
296 assert!(
297 self.get_media_content(&request_other_file).await.unwrap().is_some(),
298 "other media was removed"
299 );
300 assert!(
301 self.get_media_content_for_uri(uri).await.unwrap().is_none(),
302 "media found by URI wasn't removed"
303 );
304 assert!(
305 self.get_media_content_for_uri(other_uri).await.unwrap().is_some(),
306 "other media found by URI was removed"
307 );
308 }
309
310 async fn test_replace_media_key(&self) {
311 let uri = mxc_uri!("mxc://sendqueue.local/tr4n-s4ct-10n1-d");
312 let req = MediaRequestParameters {
313 source: MediaSource::Plain(uri.to_owned()),
314 format: MediaFormat::File,
315 };
316
317 let content = "hello".as_bytes().to_owned();
318
319 assert!(self.get_media_content(&req).await.unwrap().is_none(), "unexpected media found");
321
322 self.add_media_content(&req, content.clone(), IgnoreMediaRetentionPolicy::No)
324 .await
325 .expect("adding media failed");
326
327 assert_eq!(self.get_media_content(&req).await.unwrap().unwrap(), b"hello");
329
330 let new_uri = mxc_uri!("mxc://matrix.org/tr4n-s4ct-10n1-d");
332 let new_req = MediaRequestParameters {
333 source: MediaSource::Plain(new_uri.to_owned()),
334 format: MediaFormat::File,
335 };
336 self.replace_media_key(&req, &new_req)
337 .await
338 .expect("replacing the media request key failed");
339
340 assert!(
342 self.get_media_content(&req).await.unwrap().is_none(),
343 "unexpected media found with the old key"
344 );
345
346 assert_eq!(self.get_media_content(&new_req).await.unwrap().unwrap(), b"hello");
348 }
349
350 async fn test_handle_updates_and_rebuild_linked_chunk(&self) {
351 let room_id = room_id!("!r0:matrix.org");
352 let linked_chunk_id = LinkedChunkId::Room(room_id);
353
354 self.handle_linked_chunk_updates(
355 linked_chunk_id,
356 vec![
357 Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
359 Update::PushItems {
361 at: Position::new(CId::new(0), 0),
362 items: vec![
363 make_test_event(room_id, "hello"),
364 make_test_event(room_id, "world"),
365 ],
366 },
367 Update::NewGapChunk {
369 previous: Some(CId::new(0)),
370 new: CId::new(1),
371 next: None,
372 gap: Gap { prev_token: "parmesan".to_owned() },
373 },
374 Update::NewItemsChunk { previous: Some(CId::new(1)), new: CId::new(2), next: None },
376 Update::PushItems {
378 at: Position::new(CId::new(2), 0),
379 items: vec![make_test_event(room_id, "sup")],
380 },
381 ],
382 )
383 .await
384 .unwrap();
385
386 let lc = lazy_loader::from_all_chunks::<3, _, _>(
388 self.load_all_chunks(linked_chunk_id).await.unwrap(),
389 )
390 .unwrap()
391 .unwrap();
392
393 let mut chunks = lc.chunks();
394
395 {
396 let first = chunks.next().unwrap();
397 assert_eq!(first.identifier(), CId::new(0));
400
401 assert_matches!(first.content(), ChunkContent::Items(events) => {
402 assert_eq!(events.len(), 2);
403 check_test_event(&events[0], "hello");
404 check_test_event(&events[1], "world");
405 });
406 }
407
408 {
409 let second = chunks.next().unwrap();
410 assert_eq!(second.identifier(), CId::new(1));
411
412 assert_matches!(second.content(), ChunkContent::Gap(gap) => {
413 assert_eq!(gap.prev_token, "parmesan");
414 });
415 }
416
417 {
418 let third = chunks.next().unwrap();
419 assert_eq!(third.identifier(), CId::new(2));
420
421 assert_matches!(third.content(), ChunkContent::Items(events) => {
422 assert_eq!(events.len(), 1);
423 check_test_event(&events[0], "sup");
424 });
425 }
426
427 assert!(chunks.next().is_none());
428 }
429
430 async fn test_load_all_chunks_metadata(&self) {
431 let room_id = room_id!("!r0:matrix.org");
432 let linked_chunk_id = LinkedChunkId::Room(room_id);
433
434 self.handle_linked_chunk_updates(
435 linked_chunk_id,
436 vec![
437 Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
439 Update::PushItems {
441 at: Position::new(CId::new(0), 0),
442 items: vec![
443 make_test_event(room_id, "hello"),
444 make_test_event(room_id, "world"),
445 ],
446 },
447 Update::NewGapChunk {
449 previous: Some(CId::new(0)),
450 new: CId::new(1),
451 next: None,
452 gap: Gap { prev_token: "parmesan".to_owned() },
453 },
454 Update::NewItemsChunk { previous: Some(CId::new(1)), new: CId::new(2), next: None },
456 Update::PushItems {
458 at: Position::new(CId::new(2), 0),
459 items: vec![make_test_event(room_id, "sup")],
460 },
461 Update::NewItemsChunk { previous: Some(CId::new(2)), new: CId::new(3), next: None },
463 ],
464 )
465 .await
466 .unwrap();
467
468 let metas = self.load_all_chunks_metadata(linked_chunk_id).await.unwrap();
469 assert_eq!(metas.len(), 4);
470
471 assert_eq!(metas[0].identifier, CId::new(0));
473 assert_eq!(metas[0].previous, None);
474 assert_eq!(metas[0].next, Some(CId::new(1)));
475 assert_eq!(metas[0].num_items, 2);
476
477 assert_eq!(metas[1].identifier, CId::new(1));
479 assert_eq!(metas[1].previous, Some(CId::new(0)));
480 assert_eq!(metas[1].next, Some(CId::new(2)));
481 assert_eq!(metas[1].num_items, 0);
482
483 assert_eq!(metas[2].identifier, CId::new(2));
485 assert_eq!(metas[2].previous, Some(CId::new(1)));
486 assert_eq!(metas[2].next, Some(CId::new(3)));
487 assert_eq!(metas[2].num_items, 1);
488
489 assert_eq!(metas[3].identifier, CId::new(3));
491 assert_eq!(metas[3].previous, Some(CId::new(2)));
492 assert_eq!(metas[3].next, None);
493 assert_eq!(metas[3].num_items, 0);
494 }
495
496 async fn test_linked_chunk_incremental_loading(&self) {
497 let room_id = room_id!("!r0:matrix.org");
498 let linked_chunk_id = LinkedChunkId::Room(room_id);
499 let event = |msg: &str| make_test_event(room_id, msg);
500
501 {
503 let (last_chunk, chunk_identifier_generator) =
504 self.load_last_chunk(linked_chunk_id).await.unwrap();
505
506 assert!(last_chunk.is_none());
507 assert_eq!(chunk_identifier_generator.current(), 0);
508 }
509
510 self.handle_linked_chunk_updates(
511 linked_chunk_id,
512 vec![
513 Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
515 Update::PushItems {
517 at: Position::new(CId::new(0), 0),
518 items: vec![event("a"), event("b")],
519 },
520 Update::NewGapChunk {
522 previous: Some(CId::new(0)),
523 new: CId::new(1),
524 next: None,
525 gap: Gap { prev_token: "morbier".to_owned() },
526 },
527 Update::NewItemsChunk { previous: Some(CId::new(1)), new: CId::new(2), next: None },
529 Update::PushItems {
531 at: Position::new(CId::new(2), 0),
532 items: vec![event("c"), event("d"), event("e")],
533 },
534 ],
535 )
536 .await
537 .unwrap();
538
539 let mut linked_chunk = {
541 let (last_chunk, chunk_identifier_generator) =
542 self.load_last_chunk(linked_chunk_id).await.unwrap();
543
544 assert_eq!(chunk_identifier_generator.current(), 2);
545
546 let linked_chunk = lazy_loader::from_last_chunk::<DEFAULT_CHUNK_CAPACITY, _, _>(
547 last_chunk,
548 chunk_identifier_generator,
549 )
550 .unwrap() .unwrap(); let mut rchunks = linked_chunk.rchunks();
554
555 assert_matches!(rchunks.next(), Some(chunk) => {
557 assert_eq!(chunk.identifier(), 2);
558 assert_eq!(chunk.lazy_previous(), Some(CId::new(1)));
559
560 assert_matches!(chunk.content(), ChunkContent::Items(events) => {
561 assert_eq!(events.len(), 3);
562 check_test_event(&events[0], "c");
563 check_test_event(&events[1], "d");
564 check_test_event(&events[2], "e");
565 });
566 });
567
568 assert!(rchunks.next().is_none());
569
570 linked_chunk
571 };
572
573 {
575 let first_chunk = linked_chunk.chunks().next().unwrap().identifier();
576 let previous_chunk =
577 self.load_previous_chunk(linked_chunk_id, first_chunk).await.unwrap().unwrap();
578
579 lazy_loader::insert_new_first_chunk(&mut linked_chunk, previous_chunk).unwrap();
580
581 let mut rchunks = linked_chunk.rchunks();
582
583 assert_matches!(rchunks.next(), Some(chunk) => {
585 assert_eq!(chunk.identifier(), 2);
586 assert!(chunk.lazy_previous().is_none());
587
588 assert_matches!(chunk.content(), ChunkContent::Items(events) => {
590 assert_eq!(events.len(), 3);
591 check_test_event(&events[0], "c");
592 check_test_event(&events[1], "d");
593 check_test_event(&events[2], "e");
594 });
595 });
596
597 assert_matches!(rchunks.next(), Some(chunk) => {
599 assert_eq!(chunk.identifier(), 1);
600 assert_eq!(chunk.lazy_previous(), Some(CId::new(0)));
601
602 assert_matches!(chunk.content(), ChunkContent::Gap(gap) => {
603 assert_eq!(gap.prev_token, "morbier");
604 });
605 });
606
607 assert!(rchunks.next().is_none());
608 }
609
610 {
612 let first_chunk = linked_chunk.chunks().next().unwrap().identifier();
613 let previous_chunk =
614 self.load_previous_chunk(linked_chunk_id, first_chunk).await.unwrap().unwrap();
615
616 lazy_loader::insert_new_first_chunk(&mut linked_chunk, previous_chunk).unwrap();
617
618 let mut rchunks = linked_chunk.rchunks();
619
620 assert_matches!(rchunks.next(), Some(chunk) => {
622 assert_eq!(chunk.identifier(), 2);
623 assert!(chunk.lazy_previous().is_none());
624
625 assert_matches!(chunk.content(), ChunkContent::Items(events) => {
627 assert_eq!(events.len(), 3);
628 check_test_event(&events[0], "c");
629 check_test_event(&events[1], "d");
630 check_test_event(&events[2], "e");
631 });
632 });
633
634 assert_matches!(rchunks.next(), Some(chunk) => {
636 assert_eq!(chunk.identifier(), 1);
637 assert!(chunk.lazy_previous().is_none());
638
639 assert_matches!(chunk.content(), ChunkContent::Gap(gap) => {
641 assert_eq!(gap.prev_token, "morbier");
642 });
643 });
644
645 assert_matches!(rchunks.next(), Some(chunk) => {
647 assert_eq!(chunk.identifier(), 0);
648 assert!(chunk.lazy_previous().is_none());
649
650 assert_matches!(chunk.content(), ChunkContent::Items(events) => {
651 assert_eq!(events.len(), 2);
652 check_test_event(&events[0], "a");
653 check_test_event(&events[1], "b");
654 });
655 });
656
657 assert!(rchunks.next().is_none());
658 }
659
660 {
662 let first_chunk = linked_chunk.chunks().next().unwrap().identifier();
663 let previous_chunk =
664 self.load_previous_chunk(linked_chunk_id, first_chunk).await.unwrap();
665
666 assert!(previous_chunk.is_none());
667 }
668
669 {
672 let mut chunks = linked_chunk.chunks();
673
674 assert_matches!(chunks.next(), Some(chunk) => {
676 assert_eq!(chunk.identifier(), 0);
677 assert!(chunk.lazy_previous().is_none());
678
679 assert_matches!(chunk.content(), ChunkContent::Items(events) => {
680 assert_eq!(events.len(), 2);
681 check_test_event(&events[0], "a");
682 check_test_event(&events[1], "b");
683 });
684 });
685
686 assert_matches!(chunks.next(), Some(chunk) => {
688 assert_eq!(chunk.identifier(), 1);
689 assert!(chunk.lazy_previous().is_none());
690
691 assert_matches!(chunk.content(), ChunkContent::Gap(gap) => {
692 assert_eq!(gap.prev_token, "morbier");
693 });
694 });
695
696 assert_matches!(chunks.next(), Some(chunk) => {
698 assert_eq!(chunk.identifier(), 2);
699 assert!(chunk.lazy_previous().is_none());
700
701 assert_matches!(chunk.content(), ChunkContent::Items(events) => {
702 assert_eq!(events.len(), 3);
703 check_test_event(&events[0], "c");
704 check_test_event(&events[1], "d");
705 check_test_event(&events[2], "e");
706 });
707 });
708
709 assert!(chunks.next().is_none());
710 }
711 }
712
713 async fn test_rebuild_empty_linked_chunk(&self) {
714 let linked_chunk = lazy_loader::from_all_chunks::<3, _, _>(
716 self.load_all_chunks(LinkedChunkId::Room(&DEFAULT_TEST_ROOM_ID)).await.unwrap(),
717 )
718 .unwrap();
719 assert!(linked_chunk.is_none());
720 }
721
722 async fn test_clear_all_linked_chunks(&self) {
723 let r0 = room_id!("!r0:matrix.org");
724 let linked_chunk_id0 = LinkedChunkId::Room(r0);
725 let r1 = room_id!("!r1:matrix.org");
726 let linked_chunk_id1 = LinkedChunkId::Room(r1);
727
728 self.handle_linked_chunk_updates(
730 linked_chunk_id0,
731 vec![
732 Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
734 Update::PushItems {
736 at: Position::new(CId::new(0), 0),
737 items: vec![make_test_event(r0, "hello"), make_test_event(r0, "world")],
738 },
739 ],
740 )
741 .await
742 .unwrap();
743
744 self.handle_linked_chunk_updates(
746 linked_chunk_id1,
747 vec![
748 Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
750 Update::NewGapChunk {
752 previous: Some(CId::new(0)),
753 new: CId::new(1),
754 next: None,
755 gap: Gap { prev_token: "bleu d'auvergne".to_owned() },
756 },
757 Update::NewItemsChunk { previous: Some(CId::new(1)), new: CId::new(2), next: None },
759 Update::PushItems {
761 at: Position::new(CId::new(2), 0),
762 items: vec![make_test_event(r0, "yummy")],
763 },
764 ],
765 )
766 .await
767 .unwrap();
768
769 assert!(
771 lazy_loader::from_all_chunks::<3, _, _>(
772 self.load_all_chunks(linked_chunk_id0).await.unwrap()
773 )
774 .unwrap()
775 .is_some()
776 );
777 assert!(
778 lazy_loader::from_all_chunks::<3, _, _>(
779 self.load_all_chunks(linked_chunk_id1).await.unwrap()
780 )
781 .unwrap()
782 .is_some()
783 );
784
785 self.clear_all_linked_chunks().await.unwrap();
787
788 assert!(
790 lazy_loader::from_all_chunks::<3, _, _>(
791 self.load_all_chunks(linked_chunk_id0).await.unwrap()
792 )
793 .unwrap()
794 .is_none()
795 );
796 assert!(
797 lazy_loader::from_all_chunks::<3, _, _>(
798 self.load_all_chunks(linked_chunk_id1).await.unwrap()
799 )
800 .unwrap()
801 .is_none()
802 );
803 }
804
805 async fn test_remove_room(&self) {
806 let r0 = room_id!("!r0:matrix.org");
807 let linked_chunk_id0 = LinkedChunkId::Room(r0);
808 let r1 = room_id!("!r1:matrix.org");
809 let linked_chunk_id1 = LinkedChunkId::Room(r1);
810
811 self.handle_linked_chunk_updates(
813 linked_chunk_id0,
814 vec![
815 Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
817 Update::PushItems {
819 at: Position::new(CId::new(0), 0),
820 items: vec![make_test_event(r0, "hello"), make_test_event(r0, "world")],
821 },
822 ],
823 )
824 .await
825 .unwrap();
826
827 self.handle_linked_chunk_updates(
829 linked_chunk_id1,
830 vec![
831 Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
833 Update::PushItems {
835 at: Position::new(CId::new(0), 0),
836 items: vec![make_test_event(r0, "yummy")],
837 },
838 ],
839 )
840 .await
841 .unwrap();
842
843 self.remove_room(r0).await.unwrap();
845
846 let r0_linked_chunk = self.load_all_chunks(linked_chunk_id0).await.unwrap();
848 assert!(r0_linked_chunk.is_empty());
849
850 let r1_linked_chunk = self.load_all_chunks(linked_chunk_id1).await.unwrap();
852 assert!(!r1_linked_chunk.is_empty());
853 }
854
855 async fn test_filter_duplicated_events(&self) {
856 let room_id = room_id!("!r0:matrix.org");
857 let linked_chunk_id = LinkedChunkId::Room(room_id);
858 let another_room_id = room_id!("!r1:matrix.org");
859 let another_linked_chunk_id = LinkedChunkId::Room(another_room_id);
860 let event = |msg: &str| make_test_event(room_id, msg);
861
862 let event_comte = event("comté");
863 let event_brigand = event("brigand du jorat");
864 let event_raclette = event("raclette");
865 let event_morbier = event("morbier");
866 let event_gruyere = event("gruyère");
867 let event_tome = event("tome");
868 let event_mont_dor = event("mont d'or");
869
870 self.handle_linked_chunk_updates(
871 linked_chunk_id,
872 vec![
873 Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
874 Update::PushItems {
875 at: Position::new(CId::new(0), 0),
876 items: vec![event_comte.clone(), event_brigand.clone()],
877 },
878 Update::NewGapChunk {
879 previous: Some(CId::new(0)),
880 new: CId::new(1),
881 next: None,
882 gap: Gap { prev_token: "brillat-savarin".to_owned() },
883 },
884 Update::NewItemsChunk { previous: Some(CId::new(1)), new: CId::new(2), next: None },
885 Update::PushItems {
886 at: Position::new(CId::new(2), 0),
887 items: vec![event_morbier.clone(), event_mont_dor.clone()],
888 },
889 ],
890 )
891 .await
892 .unwrap();
893
894 self.handle_linked_chunk_updates(
897 another_linked_chunk_id,
898 vec![
899 Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
900 Update::PushItems {
901 at: Position::new(CId::new(0), 0),
902 items: vec![event_tome.clone()],
903 },
904 ],
905 )
906 .await
907 .unwrap();
908
909 let duplicated_events = BTreeMap::from_iter(
910 self.filter_duplicated_events(
911 linked_chunk_id,
912 vec![
913 event_comte.event_id().unwrap().to_owned(),
914 event_raclette.event_id().unwrap().to_owned(),
915 event_morbier.event_id().unwrap().to_owned(),
916 event_gruyere.event_id().unwrap().to_owned(),
917 event_tome.event_id().unwrap().to_owned(),
918 event_mont_dor.event_id().unwrap().to_owned(),
919 ],
920 )
921 .await
922 .unwrap(),
923 );
924
925 assert_eq!(duplicated_events.len(), 3);
926
927 assert_eq!(
928 *duplicated_events.get(&event_comte.event_id().unwrap()).unwrap(),
929 Position::new(CId::new(0), 0)
930 );
931 assert_eq!(
932 *duplicated_events.get(&event_morbier.event_id().unwrap()).unwrap(),
933 Position::new(CId::new(2), 0)
934 );
935 assert_eq!(
936 *duplicated_events.get(&event_mont_dor.event_id().unwrap()).unwrap(),
937 Position::new(CId::new(2), 1)
938 );
939 }
940
941 async fn test_find_event(&self) {
942 let room_id = room_id!("!r0:matrix.org");
943 let another_room_id = room_id!("!r1:matrix.org");
944 let another_linked_chunk_id = LinkedChunkId::Room(another_room_id);
945 let event = |msg: &str| make_test_event(room_id, msg);
946
947 let event_comte = event("comté");
948 let event_gruyere = event("gruyère");
949
950 self.handle_linked_chunk_updates(
952 LinkedChunkId::Room(room_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_comte.clone()],
958 },
959 ],
960 )
961 .await
962 .unwrap();
963
964 self.handle_linked_chunk_updates(
966 another_linked_chunk_id,
967 vec![
968 Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
969 Update::PushItems {
970 at: Position::new(CId::new(0), 0),
971 items: vec![event_gruyere.clone()],
972 },
973 ],
974 )
975 .await
976 .unwrap();
977
978 let event = self
980 .find_event(room_id, event_comte.event_id().unwrap().as_ref())
981 .await
982 .expect("failed to query for finding an event")
983 .expect("failed to find an event");
984
985 assert_eq!(event.event_id(), event_comte.event_id());
986
987 assert!(
989 self.find_event(room_id, event_gruyere.event_id().unwrap().as_ref())
990 .await
991 .expect("failed to query for finding an event")
992 .is_none()
993 );
994
995 self.clear_all_linked_chunks().await.expect("failed to clear all rooms chunks");
997 assert!(
998 self.find_event(room_id, event_comte.event_id().unwrap().as_ref())
999 .await
1000 .expect("failed to query for finding an event")
1001 .is_none()
1002 );
1003 }
1004
1005 async fn test_find_event_relations(&self) {
1006 let room_id = room_id!("!r0:matrix.org");
1007 let another_room_id = room_id!("!r1:matrix.org");
1008
1009 let f = EventFactory::new().room(room_id).sender(*ALICE);
1010
1011 let eid1 = event_id!("$event1:matrix.org");
1013 let e1 = f.text_msg("comter").event_id(eid1).into_event();
1014
1015 let edit_eid1 = event_id!("$edit_event1:matrix.org");
1016 let edit_e1 = f
1017 .text_msg("* comté")
1018 .event_id(edit_eid1)
1019 .edit(eid1, RoomMessageEventContentWithoutRelation::text_plain("comté"))
1020 .into_event();
1021
1022 let reaction_eid1 = event_id!("$reaction_event1:matrix.org");
1023 let reaction_e1 = f.reaction(eid1, "👍").event_id(reaction_eid1).into_event();
1024
1025 let eid2 = event_id!("$event2:matrix.org");
1026 let e2 = f.text_msg("galette saucisse").event_id(eid2).into_event();
1027
1028 let f = f.room(another_room_id);
1030
1031 let eid3 = event_id!("$event3:matrix.org");
1032 let e3 = f.text_msg("gruyère").event_id(eid3).into_event();
1033
1034 let reaction_eid3 = event_id!("$reaction_event3:matrix.org");
1035 let reaction_e3 = f.reaction(eid3, "👍").event_id(reaction_eid3).into_event();
1036
1037 self.save_event(room_id, e1).await.unwrap();
1039 self.save_event(room_id, edit_e1).await.unwrap();
1040 self.save_event(room_id, reaction_e1.clone()).await.unwrap();
1041 self.save_event(room_id, e2).await.unwrap();
1042 self.save_event(another_room_id, e3).await.unwrap();
1043 self.save_event(another_room_id, reaction_e3).await.unwrap();
1044
1045 let relations = self.find_event_relations(room_id, eid1, None).await.unwrap();
1047 assert_eq!(relations.len(), 2);
1048 assert!(
1050 relations
1051 .iter()
1052 .any(|(ev, pos)| ev.event_id().as_deref() == Some(edit_eid1) && pos.is_none())
1053 );
1054 assert!(
1055 relations
1056 .iter()
1057 .any(|(ev, pos)| ev.event_id().as_deref() == Some(reaction_eid1) && pos.is_none())
1058 );
1059
1060 let relations = self
1062 .find_event_relations(room_id, eid1, Some(&[RelationType::Replacement]))
1063 .await
1064 .unwrap();
1065 assert_eq!(relations.len(), 1);
1066 assert_eq!(relations[0].0.event_id().as_deref(), Some(edit_eid1));
1067
1068 let relations = self
1069 .find_event_relations(
1070 room_id,
1071 eid1,
1072 Some(&[RelationType::Replacement, RelationType::Annotation]),
1073 )
1074 .await
1075 .unwrap();
1076 assert_eq!(relations.len(), 2);
1077 assert!(relations.iter().any(|r| r.0.event_id().as_deref() == Some(edit_eid1)));
1078 assert!(relations.iter().any(|r| r.0.event_id().as_deref() == Some(reaction_eid1)));
1079
1080 let relations = self
1082 .find_event_relations(another_room_id, eid1, Some(&[RelationType::Replacement]))
1083 .await
1084 .unwrap();
1085 assert!(relations.is_empty());
1086
1087 self.handle_linked_chunk_updates(
1092 LinkedChunkId::Room(room_id),
1093 vec![
1094 Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
1095 Update::PushItems { at: Position::new(CId::new(0), 0), items: vec![reaction_e1] },
1096 ],
1097 )
1098 .await
1099 .unwrap();
1100
1101 let relations = self.find_event_relations(room_id, eid1, None).await.unwrap();
1104
1105 assert!(relations.iter().any(|(ev, pos)| {
1107 ev.event_id().as_deref() == Some(reaction_eid1)
1108 && *pos == Some(Position::new(CId::new(0), 0))
1109 }));
1110
1111 assert!(
1113 relations
1114 .iter()
1115 .any(|(ev, pos)| ev.event_id().as_deref() == Some(edit_eid1) && pos.is_none())
1116 );
1117 }
1118
1119 async fn test_save_event(&self) {
1120 let room_id = room_id!("!r0:matrix.org");
1121 let another_room_id = room_id!("!r1:matrix.org");
1122
1123 let event = |msg: &str| make_test_event(room_id, msg);
1124 let event_comte = event("comté");
1125 let event_gruyere = event("gruyère");
1126
1127 self.save_event(room_id, event_comte.clone()).await.unwrap();
1129
1130 self.save_event(another_room_id, event_gruyere.clone()).await.unwrap();
1132
1133 let event = self
1135 .find_event(room_id, event_comte.event_id().unwrap().as_ref())
1136 .await
1137 .expect("failed to query for finding an event")
1138 .expect("failed to find an event");
1139 assert_eq!(event.event_id(), event_comte.event_id());
1140
1141 let event = self
1142 .find_event(another_room_id, event_gruyere.event_id().unwrap().as_ref())
1143 .await
1144 .expect("failed to query for finding an event")
1145 .expect("failed to find an event");
1146 assert_eq!(event.event_id(), event_gruyere.event_id());
1147
1148 assert!(
1150 self.find_event(another_room_id, event_comte.event_id().unwrap().as_ref())
1151 .await
1152 .expect("failed to query for finding an event")
1153 .is_none()
1154 );
1155 assert!(
1156 self.find_event(room_id, event_gruyere.event_id().unwrap().as_ref())
1157 .await
1158 .expect("failed to query for finding an event")
1159 .is_none()
1160 );
1161 }
1162
1163 async fn test_thread_vs_room_linked_chunk(&self) {
1164 let room_id = room_id!("!r0:matrix.org");
1165
1166 let event = |msg: &str| make_test_event(room_id, msg);
1167
1168 let thread1_ev = event("comté");
1169 let thread2_ev = event("gruyère");
1170 let thread2_ev2 = event("beaufort");
1171 let room_ev = event("brillat savarin triple crème");
1172
1173 let thread_root1 = event("thread1");
1174 let thread_root2 = event("thread2");
1175
1176 self.handle_linked_chunk_updates(
1178 LinkedChunkId::Thread(room_id, thread_root1.event_id().unwrap().as_ref()),
1179 vec![
1180 Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
1181 Update::PushItems {
1182 at: Position::new(CId::new(0), 0),
1183 items: vec![thread1_ev.clone()],
1184 },
1185 ],
1186 )
1187 .await
1188 .unwrap();
1189
1190 self.handle_linked_chunk_updates(
1192 LinkedChunkId::Thread(room_id, thread_root2.event_id().unwrap().as_ref()),
1193 vec![
1194 Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
1195 Update::PushItems {
1196 at: Position::new(CId::new(0), 0),
1197 items: vec![thread2_ev.clone(), thread2_ev2.clone()],
1198 },
1199 ],
1200 )
1201 .await
1202 .unwrap();
1203
1204 self.handle_linked_chunk_updates(
1206 LinkedChunkId::Room(room_id),
1207 vec![
1208 Update::NewItemsChunk { previous: None, new: CId::new(0), next: None },
1209 Update::PushItems {
1210 at: Position::new(CId::new(0), 0),
1211 items: vec![room_ev.clone()],
1212 },
1213 ],
1214 )
1215 .await
1216 .unwrap();
1217
1218 self.find_event(room_id, thread2_ev.event_id().unwrap().as_ref())
1220 .await
1221 .expect("failed to query for finding an event")
1222 .expect("failed to find thread1_ev");
1223
1224 self.find_event(room_id, thread2_ev.event_id().unwrap().as_ref())
1225 .await
1226 .expect("failed to query for finding an event")
1227 .expect("failed to find thread2_ev");
1228
1229 self.find_event(room_id, thread2_ev2.event_id().unwrap().as_ref())
1230 .await
1231 .expect("failed to query for finding an event")
1232 .expect("failed to find thread2_ev2");
1233
1234 self.find_event(room_id, room_ev.event_id().unwrap().as_ref())
1235 .await
1236 .expect("failed to query for finding an event")
1237 .expect("failed to find room_ev");
1238
1239 let dups = self
1241 .filter_duplicated_events(
1242 LinkedChunkId::Thread(room_id, thread_root1.event_id().unwrap().as_ref()),
1243 vec![
1244 thread1_ev.event_id().unwrap().to_owned(),
1245 room_ev.event_id().unwrap().to_owned(),
1246 ],
1247 )
1248 .await
1249 .unwrap();
1250 assert_eq!(dups.len(), 1);
1251 assert_eq!(dups[0].0, thread1_ev.event_id().unwrap());
1252
1253 let all_chunks = self
1255 .load_all_chunks(LinkedChunkId::Thread(
1256 room_id,
1257 thread_root2.event_id().unwrap().as_ref(),
1258 ))
1259 .await
1260 .unwrap();
1261 assert_eq!(all_chunks.len(), 1);
1262 assert_eq!(all_chunks[0].identifier, CId::new(0));
1263 assert_let!(ChunkContent::Items(observed_items) = all_chunks[0].content.clone());
1264 assert_eq!(observed_items.len(), 2);
1265 assert_eq!(observed_items[0].event_id(), thread2_ev.event_id());
1266 assert_eq!(observed_items[1].event_id(), thread2_ev2.event_id());
1267
1268 let metas = self
1271 .load_all_chunks_metadata(LinkedChunkId::Thread(
1272 room_id,
1273 thread_root2.event_id().unwrap().as_ref(),
1274 ))
1275 .await
1276 .unwrap();
1277 assert_eq!(metas.len(), 1);
1278 assert_eq!(metas[0].identifier, CId::new(0));
1279 assert_eq!(metas[0].num_items, 2);
1280
1281 let (last_chunk, _chunk_identifier_generator) = self
1283 .load_last_chunk(LinkedChunkId::Thread(
1284 room_id,
1285 thread_root1.event_id().unwrap().as_ref(),
1286 ))
1287 .await
1288 .unwrap();
1289 let last_chunk = last_chunk.unwrap();
1290 assert_eq!(last_chunk.identifier, CId::new(0));
1291 assert_let!(ChunkContent::Items(observed_items) = last_chunk.content);
1292 assert_eq!(observed_items.len(), 1);
1293 assert_eq!(observed_items[0].event_id(), thread1_ev.event_id());
1294 }
1295}
1296
1297#[allow(unused_macros, unused_extern_crates)]
1325#[macro_export]
1326macro_rules! event_cache_store_integration_tests {
1327 () => {
1328 mod event_cache_store_integration_tests {
1329 use matrix_sdk_test::async_test;
1330 use $crate::event_cache::store::{
1331 EventCacheStoreIntegrationTests, IntoEventCacheStore,
1332 };
1333
1334 use super::get_event_cache_store;
1335
1336 #[async_test]
1337 async fn test_media_content() {
1338 let event_cache_store =
1339 get_event_cache_store().await.unwrap().into_event_cache_store();
1340 event_cache_store.test_media_content().await;
1341 }
1342
1343 #[async_test]
1344 async fn test_replace_media_key() {
1345 let event_cache_store =
1346 get_event_cache_store().await.unwrap().into_event_cache_store();
1347 event_cache_store.test_replace_media_key().await;
1348 }
1349
1350 #[async_test]
1351 async fn test_handle_updates_and_rebuild_linked_chunk() {
1352 let event_cache_store =
1353 get_event_cache_store().await.unwrap().into_event_cache_store();
1354 event_cache_store.test_handle_updates_and_rebuild_linked_chunk().await;
1355 }
1356
1357 #[async_test]
1358 async fn test_linked_chunk_incremental_loading() {
1359 let event_cache_store =
1360 get_event_cache_store().await.unwrap().into_event_cache_store();
1361 event_cache_store.test_linked_chunk_incremental_loading().await;
1362 }
1363
1364 #[async_test]
1365 async fn test_rebuild_empty_linked_chunk() {
1366 let event_cache_store =
1367 get_event_cache_store().await.unwrap().into_event_cache_store();
1368 event_cache_store.test_rebuild_empty_linked_chunk().await;
1369 }
1370
1371 #[async_test]
1372 async fn test_load_all_chunks_metadata() {
1373 let event_cache_store =
1374 get_event_cache_store().await.unwrap().into_event_cache_store();
1375 event_cache_store.test_load_all_chunks_metadata().await;
1376 }
1377
1378 #[async_test]
1379 async fn test_clear_all_linked_chunks() {
1380 let event_cache_store =
1381 get_event_cache_store().await.unwrap().into_event_cache_store();
1382 event_cache_store.test_clear_all_linked_chunks().await;
1383 }
1384
1385 #[async_test]
1386 async fn test_remove_room() {
1387 let event_cache_store =
1388 get_event_cache_store().await.unwrap().into_event_cache_store();
1389 event_cache_store.test_remove_room().await;
1390 }
1391
1392 #[async_test]
1393 async fn test_filter_duplicated_events() {
1394 let event_cache_store =
1395 get_event_cache_store().await.unwrap().into_event_cache_store();
1396 event_cache_store.test_filter_duplicated_events().await;
1397 }
1398
1399 #[async_test]
1400 async fn test_find_event() {
1401 let event_cache_store =
1402 get_event_cache_store().await.unwrap().into_event_cache_store();
1403 event_cache_store.test_find_event().await;
1404 }
1405
1406 #[async_test]
1407 async fn test_find_event_relations() {
1408 let event_cache_store =
1409 get_event_cache_store().await.unwrap().into_event_cache_store();
1410 event_cache_store.test_find_event_relations().await;
1411 }
1412
1413 #[async_test]
1414 async fn test_save_event() {
1415 let event_cache_store =
1416 get_event_cache_store().await.unwrap().into_event_cache_store();
1417 event_cache_store.test_save_event().await;
1418 }
1419
1420 #[async_test]
1421 async fn test_thread_vs_room_linked_chunk() {
1422 let event_cache_store =
1423 get_event_cache_store().await.unwrap().into_event_cache_store();
1424 event_cache_store.test_thread_vs_room_linked_chunk().await;
1425 }
1426 }
1427 };
1428}
1429
1430#[allow(unused_macros)]
1433#[macro_export]
1434macro_rules! event_cache_store_integration_tests_time {
1435 () => {
1436 mod event_cache_store_integration_tests_time {
1437 use std::time::Duration;
1438
1439 #[cfg(all(target_family = "wasm", target_os = "unknown"))]
1440 use gloo_timers::future::sleep;
1441 use matrix_sdk_test::async_test;
1442 #[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
1443 use tokio::time::sleep;
1444 use $crate::event_cache::store::IntoEventCacheStore;
1445
1446 use super::get_event_cache_store;
1447
1448 #[async_test]
1449 async fn test_lease_locks() {
1450 let store = get_event_cache_store().await.unwrap().into_event_cache_store();
1451
1452 let acquired0 = store.try_take_leased_lock(0, "key", "alice").await.unwrap();
1453 assert!(acquired0);
1454
1455 let acquired2 = store.try_take_leased_lock(300, "key", "alice").await.unwrap();
1457 assert!(acquired2);
1458
1459 let acquired3 = store.try_take_leased_lock(300, "key", "alice").await.unwrap();
1461 assert!(acquired3);
1462
1463 let acquired4 = store.try_take_leased_lock(300, "key", "bob").await.unwrap();
1465 assert!(!acquired4);
1466
1467 let acquired5 = store.try_take_leased_lock(300, "key", "bob").await.unwrap();
1469 assert!(!acquired5);
1470
1471 sleep(Duration::from_millis(50)).await;
1473
1474 let acquired55 = store.try_take_leased_lock(300, "key", "bob").await.unwrap();
1476 assert!(!acquired55);
1477
1478 sleep(Duration::from_millis(250)).await;
1480
1481 let acquired6 = store.try_take_leased_lock(0, "key", "bob").await.unwrap();
1483 assert!(acquired6);
1484
1485 sleep(Duration::from_millis(1)).await;
1486
1487 let acquired7 = store.try_take_leased_lock(0, "key", "alice").await.unwrap();
1489 assert!(acquired7);
1490
1491 sleep(Duration::from_millis(1)).await;
1492
1493 let acquired8 = store.try_take_leased_lock(300, "key", "bob").await.unwrap();
1495 assert!(acquired8);
1496
1497 let acquired9 = store.try_take_leased_lock(300, "key", "alice").await.unwrap();
1499 assert!(!acquired9);
1500
1501 let acquired10 = store.try_take_leased_lock(300, "key", "bob").await.unwrap();
1503 assert!(acquired10);
1504 }
1505 }
1506 };
1507}