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