matrix_sdk_base/media/store/
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 `MediaStoreInner`
16//! implementations.
17
18use ruma::{
19    events::room::MediaSource,
20    media::Method,
21    mxc_uri, owned_mxc_uri,
22    time::{Duration, SystemTime},
23    uint,
24};
25
26use super::{MediaRetentionPolicy, MediaStoreInner, media_service::IgnoreMediaRetentionPolicy};
27use crate::media::{
28    MediaFormat, MediaRequestParameters, MediaThumbnailSettings, store::MediaStore,
29};
30
31/// [`MediaStoreInner`] integration tests.
32///
33/// This trait is not meant to be used directly, but will be used with the
34/// `media_store_inner_integration_tests!` macro.
35#[allow(async_fn_in_trait)]
36pub trait MediaStoreInnerIntegrationTests {
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
61impl<Store> MediaStoreInnerIntegrationTests for Store
62where
63    Store: MediaStoreInner + std::fmt::Debug,
64{
65    async fn test_store_media_retention_policy(&self) {
66        let stored = self.media_retention_policy_inner().await.unwrap();
67        assert!(stored.is_none());
68
69        let policy = MediaRetentionPolicy::default();
70        self.set_media_retention_policy_inner(policy).await.unwrap();
71
72        let stored = self.media_retention_policy_inner().await.unwrap();
73        assert_eq!(stored, Some(policy));
74    }
75
76    async fn test_media_max_file_size(&self) {
77        let time = SystemTime::now();
78
79        // 256 bytes content.
80        let content_big = vec![0; 256];
81        let uri_big = owned_mxc_uri!("mxc://localhost/big-media");
82        let request_big = MediaRequestParameters {
83            source: MediaSource::Plain(uri_big),
84            format: MediaFormat::File,
85        };
86
87        // 128 bytes content.
88        let content_avg = vec![0; 128];
89        let uri_avg = owned_mxc_uri!("mxc://localhost/average-media");
90        let request_avg = MediaRequestParameters {
91            source: MediaSource::Plain(uri_avg),
92            format: MediaFormat::File,
93        };
94
95        // 64 bytes content.
96        let content_small = vec![0; 64];
97        let uri_small = owned_mxc_uri!("mxc://localhost/small-media");
98        let request_small = MediaRequestParameters {
99            source: MediaSource::Plain(uri_small),
100            format: MediaFormat::File,
101        };
102
103        // First, with a policy that doesn't accept the big media.
104        let policy = MediaRetentionPolicy::empty().with_max_file_size(Some(200));
105
106        self.add_media_content_inner(
107            &request_big,
108            content_big.clone(),
109            time,
110            policy,
111            IgnoreMediaRetentionPolicy::No,
112        )
113        .await
114        .unwrap();
115        self.add_media_content_inner(
116            &request_avg,
117            content_avg.clone(),
118            time,
119            policy,
120            IgnoreMediaRetentionPolicy::No,
121        )
122        .await
123        .unwrap();
124        self.add_media_content_inner(
125            &request_small,
126            content_small,
127            time,
128            policy,
129            IgnoreMediaRetentionPolicy::No,
130        )
131        .await
132        .unwrap();
133
134        // The big content was NOT cached but the others were.
135        let stored = self.get_media_content_inner(&request_big, time).await.unwrap();
136        assert!(stored.is_none());
137        let stored = self.get_media_content_inner(&request_avg, time).await.unwrap();
138        assert!(stored.is_some());
139        let stored = self.get_media_content_inner(&request_small, time).await.unwrap();
140        assert!(stored.is_some());
141
142        // A cleanup doesn't have any effect.
143        self.clean_inner(policy, time).await.unwrap();
144
145        let stored = self.get_media_content_inner(&request_avg, time).await.unwrap();
146        assert!(stored.is_some());
147        let stored = self.get_media_content_inner(&request_small, time).await.unwrap();
148        assert!(stored.is_some());
149
150        // Change to a policy that doesn't accept the average media.
151        let policy = MediaRetentionPolicy::empty().with_max_file_size(Some(100));
152
153        // The cleanup removes the average media.
154        self.clean_inner(policy, time).await.unwrap();
155
156        let stored = self.get_media_content_inner(&request_avg, time).await.unwrap();
157        assert!(stored.is_none());
158        let stored = self.get_media_content_inner(&request_small, time).await.unwrap();
159        assert!(stored.is_some());
160
161        // Caching big and average media doesn't work.
162        self.add_media_content_inner(
163            &request_big,
164            content_big.clone(),
165            time,
166            policy,
167            IgnoreMediaRetentionPolicy::No,
168        )
169        .await
170        .unwrap();
171        self.add_media_content_inner(
172            &request_avg,
173            content_avg.clone(),
174            time,
175            policy,
176            IgnoreMediaRetentionPolicy::No,
177        )
178        .await
179        .unwrap();
180
181        let stored = self.get_media_content_inner(&request_big, time).await.unwrap();
182        assert!(stored.is_none());
183        let stored = self.get_media_content_inner(&request_avg, time).await.unwrap();
184        assert!(stored.is_none());
185
186        // If there are both a cache size and a file size, the minimum value is used.
187        let policy = MediaRetentionPolicy::empty()
188            .with_max_cache_size(Some(200))
189            .with_max_file_size(Some(1000));
190
191        // Caching big doesn't work.
192        self.add_media_content_inner(
193            &request_big,
194            content_big.clone(),
195            time,
196            policy,
197            IgnoreMediaRetentionPolicy::No,
198        )
199        .await
200        .unwrap();
201        self.add_media_content_inner(
202            &request_avg,
203            content_avg.clone(),
204            time,
205            policy,
206            IgnoreMediaRetentionPolicy::No,
207        )
208        .await
209        .unwrap();
210
211        let stored = self.get_media_content_inner(&request_big, time).await.unwrap();
212        assert!(stored.is_none());
213        let stored = self.get_media_content_inner(&request_avg, time).await.unwrap();
214        assert!(stored.is_some());
215
216        // Change to a policy that doesn't accept the average media.
217        let policy = MediaRetentionPolicy::empty()
218            .with_max_cache_size(Some(100))
219            .with_max_file_size(Some(1000));
220
221        // The cleanup removes the average media.
222        self.clean_inner(policy, time).await.unwrap();
223
224        let stored = self.get_media_content_inner(&request_avg, time).await.unwrap();
225        assert!(stored.is_none());
226        let stored = self.get_media_content_inner(&request_small, time).await.unwrap();
227        assert!(stored.is_some());
228
229        // Caching big and average media doesn't work.
230        self.add_media_content_inner(
231            &request_big,
232            content_big,
233            time,
234            policy,
235            IgnoreMediaRetentionPolicy::No,
236        )
237        .await
238        .unwrap();
239        self.add_media_content_inner(
240            &request_avg,
241            content_avg,
242            time,
243            policy,
244            IgnoreMediaRetentionPolicy::No,
245        )
246        .await
247        .unwrap();
248
249        let stored = self.get_media_content_inner(&request_big, time).await.unwrap();
250        assert!(stored.is_none());
251        let stored = self.get_media_content_inner(&request_avg, time).await.unwrap();
252        assert!(stored.is_none());
253    }
254
255    async fn test_media_max_cache_size(&self) {
256        // 256 bytes content.
257        let content_big = vec![0; 256];
258        let uri_big = owned_mxc_uri!("mxc://localhost/big-media");
259        let request_big = MediaRequestParameters {
260            source: MediaSource::Plain(uri_big),
261            format: MediaFormat::File,
262        };
263
264        // 128 bytes content.
265        let content_avg = vec![0; 128];
266        let uri_avg = mxc_uri!("mxc://localhost/average-media");
267        let request_avg = MediaRequestParameters {
268            source: MediaSource::Plain(uri_avg.to_owned()),
269            format: MediaFormat::File,
270        };
271
272        // 64 bytes content.
273        let content_small = vec![0; 64];
274        let uri_small_1 = owned_mxc_uri!("mxc://localhost/small-media-1");
275        let request_small_1 = MediaRequestParameters {
276            source: MediaSource::Plain(uri_small_1),
277            format: MediaFormat::File,
278        };
279        let uri_small_2 = owned_mxc_uri!("mxc://localhost/small-media-2");
280        let request_small_2 = MediaRequestParameters {
281            source: MediaSource::Plain(uri_small_2),
282            format: MediaFormat::File,
283        };
284        let uri_small_3 = owned_mxc_uri!("mxc://localhost/small-media-3");
285        let request_small_3 = MediaRequestParameters {
286            source: MediaSource::Plain(uri_small_3),
287            format: MediaFormat::File,
288        };
289        let uri_small_4 = owned_mxc_uri!("mxc://localhost/small-media-4");
290        let request_small_4 = MediaRequestParameters {
291            source: MediaSource::Plain(uri_small_4),
292            format: MediaFormat::File,
293        };
294        let uri_small_5 = owned_mxc_uri!("mxc://localhost/small-media-5");
295        let request_small_5 = MediaRequestParameters {
296            source: MediaSource::Plain(uri_small_5),
297            format: MediaFormat::File,
298        };
299
300        // A policy that doesn't accept the big media.
301        let policy = MediaRetentionPolicy::empty().with_max_cache_size(Some(200));
302
303        // Try to add all the content at different times.
304        let mut time = SystemTime::UNIX_EPOCH;
305        self.add_media_content_inner(
306            &request_big,
307            content_big,
308            time,
309            policy,
310            IgnoreMediaRetentionPolicy::No,
311        )
312        .await
313        .unwrap();
314        time += Duration::from_secs(1);
315        self.add_media_content_inner(
316            &request_small_1,
317            content_small.clone(),
318            time,
319            policy,
320            IgnoreMediaRetentionPolicy::No,
321        )
322        .await
323        .unwrap();
324        time += Duration::from_secs(1);
325        self.add_media_content_inner(
326            &request_small_2,
327            content_small.clone(),
328            time,
329            policy,
330            IgnoreMediaRetentionPolicy::No,
331        )
332        .await
333        .unwrap();
334        time += Duration::from_secs(1);
335        self.add_media_content_inner(
336            &request_small_3,
337            content_small.clone(),
338            time,
339            policy,
340            IgnoreMediaRetentionPolicy::No,
341        )
342        .await
343        .unwrap();
344        time += Duration::from_secs(1);
345        self.add_media_content_inner(
346            &request_small_4,
347            content_small.clone(),
348            time,
349            policy,
350            IgnoreMediaRetentionPolicy::No,
351        )
352        .await
353        .unwrap();
354        time += Duration::from_secs(1);
355        self.add_media_content_inner(
356            &request_small_5,
357            content_small.clone(),
358            time,
359            policy,
360            IgnoreMediaRetentionPolicy::No,
361        )
362        .await
363        .unwrap();
364        time += Duration::from_secs(1);
365        self.add_media_content_inner(
366            &request_avg,
367            content_avg,
368            time,
369            policy,
370            IgnoreMediaRetentionPolicy::No,
371        )
372        .await
373        .unwrap();
374
375        // The big content was NOT cached but the others were.
376        time += Duration::from_secs(1);
377        let stored = self.get_media_content_inner(&request_big, time).await.unwrap();
378        assert!(stored.is_none());
379        time += Duration::from_secs(1);
380        let stored = self.get_media_content_inner(&request_small_1, time).await.unwrap();
381        assert!(stored.is_some());
382        time += Duration::from_secs(1);
383        let stored = self.get_media_content_inner(&request_small_2, time).await.unwrap();
384        assert!(stored.is_some());
385        time += Duration::from_secs(1);
386        let stored = self.get_media_content_inner(&request_small_3, time).await.unwrap();
387        assert!(stored.is_some());
388        time += Duration::from_secs(1);
389        let stored = self.get_media_content_inner(&request_small_4, time).await.unwrap();
390        assert!(stored.is_some());
391        time += Duration::from_secs(1);
392        let stored = self.get_media_content_inner(&request_small_5, time).await.unwrap();
393        assert!(stored.is_some());
394        time += Duration::from_secs(1);
395        let stored = self.get_media_content_for_uri_inner(uri_avg, time).await.unwrap();
396        assert!(stored.is_some());
397
398        // Cleanup removes the oldest content first.
399        time += Duration::from_secs(1);
400        self.clean_inner(policy, time).await.unwrap();
401
402        time += Duration::from_secs(1);
403        let stored = self.get_media_content_inner(&request_small_1, time).await.unwrap();
404        assert!(stored.is_none());
405        time += Duration::from_secs(1);
406        let stored = self.get_media_content_inner(&request_small_2, time).await.unwrap();
407        assert!(stored.is_none());
408        time += Duration::from_secs(1);
409        let stored = self.get_media_content_inner(&request_small_3, time).await.unwrap();
410        assert!(stored.is_none());
411        time += Duration::from_secs(1);
412        let stored = self.get_media_content_inner(&request_small_4, time).await.unwrap();
413        assert!(stored.is_none());
414        time += Duration::from_secs(1);
415        let stored = self.get_media_content_inner(&request_small_5, time).await.unwrap();
416        assert!(stored.is_some());
417        time += Duration::from_secs(1);
418        let stored = self.get_media_content_inner(&request_avg, time).await.unwrap();
419        assert!(stored.is_some());
420
421        // Reinsert the small medias that were removed.
422        time += Duration::from_secs(1);
423        self.add_media_content_inner(
424            &request_small_1,
425            content_small.clone(),
426            time,
427            policy,
428            IgnoreMediaRetentionPolicy::No,
429        )
430        .await
431        .unwrap();
432        time += Duration::from_secs(1);
433        self.add_media_content_inner(
434            &request_small_2,
435            content_small.clone(),
436            time,
437            policy,
438            IgnoreMediaRetentionPolicy::No,
439        )
440        .await
441        .unwrap();
442        time += Duration::from_secs(1);
443        self.add_media_content_inner(
444            &request_small_3,
445            content_small.clone(),
446            time,
447            policy,
448            IgnoreMediaRetentionPolicy::No,
449        )
450        .await
451        .unwrap();
452        time += Duration::from_secs(1);
453        self.add_media_content_inner(
454            &request_small_4,
455            content_small,
456            time,
457            policy,
458            IgnoreMediaRetentionPolicy::No,
459        )
460        .await
461        .unwrap();
462
463        // Check that they are cached.
464        time += Duration::from_secs(1);
465        let stored = self.get_media_content_inner(&request_small_1, time).await.unwrap();
466        assert!(stored.is_some());
467        time += Duration::from_secs(1);
468        let stored = self.get_media_content_inner(&request_small_2, time).await.unwrap();
469        assert!(stored.is_some());
470        time += Duration::from_secs(1);
471        let stored = self.get_media_content_inner(&request_small_3, time).await.unwrap();
472        assert!(stored.is_some());
473        time += Duration::from_secs(1);
474        let stored = self.get_media_content_inner(&request_small_4, time).await.unwrap();
475        assert!(stored.is_some());
476
477        // Access small_5 too so its last access is updated too.
478        time += Duration::from_secs(1);
479        let stored = self.get_media_content_inner(&request_small_5, time).await.unwrap();
480        assert!(stored.is_some());
481
482        // Cleanup still removes the oldest content first, which is not the same as
483        // before.
484        time += Duration::from_secs(1);
485        tracing::info!(?self, "before");
486        self.clean_inner(policy, time).await.unwrap();
487        tracing::info!(?self, "after");
488        time += Duration::from_secs(1);
489        let stored = self.get_media_content_inner(&request_small_1, time).await.unwrap();
490        assert!(stored.is_none());
491        time += Duration::from_secs(1);
492        let stored = self.get_media_content_inner(&request_small_2, time).await.unwrap();
493        assert!(stored.is_none());
494        time += Duration::from_secs(1);
495        let stored = self.get_media_content_inner(&request_small_3, time).await.unwrap();
496        assert!(stored.is_some());
497        time += Duration::from_secs(1);
498        let stored = self.get_media_content_inner(&request_small_4, time).await.unwrap();
499        assert!(stored.is_some());
500        time += Duration::from_secs(1);
501        let stored = self.get_media_content_inner(&request_small_5, time).await.unwrap();
502        assert!(stored.is_some());
503        time += Duration::from_secs(1);
504        let stored = self.get_media_content_inner(&request_avg, time).await.unwrap();
505        assert!(stored.is_none());
506    }
507
508    async fn test_media_expiry(&self) {
509        // 64 bytes content.
510        let content = vec![0; 64];
511
512        let uri_1 = owned_mxc_uri!("mxc://localhost/media-1");
513        let request_1 =
514            MediaRequestParameters { source: MediaSource::Plain(uri_1), format: MediaFormat::File };
515        let uri_2 = owned_mxc_uri!("mxc://localhost/media-2");
516        let request_2 =
517            MediaRequestParameters { source: MediaSource::Plain(uri_2), format: MediaFormat::File };
518        let uri_3 = owned_mxc_uri!("mxc://localhost/media-3");
519        let request_3 =
520            MediaRequestParameters { source: MediaSource::Plain(uri_3), format: MediaFormat::File };
521        let uri_4 = owned_mxc_uri!("mxc://localhost/media-4");
522        let request_4 =
523            MediaRequestParameters { source: MediaSource::Plain(uri_4), format: MediaFormat::File };
524        let uri_5 = owned_mxc_uri!("mxc://localhost/media-5");
525        let request_5 =
526            MediaRequestParameters { source: MediaSource::Plain(uri_5), format: MediaFormat::File };
527
528        // A policy with 30 seconds expiry.
529        let policy =
530            MediaRetentionPolicy::empty().with_last_access_expiry(Some(Duration::from_secs(30)));
531
532        // Add all the content at different times.
533        let mut time = SystemTime::UNIX_EPOCH;
534        self.add_media_content_inner(
535            &request_1,
536            content.clone(),
537            time,
538            policy,
539            IgnoreMediaRetentionPolicy::No,
540        )
541        .await
542        .unwrap();
543        time += Duration::from_secs(1);
544        self.add_media_content_inner(
545            &request_2,
546            content.clone(),
547            time,
548            policy,
549            IgnoreMediaRetentionPolicy::No,
550        )
551        .await
552        .unwrap();
553        time += Duration::from_secs(1);
554        self.add_media_content_inner(
555            &request_3,
556            content.clone(),
557            time,
558            policy,
559            IgnoreMediaRetentionPolicy::No,
560        )
561        .await
562        .unwrap();
563        time += Duration::from_secs(1);
564        self.add_media_content_inner(
565            &request_4,
566            content.clone(),
567            time,
568            policy,
569            IgnoreMediaRetentionPolicy::No,
570        )
571        .await
572        .unwrap();
573        time += Duration::from_secs(1);
574        self.add_media_content_inner(
575            &request_5,
576            content,
577            time,
578            policy,
579            IgnoreMediaRetentionPolicy::No,
580        )
581        .await
582        .unwrap();
583
584        // The content was cached.
585        time += Duration::from_secs(1);
586        let stored = self.get_media_content_inner(&request_1, time).await.unwrap();
587        assert!(stored.is_some());
588        time += Duration::from_secs(1);
589        let stored = self.get_media_content_inner(&request_2, time).await.unwrap();
590        assert!(stored.is_some());
591        time += Duration::from_secs(1);
592        let stored = self.get_media_content_inner(&request_3, time).await.unwrap();
593        assert!(stored.is_some());
594        time += Duration::from_secs(1);
595        let stored = self.get_media_content_inner(&request_4, time).await.unwrap();
596        assert!(stored.is_some());
597        time += Duration::from_secs(1);
598        let stored = self.get_media_content_inner(&request_5, time).await.unwrap();
599        assert!(stored.is_some());
600
601        // We are now at UNIX_EPOCH + 10 seconds, the oldest content was accessed 5
602        // seconds ago.
603        time += Duration::from_secs(1);
604        assert_eq!(time, SystemTime::UNIX_EPOCH + Duration::from_secs(10));
605
606        // Cleanup has no effect, nothing has expired.
607        self.clean_inner(policy, time).await.unwrap();
608
609        time += Duration::from_secs(1);
610        let stored = self.get_media_content_inner(&request_1, time).await.unwrap();
611        assert!(stored.is_some());
612        time += Duration::from_secs(1);
613        let stored = self.get_media_content_inner(&request_2, time).await.unwrap();
614        assert!(stored.is_some());
615        time += Duration::from_secs(1);
616        let stored = self.get_media_content_inner(&request_3, time).await.unwrap();
617        assert!(stored.is_some());
618        time += Duration::from_secs(1);
619        let stored = self.get_media_content_inner(&request_4, time).await.unwrap();
620        assert!(stored.is_some());
621        time += Duration::from_secs(1);
622        let stored = self.get_media_content_inner(&request_5, time).await.unwrap();
623        assert!(stored.is_some());
624
625        // We are now at UNIX_EPOCH + 16 seconds, the oldest content was accessed 5
626        // seconds ago.
627        time += Duration::from_secs(1);
628        assert_eq!(time, SystemTime::UNIX_EPOCH + Duration::from_secs(16));
629
630        // Jump 26 seconds in the future, so the 2 first media contents are expired.
631        time += Duration::from_secs(26);
632
633        // Cleanup removes the two oldest media contents.
634        self.clean_inner(policy, time).await.unwrap();
635
636        time += Duration::from_secs(1);
637        let stored = self.get_media_content_inner(&request_1, time).await.unwrap();
638        assert!(stored.is_none());
639        time += Duration::from_secs(1);
640        let stored = self.get_media_content_inner(&request_2, time).await.unwrap();
641        assert!(stored.is_none());
642        time += Duration::from_secs(1);
643        let stored = self.get_media_content_inner(&request_3, time).await.unwrap();
644        assert!(stored.is_some());
645        time += Duration::from_secs(1);
646        let stored = self.get_media_content_inner(&request_4, time).await.unwrap();
647        assert!(stored.is_some());
648        time += Duration::from_secs(1);
649        let stored = self.get_media_content_inner(&request_5, time).await.unwrap();
650        assert!(stored.is_some());
651    }
652
653    async fn test_media_ignore_max_size(&self) {
654        // 256 bytes content.
655        let content_big = vec![0; 256];
656        let uri_big = owned_mxc_uri!("mxc://localhost/big-media");
657        let request_big = MediaRequestParameters {
658            source: MediaSource::Plain(uri_big),
659            format: MediaFormat::File,
660        };
661
662        // 128 bytes content.
663        let content_avg = vec![0; 128];
664        let uri_avg = mxc_uri!("mxc://localhost/average-media");
665        let request_avg = MediaRequestParameters {
666            source: MediaSource::Plain(uri_avg.to_owned()),
667            format: MediaFormat::File,
668        };
669
670        // 64 bytes content.
671        let content_small = vec![0; 64];
672        let uri_small = owned_mxc_uri!("mxc://localhost/small-media-1");
673        let request_small = MediaRequestParameters {
674            source: MediaSource::Plain(uri_small),
675            format: MediaFormat::File,
676        };
677
678        // A policy that will result in only one media content in the cache, which is
679        // the average or small content, depending on the last access time.
680        let policy = MediaRetentionPolicy::empty().with_max_cache_size(Some(150));
681
682        // Try to add all the big content without ignoring the policy, it should fail.
683        let mut time = SystemTime::UNIX_EPOCH;
684        self.add_media_content_inner(
685            &request_big,
686            content_big.clone(),
687            time,
688            policy,
689            IgnoreMediaRetentionPolicy::No,
690        )
691        .await
692        .unwrap();
693
694        let stored = self.get_media_content_inner(&request_big, time).await.unwrap();
695        assert!(stored.is_none());
696
697        // Try to add it again but ignore the policy this time, it should succeed.
698        time += Duration::from_secs(1);
699        self.add_media_content_inner(
700            &request_big,
701            content_big,
702            time,
703            policy,
704            IgnoreMediaRetentionPolicy::Yes,
705        )
706        .await
707        .unwrap();
708
709        time += Duration::from_secs(1);
710        let stored = self.get_media_content_inner(&request_big, time).await.unwrap();
711        assert!(stored.is_some());
712
713        // Add the other contents.
714        time += Duration::from_secs(1);
715        self.add_media_content_inner(
716            &request_small,
717            content_small.clone(),
718            time,
719            policy,
720            IgnoreMediaRetentionPolicy::No,
721        )
722        .await
723        .unwrap();
724        time += Duration::from_secs(1);
725        self.add_media_content_inner(
726            &request_avg,
727            content_avg,
728            time,
729            policy,
730            IgnoreMediaRetentionPolicy::No,
731        )
732        .await
733        .unwrap();
734
735        // The other contents were added.
736        time += Duration::from_secs(1);
737        let stored = self.get_media_content_inner(&request_small, time).await.unwrap();
738        assert!(stored.is_some());
739        time += Duration::from_secs(1);
740        let stored = self.get_media_content_for_uri_inner(uri_avg, time).await.unwrap();
741        assert!(stored.is_some());
742
743        // Ignore the average content for now so the max cache size is not reached.
744        self.set_ignore_media_retention_policy_inner(&request_avg, IgnoreMediaRetentionPolicy::Yes)
745            .await
746            .unwrap();
747
748        // Because the big and average contents are ignored, cleanup has no effect.
749        time += Duration::from_secs(1);
750        self.clean_inner(policy, time).await.unwrap();
751
752        time += Duration::from_secs(1);
753        let stored = self.get_media_content_inner(&request_small, time).await.unwrap();
754        assert!(stored.is_some());
755        time += Duration::from_secs(1);
756        let stored = self.get_media_content_for_uri_inner(uri_avg, time).await.unwrap();
757        assert!(stored.is_some());
758        time += Duration::from_secs(1);
759        let stored = self.get_media_content_inner(&request_big, time).await.unwrap();
760        assert!(stored.is_some());
761
762        // Stop ignoring the big media, it should then be cleaned up.
763        self.set_ignore_media_retention_policy_inner(&request_big, IgnoreMediaRetentionPolicy::No)
764            .await
765            .unwrap();
766
767        time += Duration::from_secs(1);
768        self.clean_inner(policy, time).await.unwrap();
769
770        time += Duration::from_secs(1);
771        let stored = self.get_media_content_inner(&request_small, time).await.unwrap();
772        assert!(stored.is_some());
773        time += Duration::from_secs(1);
774        let stored = self.get_media_content_for_uri_inner(uri_avg, time).await.unwrap();
775        assert!(stored.is_some());
776        time += Duration::from_secs(1);
777        let stored = self.get_media_content_inner(&request_big, time).await.unwrap();
778        assert!(stored.is_none());
779
780        // Stop ignoring the average media. Since the cache size is bigger than
781        // the max, the content that was not the last accessed should be cleaned up.
782        self.set_ignore_media_retention_policy_inner(&request_avg, IgnoreMediaRetentionPolicy::No)
783            .await
784            .unwrap();
785
786        time += Duration::from_secs(1);
787        self.clean_inner(policy, time).await.unwrap();
788
789        time += Duration::from_secs(1);
790        let stored = self.get_media_content_inner(&request_small, time).await.unwrap();
791        assert!(stored.is_none());
792        time += Duration::from_secs(1);
793        let stored = self.get_media_content_for_uri_inner(uri_avg, time).await.unwrap();
794        assert!(stored.is_some());
795        time += Duration::from_secs(1);
796        let stored = self.get_media_content_inner(&request_big, time).await.unwrap();
797        assert!(stored.is_none());
798    }
799
800    async fn test_media_ignore_expiry(&self) {
801        // 64 bytes content.
802        let content = vec![0; 64];
803
804        let uri_1 = owned_mxc_uri!("mxc://localhost/media-1");
805        let request_1 =
806            MediaRequestParameters { source: MediaSource::Plain(uri_1), format: MediaFormat::File };
807        let uri_2 = owned_mxc_uri!("mxc://localhost/media-2");
808        let request_2 =
809            MediaRequestParameters { source: MediaSource::Plain(uri_2), format: MediaFormat::File };
810        let uri_3 = owned_mxc_uri!("mxc://localhost/media-3");
811        let request_3 =
812            MediaRequestParameters { source: MediaSource::Plain(uri_3), format: MediaFormat::File };
813        let uri_4 = owned_mxc_uri!("mxc://localhost/media-4");
814        let request_4 =
815            MediaRequestParameters { source: MediaSource::Plain(uri_4), format: MediaFormat::File };
816        let uri_5 = owned_mxc_uri!("mxc://localhost/media-5");
817        let request_5 =
818            MediaRequestParameters { source: MediaSource::Plain(uri_5), format: MediaFormat::File };
819
820        // A policy with 30 seconds expiry.
821        let policy =
822            MediaRetentionPolicy::empty().with_last_access_expiry(Some(Duration::from_secs(30)));
823
824        // Add all the content at different times.
825        let mut time = SystemTime::UNIX_EPOCH;
826        self.add_media_content_inner(
827            &request_1,
828            content.clone(),
829            time,
830            policy,
831            IgnoreMediaRetentionPolicy::Yes,
832        )
833        .await
834        .unwrap();
835        time += Duration::from_secs(1);
836        self.add_media_content_inner(
837            &request_2,
838            content.clone(),
839            time,
840            policy,
841            IgnoreMediaRetentionPolicy::Yes,
842        )
843        .await
844        .unwrap();
845        time += Duration::from_secs(1);
846        self.add_media_content_inner(
847            &request_3,
848            content.clone(),
849            time,
850            policy,
851            IgnoreMediaRetentionPolicy::No,
852        )
853        .await
854        .unwrap();
855        time += Duration::from_secs(1);
856        self.add_media_content_inner(
857            &request_4,
858            content.clone(),
859            time,
860            policy,
861            IgnoreMediaRetentionPolicy::No,
862        )
863        .await
864        .unwrap();
865        time += Duration::from_secs(1);
866        self.add_media_content_inner(
867            &request_5,
868            content,
869            time,
870            policy,
871            IgnoreMediaRetentionPolicy::No,
872        )
873        .await
874        .unwrap();
875
876        // The content was cached.
877        time += Duration::from_secs(1);
878        let stored = self.get_media_content_inner(&request_1, time).await.unwrap();
879        assert!(stored.is_some());
880        time += Duration::from_secs(1);
881        let stored = self.get_media_content_inner(&request_2, time).await.unwrap();
882        assert!(stored.is_some());
883        time += Duration::from_secs(1);
884        let stored = self.get_media_content_inner(&request_3, time).await.unwrap();
885        assert!(stored.is_some());
886        time += Duration::from_secs(1);
887        let stored = self.get_media_content_inner(&request_4, time).await.unwrap();
888        assert!(stored.is_some());
889        time += Duration::from_secs(1);
890        let stored = self.get_media_content_inner(&request_5, time).await.unwrap();
891        assert!(stored.is_some());
892
893        // We advance of 120 seconds, all media should be expired.
894        time += Duration::from_secs(120);
895
896        // Cleanup removes all the media contents that are not ignored.
897        self.clean_inner(policy, time).await.unwrap();
898
899        time += Duration::from_secs(1);
900        let stored = self.get_media_content_inner(&request_1, time).await.unwrap();
901        assert!(stored.is_some());
902        time += Duration::from_secs(1);
903        let stored = self.get_media_content_inner(&request_2, time).await.unwrap();
904        assert!(stored.is_some());
905        time += Duration::from_secs(1);
906        let stored = self.get_media_content_inner(&request_3, time).await.unwrap();
907        assert!(stored.is_none());
908        time += Duration::from_secs(1);
909        let stored = self.get_media_content_inner(&request_4, time).await.unwrap();
910        assert!(stored.is_none());
911        time += Duration::from_secs(1);
912        let stored = self.get_media_content_inner(&request_5, time).await.unwrap();
913        assert!(stored.is_none());
914
915        // Do no ignore the content anymore.
916        self.set_ignore_media_retention_policy_inner(&request_1, IgnoreMediaRetentionPolicy::No)
917            .await
918            .unwrap();
919        self.set_ignore_media_retention_policy_inner(&request_2, IgnoreMediaRetentionPolicy::No)
920            .await
921            .unwrap();
922
923        // We advance of 120 seconds, all media should be expired again.
924        time += Duration::from_secs(120);
925
926        // Cleanup removes the remaining media contents.
927        self.clean_inner(policy, time).await.unwrap();
928
929        time += Duration::from_secs(1);
930        let stored = self.get_media_content_inner(&request_1, time).await.unwrap();
931        assert!(stored.is_none());
932        time += Duration::from_secs(1);
933        let stored = self.get_media_content_inner(&request_2, time).await.unwrap();
934        assert!(stored.is_none());
935        time += Duration::from_secs(1);
936        let stored = self.get_media_content_inner(&request_3, time).await.unwrap();
937        assert!(stored.is_none());
938        time += Duration::from_secs(1);
939        let stored = self.get_media_content_inner(&request_4, time).await.unwrap();
940        assert!(stored.is_none());
941        time += Duration::from_secs(1);
942        let stored = self.get_media_content_inner(&request_5, time).await.unwrap();
943        assert!(stored.is_none());
944    }
945
946    async fn test_store_last_media_cleanup_time(&self) {
947        let initial = self.last_media_cleanup_time_inner().await.unwrap();
948        let new_time = initial.unwrap_or_else(SystemTime::now) + Duration::from_secs(60);
949
950        // With an empty policy.
951        let policy = MediaRetentionPolicy::empty();
952        self.clean_inner(policy, new_time).await.unwrap();
953
954        let stored = self.last_media_cleanup_time_inner().await.unwrap();
955        assert_eq!(stored, initial);
956
957        // With the default policy.
958        let policy = MediaRetentionPolicy::default();
959        self.clean_inner(policy, new_time).await.unwrap();
960
961        let stored = self.last_media_cleanup_time_inner().await.unwrap();
962        assert_eq!(stored, Some(new_time));
963    }
964}
965
966/// Macro building to allow your [`MediaStoreInner`] implementation to run
967/// the entire tests suite locally.
968///
969/// Can be run with the `with_media_size_tests` argument to include more tests
970/// about the media cache retention policy based on content size. It is not
971/// recommended to run those in encrypted stores because the size of the
972/// encrypted content may vary compared to what the tests expect.
973///
974/// You need to provide an `async fn get_media_store() ->
975/// media::store::Result<Store>` that provides a fresh media store
976/// that implements `MediaStoreInner` on the same level you invoke the
977/// macro.
978///
979/// ## Usage Example:
980/// ```no_run
981/// # use matrix_sdk_base::media::store::{
982/// #    MediaStore,
983/// #    MemoryMediaStore as MyStore,
984/// #    Result as MediaStoreResult,
985/// # };
986///
987/// #[cfg(test)]
988/// mod tests {
989///     use super::{MediaStoreResult, MyStore};
990///
991///     async fn get_media_store() -> MediaStoreResult<MyStore> {
992///         Ok(MyStore::new())
993///     }
994///
995///     media_store_inner_integration_tests!();
996/// }
997/// ```
998#[allow(unused_macros, unused_extern_crates)]
999#[macro_export]
1000macro_rules! media_store_inner_integration_tests {
1001    (with_media_size_tests) => {
1002        mod media_store_inner_integration_tests {
1003            $crate::media_store_inner_integration_tests!(@inner);
1004
1005            #[async_test]
1006            async fn test_media_max_file_size() {
1007                let media_store_inner = get_media_store().await.unwrap();
1008                media_store_inner.test_media_max_file_size().await;
1009            }
1010
1011            #[async_test]
1012            async fn test_media_max_cache_size() {
1013                let media_store_inner = get_media_store().await.unwrap();
1014                media_store_inner.test_media_max_cache_size().await;
1015            }
1016
1017            #[async_test]
1018            async fn test_media_ignore_max_size() {
1019                let media_store_inner = get_media_store().await.unwrap();
1020                media_store_inner.test_media_ignore_max_size().await;
1021            }
1022        }
1023    };
1024
1025    () => {
1026        mod media_store_inner_integration_tests {
1027            $crate::media_store_inner_integration_tests!(@inner);
1028        }
1029    };
1030
1031    (@inner) => {
1032        use matrix_sdk_test::async_test;
1033        use $crate::media::store::MediaStoreInnerIntegrationTests;
1034
1035        use super::get_media_store;
1036
1037        #[async_test]
1038        async fn test_store_media_retention_policy() {
1039            let media_store_inner = get_media_store().await.unwrap();
1040            media_store_inner.test_store_media_retention_policy().await;
1041        }
1042
1043        #[async_test]
1044        async fn test_media_expiry() {
1045            let media_store_inner = get_media_store().await.unwrap();
1046            media_store_inner.test_media_expiry().await;
1047        }
1048
1049        #[async_test]
1050        async fn test_media_ignore_expiry() {
1051            let media_store_inner = get_media_store().await.unwrap();
1052            media_store_inner.test_media_ignore_expiry().await;
1053        }
1054
1055        #[async_test]
1056        async fn test_store_last_media_cleanup_time() {
1057            let media_store_inner = get_media_store().await.unwrap();
1058            media_store_inner.test_store_last_media_cleanup_time().await;
1059        }
1060    };
1061}
1062
1063/// [`MediaStore`] integration tests.
1064///
1065/// This trait is not meant to be used directly, but will be used with the
1066/// `media_store_inner_integration_tests!` macro.
1067#[allow(async_fn_in_trait)]
1068pub trait MediaStoreIntegrationTests {
1069    /// Test media content storage.
1070    async fn test_media_content(&self);
1071
1072    /// Test replacing a MXID.
1073    async fn test_replace_media_key(&self);
1074}
1075
1076impl<Store> MediaStoreIntegrationTests for Store
1077where
1078    Store: MediaStore + std::fmt::Debug,
1079{
1080    async fn test_media_content(&self) {
1081        let uri = mxc_uri!("mxc://localhost/media");
1082        let request_file = MediaRequestParameters {
1083            source: MediaSource::Plain(uri.to_owned()),
1084            format: MediaFormat::File,
1085        };
1086        let request_thumbnail = MediaRequestParameters {
1087            source: MediaSource::Plain(uri.to_owned()),
1088            format: MediaFormat::Thumbnail(MediaThumbnailSettings::with_method(
1089                Method::Crop,
1090                uint!(100),
1091                uint!(100),
1092            )),
1093        };
1094
1095        let other_uri = mxc_uri!("mxc://localhost/media-other");
1096        let request_other_file = MediaRequestParameters {
1097            source: MediaSource::Plain(other_uri.to_owned()),
1098            format: MediaFormat::File,
1099        };
1100
1101        let content: Vec<u8> = "hello".into();
1102        let thumbnail_content: Vec<u8> = "world".into();
1103        let other_content: Vec<u8> = "foo".into();
1104
1105        // Media isn't present in the cache.
1106        assert!(
1107            self.get_media_content(&request_file).await.unwrap().is_none(),
1108            "unexpected media found"
1109        );
1110        assert!(
1111            self.get_media_content(&request_thumbnail).await.unwrap().is_none(),
1112            "media not found"
1113        );
1114
1115        // Let's add the media.
1116        self.add_media_content(&request_file, content.clone(), IgnoreMediaRetentionPolicy::No)
1117            .await
1118            .expect("adding media failed");
1119
1120        // Media is present in the cache.
1121        assert_eq!(
1122            self.get_media_content(&request_file).await.unwrap().as_ref(),
1123            Some(&content),
1124            "media not found though added"
1125        );
1126        assert_eq!(
1127            self.get_media_content_for_uri(uri).await.unwrap().as_ref(),
1128            Some(&content),
1129            "media not found by URI though added"
1130        );
1131
1132        // Let's remove the media.
1133        self.remove_media_content(&request_file).await.expect("removing media failed");
1134
1135        // Media isn't present in the cache.
1136        assert!(
1137            self.get_media_content(&request_file).await.unwrap().is_none(),
1138            "media still there after removing"
1139        );
1140        assert!(
1141            self.get_media_content_for_uri(uri).await.unwrap().is_none(),
1142            "media still found by URI after removing"
1143        );
1144
1145        // Let's add the media again.
1146        self.add_media_content(&request_file, content.clone(), IgnoreMediaRetentionPolicy::No)
1147            .await
1148            .expect("adding media again failed");
1149
1150        assert_eq!(
1151            self.get_media_content(&request_file).await.unwrap().as_ref(),
1152            Some(&content),
1153            "media not found after adding again"
1154        );
1155
1156        // Let's add the thumbnail media.
1157        self.add_media_content(
1158            &request_thumbnail,
1159            thumbnail_content.clone(),
1160            IgnoreMediaRetentionPolicy::No,
1161        )
1162        .await
1163        .expect("adding thumbnail failed");
1164
1165        // Media's thumbnail is present.
1166        assert_eq!(
1167            self.get_media_content(&request_thumbnail).await.unwrap().as_ref(),
1168            Some(&thumbnail_content),
1169            "thumbnail not found"
1170        );
1171
1172        // We get a file with the URI, we don't know which one.
1173        assert!(
1174            self.get_media_content_for_uri(uri).await.unwrap().is_some(),
1175            "media not found by URI though two where added"
1176        );
1177
1178        // Let's add another media with a different URI.
1179        self.add_media_content(
1180            &request_other_file,
1181            other_content.clone(),
1182            IgnoreMediaRetentionPolicy::No,
1183        )
1184        .await
1185        .expect("adding other media failed");
1186
1187        // Other file is present.
1188        assert_eq!(
1189            self.get_media_content(&request_other_file).await.unwrap().as_ref(),
1190            Some(&other_content),
1191            "other file not found"
1192        );
1193        assert_eq!(
1194            self.get_media_content_for_uri(other_uri).await.unwrap().as_ref(),
1195            Some(&other_content),
1196            "other file not found by URI"
1197        );
1198
1199        // Let's remove media based on URI.
1200        self.remove_media_content_for_uri(uri).await.expect("removing all media for uri failed");
1201
1202        assert!(
1203            self.get_media_content(&request_file).await.unwrap().is_none(),
1204            "media wasn't removed"
1205        );
1206        assert!(
1207            self.get_media_content(&request_thumbnail).await.unwrap().is_none(),
1208            "thumbnail wasn't removed"
1209        );
1210        assert!(
1211            self.get_media_content(&request_other_file).await.unwrap().is_some(),
1212            "other media was removed"
1213        );
1214        assert!(
1215            self.get_media_content_for_uri(uri).await.unwrap().is_none(),
1216            "media found by URI wasn't removed"
1217        );
1218        assert!(
1219            self.get_media_content_for_uri(other_uri).await.unwrap().is_some(),
1220            "other media found by URI was removed"
1221        );
1222    }
1223
1224    async fn test_replace_media_key(&self) {
1225        let uri = mxc_uri!("mxc://sendqueue.local/tr4n-s4ct-10n1-d");
1226        let req = MediaRequestParameters {
1227            source: MediaSource::Plain(uri.to_owned()),
1228            format: MediaFormat::File,
1229        };
1230
1231        let content = "hello".as_bytes().to_owned();
1232
1233        // Media isn't present in the cache.
1234        assert!(self.get_media_content(&req).await.unwrap().is_none(), "unexpected media found");
1235
1236        // Add the media.
1237        self.add_media_content(&req, content.clone(), IgnoreMediaRetentionPolicy::No)
1238            .await
1239            .expect("adding media failed");
1240
1241        // Sanity-check: media is found after adding it.
1242        assert_eq!(self.get_media_content(&req).await.unwrap().unwrap(), b"hello");
1243
1244        // Replacing a media request works.
1245        let new_uri = mxc_uri!("mxc://matrix.org/tr4n-s4ct-10n1-d");
1246        let new_req = MediaRequestParameters {
1247            source: MediaSource::Plain(new_uri.to_owned()),
1248            format: MediaFormat::File,
1249        };
1250        self.replace_media_key(&req, &new_req)
1251            .await
1252            .expect("replacing the media request key failed");
1253
1254        // Finding with the previous request doesn't work anymore.
1255        assert!(
1256            self.get_media_content(&req).await.unwrap().is_none(),
1257            "unexpected media found with the old key"
1258        );
1259
1260        // Finding with the new request does work.
1261        assert_eq!(self.get_media_content(&new_req).await.unwrap().unwrap(), b"hello");
1262    }
1263}
1264
1265/// Macro building to allow your [`MediaStore`] implementation to run
1266/// the entire tests suite locally.
1267///
1268/// You need to provide an `async fn get_media_store() ->
1269/// media::store::Result<Store>` that provides a fresh media store
1270/// that implements `MediaStoreInner` on the same level you invoke the
1271/// macro.
1272///
1273/// ## Usage Example:
1274/// ```no_run
1275/// # use matrix_sdk_base::media::store::{
1276/// #    MediaStore,
1277/// #    MemoryMediaStore as MyStore,
1278/// #    Result as MediaStoreResult,
1279/// # };
1280///
1281/// #[cfg(test)]
1282/// mod tests {
1283///     use super::{MediaStoreResult, MyStore};
1284///
1285///     async fn get_media_store() -> MediaStoreResult<MyStore> {
1286///         Ok(MyStore::new())
1287///     }
1288///
1289///     media_store_integration_tests!();
1290/// }
1291/// ```
1292#[allow(unused_macros, unused_extern_crates)]
1293#[macro_export]
1294macro_rules! media_store_integration_tests {
1295    () => {
1296        mod media_store_integration_tests {
1297            use matrix_sdk_test::async_test;
1298            use $crate::media::store::integration_tests::MediaStoreIntegrationTests;
1299
1300            use super::get_media_store;
1301
1302            #[async_test]
1303            async fn test_media_content() {
1304                let media_store = get_media_store().await.unwrap();
1305                media_store.test_media_content().await;
1306            }
1307
1308            #[async_test]
1309            async fn test_replace_media_key() {
1310                let media_store = get_media_store().await.unwrap();
1311                media_store.test_replace_media_key().await;
1312            }
1313        }
1314    };
1315}
1316
1317/// Macro generating tests for the media store, related to time (mostly
1318/// for the cross-process lock).
1319#[allow(unused_macros)]
1320#[macro_export]
1321macro_rules! media_store_integration_tests_time {
1322    () => {
1323        mod media_store_integration_tests_time {
1324            use std::time::Duration;
1325
1326            #[cfg(all(target_family = "wasm", target_os = "unknown"))]
1327            use gloo_timers::future::sleep;
1328            use matrix_sdk_test::async_test;
1329            #[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
1330            use tokio::time::sleep;
1331            use $crate::media::store::MediaStore;
1332
1333            use super::get_media_store;
1334
1335            #[async_test]
1336            async fn test_lease_locks() {
1337                let store = get_media_store().await.unwrap();
1338
1339                let acquired0 = store.try_take_leased_lock(0, "key", "alice").await.unwrap();
1340                assert!(acquired0);
1341
1342                // Should extend the lease automatically (same holder).
1343                let acquired2 = store.try_take_leased_lock(300, "key", "alice").await.unwrap();
1344                assert!(acquired2);
1345
1346                // Should extend the lease automatically (same holder + time is ok).
1347                let acquired3 = store.try_take_leased_lock(300, "key", "alice").await.unwrap();
1348                assert!(acquired3);
1349
1350                // Another attempt at taking the lock should fail, because it's taken.
1351                let acquired4 = store.try_take_leased_lock(300, "key", "bob").await.unwrap();
1352                assert!(!acquired4);
1353
1354                // Even if we insist.
1355                let acquired5 = store.try_take_leased_lock(300, "key", "bob").await.unwrap();
1356                assert!(!acquired5);
1357
1358                // That's a nice test we got here, go take a little nap.
1359                sleep(Duration::from_millis(50)).await;
1360
1361                // Still too early.
1362                let acquired55 = store.try_take_leased_lock(300, "key", "bob").await.unwrap();
1363                assert!(!acquired55);
1364
1365                // Ok you can take another nap then.
1366                sleep(Duration::from_millis(250)).await;
1367
1368                // At some point, we do get the lock.
1369                let acquired6 = store.try_take_leased_lock(0, "key", "bob").await.unwrap();
1370                assert!(acquired6);
1371
1372                sleep(Duration::from_millis(1)).await;
1373
1374                // The other gets it almost immediately too.
1375                let acquired7 = store.try_take_leased_lock(0, "key", "alice").await.unwrap();
1376                assert!(acquired7);
1377
1378                sleep(Duration::from_millis(1)).await;
1379
1380                // But when we take a longer lease...
1381                let acquired8 = store.try_take_leased_lock(300, "key", "bob").await.unwrap();
1382                assert!(acquired8);
1383
1384                // It blocks the other user.
1385                let acquired9 = store.try_take_leased_lock(300, "key", "alice").await.unwrap();
1386                assert!(!acquired9);
1387
1388                // We can hold onto our lease.
1389                let acquired10 = store.try_take_leased_lock(300, "key", "bob").await.unwrap();
1390                assert!(acquired10);
1391            }
1392        }
1393    };
1394}