atagen

macltok

public
Star0
Watch0
fe81a9ceeea23a430f9fa89dfe37c40e8709fedc
3
ssh://brewery@pls.lobotomise.me:33/atagen/macltok.git

Blame: tests/nonblocking.rs

31ce2453c82fatagen2026-09-24 13:07 UTC1
mod support;
31ce2453c82fatagen2026-09-24 13:07 UTC2
96fdf90aacd6atagen2026-09-08 08:24 UTC3
use std::time::Duration;
96fdf90aacd6atagen2026-09-08 08:24 UTC4
96fdf90aacd6atagen2026-09-08 08:24 UTC5
use macltok::{
31ce2453c82fatagen2026-09-24 13:07 UTC6
    Command, Engine, EngineConfig, ErrorKind, Event, MediaSize, MessageContent, TimelineCommand,
96fdf90aacd6atagen2026-09-08 08:24 UTC7
};
96fdf90aacd6atagen2026-09-08 08:24 UTC8
use matrix_sdk::{
96fdf90aacd6atagen2026-09-08 08:24 UTC9
    ruma::{api::client::sync::sync_events::v5, mxc_uri, room_id},
96fdf90aacd6atagen2026-09-08 08:24 UTC10
    test_utils::mocks::MatrixMockServer,
96fdf90aacd6atagen2026-09-08 08:24 UTC11
};
96fdf90aacd6atagen2026-09-08 08:24 UTC12
use matrix_sdk_test::{ALICE, JoinedRoomBuilder, event_factory::EventFactory};
31ce2453c82fatagen2026-09-24 13:07 UTC13
use support::{event_messages, receive_until, shut_down};
96fdf90aacd6atagen2026-09-08 08:24 UTC14
use wiremock::ResponseTemplate;
96fdf90aacd6atagen2026-09-08 08:24 UTC15
96fdf90aacd6atagen2026-09-08 08:24 UTC16
#[tokio::test]
96fdf90aacd6atagen2026-09-08 08:24 UTC17
async fn delayed_media_fetch_does_not_block_timeline_command() {
96fdf90aacd6atagen2026-09-08 08:24 UTC18
    let server = MatrixMockServer::new().await;
96fdf90aacd6atagen2026-09-08 08:24 UTC19
    server
96fdf90aacd6atagen2026-09-08 08:24 UTC20
        .mock_sliding_sync()
96fdf90aacd6atagen2026-09-08 08:24 UTC21
        .ok(v5::Response::new("1".to_owned()))
96fdf90aacd6atagen2026-09-08 08:24 UTC22
        .mount()
96fdf90aacd6atagen2026-09-08 08:24 UTC23
        .await;
96fdf90aacd6atagen2026-09-08 08:24 UTC24
    let client = server.client_builder().build().await;
96fdf90aacd6atagen2026-09-08 08:24 UTC25
    let sync_client = client.clone();
96fdf90aacd6atagen2026-09-08 08:24 UTC26
    let room_id = room_id!("!nonblocking:localhost");
96fdf90aacd6atagen2026-09-08 08:24 UTC27
    server.mock_room_state_encryption().plain().mount().await;
96fdf90aacd6atagen2026-09-08 08:24 UTC28
    server.sync_joined_room(&client, room_id).await;
96fdf90aacd6atagen2026-09-08 08:24 UTC29
    let config = EngineConfig::default().with_initial_history_limit(0);
96fdf90aacd6atagen2026-09-08 08:24 UTC30
    let (engine, handle, mut events) = Engine::from_client(client, config).await.unwrap();
96fdf90aacd6atagen2026-09-08 08:24 UTC31
    let running = tokio::spawn(engine.run());
96fdf90aacd6atagen2026-09-08 08:24 UTC32
31ce2453c82fatagen2026-09-24 13:07 UTC33
    handle.open_room(room_id.to_owned()).await.unwrap();
96fdf90aacd6atagen2026-09-08 08:24 UTC34
    receive_until(
96fdf90aacd6atagen2026-09-08 08:24 UTC35
        &mut events,
96fdf90aacd6atagen2026-09-08 08:24 UTC36
        |event| matches!(event, Event::TimelineReset { timeline_id: reset, .. } if reset.room_id() == room_id),
96fdf90aacd6atagen2026-09-08 08:24 UTC37
    )
96fdf90aacd6atagen2026-09-08 08:24 UTC38
    .await;
96fdf90aacd6atagen2026-09-08 08:24 UTC39
96fdf90aacd6atagen2026-09-08 08:24 UTC40
    server
96fdf90aacd6atagen2026-09-08 08:24 UTC41
        .sync_room(
96fdf90aacd6atagen2026-09-08 08:24 UTC42
            &sync_client,
96fdf90aacd6atagen2026-09-08 08:24 UTC43
            JoinedRoomBuilder::new(room_id).add_timeline_event(
96fdf90aacd6atagen2026-09-08 08:24 UTC44
                EventFactory::new()
96fdf90aacd6atagen2026-09-08 08:24 UTC45
                    .image(
96fdf90aacd6atagen2026-09-08 08:24 UTC46
                        "image.jpg".into(),
96fdf90aacd6atagen2026-09-08 08:24 UTC47
                        mxc_uri!("mxc://localhost/image").to_owned(),
96fdf90aacd6atagen2026-09-08 08:24 UTC48
                    )
96fdf90aacd6atagen2026-09-08 08:24 UTC49
                    .sender(&ALICE),
96fdf90aacd6atagen2026-09-08 08:24 UTC50
            ),
96fdf90aacd6atagen2026-09-08 08:24 UTC51
        )
96fdf90aacd6atagen2026-09-08 08:24 UTC52
        .await;
96fdf90aacd6atagen2026-09-08 08:24 UTC53
    let media_event = receive_until(&mut events, |event| {
31ce2453c82fatagen2026-09-24 13:07 UTC54
        event_messages(event)
31ce2453c82fatagen2026-09-24 13:07 UTC55
            .into_iter()
960ac0721079atagen2026-09-25 00:24 UTC56
            .any(|event| support::media_of(event).is_some())
96fdf90aacd6atagen2026-09-08 08:24 UTC57
    })
96fdf90aacd6atagen2026-09-08 08:24 UTC58
    .await;
96fdf90aacd6atagen2026-09-08 08:24 UTC59
    let media_id = event_messages(&media_event)
31ce2453c82fatagen2026-09-24 13:07 UTC60
        .into_iter()
960ac0721079atagen2026-09-25 00:24 UTC61
        .find_map(|event| support::media_of(event).map(|media| media.id.clone()))
96fdf90aacd6atagen2026-09-08 08:24 UTC62
        .unwrap();
96fdf90aacd6atagen2026-09-08 08:24 UTC63
96fdf90aacd6atagen2026-09-08 08:24 UTC64
    let delayed_image = || {
96fdf90aacd6atagen2026-09-08 08:24 UTC65
        ResponseTemplate::new(200)
96fdf90aacd6atagen2026-09-08 08:24 UTC66
            .set_body_raw(b"binaryjpegfullimagedata", "image/jpeg")
96fdf90aacd6atagen2026-09-08 08:24 UTC67
            .set_delay(Duration::from_millis(500))
96fdf90aacd6atagen2026-09-08 08:24 UTC68
    };
96fdf90aacd6atagen2026-09-08 08:24 UTC69
    server
96fdf90aacd6atagen2026-09-08 08:24 UTC70
        .mock_media_download()
96fdf90aacd6atagen2026-09-08 08:24 UTC71
        .respond_with(delayed_image())
96fdf90aacd6atagen2026-09-08 08:24 UTC72
        .mount()
96fdf90aacd6atagen2026-09-08 08:24 UTC73
        .await;
96fdf90aacd6atagen2026-09-08 08:24 UTC74
    server
96fdf90aacd6atagen2026-09-08 08:24 UTC75
        .mock_authed_media_download()
96fdf90aacd6atagen2026-09-08 08:24 UTC76
        .respond_with(delayed_image())
96fdf90aacd6atagen2026-09-08 08:24 UTC77
        .mount()
96fdf90aacd6atagen2026-09-08 08:24 UTC78
        .await;
96fdf90aacd6atagen2026-09-08 08:24 UTC79
31ce2453c82fatagen2026-09-24 13:07 UTC80
    // media no longer travels through the command queue, so driving it on its
31ce2453c82fatagen2026-09-24 13:07 UTC81
    // own task is what puts the request in flight concurrently with the send below
31ce2453c82fatagen2026-09-24 13:07 UTC82
    let media_task = tokio::spawn({
31ce2453c82fatagen2026-09-24 13:07 UTC83
        let handle = handle.clone();
31ce2453c82fatagen2026-09-24 13:07 UTC84
        async move { handle.media(&media_id, MediaSize::Full).await }
31ce2453c82fatagen2026-09-24 13:07 UTC85
    });
96fdf90aacd6atagen2026-09-08 08:24 UTC86
    wait_for_media_request(&server).await;
31ce2453c82fatagen2026-09-24 13:07 UTC87
31ce2453c82fatagen2026-09-24 13:07 UTC88
    let error = tokio::time::timeout(
31ce2453c82fatagen2026-09-24 13:07 UTC89
        Duration::from_millis(300),
31ce2453c82fatagen2026-09-24 13:07 UTC90
        handle.execute(Command::Timeline(TimelineCommand::Send {
96fdf90aacd6atagen2026-09-08 08:24 UTC91
            timeline_id: room_id.to_owned().into(),
96fdf90aacd6atagen2026-09-08 08:24 UTC92
            content: MessageContent::text(String::new()),
31ce2453c82fatagen2026-09-24 13:07 UTC93
        })),
31ce2453c82fatagen2026-09-24 13:07 UTC94
    )
96fdf90aacd6atagen2026-09-08 08:24 UTC95
    .await
31ce2453c82fatagen2026-09-24 13:07 UTC96
    .expect("timeline command was blocked by media fetch")
31ce2453c82fatagen2026-09-24 13:07 UTC97
    .expect_err("empty body command unexpectedly succeeded");
31ce2453c82fatagen2026-09-24 13:07 UTC98
    assert_eq!(error.kind, ErrorKind::EmptyBody);
96fdf90aacd6atagen2026-09-08 08:24 UTC99
31ce2453c82fatagen2026-09-24 13:07 UTC100
    let media = tokio::time::timeout(Duration::from_secs(2), media_task)
96fdf90aacd6atagen2026-09-08 08:24 UTC101
        .await
31ce2453c82fatagen2026-09-24 13:07 UTC102
        .expect("media fetch did not complete")
31ce2453c82fatagen2026-09-24 13:07 UTC103
        .unwrap()
96fdf90aacd6atagen2026-09-08 08:24 UTC104
        .unwrap();
31ce2453c82fatagen2026-09-24 13:07 UTC105
    assert!(!media.bytes.is_empty());
31ce2453c82fatagen2026-09-24 13:07 UTC106
31ce2453c82fatagen2026-09-24 13:07 UTC107
    shut_down(&handle, &mut events, running).await;
96fdf90aacd6atagen2026-09-08 08:24 UTC108
}
96fdf90aacd6atagen2026-09-08 08:24 UTC109
96fdf90aacd6atagen2026-09-08 08:24 UTC110
async fn wait_for_media_request(server: &MatrixMockServer) {
96fdf90aacd6atagen2026-09-08 08:24 UTC111
    tokio::time::timeout(Duration::from_secs(2), async {
96fdf90aacd6atagen2026-09-08 08:24 UTC112
        loop {
96fdf90aacd6atagen2026-09-08 08:24 UTC113
            if server
96fdf90aacd6atagen2026-09-08 08:24 UTC114
                .received_requests()
96fdf90aacd6atagen2026-09-08 08:24 UTC115
                .await
96fdf90aacd6atagen2026-09-08 08:24 UTC116
                .unwrap()
96fdf90aacd6atagen2026-09-08 08:24 UTC117
                .iter()
96fdf90aacd6atagen2026-09-08 08:24 UTC118
                .any(|request| request.url.path().ends_with("/download/localhost/image"))
96fdf90aacd6atagen2026-09-08 08:24 UTC119
            {
96fdf90aacd6atagen2026-09-08 08:24 UTC120
                break;
96fdf90aacd6atagen2026-09-08 08:24 UTC121
            }
96fdf90aacd6atagen2026-09-08 08:24 UTC122
            tokio::task::yield_now().await;
96fdf90aacd6atagen2026-09-08 08:24 UTC123
        }
96fdf90aacd6atagen2026-09-08 08:24 UTC124
    })
96fdf90aacd6atagen2026-09-08 08:24 UTC125
    .await
96fdf90aacd6atagen2026-09-08 08:24 UTC126
    .expect("media request did not reach the homeserver");
96fdf90aacd6atagen2026-09-08 08:24 UTC127
}