atagen

macltok

public
Star0
Watch0
960ac07210797dcdf9149ef8465c1ccd46dafbda
3
ssh://brewery@pls.lobotomise.me:33/atagen/macltok.git
tests/media.rsRust809 lines24.7 KB
1
mod support;
2
3
use std::{sync::Arc, time::Duration};
4
5
use macltok::{
6
    AttachmentUpload, ClientHandle, Engine, EngineConfig, EngineError, ErrorKind, Event,
7
    EventStream, MediaId, MediaSize, UploadDetails, UploadThumbnail,
8
};
9
use matrix_sdk::{
10
    media::{MediaFormat, MediaRequestParameters},
11
    ruma::{
12
        api::client::sync::sync_events::v5,
13
        event_id,
14
        events::room::{
15
            EncryptedFile, MediaSource,
16
            message::{
17
                AudioMessageEventContent, FileInfo, FileMessageEventContent,
18
                ImageMessageEventContent, MessageType, RoomMessageEventContent,
19
            },
20
        },
21
        media::Method,
22
        mxc_uri, room_id,
23
    },
24
    test_utils::mocks::MatrixMockServer,
25
};
26
use matrix_sdk_test::{ALICE, JoinedRoomBuilder, event_factory::EventFactory};
27
use serde_json::{Value, json};
28
use wiremock::{
29
    Mock, ResponseTemplate,
30
    matchers::{method, path_regex},
31
};
32
33
struct MediaHarness {
34
    server: MatrixMockServer,
35
    client: matrix_sdk::Client,
36
    handle: ClientHandle,
37
    events: EventStream,
38
    running: tokio::task::JoinHandle<Result<(), EngineError>>,
39
    media_id: MediaId,
40
}
41
42
#[tokio::test]
43
async fn full_size_uses_full_source() {
44
    let mut harness = start(file_with_thumbnail()).await;
45
    mount_downloads(&harness.server).await;
46
47
    let bytes = fetch(&harness, MediaSize::Full).await;
48
    assert_eq!(&*bytes, b"binaryjpegfullimagedata");
49
    assert!(
50
        media_paths(&harness.server)
51
            .await
52
            .iter()
53
            .any(|path| path.ends_with("/localhost/full"))
54
    );
55
56
    support::shut_down(&harness.handle, &mut harness.events, harness.running).await;
57
}
58
59
#[tokio::test]
60
async fn encrypted_media_is_cached_as_plaintext_like_the_sdk_does() {
61
    use std::io::Read as _;
62
63
    let plaintext = b"very secret image".to_vec();
64
    let mut cursor = std::io::Cursor::new(plaintext.clone());
65
    let mut encryptor = matrix_sdk_crypto::AttachmentEncryptor::new(&mut cursor);
66
    let mut ciphertext = Vec::new();
67
    encryptor.read_to_end(&mut ciphertext).unwrap();
68
    let keys = encryptor.finish();
69
    let file = EncryptedFile::new(
70
        mxc_uri!("mxc://localhost/secret").to_owned(),
71
        keys.encryption_info,
72
        keys.hashes,
73
    );
74
    let source = MediaSource::Encrypted(Box::new(file.clone()));
75
    let mut harness = start(RoomMessageEventContent::new(MessageType::Image(
76
        ImageMessageEventContent::encrypted("secret.png".to_owned(), file),
77
    )))
78
    .await;
79
    Mock::given(method("GET"))
80
        .and(path_regex("/download/localhost/secret$"))
81
        .respond_with(ResponseTemplate::new(200).set_body_bytes(ciphertext))
82
        .mount(harness.server.server())
83
        .await;
84
85
    let from_sdk = harness
86
        .client
87
        .media()
88
        .get_media_content(
89
            &MediaRequestParameters {
90
                source,
91
                format: MediaFormat::File,
92
            },
93
            true,
94
        )
95
        .await
96
        .unwrap();
97
    assert_eq!(from_sdk, plaintext);
98
    assert_eq!(&*fetch(&harness, MediaSize::Full).await, &plaintext[..]);
99
    assert_eq!(media_paths(&harness.server).await.len(), 1);
100
101
    support::shut_down(&harness.handle, &mut harness.events, harness.running).await;
102
}
103
104
#[tokio::test]
105
async fn simultaneous_fetches_share_one_download() {
106
    let mut harness = start(plain_image()).await;
107
    Mock::given(method("GET"))
108
        .and(path_regex("/download/localhost/image$"))
109
        .respond_with(
110
            ResponseTemplate::new(200)
111
                .set_body_bytes(b"shared image bytes".to_vec())
112
                .set_delay(Duration::from_millis(300)),
113
        )
114
        .mount(harness.server.server())
115
        .await;
116
117
    let (first, second) = tokio::time::timeout(Duration::from_secs(5), async {
118
        tokio::join!(
119
            harness.handle.media(&harness.media_id, MediaSize::Full),
120
            harness.handle.media(&harness.media_id, MediaSize::Full),
121
        )
122
    })
123
    .await
124
    .expect("both fetches did not complete");
125
    assert_eq!(&*first.unwrap().bytes, b"shared image bytes");
126
    assert_eq!(&*second.unwrap().bytes, b"shared image bytes");
127
128
    let downloads = harness
129
        .server
130
        .received_requests()
131
        .await
132
        .unwrap()
133
        .iter()
134
        .filter(|request| request.url.path().ends_with("/download/localhost/image"))
135
        .count();
136
    assert_eq!(downloads, 1, "duplicate in-flight media download");
137
    support::shut_down(&harness.handle, &mut harness.events, harness.running).await;
138
}
139
140
#[tokio::test]
141
async fn downloaded_media_over_limit_is_rejected() {
142
    let mut harness = start_with_config(
143
        plain_image(),
144
        EngineConfig::default()
145
            .with_initial_history_limit(0)
146
            .with_max_media_bytes(4),
147
    )
148
    .await;
149
    Mock::given(method("GET"))
150
        .and(path_regex("/download/localhost/image$"))
151
        .respond_with(ResponseTemplate::new(200).set_body_bytes(b"too large".to_vec()))
152
        .mount(harness.server.server())
153
        .await;
154
155
    let error = harness
156
        .handle
157
        .media(&harness.media_id, MediaSize::Full)
158
        .await
159
        .unwrap_err();
160
    assert_eq!(error.kind, ErrorKind::MediaTooLarge);
161
162
    support::shut_down(&harness.handle, &mut harness.events, harness.running).await;
163
}
164
165
#[tokio::test]
166
async fn thumbnail_size_prefers_explicit_thumbnail() {
167
    let mut harness = start(file_with_thumbnail()).await;
168
    mount_downloads(&harness.server).await;
169
170
    let bytes = fetch(&harness, thumbnail_size()).await;
171
    assert_eq!(&*bytes, b"binaryjpegfullimagedata");
172
    let paths = media_paths(&harness.server).await;
173
    assert!(
174
        paths
175
            .iter()
176
            .any(|path| path.ends_with("/localhost/explicit-thumbnail"))
177
    );
178
    assert!(!paths.iter().any(|path| path.ends_with("/localhost/full")));
179
180
    support::shut_down(&harness.handle, &mut harness.events, harness.running).await;
181
}
182
183
#[tokio::test]
184
async fn plain_image_thumbnail_uses_thumbnail_endpoint() {
185
    let mut harness = start(plain_image()).await;
186
    harness
187
        .server
188
        .mock_media_thumbnail(Method::Scale, 800, 800, false)
189
        .ok()
190
        .mount()
191
        .await;
192
    harness
193
        .server
194
        .mock_authed_media_thumbnail(Method::Scale, 800, 800, false)
195
        .ok()
196
        .mount()
197
        .await;
198
199
    let bytes = fetch(&harness, thumbnail_size()).await;
200
    assert_eq!(&*bytes, b"binaryjpegthumbnaildata");
201
    assert!(
202
        media_paths(&harness.server)
203
            .await
204
            .iter()
205
            .any(|path| path.contains("/thumbnail/localhost/image"))
206
    );
207
208
    support::shut_down(&harness.handle, &mut harness.events, harness.running).await;
209
}
210
211
#[tokio::test]
212
async fn audio_thumbnail_fetch_fails_with_media_unavailable() {
213
    let mut harness = start(plain_audio()).await;
214
215
    let error = harness
216
        .handle
217
        .media(&harness.media_id, thumbnail_size())
218
        .await
219
        .unwrap_err();
220
    assert_eq!(error.kind, ErrorKind::MediaUnavailable);
221
222
    support::shut_down(&harness.handle, &mut harness.events, harness.running).await;
223
}
224
225
#[tokio::test]
226
async fn media_upload_is_queued_and_sent() {
227
    let mut harness = start(plain_image()).await;
228
    harness
229
        .server
230
        .mock_authenticated_media_config()
231
        .ok_default()
232
        .mount()
233
        .await;
234
    harness
235
        .server
236
        .mock_upload()
237
        .expect_mime_type("image/jpeg")
238
        .ok(mxc_uri!("mxc://localhost/uploaded"))
239
        .expect(1)
240
        .mount()
241
        .await;
242
    harness
243
        .server
244
        .mock_room_send()
245
        .ok(event_id!("$uploaded:localhost"))
246
        .expect(1)
247
        .mount()
248
        .await;
249
250
    let mut upload = AttachmentUpload::new(
251
        room_id!("!media:localhost").to_owned(),
252
        "photo.jpg",
253
        "image/jpeg",
254
        b"photo data".to_vec(),
255
    );
256
    upload.caption = Some("holiday".to_owned());
257
    harness.handle.upload(upload).await.unwrap();
258
259
    tokio::time::timeout(Duration::from_secs(5), async {
260
        loop {
261
            let requests = harness.server.received_requests().await.unwrap();
262
            if requests.iter().any(|request| {
263
                request.url.path().contains("/send/m.room.message/")
264
                    && String::from_utf8_lossy(&request.body).contains("mxc://localhost/uploaded")
265
            }) {
266
                break;
267
            }
268
            tokio::task::yield_now().await;
269
        }
270
    })
271
    .await
272
    .expect("queued media was not sent");
273
274
    support::shut_down(&harness.handle, &mut harness.events, harness.running).await;
275
}
276
277
#[tokio::test]
278
async fn queued_offline_attachment_exposes_fetchable_local_media() {
279
    let mut harness = start(plain_image()).await;
280
    harness.client.send_queue().set_enabled(false).await;
281
    let original = b"offline image bytes".to_vec();
282
    let mut upload = AttachmentUpload::new(
283
        room_id!("!media:localhost").to_owned(),
284
        "pending.png",
285
        "image/png",
286
        original.clone(),
287
    );
288
    upload.details = Some(UploadDetails::Image {
289
        width: Some(32),
290
        height: Some(16),
291
        blurhash: None,
292
    });
293
    harness.handle.upload(upload).await.unwrap();
294
    let event = support::receive_until(&mut harness.events, |event| {
295
        support::event_messages(event).into_iter().any(|event| {
296
            event.own
297
                && event.transaction_id.is_some()
298
                && support::media_of(event).is_some_and(|media| media.filename == "pending.png")
299
        })
300
    })
301
    .await;
302
    let media = support::event_messages(&event)
303
        .into_iter()
304
        .find(|event| event.own && support::media_of(event).is_some())
305
        .and_then(support::media_of)
306
        .unwrap();
307
    assert_eq!((media.width, media.height), (Some(32), Some(16)));
308
    assert_eq!(media.size_bytes, Some(original.len() as u64));
309
    let media_id = media.id.clone();
310
311
    let fetched = harness
312
        .handle
313
        .media(&media_id, MediaSize::Full)
314
        .await
315
        .unwrap();
316
    assert_eq!(&*fetched.bytes, &original[..]);
317
318
    support::shut_down(&harness.handle, &mut harness.events, harness.running).await;
319
}
320
321
#[tokio::test]
322
async fn image_upload_uses_caller_supplied_metadata_and_thumbnail() {
323
    let mut harness = start(plain_image()).await;
324
    harness
325
        .server
326
        .mock_authenticated_media_config()
327
        .ok_default()
328
        .mount()
329
        .await;
330
    harness
331
        .server
332
        .mock_upload()
333
        .expect_mime_type("image/png")
334
        .respond_with(|request: &wiremock::Request| {
335
            let uri = if request.body == b"thumbnail bytes" {
336
                "mxc://localhost/image-thumb"
337
            } else {
338
                "mxc://localhost/image-upload"
339
            };
340
            ResponseTemplate::new(200).set_body_json(json!({ "content_uri": uri }))
341
        })
342
        .expect(2)
343
        .mount()
344
        .await;
345
    harness
346
        .server
347
        .mock_room_send()
348
        .ok(event_id!("$image-upload:localhost"))
349
        .expect(1)
350
        .mount()
351
        .await;
352
353
    let original = b"image bytes".to_vec();
354
    let thumbnail_bytes = b"thumbnail bytes".to_vec();
355
    let mut upload = AttachmentUpload::new(
356
        room_id!("!media:localhost").to_owned(),
357
        "large.png",
358
        "image/png",
359
        original.clone(),
360
    );
361
    upload.details = Some(UploadDetails::Image {
362
        width: Some(900),
363
        height: Some(450),
364
        blurhash: None,
365
    });
366
    upload.thumbnail = Some(UploadThumbnail::new(
367
        thumbnail_bytes.clone(),
368
        "image/png",
369
        800,
370
        400,
371
    ));
372
    harness.handle.upload(upload).await.unwrap();
373
374
    let content = uploaded_content(&harness.server).await;
375
    assert_eq!(content["msgtype"], "m.image");
376
    assert_eq!(content["info"]["w"], 900);
377
    assert_eq!(content["info"]["h"], 450);
378
    assert_eq!(content["info"]["size"], original.len());
379
    assert_eq!(content["info"]["thumbnail_info"]["w"], 800);
380
    assert_eq!(content["info"]["thumbnail_info"]["h"], 400);
381
    assert_eq!(content["info"]["thumbnail_info"]["mimetype"], "image/png");
382
    assert_eq!(
383
        content["info"]["thumbnail_info"]["size"],
384
        thumbnail_bytes.len()
385
    );
386
    assert_eq!(
387
        content["info"]["thumbnail_url"],
388
        "mxc://localhost/image-thumb"
389
    );
390
    assert_eq!(content["url"], "mxc://localhost/image-upload");
391
392
    let requests = harness.server.received_requests().await.unwrap();
393
    let uploads = requests
394
        .iter()
395
        .filter(|request| request.url.path().ends_with("/_matrix/media/v3/upload"))
396
        .collect::<Vec<_>>();
397
    assert_eq!(uploads.len(), 2);
398
    assert!(uploads.iter().any(|request| request.body == original));
399
    assert!(
400
        uploads
401
            .iter()
402
            .any(|request| request.body == thumbnail_bytes)
403
    );
404
405
    support::shut_down(&harness.handle, &mut harness.events, harness.running).await;
406
}
407
408
#[tokio::test]
409
async fn video_upload_preserves_metadata_and_supplied_thumbnail() {
410
    let mut harness = start(plain_image()).await;
411
    harness
412
        .server
413
        .mock_authenticated_media_config()
414
        .ok_default()
415
        .mount()
416
        .await;
417
    harness
418
        .server
419
        .mock_upload()
420
        .expect_mime_type("image/png")
421
        .ok(mxc_uri!("mxc://localhost/video-thumb"))
422
        .expect(1)
423
        .mount()
424
        .await;
425
    harness
426
        .server
427
        .mock_upload()
428
        .expect_mime_type("video/mp4")
429
        .ok(mxc_uri!("mxc://localhost/video-upload"))
430
        .expect(1)
431
        .mount()
432
        .await;
433
    harness
434
        .server
435
        .mock_room_send()
436
        .ok(event_id!("$video-upload:localhost"))
437
        .expect(1)
438
        .mount()
439
        .await;
440
441
    let mut upload = AttachmentUpload::new(
442
        room_id!("!media:localhost").to_owned(),
443
        "clip.mp4",
444
        "video/mp4",
445
        b"video bytes".to_vec(),
446
    );
447
    upload.details = Some(UploadDetails::Video {
448
        width: Some(1920),
449
        height: Some(1080),
450
        duration_ms: Some(3500),
451
        blurhash: Some("blurhash".to_owned()),
452
    });
453
    upload.thumbnail = Some(UploadThumbnail::new(
454
        b"poster bytes".to_vec(),
455
        "image/png",
456
        320,
457
        180,
458
    ));
459
    harness.handle.upload(upload).await.unwrap();
460
461
    let content = uploaded_content(&harness.server).await;
462
    assert_eq!(content["msgtype"], "m.video");
463
    assert_eq!(content["info"]["w"], 1920);
464
    assert_eq!(content["info"]["h"], 1080);
465
    assert_eq!(content["info"]["duration"], 3500);
466
    assert_eq!(content["info"]["size"], 11);
467
    assert_eq!(content["info"]["thumbnail_info"]["w"], 320);
468
    assert_eq!(content["info"]["thumbnail_info"]["h"], 180);
469
    assert_eq!(content["info"]["thumbnail_info"]["size"], 12);
470
    assert_eq!(
471
        content["info"]["thumbnail_url"],
472
        "mxc://localhost/video-thumb"
473
    );
474
    assert_eq!(content["url"], "mxc://localhost/video-upload");
475
476
    let requests = harness.server.received_requests().await.unwrap();
477
    assert!(requests.iter().any(|request| {
478
        request.url.path().ends_with("/_matrix/media/v3/upload") && request.body == b"poster bytes"
479
    }));
480
481
    support::shut_down(&harness.handle, &mut harness.events, harness.running).await;
482
}
483
484
#[tokio::test]
485
async fn voice_upload_emits_voice_details_and_correlated_progress() {
486
    let mut harness = start(plain_image()).await;
487
    harness
488
        .server
489
        .mock_authenticated_media_config()
490
        .ok_default()
491
        .mount()
492
        .await;
493
    harness
494
        .server
495
        .mock_upload()
496
        .expect_mime_type("audio/ogg")
497
        .respond_with(
498
            ResponseTemplate::new(200)
499
                .set_body_json(json!({ "content_uri": "mxc://localhost/voice-upload" }))
500
                .set_delay(Duration::from_millis(500)),
501
        )
502
        .expect(1)
503
        .mount()
504
        .await;
505
    harness
506
        .server
507
        .mock_room_send()
508
        .ok(event_id!("$voice-upload:localhost"))
509
        .expect(1)
510
        .mount()
511
        .await;
512
513
    let data = b"voice bytes".to_vec();
514
    let mut upload = AttachmentUpload::new(
515
        room_id!("!media:localhost").to_owned(),
516
        "voice.ogg",
517
        "audio/ogg",
518
        data.clone(),
519
    );
520
    upload.details = Some(UploadDetails::Voice {
521
        duration_ms: 4200,
522
        waveform: vec![0, 512, 1024],
523
    });
524
    harness.handle.upload(upload).await.unwrap();
525
526
    let pending = harness
527
        .handle
528
        .pending_send_ids(room_id!("!media:localhost"))
529
        .await
530
        .unwrap();
531
    assert_eq!(pending.len(), 1);
532
    let transaction_id = pending[0].clone();
533
    let progress = support::receive_until(&mut harness.events, |event| {
534
        matches!(event, Event::MediaUploadProgress { transaction_id: seen, complete: true, .. } if seen == &transaction_id)
535
    })
536
    .await;
537
    assert!(matches!(progress, Event::MediaUploadProgress {
538
        room_id,
539
        transaction_id: seen,
540
        uploaded_bytes,
541
        total_bytes,
542
        complete: true,
543
    } if room_id == room_id!("!media:localhost") && seen == transaction_id && uploaded_bytes == data.len() as u64 && total_bytes == data.len() as u64));
544
545
    let content = uploaded_content(&harness.server).await;
546
    assert_eq!(content["msgtype"], "m.audio");
547
    assert_eq!(content["info"]["duration"], 4200);
548
    assert_eq!(content["info"]["size"], data.len());
549
    assert_eq!(content["org.matrix.msc1767.audio"]["duration"], 4200);
550
    assert_eq!(
551
        content["org.matrix.msc1767.audio"]["waveform"],
552
        json!([0, 512, 1024])
553
    );
554
    assert_eq!(content["org.matrix.msc3245.voice"], json!({}));
555
556
    support::shut_down(&harness.handle, &mut harness.events, harness.running).await;
557
}
558
559
#[tokio::test]
560
async fn mismatched_upload_metadata_and_invalid_waveform_fail_without_uploading() {
561
    let mut harness = start(plain_image()).await;
562
    let mut mismatched = AttachmentUpload::new(
563
        room_id!("!media:localhost").to_owned(),
564
        "clip.mp4",
565
        "video/mp4",
566
        b"video bytes".to_vec(),
567
    );
568
    mismatched.details = Some(UploadDetails::Image {
569
        width: Some(32),
570
        height: Some(32),
571
        blurhash: None,
572
    });
573
    let error = harness.handle.upload(mismatched).await.unwrap_err();
574
    assert_eq!(error.kind, ErrorKind::Invalid);
575
576
    let mut voice = AttachmentUpload::new(
577
        room_id!("!media:localhost").to_owned(),
578
        "voice.ogg",
579
        "audio/ogg",
580
        b"voice bytes".to_vec(),
581
    );
582
    voice.details = Some(UploadDetails::Voice {
583
        duration_ms: 1000,
584
        waveform: vec![1025],
585
    });
586
    let error = harness.handle.upload(voice).await.unwrap_err();
587
    assert_eq!(error.kind, ErrorKind::Invalid);
588
589
    assert!(
590
        !harness
591
            .server
592
            .received_requests()
593
            .await
594
            .unwrap()
595
            .iter()
596
            .any(|request| { request.url.path().ends_with("/_matrix/media/v3/upload") })
597
    );
598
    support::shut_down(&harness.handle, &mut harness.events, harness.running).await;
599
}
600
601
#[tokio::test]
602
async fn upload_over_media_limit_fails_without_uploading() {
603
    let mut harness = start_with_config(
604
        plain_image(),
605
        EngineConfig::default()
606
            .with_initial_history_limit(0)
607
            .with_max_media_bytes(4),
608
    )
609
    .await;
610
    let upload = AttachmentUpload::new(
611
        room_id!("!media:localhost").to_owned(),
612
        "large.bin",
613
        "application/octet-stream",
614
        b"too large".to_vec(),
615
    );
616
    let error = harness.handle.upload(upload).await.unwrap_err();
617
    assert_eq!(error.kind, ErrorKind::MediaTooLarge);
618
    assert!(
619
        !harness
620
            .server
621
            .received_requests()
622
            .await
623
            .unwrap()
624
            .iter()
625
            .any(|request| { request.url.path().ends_with("/_matrix/media/v3/upload") })
626
    );
627
    support::shut_down(&harness.handle, &mut harness.events, harness.running).await;
628
}
629
630
#[tokio::test]
631
async fn audio_thumbnail_fails_without_uploading() {
632
    let mut harness = start(plain_image()).await;
633
    let mut upload = AttachmentUpload::new(
634
        room_id!("!media:localhost").to_owned(),
635
        "voice.ogg",
636
        "audio/ogg",
637
        b"voice bytes".to_vec(),
638
    );
639
    upload.thumbnail = Some(UploadThumbnail::new(
640
        b"thumbnail".to_vec(),
641
        "image/png",
642
        80,
643
        80,
644
    ));
645
    let error = harness.handle.upload(upload).await.unwrap_err();
646
    assert_eq!(error.kind, ErrorKind::Invalid);
647
    assert!(
648
        !harness
649
            .server
650
            .received_requests()
651
            .await
652
            .unwrap()
653
            .iter()
654
            .any(|request| { request.url.path().ends_with("/_matrix/media/v3/upload") })
655
    );
656
    support::shut_down(&harness.handle, &mut harness.events, harness.running).await;
657
}
658
659
#[tokio::test]
660
async fn invalid_upload_mime_fails_without_uploading() {
661
    let mut harness = start(plain_image()).await;
662
    let upload = AttachmentUpload::new(
663
        room_id!("!media:localhost").to_owned(),
664
        "photo.jpg",
665
        "not a MIME type",
666
        b"photo data".to_vec(),
667
    );
668
    let error = harness.handle.upload(upload).await.unwrap_err();
669
    assert_eq!(error.kind, ErrorKind::Invalid);
670
671
    support::shut_down(&harness.handle, &mut harness.events, harness.running).await;
672
}
673
674
async fn start(content: RoomMessageEventContent) -> MediaHarness {
675
    start_with_config(
676
        content,
677
        EngineConfig::default().with_initial_history_limit(0),
678
    )
679
    .await
680
}
681
682
async fn start_with_config(content: RoomMessageEventContent, config: EngineConfig) -> MediaHarness {
683
    let server = MatrixMockServer::new().await;
684
    server
685
        .mock_sliding_sync()
686
        .ok(v5::Response::new("1".to_owned()))
687
        .mount()
688
        .await;
689
    let client = server.client_builder().build().await;
690
    let room_id = room_id!("!media:localhost");
691
    server.mock_room_state_encryption().plain().mount().await;
692
    server.sync_joined_room(&client, room_id).await;
693
    let sync_client = client.clone();
694
    let (engine, handle, mut events) = Engine::from_client(client, config).await.unwrap();
695
    let running = tokio::spawn(engine.run());
696
697
    handle.open_room(room_id.to_owned()).await.unwrap();
698
    support::receive_until(
699
        &mut events,
700
        |event| matches!(event, Event::TimelineReset { timeline_id: reset, .. } if reset.room_id() == room_id),
701
    )
702
    .await;
703
704
    server
705
        .sync_room(
706
            &sync_client,
707
            JoinedRoomBuilder::new(room_id)
708
                .add_timeline_event(EventFactory::new().event(content).sender(&ALICE)),
709
        )
710
        .await;
711
    let event = support::receive_until(&mut events, |event| {
712
        support::event_messages(event)
713
            .into_iter()
714
            .any(|event| support::media_of(event).is_some())
715
    })
716
    .await;
717
    let media_id = support::event_messages(&event)
718
        .into_iter()
719
        .find_map(|event| support::media_of(event).map(|media| media.id.clone()))
720
        .unwrap();
721
722
    MediaHarness {
723
        server,
724
        client: sync_client,
725
        handle,
726
        events,
727
        running,
728
        media_id,
729
    }
730
}
731
732
async fn mount_downloads(server: &MatrixMockServer) {
733
    server.mock_media_download().ok_image().mount().await;
734
    server.mock_authed_media_download().ok_image().mount().await;
735
}
736
737
fn thumbnail_size() -> MediaSize {
738
    MediaSize::Thumbnail {
739
        width: 800,
740
        height: 800,
741
    }
742
}
743
744
async fn fetch(harness: &MediaHarness, size: MediaSize) -> Arc<[u8]> {
745
    harness
746
        .handle
747
        .media(&harness.media_id, size)
748
        .await
749
        .unwrap()
750
        .bytes
751
}
752
753
async fn media_paths(server: &MatrixMockServer) -> Vec<String> {
754
    server
755
        .received_requests()
756
        .await
757
        .unwrap()
758
        .into_iter()
759
        .map(|request| request.url.path().to_owned())
760
        .filter(|path| path.contains("/media/"))
761
        .collect()
762
}
763
764
async fn uploaded_content(server: &MatrixMockServer) -> Value {
765
    tokio::time::timeout(Duration::from_secs(5), async {
766
        loop {
767
            if let Some(request) = server
768
                .received_requests()
769
                .await
770
                .unwrap()
771
                .iter()
772
                .find(|request| request.url.path().contains("/send/m.room.message/"))
773
            {
774
                return serde_json::from_slice(&request.body).unwrap();
775
            }
776
            tokio::task::yield_now().await;
777
        }
778
    })
779
    .await
780
    .expect("queued media was not sent")
781
}
782
783
fn file_with_thumbnail() -> RoomMessageEventContent {
784
    let mut info = FileInfo::new();
785
    info.thumbnail_source = Some(MediaSource::Plain(
786
        mxc_uri!("mxc://localhost/explicit-thumbnail").to_owned(),
787
    ));
788
    RoomMessageEventContent::new(MessageType::File(
789
        FileMessageEventContent::plain(
790
            "file.bin".to_owned(),
791
            mxc_uri!("mxc://localhost/full").to_owned(),
792
        )
793
        .info(Box::new(info)),
794
    ))
795
}
796
797
fn plain_image() -> RoomMessageEventContent {
798
    RoomMessageEventContent::new(MessageType::Image(ImageMessageEventContent::plain(
799
        "image.jpg".to_owned(),
800
        mxc_uri!("mxc://localhost/image").to_owned(),
801
    )))
802
}
803
804
fn plain_audio() -> RoomMessageEventContent {
805
    RoomMessageEventContent::new(MessageType::Audio(AudioMessageEventContent::plain(
806
        "audio.ogg".to_owned(),
807
        mxc_uri!("mxc://localhost/audio").to_owned(),
808
    )))
809
}