atagen

macltok

public
Star0
Watch0
31ce2453c82f470aa8b5d3861ab0eec4273afc5d
3
ssh://brewery@pls.lobotomise.me:33/atagen/macltok.git
tests/media.rsRust812 lines24.8 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(|message| {
296
            message.own
297
                && message.transaction_id.is_some()
298
                && message
299
                    .media
300
                    .as_ref()
301
                    .is_some_and(|media| media.filename == "pending.png")
302
        })
303
    })
304
    .await;
305
    let media = support::event_messages(&event)
306
        .into_iter()
307
        .find(|message| message.own && message.media.is_some())
308
        .and_then(|message| message.media.as_ref())
309
        .unwrap();
310
    assert_eq!((media.width, media.height), (Some(32), Some(16)));
311
    assert_eq!(media.size_bytes, Some(original.len() as u64));
312
    let media_id = media.id.clone();
313
314
    let fetched = harness
315
        .handle
316
        .media(&media_id, MediaSize::Full)
317
        .await
318
        .unwrap();
319
    assert_eq!(&*fetched.bytes, &original[..]);
320
321
    support::shut_down(&harness.handle, &mut harness.events, harness.running).await;
322
}
323
324
#[tokio::test]
325
async fn image_upload_uses_caller_supplied_metadata_and_thumbnail() {
326
    let mut harness = start(plain_image()).await;
327
    harness
328
        .server
329
        .mock_authenticated_media_config()
330
        .ok_default()
331
        .mount()
332
        .await;
333
    harness
334
        .server
335
        .mock_upload()
336
        .expect_mime_type("image/png")
337
        .respond_with(|request: &wiremock::Request| {
338
            let uri = if request.body == b"thumbnail bytes" {
339
                "mxc://localhost/image-thumb"
340
            } else {
341
                "mxc://localhost/image-upload"
342
            };
343
            ResponseTemplate::new(200).set_body_json(json!({ "content_uri": uri }))
344
        })
345
        .expect(2)
346
        .mount()
347
        .await;
348
    harness
349
        .server
350
        .mock_room_send()
351
        .ok(event_id!("$image-upload:localhost"))
352
        .expect(1)
353
        .mount()
354
        .await;
355
356
    let original = b"image bytes".to_vec();
357
    let thumbnail_bytes = b"thumbnail bytes".to_vec();
358
    let mut upload = AttachmentUpload::new(
359
        room_id!("!media:localhost").to_owned(),
360
        "large.png",
361
        "image/png",
362
        original.clone(),
363
    );
364
    upload.details = Some(UploadDetails::Image {
365
        width: Some(900),
366
        height: Some(450),
367
        blurhash: None,
368
    });
369
    upload.thumbnail = Some(UploadThumbnail::new(
370
        thumbnail_bytes.clone(),
371
        "image/png",
372
        800,
373
        400,
374
    ));
375
    harness.handle.upload(upload).await.unwrap();
376
377
    let content = uploaded_content(&harness.server).await;
378
    assert_eq!(content["msgtype"], "m.image");
379
    assert_eq!(content["info"]["w"], 900);
380
    assert_eq!(content["info"]["h"], 450);
381
    assert_eq!(content["info"]["size"], original.len());
382
    assert_eq!(content["info"]["thumbnail_info"]["w"], 800);
383
    assert_eq!(content["info"]["thumbnail_info"]["h"], 400);
384
    assert_eq!(content["info"]["thumbnail_info"]["mimetype"], "image/png");
385
    assert_eq!(
386
        content["info"]["thumbnail_info"]["size"],
387
        thumbnail_bytes.len()
388
    );
389
    assert_eq!(
390
        content["info"]["thumbnail_url"],
391
        "mxc://localhost/image-thumb"
392
    );
393
    assert_eq!(content["url"], "mxc://localhost/image-upload");
394
395
    let requests = harness.server.received_requests().await.unwrap();
396
    let uploads = requests
397
        .iter()
398
        .filter(|request| request.url.path().ends_with("/_matrix/media/v3/upload"))
399
        .collect::<Vec<_>>();
400
    assert_eq!(uploads.len(), 2);
401
    assert!(uploads.iter().any(|request| request.body == original));
402
    assert!(
403
        uploads
404
            .iter()
405
            .any(|request| request.body == thumbnail_bytes)
406
    );
407
408
    support::shut_down(&harness.handle, &mut harness.events, harness.running).await;
409
}
410
411
#[tokio::test]
412
async fn video_upload_preserves_metadata_and_supplied_thumbnail() {
413
    let mut harness = start(plain_image()).await;
414
    harness
415
        .server
416
        .mock_authenticated_media_config()
417
        .ok_default()
418
        .mount()
419
        .await;
420
    harness
421
        .server
422
        .mock_upload()
423
        .expect_mime_type("image/png")
424
        .ok(mxc_uri!("mxc://localhost/video-thumb"))
425
        .expect(1)
426
        .mount()
427
        .await;
428
    harness
429
        .server
430
        .mock_upload()
431
        .expect_mime_type("video/mp4")
432
        .ok(mxc_uri!("mxc://localhost/video-upload"))
433
        .expect(1)
434
        .mount()
435
        .await;
436
    harness
437
        .server
438
        .mock_room_send()
439
        .ok(event_id!("$video-upload:localhost"))
440
        .expect(1)
441
        .mount()
442
        .await;
443
444
    let mut upload = AttachmentUpload::new(
445
        room_id!("!media:localhost").to_owned(),
446
        "clip.mp4",
447
        "video/mp4",
448
        b"video bytes".to_vec(),
449
    );
450
    upload.details = Some(UploadDetails::Video {
451
        width: Some(1920),
452
        height: Some(1080),
453
        duration_ms: Some(3500),
454
        blurhash: Some("blurhash".to_owned()),
455
    });
456
    upload.thumbnail = Some(UploadThumbnail::new(
457
        b"poster bytes".to_vec(),
458
        "image/png",
459
        320,
460
        180,
461
    ));
462
    harness.handle.upload(upload).await.unwrap();
463
464
    let content = uploaded_content(&harness.server).await;
465
    assert_eq!(content["msgtype"], "m.video");
466
    assert_eq!(content["info"]["w"], 1920);
467
    assert_eq!(content["info"]["h"], 1080);
468
    assert_eq!(content["info"]["duration"], 3500);
469
    assert_eq!(content["info"]["size"], 11);
470
    assert_eq!(content["info"]["thumbnail_info"]["w"], 320);
471
    assert_eq!(content["info"]["thumbnail_info"]["h"], 180);
472
    assert_eq!(content["info"]["thumbnail_info"]["size"], 12);
473
    assert_eq!(
474
        content["info"]["thumbnail_url"],
475
        "mxc://localhost/video-thumb"
476
    );
477
    assert_eq!(content["url"], "mxc://localhost/video-upload");
478
479
    let requests = harness.server.received_requests().await.unwrap();
480
    assert!(requests.iter().any(|request| {
481
        request.url.path().ends_with("/_matrix/media/v3/upload") && request.body == b"poster bytes"
482
    }));
483
484
    support::shut_down(&harness.handle, &mut harness.events, harness.running).await;
485
}
486
487
#[tokio::test]
488
async fn voice_upload_emits_voice_details_and_correlated_progress() {
489
    let mut harness = start(plain_image()).await;
490
    harness
491
        .server
492
        .mock_authenticated_media_config()
493
        .ok_default()
494
        .mount()
495
        .await;
496
    harness
497
        .server
498
        .mock_upload()
499
        .expect_mime_type("audio/ogg")
500
        .respond_with(
501
            ResponseTemplate::new(200)
502
                .set_body_json(json!({ "content_uri": "mxc://localhost/voice-upload" }))
503
                .set_delay(Duration::from_millis(500)),
504
        )
505
        .expect(1)
506
        .mount()
507
        .await;
508
    harness
509
        .server
510
        .mock_room_send()
511
        .ok(event_id!("$voice-upload:localhost"))
512
        .expect(1)
513
        .mount()
514
        .await;
515
516
    let data = b"voice bytes".to_vec();
517
    let mut upload = AttachmentUpload::new(
518
        room_id!("!media:localhost").to_owned(),
519
        "voice.ogg",
520
        "audio/ogg",
521
        data.clone(),
522
    );
523
    upload.details = Some(UploadDetails::Voice {
524
        duration_ms: 4200,
525
        waveform: vec![0, 512, 1024],
526
    });
527
    harness.handle.upload(upload).await.unwrap();
528
529
    let pending = harness
530
        .handle
531
        .pending_send_ids(room_id!("!media:localhost"))
532
        .await
533
        .unwrap();
534
    assert_eq!(pending.len(), 1);
535
    let transaction_id = pending[0].clone();
536
    let progress = support::receive_until(&mut harness.events, |event| {
537
        matches!(event, Event::MediaUploadProgress { transaction_id: seen, complete: true, .. } if seen == &transaction_id)
538
    })
539
    .await;
540
    assert!(matches!(progress, Event::MediaUploadProgress {
541
        room_id,
542
        transaction_id: seen,
543
        uploaded_bytes,
544
        total_bytes,
545
        complete: true,
546
    } if room_id == room_id!("!media:localhost") && seen == transaction_id && uploaded_bytes == data.len() as u64 && total_bytes == data.len() as u64));
547
548
    let content = uploaded_content(&harness.server).await;
549
    assert_eq!(content["msgtype"], "m.audio");
550
    assert_eq!(content["info"]["duration"], 4200);
551
    assert_eq!(content["info"]["size"], data.len());
552
    assert_eq!(content["org.matrix.msc1767.audio"]["duration"], 4200);
553
    assert_eq!(
554
        content["org.matrix.msc1767.audio"]["waveform"],
555
        json!([0, 512, 1024])
556
    );
557
    assert_eq!(content["org.matrix.msc3245.voice"], json!({}));
558
559
    support::shut_down(&harness.handle, &mut harness.events, harness.running).await;
560
}
561
562
#[tokio::test]
563
async fn mismatched_upload_metadata_and_invalid_waveform_fail_without_uploading() {
564
    let mut harness = start(plain_image()).await;
565
    let mut mismatched = AttachmentUpload::new(
566
        room_id!("!media:localhost").to_owned(),
567
        "clip.mp4",
568
        "video/mp4",
569
        b"video bytes".to_vec(),
570
    );
571
    mismatched.details = Some(UploadDetails::Image {
572
        width: Some(32),
573
        height: Some(32),
574
        blurhash: None,
575
    });
576
    let error = harness.handle.upload(mismatched).await.unwrap_err();
577
    assert_eq!(error.kind, ErrorKind::Invalid);
578
579
    let mut voice = AttachmentUpload::new(
580
        room_id!("!media:localhost").to_owned(),
581
        "voice.ogg",
582
        "audio/ogg",
583
        b"voice bytes".to_vec(),
584
    );
585
    voice.details = Some(UploadDetails::Voice {
586
        duration_ms: 1000,
587
        waveform: vec![1025],
588
    });
589
    let error = harness.handle.upload(voice).await.unwrap_err();
590
    assert_eq!(error.kind, ErrorKind::Invalid);
591
592
    assert!(
593
        !harness
594
            .server
595
            .received_requests()
596
            .await
597
            .unwrap()
598
            .iter()
599
            .any(|request| { request.url.path().ends_with("/_matrix/media/v3/upload") })
600
    );
601
    support::shut_down(&harness.handle, &mut harness.events, harness.running).await;
602
}
603
604
#[tokio::test]
605
async fn upload_over_media_limit_fails_without_uploading() {
606
    let mut harness = start_with_config(
607
        plain_image(),
608
        EngineConfig::default()
609
            .with_initial_history_limit(0)
610
            .with_max_media_bytes(4),
611
    )
612
    .await;
613
    let upload = AttachmentUpload::new(
614
        room_id!("!media:localhost").to_owned(),
615
        "large.bin",
616
        "application/octet-stream",
617
        b"too large".to_vec(),
618
    );
619
    let error = harness.handle.upload(upload).await.unwrap_err();
620
    assert_eq!(error.kind, ErrorKind::MediaTooLarge);
621
    assert!(
622
        !harness
623
            .server
624
            .received_requests()
625
            .await
626
            .unwrap()
627
            .iter()
628
            .any(|request| { request.url.path().ends_with("/_matrix/media/v3/upload") })
629
    );
630
    support::shut_down(&harness.handle, &mut harness.events, harness.running).await;
631
}
632
633
#[tokio::test]
634
async fn audio_thumbnail_fails_without_uploading() {
635
    let mut harness = start(plain_image()).await;
636
    let mut upload = AttachmentUpload::new(
637
        room_id!("!media:localhost").to_owned(),
638
        "voice.ogg",
639
        "audio/ogg",
640
        b"voice bytes".to_vec(),
641
    );
642
    upload.thumbnail = Some(UploadThumbnail::new(
643
        b"thumbnail".to_vec(),
644
        "image/png",
645
        80,
646
        80,
647
    ));
648
    let error = harness.handle.upload(upload).await.unwrap_err();
649
    assert_eq!(error.kind, ErrorKind::Invalid);
650
    assert!(
651
        !harness
652
            .server
653
            .received_requests()
654
            .await
655
            .unwrap()
656
            .iter()
657
            .any(|request| { request.url.path().ends_with("/_matrix/media/v3/upload") })
658
    );
659
    support::shut_down(&harness.handle, &mut harness.events, harness.running).await;
660
}
661
662
#[tokio::test]
663
async fn invalid_upload_mime_fails_without_uploading() {
664
    let mut harness = start(plain_image()).await;
665
    let upload = AttachmentUpload::new(
666
        room_id!("!media:localhost").to_owned(),
667
        "photo.jpg",
668
        "not a MIME type",
669
        b"photo data".to_vec(),
670
    );
671
    let error = harness.handle.upload(upload).await.unwrap_err();
672
    assert_eq!(error.kind, ErrorKind::Invalid);
673
674
    support::shut_down(&harness.handle, &mut harness.events, harness.running).await;
675
}
676
677
async fn start(content: RoomMessageEventContent) -> MediaHarness {
678
    start_with_config(
679
        content,
680
        EngineConfig::default().with_initial_history_limit(0),
681
    )
682
    .await
683
}
684
685
async fn start_with_config(content: RoomMessageEventContent, config: EngineConfig) -> MediaHarness {
686
    let server = MatrixMockServer::new().await;
687
    server
688
        .mock_sliding_sync()
689
        .ok(v5::Response::new("1".to_owned()))
690
        .mount()
691
        .await;
692
    let client = server.client_builder().build().await;
693
    let room_id = room_id!("!media:localhost");
694
    server.mock_room_state_encryption().plain().mount().await;
695
    server.sync_joined_room(&client, room_id).await;
696
    let sync_client = client.clone();
697
    let (engine, handle, mut events) = Engine::from_client(client, config).await.unwrap();
698
    let running = tokio::spawn(engine.run());
699
700
    handle.open_room(room_id.to_owned()).await.unwrap();
701
    support::receive_until(
702
        &mut events,
703
        |event| matches!(event, Event::TimelineReset { timeline_id: reset, .. } if reset.room_id() == room_id),
704
    )
705
    .await;
706
707
    server
708
        .sync_room(
709
            &sync_client,
710
            JoinedRoomBuilder::new(room_id)
711
                .add_timeline_event(EventFactory::new().event(content).sender(&ALICE)),
712
        )
713
        .await;
714
    let event = support::receive_until(&mut events, |event| {
715
        support::event_messages(event)
716
            .into_iter()
717
            .any(|message| message.media.is_some())
718
    })
719
    .await;
720
    let media_id = support::event_messages(&event)
721
        .into_iter()
722
        .find_map(|message| message.media.as_ref().map(|media| media.id.clone()))
723
        .unwrap();
724
725
    MediaHarness {
726
        server,
727
        client: sync_client,
728
        handle,
729
        events,
730
        running,
731
        media_id,
732
    }
733
}
734
735
async fn mount_downloads(server: &MatrixMockServer) {
736
    server.mock_media_download().ok_image().mount().await;
737
    server.mock_authed_media_download().ok_image().mount().await;
738
}
739
740
fn thumbnail_size() -> MediaSize {
741
    MediaSize::Thumbnail {
742
        width: 800,
743
        height: 800,
744
    }
745
}
746
747
async fn fetch(harness: &MediaHarness, size: MediaSize) -> Arc<[u8]> {
748
    harness
749
        .handle
750
        .media(&harness.media_id, size)
751
        .await
752
        .unwrap()
753
        .bytes
754
}
755
756
async fn media_paths(server: &MatrixMockServer) -> Vec<String> {
757
    server
758
        .received_requests()
759
        .await
760
        .unwrap()
761
        .into_iter()
762
        .map(|request| request.url.path().to_owned())
763
        .filter(|path| path.contains("/media/"))
764
        .collect()
765
}
766
767
async fn uploaded_content(server: &MatrixMockServer) -> Value {
768
    tokio::time::timeout(Duration::from_secs(5), async {
769
        loop {
770
            if let Some(request) = server
771
                .received_requests()
772
                .await
773
                .unwrap()
774
                .iter()
775
                .find(|request| request.url.path().contains("/send/m.room.message/"))
776
            {
777
                return serde_json::from_slice(&request.body).unwrap();
778
            }
779
            tokio::task::yield_now().await;
780
        }
781
    })
782
    .await
783
    .expect("queued media was not sent")
784
}
785
786
fn file_with_thumbnail() -> RoomMessageEventContent {
787
    let mut info = FileInfo::new();
788
    info.thumbnail_source = Some(MediaSource::Plain(
789
        mxc_uri!("mxc://localhost/explicit-thumbnail").to_owned(),
790
    ));
791
    RoomMessageEventContent::new(MessageType::File(
792
        FileMessageEventContent::plain(
793
            "file.bin".to_owned(),
794
            mxc_uri!("mxc://localhost/full").to_owned(),
795
        )
796
        .info(Box::new(info)),
797
    ))
798
}
799
800
fn plain_image() -> RoomMessageEventContent {
801
    RoomMessageEventContent::new(MessageType::Image(ImageMessageEventContent::plain(
802
        "image.jpg".to_owned(),
803
        mxc_uri!("mxc://localhost/image").to_owned(),
804
    )))
805
}
806
807
fn plain_audio() -> RoomMessageEventContent {
808
    RoomMessageEventContent::new(MessageType::Audio(AudioMessageEventContent::plain(
809
        "audio.ogg".to_owned(),
810
        mxc_uri!("mxc://localhost/audio").to_owned(),
811
    )))
812
}