matrix_sdk_base/event_cache/store/media/
integration_tests.rs

1// Copyright 2025 Kévin Commaille
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! Trait and macro of integration tests for `EventCacheStoreMedia`
16//! implementations.
17
18use ruma::{
19    events::room::MediaSource,
20    mxc_uri, owned_mxc_uri,
21    time::{Duration, SystemTime},
22};
23
24use super::{
25    EventCacheStoreMedia, MediaRetentionPolicy, media_service::IgnoreMediaRetentionPolicy,
26};
27use crate::media::{MediaFormat, MediaRequestParameters};
28
29/// [`EventCacheStoreMedia`] integration tests.
30///
31/// This trait is not meant to be used directly, but will be used with the
32/// `event_cache_store_media_integration_tests!` macro.
33#[allow(async_fn_in_trait)]
34pub trait EventCacheStoreMediaIntegrationTests {
35    /// Test media retention policy storage.
36    async fn test_store_media_retention_policy(&self);
37
38    /// Test media content's retention policy max file size.
39    async fn test_media_max_file_size(&self);
40
41    /// Test media content's retention policy max cache size.
42    async fn test_media_max_cache_size(&self);
43
44    /// Test media content's retention policy expiry.
45    async fn test_media_expiry(&self);
46
47    /// Test [`IgnoreMediaRetentionPolicy`] with the media content's retention
48    /// policy max sizes.
49    async fn test_media_ignore_max_size(&self);
50
51    /// Test [`IgnoreMediaRetentionPolicy`] with the media content's retention
52    /// policy expiry.
53    async fn test_media_ignore_expiry(&self);
54
55    /// Test last media cleanup time storage.
56    async fn test_store_last_media_cleanup_time(&self);
57}
58
59impl<Store> EventCacheStoreMediaIntegrationTests for Store
60where
61    Store: EventCacheStoreMedia + std::fmt::Debug,
62{
63    async fn test_store_media_retention_policy(&self) {
64        let stored = self.media_retention_policy_inner().await.unwrap();
65        assert!(stored.is_none());
66
67        let policy = MediaRetentionPolicy::default();
68        self.set_media_retention_policy_inner(policy).await.unwrap();
69
70        let stored = self.media_retention_policy_inner().await.unwrap();
71        assert_eq!(stored, Some(policy));
72    }
73
74    async fn test_media_max_file_size(&self) {
75        let time = SystemTime::now();
76
77        // 256 bytes content.
78        let content_big = vec![0; 256];
79        let uri_big = owned_mxc_uri!("mxc://localhost/big-media");
80        let request_big = MediaRequestParameters {
81            source: MediaSource::Plain(uri_big),
82            format: MediaFormat::File,
83        };
84
85        // 128 bytes content.
86        let content_avg = vec![0; 128];
87        let uri_avg = owned_mxc_uri!("mxc://localhost/average-media");
88        let request_avg = MediaRequestParameters {
89            source: MediaSource::Plain(uri_avg),
90            format: MediaFormat::File,
91        };
92
93        // 64 bytes content.
94        let content_small = vec![0; 64];
95        let uri_small = owned_mxc_uri!("mxc://localhost/small-media");
96        let request_small = MediaRequestParameters {
97            source: MediaSource::Plain(uri_small),
98            format: MediaFormat::File,
99        };
100
101        // First, with a policy that doesn't accept the big media.
102        let policy = MediaRetentionPolicy::empty().with_max_file_size(Some(200));
103
104        self.add_media_content_inner(
105            &request_big,
106            content_big.clone(),
107            time,
108            policy,
109            IgnoreMediaRetentionPolicy::No,
110        )
111        .await
112        .unwrap();
113        self.add_media_content_inner(
114            &request_avg,
115            content_avg.clone(),
116            time,
117            policy,
118            IgnoreMediaRetentionPolicy::No,
119        )
120        .await
121        .unwrap();
122        self.add_media_content_inner(
123            &request_small,
124            content_small,
125            time,
126            policy,
127            IgnoreMediaRetentionPolicy::No,
128        )
129        .await
130        .unwrap();
131
132        // The big content was NOT cached but the others were.
133        let stored = self.get_media_content_inner(&request_big, time).await.unwrap();
134        assert!(stored.is_none());
135        let stored = self.get_media_content_inner(&request_avg, time).await.unwrap();
136        assert!(stored.is_some());
137        let stored = self.get_media_content_inner(&request_small, time).await.unwrap();
138        assert!(stored.is_some());
139
140        // A cleanup doesn't have any effect.
141        self.clean_up_media_cache_inner(policy, time).await.unwrap();
142
143        let stored = self.get_media_content_inner(&request_avg, time).await.unwrap();
144        assert!(stored.is_some());
145        let stored = self.get_media_content_inner(&request_small, time).await.unwrap();
146        assert!(stored.is_some());
147
148        // Change to a policy that doesn't accept the average media.
149        let policy = MediaRetentionPolicy::empty().with_max_file_size(Some(100));
150
151        // The cleanup removes the average media.
152        self.clean_up_media_cache_inner(policy, time).await.unwrap();
153
154        let stored = self.get_media_content_inner(&request_avg, time).await.unwrap();
155        assert!(stored.is_none());
156        let stored = self.get_media_content_inner(&request_small, time).await.unwrap();
157        assert!(stored.is_some());
158
159        // Caching big and average media doesn't work.
160        self.add_media_content_inner(
161            &request_big,
162            content_big.clone(),
163            time,
164            policy,
165            IgnoreMediaRetentionPolicy::No,
166        )
167        .await
168        .unwrap();
169        self.add_media_content_inner(
170            &request_avg,
171            content_avg.clone(),
172            time,
173            policy,
174            IgnoreMediaRetentionPolicy::No,
175        )
176        .await
177        .unwrap();
178
179        let stored = self.get_media_content_inner(&request_big, time).await.unwrap();
180        assert!(stored.is_none());
181        let stored = self.get_media_content_inner(&request_avg, time).await.unwrap();
182        assert!(stored.is_none());
183
184        // If there are both a cache size and a file size, the minimum value is used.
185        let policy = MediaRetentionPolicy::empty()
186            .with_max_cache_size(Some(200))
187            .with_max_file_size(Some(1000));
188
189        // Caching big doesn't work.
190        self.add_media_content_inner(
191            &request_big,
192            content_big.clone(),
193            time,
194            policy,
195            IgnoreMediaRetentionPolicy::No,
196        )
197        .await
198        .unwrap();
199        self.add_media_content_inner(
200            &request_avg,
201            content_avg.clone(),
202            time,
203            policy,
204            IgnoreMediaRetentionPolicy::No,
205        )
206        .await
207        .unwrap();
208
209        let stored = self.get_media_content_inner(&request_big, time).await.unwrap();
210        assert!(stored.is_none());
211        let stored = self.get_media_content_inner(&request_avg, time).await.unwrap();
212        assert!(stored.is_some());
213
214        // Change to a policy that doesn't accept the average media.
215        let policy = MediaRetentionPolicy::empty()
216            .with_max_cache_size(Some(100))
217            .with_max_file_size(Some(1000));
218
219        // The cleanup removes the average media.
220        self.clean_up_media_cache_inner(policy, time).await.unwrap();
221
222        let stored = self.get_media_content_inner(&request_avg, time).await.unwrap();
223        assert!(stored.is_none());
224        let stored = self.get_media_content_inner(&request_small, time).await.unwrap();
225        assert!(stored.is_some());
226
227        // Caching big and average media doesn't work.
228        self.add_media_content_inner(
229            &request_big,
230            content_big,
231            time,
232            policy,
233            IgnoreMediaRetentionPolicy::No,
234        )
235        .await
236        .unwrap();
237        self.add_media_content_inner(
238            &request_avg,
239            content_avg,
240            time,
241            policy,
242            IgnoreMediaRetentionPolicy::No,
243        )
244        .await
245        .unwrap();
246
247        let stored = self.get_media_content_inner(&request_big, time).await.unwrap();
248        assert!(stored.is_none());
249        let stored = self.get_media_content_inner(&request_avg, time).await.unwrap();
250        assert!(stored.is_none());
251    }
252
253    async fn test_media_max_cache_size(&self) {
254        // 256 bytes content.
255        let content_big = vec![0; 256];
256        let uri_big = owned_mxc_uri!("mxc://localhost/big-media");
257        let request_big = MediaRequestParameters {
258            source: MediaSource::Plain(uri_big),
259            format: MediaFormat::File,
260        };
261
262        // 128 bytes content.
263        let content_avg = vec![0; 128];
264        let uri_avg = mxc_uri!("mxc://localhost/average-media");
265        let request_avg = MediaRequestParameters {
266            source: MediaSource::Plain(uri_avg.to_owned()),
267            format: MediaFormat::File,
268        };
269
270        // 64 bytes content.
271        let content_small = vec![0; 64];
272        let uri_small_1 = owned_mxc_uri!("mxc://localhost/small-media-1");
273        let request_small_1 = MediaRequestParameters {
274            source: MediaSource::Plain(uri_small_1),
275            format: MediaFormat::File,
276        };
277        let uri_small_2 = owned_mxc_uri!("mxc://localhost/small-media-2");
278        let request_small_2 = MediaRequestParameters {
279            source: MediaSource::Plain(uri_small_2),
280            format: MediaFormat::File,
281        };
282        let uri_small_3 = owned_mxc_uri!("mxc://localhost/small-media-3");
283        let request_small_3 = MediaRequestParameters {
284            source: MediaSource::Plain(uri_small_3),
285            format: MediaFormat::File,
286        };
287        let uri_small_4 = owned_mxc_uri!("mxc://localhost/small-media-4");
288        let request_small_4 = MediaRequestParameters {
289            source: MediaSource::Plain(uri_small_4),
290            format: MediaFormat::File,
291        };
292        let uri_small_5 = owned_mxc_uri!("mxc://localhost/small-media-5");
293        let request_small_5 = MediaRequestParameters {
294            source: MediaSource::Plain(uri_small_5),
295            format: MediaFormat::File,
296        };
297
298        // A policy that doesn't accept the big media.
299        let policy = MediaRetentionPolicy::empty().with_max_cache_size(Some(200));
300
301        // Try to add all the content at different times.
302        let mut time = SystemTime::UNIX_EPOCH;
303        self.add_media_content_inner(
304            &request_big,
305            content_big,
306            time,
307            policy,
308            IgnoreMediaRetentionPolicy::No,
309        )
310        .await
311        .unwrap();
312        time += Duration::from_secs(1);
313        self.add_media_content_inner(
314            &request_small_1,
315            content_small.clone(),
316            time,
317            policy,
318            IgnoreMediaRetentionPolicy::No,
319        )
320        .await
321        .unwrap();
322        time += Duration::from_secs(1);
323        self.add_media_content_inner(
324            &request_small_2,
325            content_small.clone(),
326            time,
327            policy,
328            IgnoreMediaRetentionPolicy::No,
329        )
330        .await
331        .unwrap();
332        time += Duration::from_secs(1);
333        self.add_media_content_inner(
334            &request_small_3,
335            content_small.clone(),
336            time,
337            policy,
338            IgnoreMediaRetentionPolicy::No,
339        )
340        .await
341        .unwrap();
342        time += Duration::from_secs(1);
343        self.add_media_content_inner(
344            &request_small_4,
345            content_small.clone(),
346            time,
347            policy,
348            IgnoreMediaRetentionPolicy::No,
349        )
350        .await
351        .unwrap();
352        time += Duration::from_secs(1);
353        self.add_media_content_inner(
354            &request_small_5,
355            content_small.clone(),
356            time,
357            policy,
358            IgnoreMediaRetentionPolicy::No,
359        )
360        .await
361        .unwrap();
362        time += Duration::from_secs(1);
363        self.add_media_content_inner(
364            &request_avg,
365            content_avg,
366            time,
367            policy,
368            IgnoreMediaRetentionPolicy::No,
369        )
370        .await
371        .unwrap();
372
373        // The big content was NOT cached but the others were.
374        time += Duration::from_secs(1);
375        let stored = self.get_media_content_inner(&request_big, time).await.unwrap();
376        assert!(stored.is_none());
377        time += Duration::from_secs(1);
378        let stored = self.get_media_content_inner(&request_small_1, time).await.unwrap();
379        assert!(stored.is_some());
380        time += Duration::from_secs(1);
381        let stored = self.get_media_content_inner(&request_small_2, time).await.unwrap();
382        assert!(stored.is_some());
383        time += Duration::from_secs(1);
384        let stored = self.get_media_content_inner(&request_small_3, time).await.unwrap();
385        assert!(stored.is_some());
386        time += Duration::from_secs(1);
387        let stored = self.get_media_content_inner(&request_small_4, time).await.unwrap();
388        assert!(stored.is_some());
389        time += Duration::from_secs(1);
390        let stored = self.get_media_content_inner(&request_small_5, time).await.unwrap();
391        assert!(stored.is_some());
392        time += Duration::from_secs(1);
393        let stored = self.get_media_content_for_uri_inner(uri_avg, time).await.unwrap();
394        assert!(stored.is_some());
395
396        // Cleanup removes the oldest content first.
397        time += Duration::from_secs(1);
398        self.clean_up_media_cache_inner(policy, time).await.unwrap();
399
400        time += Duration::from_secs(1);
401        let stored = self.get_media_content_inner(&request_small_1, time).await.unwrap();
402        assert!(stored.is_none());
403        time += Duration::from_secs(1);
404        let stored = self.get_media_content_inner(&request_small_2, time).await.unwrap();
405        assert!(stored.is_none());
406        time += Duration::from_secs(1);
407        let stored = self.get_media_content_inner(&request_small_3, time).await.unwrap();
408        assert!(stored.is_none());
409        time += Duration::from_secs(1);
410        let stored = self.get_media_content_inner(&request_small_4, time).await.unwrap();
411        assert!(stored.is_none());
412        time += Duration::from_secs(1);
413        let stored = self.get_media_content_inner(&request_small_5, time).await.unwrap();
414        assert!(stored.is_some());
415        time += Duration::from_secs(1);
416        let stored = self.get_media_content_inner(&request_avg, time).await.unwrap();
417        assert!(stored.is_some());
418
419        // Reinsert the small medias that were removed.
420        time += Duration::from_secs(1);
421        self.add_media_content_inner(
422            &request_small_1,
423            content_small.clone(),
424            time,
425            policy,
426            IgnoreMediaRetentionPolicy::No,
427        )
428        .await
429        .unwrap();
430        time += Duration::from_secs(1);
431        self.add_media_content_inner(
432            &request_small_2,
433            content_small.clone(),
434            time,
435            policy,
436            IgnoreMediaRetentionPolicy::No,
437        )
438        .await
439        .unwrap();
440        time += Duration::from_secs(1);
441        self.add_media_content_inner(
442            &request_small_3,
443            content_small.clone(),
444            time,
445            policy,
446            IgnoreMediaRetentionPolicy::No,
447        )
448        .await
449        .unwrap();
450        time += Duration::from_secs(1);
451        self.add_media_content_inner(
452            &request_small_4,
453            content_small,
454            time,
455            policy,
456            IgnoreMediaRetentionPolicy::No,
457        )
458        .await
459        .unwrap();
460
461        // Check that they are cached.
462        time += Duration::from_secs(1);
463        let stored = self.get_media_content_inner(&request_small_1, time).await.unwrap();
464        assert!(stored.is_some());
465        time += Duration::from_secs(1);
466        let stored = self.get_media_content_inner(&request_small_2, time).await.unwrap();
467        assert!(stored.is_some());
468        time += Duration::from_secs(1);
469        let stored = self.get_media_content_inner(&request_small_3, time).await.unwrap();
470        assert!(stored.is_some());
471        time += Duration::from_secs(1);
472        let stored = self.get_media_content_inner(&request_small_4, time).await.unwrap();
473        assert!(stored.is_some());
474
475        // Access small_5 too so its last access is updated too.
476        time += Duration::from_secs(1);
477        let stored = self.get_media_content_inner(&request_small_5, time).await.unwrap();
478        assert!(stored.is_some());
479
480        // Cleanup still removes the oldest content first, which is not the same as
481        // before.
482        time += Duration::from_secs(1);
483        tracing::info!(?self, "before");
484        self.clean_up_media_cache_inner(policy, time).await.unwrap();
485        tracing::info!(?self, "after");
486        time += Duration::from_secs(1);
487        let stored = self.get_media_content_inner(&request_small_1, time).await.unwrap();
488        assert!(stored.is_none());
489        time += Duration::from_secs(1);
490        let stored = self.get_media_content_inner(&request_small_2, time).await.unwrap();
491        assert!(stored.is_none());
492        time += Duration::from_secs(1);
493        let stored = self.get_media_content_inner(&request_small_3, time).await.unwrap();
494        assert!(stored.is_some());
495        time += Duration::from_secs(1);
496        let stored = self.get_media_content_inner(&request_small_4, time).await.unwrap();
497        assert!(stored.is_some());
498        time += Duration::from_secs(1);
499        let stored = self.get_media_content_inner(&request_small_5, time).await.unwrap();
500        assert!(stored.is_some());
501        time += Duration::from_secs(1);
502        let stored = self.get_media_content_inner(&request_avg, time).await.unwrap();
503        assert!(stored.is_none());
504    }
505
506    async fn test_media_expiry(&self) {
507        // 64 bytes content.
508        let content = vec![0; 64];
509
510        let uri_1 = owned_mxc_uri!("mxc://localhost/media-1");
511        let request_1 =
512            MediaRequestParameters { source: MediaSource::Plain(uri_1), format: MediaFormat::File };
513        let uri_2 = owned_mxc_uri!("mxc://localhost/media-2");
514        let request_2 =
515            MediaRequestParameters { source: MediaSource::Plain(uri_2), format: MediaFormat::File };
516        let uri_3 = owned_mxc_uri!("mxc://localhost/media-3");
517        let request_3 =
518            MediaRequestParameters { source: MediaSource::Plain(uri_3), format: MediaFormat::File };
519        let uri_4 = owned_mxc_uri!("mxc://localhost/media-4");
520        let request_4 =
521            MediaRequestParameters { source: MediaSource::Plain(uri_4), format: MediaFormat::File };
522        let uri_5 = owned_mxc_uri!("mxc://localhost/media-5");
523        let request_5 =
524            MediaRequestParameters { source: MediaSource::Plain(uri_5), format: MediaFormat::File };
525
526        // A policy with 30 seconds expiry.
527        let policy =
528            MediaRetentionPolicy::empty().with_last_access_expiry(Some(Duration::from_secs(30)));
529
530        // Add all the content at different times.
531        let mut time = SystemTime::UNIX_EPOCH;
532        self.add_media_content_inner(
533            &request_1,
534            content.clone(),
535            time,
536            policy,
537            IgnoreMediaRetentionPolicy::No,
538        )
539        .await
540        .unwrap();
541        time += Duration::from_secs(1);
542        self.add_media_content_inner(
543            &request_2,
544            content.clone(),
545            time,
546            policy,
547            IgnoreMediaRetentionPolicy::No,
548        )
549        .await
550        .unwrap();
551        time += Duration::from_secs(1);
552        self.add_media_content_inner(
553            &request_3,
554            content.clone(),
555            time,
556            policy,
557            IgnoreMediaRetentionPolicy::No,
558        )
559        .await
560        .unwrap();
561        time += Duration::from_secs(1);
562        self.add_media_content_inner(
563            &request_4,
564            content.clone(),
565            time,
566            policy,
567            IgnoreMediaRetentionPolicy::No,
568        )
569        .await
570        .unwrap();
571        time += Duration::from_secs(1);
572        self.add_media_content_inner(
573            &request_5,
574            content,
575            time,
576            policy,
577            IgnoreMediaRetentionPolicy::No,
578        )
579        .await
580        .unwrap();
581
582        // The content was cached.
583        time += Duration::from_secs(1);
584        let stored = self.get_media_content_inner(&request_1, time).await.unwrap();
585        assert!(stored.is_some());
586        time += Duration::from_secs(1);
587        let stored = self.get_media_content_inner(&request_2, time).await.unwrap();
588        assert!(stored.is_some());
589        time += Duration::from_secs(1);
590        let stored = self.get_media_content_inner(&request_3, time).await.unwrap();
591        assert!(stored.is_some());
592        time += Duration::from_secs(1);
593        let stored = self.get_media_content_inner(&request_4, time).await.unwrap();
594        assert!(stored.is_some());
595        time += Duration::from_secs(1);
596        let stored = self.get_media_content_inner(&request_5, time).await.unwrap();
597        assert!(stored.is_some());
598
599        // We are now at UNIX_EPOCH + 10 seconds, the oldest content was accessed 5
600        // seconds ago.
601        time += Duration::from_secs(1);
602        assert_eq!(time, SystemTime::UNIX_EPOCH + Duration::from_secs(10));
603
604        // Cleanup has no effect, nothing has expired.
605        self.clean_up_media_cache_inner(policy, time).await.unwrap();
606
607        time += Duration::from_secs(1);
608        let stored = self.get_media_content_inner(&request_1, time).await.unwrap();
609        assert!(stored.is_some());
610        time += Duration::from_secs(1);
611        let stored = self.get_media_content_inner(&request_2, time).await.unwrap();
612        assert!(stored.is_some());
613        time += Duration::from_secs(1);
614        let stored = self.get_media_content_inner(&request_3, time).await.unwrap();
615        assert!(stored.is_some());
616        time += Duration::from_secs(1);
617        let stored = self.get_media_content_inner(&request_4, time).await.unwrap();
618        assert!(stored.is_some());
619        time += Duration::from_secs(1);
620        let stored = self.get_media_content_inner(&request_5, time).await.unwrap();
621        assert!(stored.is_some());
622
623        // We are now at UNIX_EPOCH + 16 seconds, the oldest content was accessed 5
624        // seconds ago.
625        time += Duration::from_secs(1);
626        assert_eq!(time, SystemTime::UNIX_EPOCH + Duration::from_secs(16));
627
628        // Jump 26 seconds in the future, so the 2 first media contents are expired.
629        time += Duration::from_secs(26);
630
631        // Cleanup removes the two oldest media contents.
632        self.clean_up_media_cache_inner(policy, time).await.unwrap();
633
634        time += Duration::from_secs(1);
635        let stored = self.get_media_content_inner(&request_1, time).await.unwrap();
636        assert!(stored.is_none());
637        time += Duration::from_secs(1);
638        let stored = self.get_media_content_inner(&request_2, time).await.unwrap();
639        assert!(stored.is_none());
640        time += Duration::from_secs(1);
641        let stored = self.get_media_content_inner(&request_3, time).await.unwrap();
642        assert!(stored.is_some());
643        time += Duration::from_secs(1);
644        let stored = self.get_media_content_inner(&request_4, time).await.unwrap();
645        assert!(stored.is_some());
646        time += Duration::from_secs(1);
647        let stored = self.get_media_content_inner(&request_5, time).await.unwrap();
648        assert!(stored.is_some());
649    }
650
651    async fn test_media_ignore_max_size(&self) {
652        // 256 bytes content.
653        let content_big = vec![0; 256];
654        let uri_big = owned_mxc_uri!("mxc://localhost/big-media");
655        let request_big = MediaRequestParameters {
656            source: MediaSource::Plain(uri_big),
657            format: MediaFormat::File,
658        };
659
660        // 128 bytes content.
661        let content_avg = vec![0; 128];
662        let uri_avg = mxc_uri!("mxc://localhost/average-media");
663        let request_avg = MediaRequestParameters {
664            source: MediaSource::Plain(uri_avg.to_owned()),
665            format: MediaFormat::File,
666        };
667
668        // 64 bytes content.
669        let content_small = vec![0; 64];
670        let uri_small = owned_mxc_uri!("mxc://localhost/small-media-1");
671        let request_small = MediaRequestParameters {
672            source: MediaSource::Plain(uri_small),
673            format: MediaFormat::File,
674        };
675
676        // A policy that will result in only one media content in the cache, which is
677        // the average or small content, depending on the last access time.
678        let policy = MediaRetentionPolicy::empty().with_max_cache_size(Some(150));
679
680        // Try to add all the big content without ignoring the policy, it should fail.
681        let mut time = SystemTime::UNIX_EPOCH;
682        self.add_media_content_inner(
683            &request_big,
684            content_big.clone(),
685            time,
686            policy,
687            IgnoreMediaRetentionPolicy::No,
688        )
689        .await
690        .unwrap();
691
692        let stored = self.get_media_content_inner(&request_big, time).await.unwrap();
693        assert!(stored.is_none());
694
695        // Try to add it again but ignore the policy this time, it should succeed.
696        time += Duration::from_secs(1);
697        self.add_media_content_inner(
698            &request_big,
699            content_big,
700            time,
701            policy,
702            IgnoreMediaRetentionPolicy::Yes,
703        )
704        .await
705        .unwrap();
706
707        time += Duration::from_secs(1);
708        let stored = self.get_media_content_inner(&request_big, time).await.unwrap();
709        assert!(stored.is_some());
710
711        // Add the other contents.
712        time += Duration::from_secs(1);
713        self.add_media_content_inner(
714            &request_small,
715            content_small.clone(),
716            time,
717            policy,
718            IgnoreMediaRetentionPolicy::No,
719        )
720        .await
721        .unwrap();
722        time += Duration::from_secs(1);
723        self.add_media_content_inner(
724            &request_avg,
725            content_avg,
726            time,
727            policy,
728            IgnoreMediaRetentionPolicy::No,
729        )
730        .await
731        .unwrap();
732
733        // The other contents were added.
734        time += Duration::from_secs(1);
735        let stored = self.get_media_content_inner(&request_small, time).await.unwrap();
736        assert!(stored.is_some());
737        time += Duration::from_secs(1);
738        let stored = self.get_media_content_for_uri_inner(uri_avg, time).await.unwrap();
739        assert!(stored.is_some());
740
741        // Ignore the average content for now so the max cache size is not reached.
742        self.set_ignore_media_retention_policy_inner(&request_avg, IgnoreMediaRetentionPolicy::Yes)
743            .await
744            .unwrap();
745
746        // Because the big and average contents are ignored, cleanup has no effect.
747        time += Duration::from_secs(1);
748        self.clean_up_media_cache_inner(policy, time).await.unwrap();
749
750        time += Duration::from_secs(1);
751        let stored = self.get_media_content_inner(&request_small, time).await.unwrap();
752        assert!(stored.is_some());
753        time += Duration::from_secs(1);
754        let stored = self.get_media_content_for_uri_inner(uri_avg, time).await.unwrap();
755        assert!(stored.is_some());
756        time += Duration::from_secs(1);
757        let stored = self.get_media_content_inner(&request_big, time).await.unwrap();
758        assert!(stored.is_some());
759
760        // Stop ignoring the big media, it should then be cleaned up.
761        self.set_ignore_media_retention_policy_inner(&request_big, IgnoreMediaRetentionPolicy::No)
762            .await
763            .unwrap();
764
765        time += Duration::from_secs(1);
766        self.clean_up_media_cache_inner(policy, time).await.unwrap();
767
768        time += Duration::from_secs(1);
769        let stored = self.get_media_content_inner(&request_small, time).await.unwrap();
770        assert!(stored.is_some());
771        time += Duration::from_secs(1);
772        let stored = self.get_media_content_for_uri_inner(uri_avg, time).await.unwrap();
773        assert!(stored.is_some());
774        time += Duration::from_secs(1);
775        let stored = self.get_media_content_inner(&request_big, time).await.unwrap();
776        assert!(stored.is_none());
777
778        // Stop ignoring the average media. Since the cache size is bigger than
779        // the max, the content that was not the last accessed should be cleaned up.
780        self.set_ignore_media_retention_policy_inner(&request_avg, IgnoreMediaRetentionPolicy::No)
781            .await
782            .unwrap();
783
784        time += Duration::from_secs(1);
785        self.clean_up_media_cache_inner(policy, time).await.unwrap();
786
787        time += Duration::from_secs(1);
788        let stored = self.get_media_content_inner(&request_small, time).await.unwrap();
789        assert!(stored.is_none());
790        time += Duration::from_secs(1);
791        let stored = self.get_media_content_for_uri_inner(uri_avg, time).await.unwrap();
792        assert!(stored.is_some());
793        time += Duration::from_secs(1);
794        let stored = self.get_media_content_inner(&request_big, time).await.unwrap();
795        assert!(stored.is_none());
796    }
797
798    async fn test_media_ignore_expiry(&self) {
799        // 64 bytes content.
800        let content = vec![0; 64];
801
802        let uri_1 = owned_mxc_uri!("mxc://localhost/media-1");
803        let request_1 =
804            MediaRequestParameters { source: MediaSource::Plain(uri_1), format: MediaFormat::File };
805        let uri_2 = owned_mxc_uri!("mxc://localhost/media-2");
806        let request_2 =
807            MediaRequestParameters { source: MediaSource::Plain(uri_2), format: MediaFormat::File };
808        let uri_3 = owned_mxc_uri!("mxc://localhost/media-3");
809        let request_3 =
810            MediaRequestParameters { source: MediaSource::Plain(uri_3), format: MediaFormat::File };
811        let uri_4 = owned_mxc_uri!("mxc://localhost/media-4");
812        let request_4 =
813            MediaRequestParameters { source: MediaSource::Plain(uri_4), format: MediaFormat::File };
814        let uri_5 = owned_mxc_uri!("mxc://localhost/media-5");
815        let request_5 =
816            MediaRequestParameters { source: MediaSource::Plain(uri_5), format: MediaFormat::File };
817
818        // A policy with 30 seconds expiry.
819        let policy =
820            MediaRetentionPolicy::empty().with_last_access_expiry(Some(Duration::from_secs(30)));
821
822        // Add all the content at different times.
823        let mut time = SystemTime::UNIX_EPOCH;
824        self.add_media_content_inner(
825            &request_1,
826            content.clone(),
827            time,
828            policy,
829            IgnoreMediaRetentionPolicy::Yes,
830        )
831        .await
832        .unwrap();
833        time += Duration::from_secs(1);
834        self.add_media_content_inner(
835            &request_2,
836            content.clone(),
837            time,
838            policy,
839            IgnoreMediaRetentionPolicy::Yes,
840        )
841        .await
842        .unwrap();
843        time += Duration::from_secs(1);
844        self.add_media_content_inner(
845            &request_3,
846            content.clone(),
847            time,
848            policy,
849            IgnoreMediaRetentionPolicy::No,
850        )
851        .await
852        .unwrap();
853        time += Duration::from_secs(1);
854        self.add_media_content_inner(
855            &request_4,
856            content.clone(),
857            time,
858            policy,
859            IgnoreMediaRetentionPolicy::No,
860        )
861        .await
862        .unwrap();
863        time += Duration::from_secs(1);
864        self.add_media_content_inner(
865            &request_5,
866            content,
867            time,
868            policy,
869            IgnoreMediaRetentionPolicy::No,
870        )
871        .await
872        .unwrap();
873
874        // The content was cached.
875        time += Duration::from_secs(1);
876        let stored = self.get_media_content_inner(&request_1, time).await.unwrap();
877        assert!(stored.is_some());
878        time += Duration::from_secs(1);
879        let stored = self.get_media_content_inner(&request_2, time).await.unwrap();
880        assert!(stored.is_some());
881        time += Duration::from_secs(1);
882        let stored = self.get_media_content_inner(&request_3, time).await.unwrap();
883        assert!(stored.is_some());
884        time += Duration::from_secs(1);
885        let stored = self.get_media_content_inner(&request_4, time).await.unwrap();
886        assert!(stored.is_some());
887        time += Duration::from_secs(1);
888        let stored = self.get_media_content_inner(&request_5, time).await.unwrap();
889        assert!(stored.is_some());
890
891        // We advance of 120 seconds, all media should be expired.
892        time += Duration::from_secs(120);
893
894        // Cleanup removes all the media contents that are not ignored.
895        self.clean_up_media_cache_inner(policy, time).await.unwrap();
896
897        time += Duration::from_secs(1);
898        let stored = self.get_media_content_inner(&request_1, time).await.unwrap();
899        assert!(stored.is_some());
900        time += Duration::from_secs(1);
901        let stored = self.get_media_content_inner(&request_2, time).await.unwrap();
902        assert!(stored.is_some());
903        time += Duration::from_secs(1);
904        let stored = self.get_media_content_inner(&request_3, time).await.unwrap();
905        assert!(stored.is_none());
906        time += Duration::from_secs(1);
907        let stored = self.get_media_content_inner(&request_4, time).await.unwrap();
908        assert!(stored.is_none());
909        time += Duration::from_secs(1);
910        let stored = self.get_media_content_inner(&request_5, time).await.unwrap();
911        assert!(stored.is_none());
912
913        // Do no ignore the content anymore.
914        self.set_ignore_media_retention_policy_inner(&request_1, IgnoreMediaRetentionPolicy::No)
915            .await
916            .unwrap();
917        self.set_ignore_media_retention_policy_inner(&request_2, IgnoreMediaRetentionPolicy::No)
918            .await
919            .unwrap();
920
921        // We advance of 120 seconds, all media should be expired again.
922        time += Duration::from_secs(120);
923
924        // Cleanup removes the remaining media contents.
925        self.clean_up_media_cache_inner(policy, time).await.unwrap();
926
927        time += Duration::from_secs(1);
928        let stored = self.get_media_content_inner(&request_1, time).await.unwrap();
929        assert!(stored.is_none());
930        time += Duration::from_secs(1);
931        let stored = self.get_media_content_inner(&request_2, time).await.unwrap();
932        assert!(stored.is_none());
933        time += Duration::from_secs(1);
934        let stored = self.get_media_content_inner(&request_3, time).await.unwrap();
935        assert!(stored.is_none());
936        time += Duration::from_secs(1);
937        let stored = self.get_media_content_inner(&request_4, time).await.unwrap();
938        assert!(stored.is_none());
939        time += Duration::from_secs(1);
940        let stored = self.get_media_content_inner(&request_5, time).await.unwrap();
941        assert!(stored.is_none());
942    }
943
944    async fn test_store_last_media_cleanup_time(&self) {
945        let initial = self.last_media_cleanup_time_inner().await.unwrap();
946        let new_time = initial.unwrap_or_else(SystemTime::now) + Duration::from_secs(60);
947
948        // With an empty policy.
949        let policy = MediaRetentionPolicy::empty();
950        self.clean_up_media_cache_inner(policy, new_time).await.unwrap();
951
952        let stored = self.last_media_cleanup_time_inner().await.unwrap();
953        assert_eq!(stored, initial);
954
955        // With the default policy.
956        let policy = MediaRetentionPolicy::default();
957        self.clean_up_media_cache_inner(policy, new_time).await.unwrap();
958
959        let stored = self.last_media_cleanup_time_inner().await.unwrap();
960        assert_eq!(stored, Some(new_time));
961    }
962}
963
964/// Macro building to allow your [`EventCacheStoreMedia`] implementation to run
965/// the entire tests suite locally.
966///
967/// Can be run with the `with_media_size_tests` argument to include more tests
968/// about the media cache retention policy based on content size. It is not
969/// recommended to run those in encrypted stores because the size of the
970/// encrypted content may vary compared to what the tests expect.
971///
972/// You need to provide an `async fn get_event_cache_store() ->
973/// event_cache::store::Result<Store>` that provides a fresh event cache store
974/// that implements `EventCacheStoreMedia` on the same level you invoke the
975/// macro.
976///
977/// ## Usage Example:
978/// ```no_run
979/// # use matrix_sdk_base::event_cache::store::{
980/// #    EventCacheStore,
981/// #    MemoryStore as MyStore,
982/// #    Result as EventCacheStoreResult,
983/// # };
984///
985/// #[cfg(test)]
986/// mod tests {
987///     use super::{EventCacheStoreResult, MyStore};
988///
989///     async fn get_event_cache_store() -> EventCacheStoreResult<MyStore> {
990///         Ok(MyStore::new())
991///     }
992///
993///     event_cache_store_media_integration_tests!();
994/// }
995/// ```
996#[allow(unused_macros, unused_extern_crates)]
997#[macro_export]
998macro_rules! event_cache_store_media_integration_tests {
999    (with_media_size_tests) => {
1000        mod event_cache_store_media_integration_tests {
1001            $crate::event_cache_store_media_integration_tests!(@inner);
1002
1003            #[async_test]
1004            async fn test_media_max_file_size() {
1005                let event_cache_store_media = get_event_cache_store().await.unwrap();
1006                event_cache_store_media.test_media_max_file_size().await;
1007            }
1008
1009            #[async_test]
1010            async fn test_media_max_cache_size() {
1011                let event_cache_store_media = get_event_cache_store().await.unwrap();
1012                event_cache_store_media.test_media_max_cache_size().await;
1013            }
1014
1015            #[async_test]
1016            async fn test_media_ignore_max_size() {
1017                let event_cache_store_media = get_event_cache_store().await.unwrap();
1018                event_cache_store_media.test_media_ignore_max_size().await;
1019            }
1020        }
1021    };
1022
1023    () => {
1024        mod event_cache_store_media_integration_tests {
1025            $crate::event_cache_store_media_integration_tests!(@inner);
1026        }
1027    };
1028
1029    (@inner) => {
1030        use matrix_sdk_test::async_test;
1031        use $crate::event_cache::store::media::EventCacheStoreMediaIntegrationTests;
1032
1033        use super::get_event_cache_store;
1034
1035        #[async_test]
1036        async fn test_store_media_retention_policy() {
1037            let event_cache_store_media = get_event_cache_store().await.unwrap();
1038            event_cache_store_media.test_store_media_retention_policy().await;
1039        }
1040
1041        #[async_test]
1042        async fn test_media_expiry() {
1043            let event_cache_store_media = get_event_cache_store().await.unwrap();
1044            event_cache_store_media.test_media_expiry().await;
1045        }
1046
1047        #[async_test]
1048        async fn test_media_ignore_expiry() {
1049            let event_cache_store_media = get_event_cache_store().await.unwrap();
1050            event_cache_store_media.test_media_ignore_expiry().await;
1051        }
1052
1053        #[async_test]
1054        async fn test_store_last_media_cleanup_time() {
1055            let event_cache_store_media = get_event_cache_store().await.unwrap();
1056            event_cache_store_media.test_store_last_media_cleanup_time().await;
1057        }
1058    };
1059}