atagen

macltok

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

Blame: tests/commands.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
    ClientHandle, Command, Engine, EngineConfig, EngineError, ErrorKind, Event, EventStream,
31ce2453c82fatagen2026-09-24 13:07 UTC7
    MessageContent, MessageEditContent, RichText, RoomCommand, TimelineCommand,
96fdf90aacd6atagen2026-09-08 08:24 UTC8
};
96fdf90aacd6atagen2026-09-08 08:24 UTC9
use matrix_sdk::{
96fdf90aacd6atagen2026-09-08 08:24 UTC10
    ruma::{RoomId, api::client::sync::sync_events::v5, event_id, room_id},
96fdf90aacd6atagen2026-09-08 08:24 UTC11
    test_utils::mocks::MatrixMockServer,
96fdf90aacd6atagen2026-09-08 08:24 UTC12
};
31ce2453c82fatagen2026-09-24 13:07 UTC13
use support::shut_down;
96fdf90aacd6atagen2026-09-08 08:24 UTC14
use tokio::task::JoinHandle;
96fdf90aacd6atagen2026-09-08 08:24 UTC15
96fdf90aacd6atagen2026-09-08 08:24 UTC16
#[tokio::test]
31ce2453c82fatagen2026-09-24 13:07 UTC17
async fn empty_message_bodies_fail_with_empty_body() {
96fdf90aacd6atagen2026-09-08 08:24 UTC18
    let room_id = room_id!("!empty-bodies:localhost");
96fdf90aacd6atagen2026-09-08 08:24 UTC19
    let (server, handle, mut events, running) = start_engine(room_id).await;
96fdf90aacd6atagen2026-09-08 08:24 UTC20
    open_room(&handle, &mut events, room_id).await;
96fdf90aacd6atagen2026-09-08 08:24 UTC21
96fdf90aacd6atagen2026-09-08 08:24 UTC22
    let commands = [
96fdf90aacd6atagen2026-09-08 08:24 UTC23
        TimelineCommand::Send {
96fdf90aacd6atagen2026-09-08 08:24 UTC24
            timeline_id: room_id.to_owned().into(),
96fdf90aacd6atagen2026-09-08 08:24 UTC25
            content: MessageContent::text(String::new()),
96fdf90aacd6atagen2026-09-08 08:24 UTC26
        },
96fdf90aacd6atagen2026-09-08 08:24 UTC27
        TimelineCommand::Send {
96fdf90aacd6atagen2026-09-08 08:24 UTC28
            timeline_id: room_id.to_owned().into(),
96fdf90aacd6atagen2026-09-08 08:24 UTC29
            content: MessageContent::emote(RichText::plain("   "), Default::default()),
96fdf90aacd6atagen2026-09-08 08:24 UTC30
        },
96fdf90aacd6atagen2026-09-08 08:24 UTC31
        TimelineCommand::Send {
96fdf90aacd6atagen2026-09-08 08:24 UTC32
            timeline_id: room_id.to_owned().into(),
96fdf90aacd6atagen2026-09-08 08:24 UTC33
            content: MessageContent::reply(
96fdf90aacd6atagen2026-09-08 08:24 UTC34
                event_id!("$reply:localhost").to_owned(),
96fdf90aacd6atagen2026-09-08 08:24 UTC35
                RichText::plain("\n"),
96fdf90aacd6atagen2026-09-08 08:24 UTC36
                Default::default(),
96fdf90aacd6atagen2026-09-08 08:24 UTC37
            ),
96fdf90aacd6atagen2026-09-08 08:24 UTC38
        },
96fdf90aacd6atagen2026-09-08 08:24 UTC39
        TimelineCommand::Edit {
96fdf90aacd6atagen2026-09-08 08:24 UTC40
            timeline_id: room_id.to_owned().into(),
960ac0721079atagen2026-09-25 00:24 UTC41
            target: event_id!("$edit:localhost").to_owned().into(),
96fdf90aacd6atagen2026-09-08 08:24 UTC42
            content: MessageEditContent::text("\t"),
96fdf90aacd6atagen2026-09-08 08:24 UTC43
        },
96fdf90aacd6atagen2026-09-08 08:24 UTC44
    ];
96fdf90aacd6atagen2026-09-08 08:24 UTC45
96fdf90aacd6atagen2026-09-08 08:24 UTC46
    for command in commands {
31ce2453c82fatagen2026-09-24 13:07 UTC47
        let error = handle
31ce2453c82fatagen2026-09-24 13:07 UTC48
            .execute(Command::Timeline(command))
31ce2453c82fatagen2026-09-24 13:07 UTC49
            .await
31ce2453c82fatagen2026-09-24 13:07 UTC50
            .expect_err("empty body command unexpectedly succeeded");
31ce2453c82fatagen2026-09-24 13:07 UTC51
        assert_eq!(error.kind, ErrorKind::EmptyBody);
96fdf90aacd6atagen2026-09-08 08:24 UTC52
    }
96fdf90aacd6atagen2026-09-08 08:24 UTC53
31ce2453c82fatagen2026-09-24 13:07 UTC54
    shut_down(&handle, &mut events, running).await;
96fdf90aacd6atagen2026-09-08 08:24 UTC55
    drop(server);
96fdf90aacd6atagen2026-09-08 08:24 UTC56
}
96fdf90aacd6atagen2026-09-08 08:24 UTC57
96fdf90aacd6atagen2026-09-08 08:24 UTC58
#[tokio::test]
96fdf90aacd6atagen2026-09-08 08:24 UTC59
async fn open_resets_the_timeline_before_succeeding() {
96fdf90aacd6atagen2026-09-08 08:24 UTC60
    let room_id = room_id!("!open-order:localhost");
96fdf90aacd6atagen2026-09-08 08:24 UTC61
    let (server, handle, mut events, running) = start_engine(room_id).await;
96fdf90aacd6atagen2026-09-08 08:24 UTC62
96fdf90aacd6atagen2026-09-08 08:24 UTC63
    open_room(&handle, &mut events, room_id).await;
96fdf90aacd6atagen2026-09-08 08:24 UTC64
31ce2453c82fatagen2026-09-24 13:07 UTC65
    shut_down(&handle, &mut events, running).await;
96fdf90aacd6atagen2026-09-08 08:24 UTC66
    drop(server);
96fdf90aacd6atagen2026-09-08 08:24 UTC67
}
96fdf90aacd6atagen2026-09-08 08:24 UTC68
96fdf90aacd6atagen2026-09-08 08:24 UTC69
#[tokio::test]
96fdf90aacd6atagen2026-09-08 08:24 UTC70
async fn same_room_sends_complete_in_submission_order() {
96fdf90aacd6atagen2026-09-08 08:24 UTC71
    let room_id = room_id!("!send-order:localhost");
96fdf90aacd6atagen2026-09-08 08:24 UTC72
    let (server, handle, mut events, running) = start_engine(room_id).await;
96fdf90aacd6atagen2026-09-08 08:24 UTC73
    open_room(&handle, &mut events, room_id).await;
31ce2453c82fatagen2026-09-24 13:07 UTC74
    server
31ce2453c82fatagen2026-09-24 13:07 UTC75
        .mock_room_send()
31ce2453c82fatagen2026-09-24 13:07 UTC76
        .ok(event_id!("$sent:localhost"))
31ce2453c82fatagen2026-09-24 13:07 UTC77
        .expect(2)
31ce2453c82fatagen2026-09-24 13:07 UTC78
        .mount()
31ce2453c82fatagen2026-09-24 13:07 UTC79
        .await;
96fdf90aacd6atagen2026-09-08 08:24 UTC80
31ce2453c82fatagen2026-09-24 13:07 UTC81
    let first = handle
31ce2453c82fatagen2026-09-24 13:07 UTC82
        .try_send(Command::Timeline(TimelineCommand::Send {
96fdf90aacd6atagen2026-09-08 08:24 UTC83
            timeline_id: room_id.to_owned().into(),
96fdf90aacd6atagen2026-09-08 08:24 UTC84
            content: MessageContent::text("first"),
96fdf90aacd6atagen2026-09-08 08:24 UTC85
        }))
96fdf90aacd6atagen2026-09-08 08:24 UTC86
        .unwrap();
31ce2453c82fatagen2026-09-24 13:07 UTC87
    let second = handle
31ce2453c82fatagen2026-09-24 13:07 UTC88
        .try_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("second"),
96fdf90aacd6atagen2026-09-08 08:24 UTC91
        }))
