atagen

macltok

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

Blame: tests/nonblocking.rs

96fdf90aacd6atagen2026-09-08 08:24 UTC1
use std::time::Duration;
96fdf90aacd6atagen2026-09-08 08:24 UTC2
96fdf90aacd6atagen2026-09-08 08:24 UTC3
use macltok::{
96fdf90aacd6atagen2026-09-08 08:24 UTC4
    Command, CommandErrorKind, CommandKind, Engine, EngineConfig, Event, LifecycleCommand,
96fdf90aacd6atagen2026-09-08 08:24 UTC5
    MediaCommand, MediaSize, MessageContent, RoomCommand, TimelineCommand, TimelineDiff,
96fdf90aacd6atagen2026-09-08 08:24 UTC6
    TimelineItem,
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};
96fdf90aacd6atagen2026-09-08 08:24 UTC13
use wiremock::ResponseTemplate;
96fdf90aacd6atagen2026-09-08 08:24 UTC14
96fdf90aacd6atagen2026-09-08 08:24 UTC15
#[tokio::test]
96fdf90aacd6atagen2026-09-08 08:24 UTC16
async fn delayed_media_fetch_does_not_block_timeline_command() {
96fdf90aacd6atagen2026-09-08 08:24 UTC17
    let server = MatrixMockServer::new().await;
96fdf90aacd6atagen2026-09-08 08:24 UTC18
    server
96fdf90aacd6atagen2026-09-08 08:24 UTC19
        .mock_sliding_sync()
96fdf90aacd6atagen2026-09-08 08:24 UTC20
        .ok(v5::Response::new("1".to_owned()))
96fdf90aacd6atagen2026-09-08 08:24 UTC21
        .mount()
96fdf90aacd6atagen2026-09-08 08:24 UTC22
        .await;
96fdf90aacd6atagen2026-09-08 08:24 UTC23
    let client = server.client_builder().build().await;
96fdf90aacd6atagen2026-09-08 08:24 UTC24
    let sync_client = client.clone();
96fdf90aacd6atagen2026-09-08 08:24 UTC25
    let room_id = room_id!("!nonblocking:localhost");
96fdf90aacd6atagen2026-09-08 08:24 UTC26
    server.mock_room_state_encryption().plain().mount().await;
96fdf90aacd6atagen2026-09-08 08:24 UTC27
    server.sync_joined_room(&client, room_id).await;
96fdf90aacd6atagen2026-09-08 08:24 UTC28
    let config = EngineConfig::default().with_initial_history_limit(0);
96fdf90aacd6atagen2026-09-08 08:24 UTC29
    let (engine, handle, mut events) = Engine::from_client(client, config).await.unwrap();
96fdf90aacd6atagen2026-09-08 08:24 UTC30
    let running = tokio::spawn(engine.run());
96fdf90aacd6atagen2026-09-08 08:24 UTC31
96fdf90aacd6atagen2026-09-08 08:24 UTC32
    handle
96fdf90aacd6atagen2026-09-08 08:24 UTC33
        .send(Command::Room(RoomCommand::Open(room_id.to_owned())))
96fdf90aacd6atagen2026-09-08 08:24 UTC34
        .await
96fdf90aacd6atagen2026-09-08 08:24 UTC35
        .unwrap();
96fdf90aacd6atagen2026-09-08 08:24 UTC36
    receive_until(
96fdf90aacd6atagen2026-09-08 08:24 UTC37
        &mut events,
96fdf90aacd6atagen2026-09-08 08:24 UTC38
        |event| matches!(event, Event::TimelineReset { timeline_id: reset, .. } if reset.room_id() == room_id),
96fdf90aacd6atagen2026-09-08 08:24 UTC39
    )
96fdf90aacd6atagen2026-09-08 08:24 UTC40
    .await;
96fdf90aacd6atagen2026-09-08 08:24 UTC41
96fdf90aacd6atagen2026-09-08 08:24 UTC42
    server
96fdf90aacd6atagen2026-09-08 08:24 UTC43
        .sync_room(
96fdf90aacd6atagen2026-09-08 08:24 UTC44
            &sync_client,
96fdf90aacd6atagen2026-09-08 08:24 UTC45
            JoinedRoomBuilder::new(room_id).add_timeline_event(
96fdf90aacd6atagen2026-09-08 08:24 UTC46
                EventFactory::new()
96fdf90aacd6atagen2026-09-08 08:24 UTC47
                    .image(
96fdf90aacd6atagen2026-09-08 08:24 UTC48
                        "image.jpg".into(),
96fdf90aacd6atagen2026-09-08 08:24 UTC49
                        mxc_uri!("mxc://localhost/image").to_owned(),
96fdf90aacd6atagen2026-09-08 08:24 UTC50
                    )
96fdf90aacd6atagen2026-09-08 08:24 UTC51
                    .sender(&ALICE),
96fdf90aacd6atagen2026-09-08 08:24 UTC52
            ),
96fdf90aacd6atagen2026-09-08 08:24 UTC53
        )
96fdf90aacd6atagen2026-09-08 08:24 UTC54
        .await;
96fdf90aacd6atagen2026-09-08 08:24 UTC55
    let media_event = receive_until(&mut events, |event| {
96fdf90aacd6atagen2026-09-08 08:24 UTC56
        event_messages(event).any(|message| message.media.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)
96fdf90aacd6atagen2026-09-08 08:24 UTC60
        .find_map(|message| message.media.as_ref().map(|media| media.id.clone()))
96fdf90aacd6atagen2026-09-08 08:24 UTC61
        .unwrap();
96fdf90aacd6atagen2026-09-08 08:24 UTC62
96fdf90aacd6atagen2026-09-08 08:24 UTC63
    let delayed_image = || {
96fdf90aacd6atagen2026-09-08 08:24 UTC64
        ResponseTemplate::new(200)
96fdf90aacd6atagen2026-09-08 08:24 UTC65
            .set_body_raw(b"binaryjpegfullimagedata", "image/jpeg")
96fdf90aacd6atagen2026-09-08 08:24 UTC66
            .set_delay(Duration::from_millis(500))
96fdf90aacd6atagen2026-09-08 08:24 UTC67
    };
96fdf90aacd6atagen2026-09-08 08:24 UTC68
    server
96fdf90aacd6atagen2026-09-08 08:24 UTC69
        .mock_media_download()
96fdf90aacd6atagen2026-09-08 08:24 UTC70
        .respond_with(delayed_image())
96fdf90aacd6atagen2026-09-08 08:24 UTC71
        .mount()
96fdf90aacd6atagen2026-09-08 08:24 UTC72
        .await;
96fdf90aacd6atagen2026-09-08 08:24 UTC73
    server
96fdf90aacd6atagen2026-09-08 08:24 UTC74
        .mock_authed_media_download()
96fdf90aacd6atagen2026-09-08 08:24 UTC75
        .respond_with(delayed_image())
96fdf90aacd6atagen2026-09-08 08:24 UTC76
        .mount()
96fdf90aacd6atagen2026-09-08 08:24 UTC77
        .await;
96fdf90aacd6atagen2026-09-08 08:24 UTC78
96fdf90aacd6atagen2026-09-08 08:24 UTC79
    let media_command_id = handle
96fdf90aacd6atagen2026-09-08 08:24 UTC80
        .send(Command::Media(MediaCommand::Fetch {
96fdf90aacd6atagen2026-09-08 08:24 UTC81
            id: media_id.clone(),
96fdf90aacd6atagen2026-09-08 08:24 UTC82
            size: MediaSize::Full,
96fdf90aacd6atagen2026-09-08 08:24 UTC83
        }))
96fdf90aacd6atagen2026-09-08 08:24 UTC84
        .await
96fdf90aacd6atagen2026-09-08 08:24 UTC85
        .unwrap();
96fdf90aacd6atagen2026-09-08 08:24 UTC86
    wait_for_media_request(&server).await;
96fdf90aacd6atagen2026-09-08 08:24 UTC87
    let timeline_command_id = handle
96fdf90aacd6atagen2026-09-08 08:24 UTC88
        .send(Command::Timeline(TimelineCommand::Send {
96fdf90aacd6atagen2026-09-08 08:24 UTC89
            timeline_id: room_id.to_owned().into(),
96fdf90aacd6atagen2026-09-08 08:24 UTC90
            content: MessageContent::text(String::new()),
96fdf90aacd6atagen2026-09-08 08:24 UTC91
        }))
96fdf90aacd6atagen2026-09-08 08:24 UTC92
        .await
96fdf90aacd6atagen2026-09-08 08:24 UTC93
        .unwrap();
96fdf90aacd6atagen2026-09-08 08:24 UTC94
96fdf90aacd6atagen2026-09-08 08:24 UTC95
    tokio::time::timeout(Duration::from_secs(2), async {
96fdf90aacd6atagen2026-09-08 08:24 UTC96
        loop {
96fdf90aacd6atagen2026-09-08 08:24 UTC97
            match events.recv().await.expect("event stream closed") {
96fdf90aacd6atagen2026-09-08 08:24 UTC98
                Event::CommandFailed(failure) if failure.id == timeline_command_id => {
96fdf90aacd6atagen2026-09-08 08:24 UTC99
                    assert_eq!(failure.command, CommandKind::Timeline);
96fdf90aacd6atagen2026-09-08 08:24 UTC100
                    assert_eq!(failure.kind, CommandErrorKind::EmptyBody);
96fdf90aacd6atagen2026-09-08 08:24 UTC101
                    break;
96fdf90aacd6atagen2026-09-08 08:24 UTC102
                }
96fdf90aacd6atagen2026-09-08 08:24 UTC103
                Event::CommandSucceeded { id, .. } if id == media_command_id => {
96fdf90aacd6atagen2026-09-08 08:24 UTC104
                    panic!("media command completed before the timeline command")
96fdf90aacd6atagen2026-09-08 08:24 UTC105
                }
96fdf90aacd6atagen2026-09-08 08:24 UTC106
                Event::CommandFailed(failure) if failure.id == media_command_id => {
96fdf90aacd6atagen2026-09-08 08:24 UTC107
                    panic!("media command failed before the timeline command {failure:?}")
96fdf90aacd6atagen2026-09-08 08:24 UTC108
                }
96fdf90aacd6atagen2026-09-08 08:24 UTC109
                Event::MediaReady {
96fdf90aacd6atagen2026-09-08 08:24 UTC110
                    media_id: ready_id, ..
96fdf90aacd6atagen2026-09-08 08:24 UTC111
                } if ready_id == media_id => {
96fdf90aacd6atagen2026-09-08 08:24 UTC112
                    panic!("media became ready before the timeline command completed")
96fdf90aacd6atagen2026-09-08 08:24 UTC113
                }
96fdf90aacd6atagen2026-09-08 08:24 UTC114
                _ => {}
96fdf90aacd6atagen2026-09-08 08:24 UTC115
            }
96fdf90aacd6atagen2026-09-08 08:24 UTC116
        }
96fdf90aacd6atagen2026-09-08 08:24 UTC117
    })
96fdf90aacd6atagen2026-09-08 08:24 UTC118
    .await
96fdf90aacd6atagen2026-09-08 08:24 UTC119
    .expect("timeline command was blocked by media fetch");
96fdf90aacd6atagen2026-09-08 08:24 UTC120
96fdf90aacd6atagen2026-09-08 08:24 UTC121
    receive_until(&mut events, |event| {
96fdf90aacd6atagen2026-09-08 08:24 UTC122
        matches!(
96fdf90aacd6atagen2026-09-08 08:24 UTC123
            event,
96fdf90aacd6atagen2026-09-08 08:24 UTC124
            Event::CommandSucceeded {
96fdf90aacd6atagen2026-09-08 08:24 UTC125
                id,
96fdf90aacd6atagen2026-09-08 08:24 UTC126
                command: CommandKind::Media,
96fdf90aacd6atagen2026-09-08 08:24 UTC127
            } if id == &media_command_id
96fdf90aacd6atagen2026-09-08 08:24 UTC128
        )
96fdf90aacd6atagen2026-09-08 08:24 UTC129
    })
96fdf90aacd6atagen2026-09-08 08:24 UTC130
    .await;
96fdf90aacd6atagen2026-09-08 08:24 UTC131
96fdf90aacd6atagen2026-09-08 08:24 UTC132
    handle
96fdf90aacd6atagen2026-09-08 08:24 UTC133
        .send(Command::Lifecycle(LifecycleCommand::Shutdown))
96fdf90aacd6atagen2026-09-08 08:24 UTC134
        .await
96fdf90aacd6atagen2026-09-08 08:24 UTC135
        .unwrap();
96fdf90aacd6atagen2026-09-08 08:24 UTC136
    receive_until(&mut events, |event| matches!(event, Event::Stopped)).await;
96fdf90aacd6atagen2026-09-08 08:24 UTC137
    running.await.unwrap().unwrap();
96fdf90aacd6atagen2026-09-08 08:24 UTC138
}
96fdf90aacd6atagen2026-09-08 08:24 UTC139
96fdf90aacd6atagen2026-09-08 08:24 UTC140
async fn wait_for_media_request(server: &MatrixMockServer) {
96fdf90aacd6atagen2026-09-08 08:24 UTC141
    tokio::time::timeout(Duration::from_secs(2), async {
96fdf90aacd6atagen2026-09-08 08:24 UTC142
        loop {
96fdf90aacd6atagen2026-09-08 08:24 UTC143
            if server
96fdf90aacd6atagen2026-09-08 08:24 UTC144
                .received_requests()
96fdf90aacd6atagen2026-09-08 08:24 UTC145
                .await
96fdf90aacd6atagen2026-09-08 08:24 UTC146
                .unwrap()
96fdf90aacd6atagen2026-09-08 08:24 UTC147
                .iter()
96fdf90aacd6atagen2026-09-08 08:24 UTC148
                .any(|request| request.url.path().ends_with("/download/localhost/image"))
96fdf90aacd6atagen2026-09-08 08:24 UTC149
            {
96fdf90aacd6atagen2026-09-08 08:24 UTC150
                break;
96fdf90aacd6atagen2026-09-08 08:24 UTC151
            }
96fdf90aacd6atagen2026-09-08 08:24 UTC152
            tokio::task::yield_now().await;
96fdf90aacd6atagen2026-09-08 08:24 UTC153
        }
96fdf90aacd6atagen2026-09-08 08:24 UTC154
    })
96fdf90aacd6atagen2026-09-08 08:24 UTC155
    .await
96fdf90aacd6atagen2026-09-08 08:24 UTC156
    .expect("media request did not reach the homeserver");
96fdf90aacd6atagen2026-09-08 08:24 UTC157
}
96fdf90aacd6atagen2026-09-08 08:24 UTC158
96fdf90aacd6atagen2026-09-08 08:24 UTC159
fn event_messages(event: &Event) -> std::vec::IntoIter<&macltok::TimelineMessage> {
96fdf90aacd6atagen2026-09-08 08:24 UTC160
    let items = match event {
96fdf90aacd6atagen2026-09-08 08:24 UTC161
        Event::TimelineReset { items, .. } => items.iter().collect::<Vec<_>>(),
96fdf90aacd6atagen2026-09-08 08:24 UTC162
        Event::TimelineChanged { diffs, .. } => {
96fdf90aacd6atagen2026-09-08 08:24 UTC163
            diffs.iter().flat_map(diff_items).collect::<Vec<_>>()
96fdf90aacd6atagen2026-09-08 08:24 UTC164
        }
96fdf90aacd6atagen2026-09-08 08:24 UTC165
        _ => Vec::new(),
96fdf90aacd6atagen2026-09-08 08:24 UTC166
    };
96fdf90aacd6atagen2026-09-08 08:24 UTC167
    items
96fdf90aacd6atagen2026-09-08 08:24 UTC168
        .into_iter()
96fdf90aacd6atagen2026-09-08 08:24 UTC169
        .filter_map(|item| match item {
96fdf90aacd6atagen2026-09-08 08:24 UTC170
            TimelineItem::Message(message) => Some(message.as_ref()),
96fdf90aacd6atagen2026-09-08 08:24 UTC171
            _ => None,
96fdf90aacd6atagen2026-09-08 08:24 UTC172
        })
96fdf90aacd6atagen2026-09-08 08:24 UTC173
        .collect::<Vec<_>>()
96fdf90aacd6atagen2026-09-08 08:24 UTC174
        .into_iter()
96fdf90aacd6atagen2026-09-08 08:24 UTC175
}
96fdf90aacd6atagen2026-09-08 08:24 UTC176
96fdf90aacd6atagen2026-09-08 08:24 UTC177
fn diff_items(diff: &TimelineDiff) -> Vec<&TimelineItem> {
96fdf90aacd6atagen2026-09-08 08:24 UTC178
    match diff {
96fdf90aacd6atagen2026-09-08 08:24 UTC179
        TimelineDiff::Append { values } | TimelineDiff::Reset { values } => values.iter().collect(),
96fdf90aacd6atagen2026-09-08 08:24 UTC180
        TimelineDiff::PushFront { value }
96fdf90aacd6atagen2026-09-08 08:24 UTC181
        | TimelineDiff::PushBack { value }
96fdf90aacd6atagen2026-09-08 08:24 UTC182
        | TimelineDiff::Insert { value, .. }
96fdf90aacd6atagen2026-09-08 08:24 UTC183
        | TimelineDiff::Set { value, .. } => vec![value],
96fdf90aacd6atagen2026-09-08 08:24 UTC184
        TimelineDiff::Clear
96fdf90aacd6atagen2026-09-08 08:24 UTC185
        | TimelineDiff::PopFront
96fdf90aacd6atagen2026-09-08 08:24 UTC186
        | TimelineDiff::PopBack
96fdf90aacd6atagen2026-09-08 08:24 UTC187
        | TimelineDiff::Remove { .. }
96fdf90aacd6atagen2026-09-08 08:24 UTC188
        | TimelineDiff::Truncate { .. } => Vec::new(),
96fdf90aacd6atagen2026-09-08 08:24 UTC189
        _ => Vec::new(),
96fdf90aacd6atagen2026-09-08 08:24 UTC190
    }
96fdf90aacd6atagen2026-09-08 08:24 UTC191
}
96fdf90aacd6atagen2026-09-08 08:24 UTC192
96fdf90aacd6atagen2026-09-08 08:24 UTC193
async fn receive_until(
96fdf90aacd6atagen2026-09-08 08:24 UTC194
    events: &mut macltok::EventStream,
96fdf90aacd6atagen2026-09-08 08:24 UTC195
    predicate: impl Fn(&Event) -> bool,
96fdf90aacd6atagen2026-09-08 08:24 UTC196
) -> Event {
96fdf90aacd6atagen2026-09-08 08:24 UTC197
    tokio::time::timeout(Duration::from_secs(2), async {
96fdf90aacd6atagen2026-09-08 08:24 UTC198
        while let Some(event) = events.recv().await {
96fdf90aacd6atagen2026-09-08 08:24 UTC199
            if predicate(&event) {
96fdf90aacd6atagen2026-09-08 08:24 UTC200
                return event;
96fdf90aacd6atagen2026-09-08 08:24 UTC201
            }
96fdf90aacd6atagen2026-09-08 08:24 UTC202
        }
96fdf90aacd6atagen2026-09-08 08:24 UTC203
        panic!("event stream closed before the expected event")
96fdf90aacd6atagen2026-09-08 08:24 UTC204
    })
96fdf90aacd6atagen2026-09-08 08:24 UTC205
    .await
96fdf90aacd6atagen2026-09-08 08:24 UTC206
    .expect("expected event did not arrive")
96fdf90aacd6atagen2026-09-08 08:24 UTC207
}