atagen

macltok

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

Blame: tests/commands.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
    ClientHandle, Command, CommandErrorKind, CommandId, CommandKind, Engine, EngineConfig,
96fdf90aacd6atagen2026-09-08 08:24 UTC5
    EngineError, Event, EventStream, LifecycleCommand, MessageContent, MessageEditContent,
96fdf90aacd6atagen2026-09-08 08:24 UTC6
    RichText, RoomCommand, TimelineCommand,
96fdf90aacd6atagen2026-09-08 08:24 UTC7
};
96fdf90aacd6atagen2026-09-08 08:24 UTC8
use matrix_sdk::{
96fdf90aacd6atagen2026-09-08 08:24 UTC9
    ruma::{RoomId, api::client::sync::sync_events::v5, event_id, 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 tokio::task::JoinHandle;
96fdf90aacd6atagen2026-09-08 08:24 UTC13
96fdf90aacd6atagen2026-09-08 08:24 UTC14
#[tokio::test]
96fdf90aacd6atagen2026-09-08 08:24 UTC15
async fn empty_message_bodies_report_their_command_ids() {
96fdf90aacd6atagen2026-09-08 08:24 UTC16
    let room_id = room_id!("!empty-bodies:localhost");
96fdf90aacd6atagen2026-09-08 08:24 UTC17
    let (server, handle, mut events, running) = start_engine(room_id).await;
96fdf90aacd6atagen2026-09-08 08:24 UTC18
    open_room(&handle, &mut events, room_id).await;
96fdf90aacd6atagen2026-09-08 08:24 UTC19
96fdf90aacd6atagen2026-09-08 08:24 UTC20
    let commands = [
96fdf90aacd6atagen2026-09-08 08:24 UTC21
        TimelineCommand::Send {
96fdf90aacd6atagen2026-09-08 08:24 UTC22
            timeline_id: room_id.to_owned().into(),
96fdf90aacd6atagen2026-09-08 08:24 UTC23
            content: MessageContent::text(String::new()),
96fdf90aacd6atagen2026-09-08 08:24 UTC24
        },
96fdf90aacd6atagen2026-09-08 08:24 UTC25
        TimelineCommand::Send {
96fdf90aacd6atagen2026-09-08 08:24 UTC26
            timeline_id: room_id.to_owned().into(),
96fdf90aacd6atagen2026-09-08 08:24 UTC27
            content: MessageContent::emote(RichText::plain("   "), Default::default()),
96fdf90aacd6atagen2026-09-08 08:24 UTC28
        },
96fdf90aacd6atagen2026-09-08 08:24 UTC29
        TimelineCommand::Send {
96fdf90aacd6atagen2026-09-08 08:24 UTC30
            timeline_id: room_id.to_owned().into(),
96fdf90aacd6atagen2026-09-08 08:24 UTC31
            content: MessageContent::reply(
96fdf90aacd6atagen2026-09-08 08:24 UTC32
                event_id!("$reply:localhost").to_owned(),
96fdf90aacd6atagen2026-09-08 08:24 UTC33
                RichText::plain("\n"),
96fdf90aacd6atagen2026-09-08 08:24 UTC34
                Default::default(),
96fdf90aacd6atagen2026-09-08 08:24 UTC35
            ),
96fdf90aacd6atagen2026-09-08 08:24 UTC36
        },
96fdf90aacd6atagen2026-09-08 08:24 UTC37
        TimelineCommand::Edit {
96fdf90aacd6atagen2026-09-08 08:24 UTC38
            timeline_id: room_id.to_owned().into(),
96fdf90aacd6atagen2026-09-08 08:24 UTC39
            event_id: event_id!("$edit:localhost").to_owned(),
96fdf90aacd6atagen2026-09-08 08:24 UTC40
            content: MessageEditContent::text("\t"),
96fdf90aacd6atagen2026-09-08 08:24 UTC41
        },
96fdf90aacd6atagen2026-09-08 08:24 UTC42
    ];
96fdf90aacd6atagen2026-09-08 08:24 UTC43
96fdf90aacd6atagen2026-09-08 08:24 UTC44
    for command in commands {
96fdf90aacd6atagen2026-09-08 08:24 UTC45
        let id = handle.send(Command::Timeline(command)).await.unwrap();
96fdf90aacd6atagen2026-09-08 08:24 UTC46
        let failure = receive_command_failure(&mut events, id).await;
96fdf90aacd6atagen2026-09-08 08:24 UTC47
        assert_eq!(failure.id, id);
96fdf90aacd6atagen2026-09-08 08:24 UTC48
        assert_eq!(failure.command, CommandKind::Timeline);
96fdf90aacd6atagen2026-09-08 08:24 UTC49
        assert_eq!(failure.kind, CommandErrorKind::EmptyBody);
96fdf90aacd6atagen2026-09-08 08:24 UTC50
    }
96fdf90aacd6atagen2026-09-08 08:24 UTC51
96fdf90aacd6atagen2026-09-08 08:24 UTC52
    stop_engine(handle, &mut events, running).await;
96fdf90aacd6atagen2026-09-08 08:24 UTC53
    drop(server);
96fdf90aacd6atagen2026-09-08 08:24 UTC54
}
96fdf90aacd6atagen2026-09-08 08:24 UTC55
96fdf90aacd6atagen2026-09-08 08:24 UTC56
#[tokio::test]
96fdf90aacd6atagen2026-09-08 08:24 UTC57
async fn open_resets_the_timeline_before_succeeding() {
96fdf90aacd6atagen2026-09-08 08:24 UTC58
    let room_id = room_id!("!open-order:localhost");
96fdf90aacd6atagen2026-09-08 08:24 UTC59
    let (server, handle, mut events, running) = start_engine(room_id).await;
96fdf90aacd6atagen2026-09-08 08:24 UTC60
96fdf90aacd6atagen2026-09-08 08:24 UTC61
    open_room(&handle, &mut events, room_id).await;
96fdf90aacd6atagen2026-09-08 08:24 UTC62
96fdf90aacd6atagen2026-09-08 08:24 UTC63
    stop_engine(handle, &mut events, running).await;
96fdf90aacd6atagen2026-09-08 08:24 UTC64
    drop(server);
96fdf90aacd6atagen2026-09-08 08:24 UTC65
}
96fdf90aacd6atagen2026-09-08 08:24 UTC66
96fdf90aacd6atagen2026-09-08 08:24 UTC67
#[tokio::test]
96fdf90aacd6atagen2026-09-08 08:24 UTC68
async fn same_room_sends_complete_in_submission_order() {
96fdf90aacd6atagen2026-09-08 08:24 UTC69
    let room_id = room_id!("!send-order:localhost");
96fdf90aacd6atagen2026-09-08 08:24 UTC70
    let (server, handle, mut events, running) = start_engine(room_id).await;
96fdf90aacd6atagen2026-09-08 08:24 UTC71
    open_room(&handle, &mut events, room_id).await;
96fdf90aacd6atagen2026-09-08 08:24 UTC72
96fdf90aacd6atagen2026-09-08 08:24 UTC73
    let first_id = handle
96fdf90aacd6atagen2026-09-08 08:24 UTC74
        .send(Command::Timeline(TimelineCommand::Send {
96fdf90aacd6atagen2026-09-08 08:24 UTC75
            timeline_id: room_id.to_owned().into(),
96fdf90aacd6atagen2026-09-08 08:24 UTC76
            content: MessageContent::text("first"),
96fdf90aacd6atagen2026-09-08 08:24 UTC77
        }))
96fdf90aacd6atagen2026-09-08 08:24 UTC78
        .await
96fdf90aacd6atagen2026-09-08 08:24 UTC79
        .unwrap();
96fdf90aacd6atagen2026-09-08 08:24 UTC80
    let second_id = handle
96fdf90aacd6atagen2026-09-08 08:24 UTC81
        .send(Command::Timeline(TimelineCommand::Send {
96fdf90aacd6atagen2026-09-08 08:24 UTC82
            timeline_id: room_id.to_owned().into(),
96fdf90aacd6atagen2026-09-08 08:24 UTC83
            content: MessageContent::text("second"),
96fdf90aacd6atagen2026-09-08 08:24 UTC84
        }))
96fdf90aacd6atagen2026-09-08 08:24 UTC85
        .await
96fdf90aacd6atagen2026-09-08 08:24 UTC86
        .unwrap();
96fdf90aacd6atagen2026-09-08 08:24 UTC87
96fdf90aacd6atagen2026-09-08 08:24 UTC88
    assert_eq!(
96fdf90aacd6atagen2026-09-08 08:24 UTC89
        receive_timeline_completion(&mut events, &[first_id, second_id]).await,
96fdf90aacd6atagen2026-09-08 08:24 UTC90
        vec![first_id, second_id]
96fdf90aacd6atagen2026-09-08 08:24 UTC91
    );
96fdf90aacd6atagen2026-09-08 08:24 UTC92
96fdf90aacd6atagen2026-09-08 08:24 UTC93
    stop_engine(handle, &mut events, running).await;
96fdf90aacd6atagen2026-09-08 08:24 UTC94
    drop(server);
96fdf90aacd6atagen2026-09-08 08:24 UTC95
}
96fdf90aacd6atagen2026-09-08 08:24 UTC96
96fdf90aacd6atagen2026-09-08 08:24 UTC97
async fn start_engine(
96fdf90aacd6atagen2026-09-08 08:24 UTC98
    room_id: &RoomId,
96fdf90aacd6atagen2026-09-08 08:24 UTC99
) -> (
96fdf90aacd6atagen2026-09-08 08:24 UTC100
    MatrixMockServer,
96fdf90aacd6atagen2026-09-08 08:24 UTC101
    ClientHandle,
96fdf90aacd6atagen2026-09-08 08:24 UTC102
    EventStream,
96fdf90aacd6atagen2026-09-08 08:24 UTC103
    JoinHandle<Result<(), EngineError>>,
96fdf90aacd6atagen2026-09-08 08:24 UTC104
) {
96fdf90aacd6atagen2026-09-08 08:24 UTC105
    let server = MatrixMockServer::new().await;
96fdf90aacd6atagen2026-09-08 08:24 UTC106
    server
96fdf90aacd6atagen2026-09-08 08:24 UTC107
        .mock_sliding_sync()
96fdf90aacd6atagen2026-09-08 08:24 UTC108
        .ok(v5::Response::new("1".to_owned()))
96fdf90aacd6atagen2026-09-08 08:24 UTC109
        .mount()
96fdf90aacd6atagen2026-09-08 08:24 UTC110
        .await;
96fdf90aacd6atagen2026-09-08 08:24 UTC111
    let client = server.client_builder().build().await;
96fdf90aacd6atagen2026-09-08 08:24 UTC112
    server.mock_room_state_encryption().plain().mount().await;
96fdf90aacd6atagen2026-09-08 08:24 UTC113
    server.sync_joined_room(&client, room_id).await;
96fdf90aacd6atagen2026-09-08 08:24 UTC114
    let config = EngineConfig::default().with_initial_history_limit(0);
96fdf90aacd6atagen2026-09-08 08:24 UTC115
    let (engine, handle, events) = Engine::from_client(client, config).await.unwrap();
96fdf90aacd6atagen2026-09-08 08:24 UTC116
    let running = tokio::spawn(engine.run());
96fdf90aacd6atagen2026-09-08 08:24 UTC117
    (server, handle, events, running)
96fdf90aacd6atagen2026-09-08 08:24 UTC118
}
96fdf90aacd6atagen2026-09-08 08:24 UTC119
96fdf90aacd6atagen2026-09-08 08:24 UTC120
async fn open_room(handle: &ClientHandle, events: &mut EventStream, room_id: &RoomId) -> CommandId {
96fdf90aacd6atagen2026-09-08 08:24 UTC121
    let id = handle
96fdf90aacd6atagen2026-09-08 08:24 UTC122
        .send(Command::Room(RoomCommand::Open(room_id.to_owned())))
96fdf90aacd6atagen2026-09-08 08:24 UTC123
        .await
96fdf90aacd6atagen2026-09-08 08:24 UTC124
        .unwrap();
96fdf90aacd6atagen2026-09-08 08:24 UTC125
    let mut reset_received = false;
96fdf90aacd6atagen2026-09-08 08:24 UTC126
96fdf90aacd6atagen2026-09-08 08:24 UTC127
    tokio::time::timeout(Duration::from_secs(2), async {
96fdf90aacd6atagen2026-09-08 08:24 UTC128
        loop {
96fdf90aacd6atagen2026-09-08 08:24 UTC129
            match events.recv().await.expect("event stream closed") {
96fdf90aacd6atagen2026-09-08 08:24 UTC130
                Event::TimelineReset {
96fdf90aacd6atagen2026-09-08 08:24 UTC131
                    timeline_id: reset_timeline,
96fdf90aacd6atagen2026-09-08 08:24 UTC132
                    ..
96fdf90aacd6atagen2026-09-08 08:24 UTC133
                } if reset_timeline.room_id() == room_id => reset_received = true,
96fdf90aacd6atagen2026-09-08 08:24 UTC134
                Event::CommandSucceeded {
96fdf90aacd6atagen2026-09-08 08:24 UTC135
                    id: succeeded_id,
96fdf90aacd6atagen2026-09-08 08:24 UTC136
                    command: CommandKind::Room,
96fdf90aacd6atagen2026-09-08 08:24 UTC137
                } if succeeded_id == id => {
96fdf90aacd6atagen2026-09-08 08:24 UTC138
                    assert!(reset_received);
96fdf90aacd6atagen2026-09-08 08:24 UTC139
                    break;
96fdf90aacd6atagen2026-09-08 08:24 UTC140
                }
96fdf90aacd6atagen2026-09-08 08:24 UTC141
                Event::CommandFailed(failure) if failure.id == id => {
96fdf90aacd6atagen2026-09-08 08:24 UTC142
                    panic!("opening room failed {failure:?}")
96fdf90aacd6atagen2026-09-08 08:24 UTC143
                }
96fdf90aacd6atagen2026-09-08 08:24 UTC144
                _ => {}
96fdf90aacd6atagen2026-09-08 08:24 UTC145
            }
96fdf90aacd6atagen2026-09-08 08:24 UTC146
        }
96fdf90aacd6atagen2026-09-08 08:24 UTC147
    })
96fdf90aacd6atagen2026-09-08 08:24 UTC148
    .await
96fdf90aacd6atagen2026-09-08 08:24 UTC149
    .expect("room did not open");
96fdf90aacd6atagen2026-09-08 08:24 UTC150
    id
96fdf90aacd6atagen2026-09-08 08:24 UTC151
}
96fdf90aacd6atagen2026-09-08 08:24 UTC152
96fdf90aacd6atagen2026-09-08 08:24 UTC153
async fn receive_command_failure(
96fdf90aacd6atagen2026-09-08 08:24 UTC154
    events: &mut EventStream,
96fdf90aacd6atagen2026-09-08 08:24 UTC155
    expected_id: CommandId,
96fdf90aacd6atagen2026-09-08 08:24 UTC156
) -> macltok::CommandFailure {
96fdf90aacd6atagen2026-09-08 08:24 UTC157
    tokio::time::timeout(Duration::from_secs(2), async {
96fdf90aacd6atagen2026-09-08 08:24 UTC158
        loop {
96fdf90aacd6atagen2026-09-08 08:24 UTC159
            match events.recv().await.expect("event stream closed") {
96fdf90aacd6atagen2026-09-08 08:24 UTC160
                Event::CommandFailed(failure) if failure.id == expected_id => return failure,
96fdf90aacd6atagen2026-09-08 08:24 UTC161
                Event::CommandSucceeded { id, .. } if id == expected_id => {
96fdf90aacd6atagen2026-09-08 08:24 UTC162
                    panic!("command unexpectedly succeeded")
96fdf90aacd6atagen2026-09-08 08:24 UTC163
                }
96fdf90aacd6atagen2026-09-08 08:24 UTC164
                _ => {}
96fdf90aacd6atagen2026-09-08 08:24 UTC165
            }
96fdf90aacd6atagen2026-09-08 08:24 UTC166
        }
96fdf90aacd6atagen2026-09-08 08:24 UTC167
    })
96fdf90aacd6atagen2026-09-08 08:24 UTC168
    .await
96fdf90aacd6atagen2026-09-08 08:24 UTC169
    .expect("command failure did not arrive")
96fdf90aacd6atagen2026-09-08 08:24 UTC170
}
96fdf90aacd6atagen2026-09-08 08:24 UTC171
96fdf90aacd6atagen2026-09-08 08:24 UTC172
async fn receive_timeline_completion(
96fdf90aacd6atagen2026-09-08 08:24 UTC173
    events: &mut EventStream,
96fdf90aacd6atagen2026-09-08 08:24 UTC174
    expected_ids: &[CommandId],
96fdf90aacd6atagen2026-09-08 08:24 UTC175
) -> Vec<CommandId> {
96fdf90aacd6atagen2026-09-08 08:24 UTC176
    tokio::time::timeout(Duration::from_secs(2), async {
96fdf90aacd6atagen2026-09-08 08:24 UTC177
        let mut completed = Vec::new();
96fdf90aacd6atagen2026-09-08 08:24 UTC178
        while completed.len() < expected_ids.len() {
96fdf90aacd6atagen2026-09-08 08:24 UTC179
            match events.recv().await.expect("event stream closed") {
96fdf90aacd6atagen2026-09-08 08:24 UTC180
                Event::CommandSucceeded {
96fdf90aacd6atagen2026-09-08 08:24 UTC181
                    id,
96fdf90aacd6atagen2026-09-08 08:24 UTC182
                    command: CommandKind::Timeline,
96fdf90aacd6atagen2026-09-08 08:24 UTC183
                } if expected_ids.contains(&id) => completed.push(id),
96fdf90aacd6atagen2026-09-08 08:24 UTC184
                Event::CommandFailed(failure) if expected_ids.contains(&failure.id) => {
96fdf90aacd6atagen2026-09-08 08:24 UTC185
                    panic!("timeline command failed {failure:?}")
96fdf90aacd6atagen2026-09-08 08:24 UTC186
                }
96fdf90aacd6atagen2026-09-08 08:24 UTC187
                _ => {}
96fdf90aacd6atagen2026-09-08 08:24 UTC188
            }
96fdf90aacd6atagen2026-09-08 08:24 UTC189
        }
96fdf90aacd6atagen2026-09-08 08:24 UTC190
        completed
96fdf90aacd6atagen2026-09-08 08:24 UTC191
    })
96fdf90aacd6atagen2026-09-08 08:24 UTC192
    .await
96fdf90aacd6atagen2026-09-08 08:24 UTC193
    .expect("timeline commands did not complete")
96fdf90aacd6atagen2026-09-08 08:24 UTC194
}
96fdf90aacd6atagen2026-09-08 08:24 UTC195
96fdf90aacd6atagen2026-09-08 08:24 UTC196
async fn stop_engine(
96fdf90aacd6atagen2026-09-08 08:24 UTC197
    handle: ClientHandle,
96fdf90aacd6atagen2026-09-08 08:24 UTC198
    events: &mut EventStream,
96fdf90aacd6atagen2026-09-08 08:24 UTC199
    running: JoinHandle<Result<(), EngineError>>,
96fdf90aacd6atagen2026-09-08 08:24 UTC200
) {
96fdf90aacd6atagen2026-09-08 08:24 UTC201
    let id = handle
96fdf90aacd6atagen2026-09-08 08:24 UTC202
        .send(Command::Lifecycle(LifecycleCommand::Shutdown))
96fdf90aacd6atagen2026-09-08 08:24 UTC203
        .await
96fdf90aacd6atagen2026-09-08 08:24 UTC204
        .unwrap();
96fdf90aacd6atagen2026-09-08 08:24 UTC205
    tokio::time::timeout(Duration::from_secs(2), async {
96fdf90aacd6atagen2026-09-08 08:24 UTC206
        let mut succeeded = false;
96fdf90aacd6atagen2026-09-08 08:24 UTC207
        while let Some(event) = events.recv().await {
96fdf90aacd6atagen2026-09-08 08:24 UTC208
            match event {
96fdf90aacd6atagen2026-09-08 08:24 UTC209
                Event::CommandSucceeded {
96fdf90aacd6atagen2026-09-08 08:24 UTC210
                    id: succeeded_id,
96fdf90aacd6atagen2026-09-08 08:24 UTC211
                    command: CommandKind::Lifecycle,
96fdf90aacd6atagen2026-09-08 08:24 UTC212
                } if succeeded_id == id => succeeded = true,
96fdf90aacd6atagen2026-09-08 08:24 UTC213
                Event::Stopped => {
96fdf90aacd6atagen2026-09-08 08:24 UTC214
                    assert!(succeeded);
96fdf90aacd6atagen2026-09-08 08:24 UTC215
                    break;
96fdf90aacd6atagen2026-09-08 08:24 UTC216
                }
96fdf90aacd6atagen2026-09-08 08:24 UTC217
                _ => {}
96fdf90aacd6atagen2026-09-08 08:24 UTC218
            }
96fdf90aacd6atagen2026-09-08 08:24 UTC219
        }
96fdf90aacd6atagen2026-09-08 08:24 UTC220
    })
96fdf90aacd6atagen2026-09-08 08:24 UTC221
    .await
96fdf90aacd6atagen2026-09-08 08:24 UTC222
    .expect("engine did not stop");
96fdf90aacd6atagen2026-09-08 08:24 UTC223
    running.await.unwrap().unwrap();
96fdf90aacd6atagen2026-09-08 08:24 UTC224
}