96fdf90aacd6atagen2026-09-08 08:24 UTC92
        .unwrap();
96fdf90aacd6atagen2026-09-08 08:24 UTC93
31ce2453c82fatagen2026-09-24 13:07 UTC94
    // both answers land almost together, so racing them can't show which was
31ce2453c82fatagen2026-09-24 13:07 UTC95
    // processed first, only the order the sends actually reached the homeserver can
31ce2453c82fatagen2026-09-24 13:07 UTC96
    let (first_result, second_result) = tokio::join!(first, second);
31ce2453c82fatagen2026-09-24 13:07 UTC97
    first_result.expect("first send failed");
31ce2453c82fatagen2026-09-24 13:07 UTC98
    second_result.expect("second send failed");
96fdf90aacd6atagen2026-09-08 08:24 UTC99
31ce2453c82fatagen2026-09-24 13:07 UTC100
    tokio::time::timeout(Duration::from_secs(2), async {
31ce2453c82fatagen2026-09-24 13:07 UTC101
        loop {
31ce2453c82fatagen2026-09-24 13:07 UTC102
            let bodies: Vec<String> = server
31ce2453c82fatagen2026-09-24 13:07 UTC103
                .received_requests()
31ce2453c82fatagen2026-09-24 13:07 UTC104
                .await
31ce2453c82fatagen2026-09-24 13:07 UTC105
                .unwrap()
31ce2453c82fatagen2026-09-24 13:07 UTC106
                .iter()
31ce2453c82fatagen2026-09-24 13:07 UTC107
                .filter(|request| request.url.path().contains("/send/m.room.message/"))
31ce2453c82fatagen2026-09-24 13:07 UTC108
                .filter_map(|request| {
31ce2453c82fatagen2026-09-24 13:07 UTC109
                    serde_json::from_slice::<serde_json::Value>(&request.body).ok()
31ce2453c82fatagen2026-09-24 13:07 UTC110
                })
31ce2453c82fatagen2026-09-24 13:07 UTC111
                .filter_map(|body| body["body"].as_str().map(str::to_owned))
31ce2453c82fatagen2026-09-24 13:07 UTC112
                .collect();
31ce2453c82fatagen2026-09-24 13:07 UTC113
            if bodies.len() >= 2 {
31ce2453c82fatagen2026-09-24 13:07 UTC114
                assert_eq!(bodies, ["first", "second"]);
31ce2453c82fatagen2026-09-24 13:07 UTC115
                break;
31ce2453c82fatagen2026-09-24 13:07 UTC116
            }
31ce2453c82fatagen2026-09-24 13:07 UTC117
            tokio::task::yield_now().await;
31ce2453c82fatagen2026-09-24 13:07 UTC118
        }
31ce2453c82fatagen2026-09-24 13:07 UTC119
    })
31ce2453c82fatagen2026-09-24 13:07 UTC120
    .await
31ce2453c82fatagen2026-09-24 13:07 UTC121
    .expect("both sends did not reach the homeserver in submission order");
31ce2453c82fatagen2026-09-24 13:07 UTC122
31ce2453c82fatagen2026-09-24 13:07 UTC123
    shut_down(&handle, &mut events, running).await;
96fdf90aacd6atagen2026-09-08 08:24 UTC124
    drop(server);
96fdf90aacd6atagen2026-09-08 08:24 UTC125
}
96fdf90aacd6atagen2026-09-08 08:24 UTC126
96fdf90aacd6atagen2026-09-08 08:24 UTC127
async fn start_engine(
96fdf90aacd6atagen2026-09-08 08:24 UTC128
    room_id: &RoomId,
96fdf90aacd6atagen2026-09-08 08:24 UTC129
) -> (
96fdf90aacd6atagen2026-09-08 08:24 UTC130
    MatrixMockServer,
96fdf90aacd6atagen2026-09-08 08:24 UTC131
    ClientHandle,
96fdf90aacd6atagen2026-09-08 08:24 UTC132
    EventStream,
fe4133dbcd47atagen2026-09-26 09:05 UTC133
    JoinHandle<Result<macltok::Termination, EngineError>>,
96fdf90aacd6atagen2026-09-08 08:24 UTC134
) {
96fdf90aacd6atagen2026-09-08 08:24 UTC135
    let server = MatrixMockServer::new().await;
96fdf90aacd6atagen2026-09-08 08:24 UTC136
    server
96fdf90aacd6atagen2026-09-08 08:24 UTC137
        .mock_sliding_sync()
96fdf90aacd6atagen2026-09-08 08:24 UTC138
        .ok(v5::Response::new("1".to_owned()))
96fdf90aacd6atagen2026-09-08 08:24 UTC139
        .mount()
96fdf90aacd6atagen2026-09-08 08:24 UTC140
        .await;
96fdf90aacd6atagen2026-09-08 08:24 UTC141
    let client = server.client_builder().build().await;
96fdf90aacd6atagen2026-09-08 08:24 UTC142
    server.mock_room_state_encryption().plain().mount().await;
96fdf90aacd6atagen2026-09-08 08:24 UTC143
    server.sync_joined_room(&client, room_id).await;
96fdf90aacd6atagen2026-09-08 08:24 UTC144
    let config = EngineConfig::default().with_initial_history_limit(0);
96fdf90aacd6atagen2026-09-08 08:24 UTC145
    let (engine, handle, events) = Engine::from_client(client, config).await.unwrap();
96fdf90aacd6atagen2026-09-08 08:24 UTC146
    let running = tokio::spawn(engine.run());
96fdf90aacd6atagen2026-09-08 08:24 UTC147
    (server, handle, events, running)
96fdf90aacd6atagen2026-09-08 08:24 UTC148
}
96fdf90aacd6atagen2026-09-08 08:24 UTC149
31ce2453c82fatagen2026-09-24 13:07 UTC150
/// opens `room_id` and confirms its `TimelineReset` was already on the
31ce2453c82fatagen2026-09-24 13:07 UTC151
/// stream by the time the open command answered
31ce2453c82fatagen2026-09-24 13:07 UTC152
async fn open_room(handle: &ClientHandle, events: &mut EventStream, room_id: &RoomId) {
31ce2453c82fatagen2026-09-24 13:07 UTC153
    let mut pending = handle
96fdf90aacd6atagen2026-09-08 08:24 UTC154
        .send(Command::Room(RoomCommand::Open(room_id.to_owned())))
96fdf90aacd6atagen2026-09-08 08:24 UTC155
        .await
96fdf90aacd6atagen2026-09-08 08:24 UTC156
        .unwrap();
96fdf90aacd6atagen2026-09-08 08:24 UTC157
    let mut reset_received = false;
96fdf90aacd6atagen2026-09-08 08:24 UTC158
    tokio::time::timeout(Duration::from_secs(2), async {
96fdf90aacd6atagen2026-09-08 08:24 UTC159
        loop {
31ce2453c82fatagen2026-09-24 13:07 UTC160
            tokio::select! {
31ce2453c82fatagen2026-09-24 13:07 UTC161
                biased;
31ce2453c82fatagen2026-09-24 13:07 UTC162
                event = events.recv() => {
31ce2453c82fatagen2026-09-24 13:07 UTC163
                    if let Event::TimelineReset { timeline_id, .. } = event.expect("event stream closed")
31ce2453c82fatagen2026-09-24 13:07 UTC164
                        && timeline_id.room_id() == room_id
31ce2453c82fatagen2026-09-24 13:07 UTC165
                    {
31ce2453c82fatagen2026-09-24 13:07 UTC166
                        reset_received = true;
31ce2453c82fatagen2026-09-24 13:07 UTC167
                    }
96fdf90aacd6atagen2026-09-08 08:24 UTC168
                }
31ce2453c82fatagen2026-09-24 13:07 UTC169
                result = &mut pending => {
31ce2453c82fatagen2026-09-24 13:07 UTC170
                    result.expect("opening room failed");
96fdf90aacd6atagen2026-09-08 08:24 UTC171
                    break;
96fdf90aacd6atagen2026-09-08 08:24 UTC172
                }
96fdf90aacd6atagen2026-09-08 08:24 UTC173
            }
96fdf90aacd6atagen2026-09-08 08:24 UTC174
        }
96fdf90aacd6atagen2026-09-08 08:24 UTC175
    })
96fdf90aacd6atagen2026-09-08 08:24 UTC176
    .await
31ce2453c82fatagen2026-09-24 13:07 UTC177
    .expect("room did not open");
31ce2453c82fatagen2026-09-24 13:07 UTC178
    assert!(reset_received, "open completed before its reset");
96fdf90aacd6atagen2026-09-08 08:24 UTC179
}