atagen

macltok

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

Blame: tests/tier0.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, ClientQueryError, Command, CommandErrorKind, CommandId, CommandKind,
96fdf90aacd6atagen2026-09-08 08:24 UTC5
    CreateRoomOptions, Engine, EngineConfig, EngineError, Event, EventStream, LifecycleCommand,
96fdf90aacd6atagen2026-09-08 08:24 UTC6
    RoomCommand, RoomPreset, RoomVisibility,
96fdf90aacd6atagen2026-09-08 08:24 UTC7
};
96fdf90aacd6atagen2026-09-08 08:24 UTC8
use matrix_sdk::{
96fdf90aacd6atagen2026-09-08 08:24 UTC9
    ruma::{
96fdf90aacd6atagen2026-09-08 08:24 UTC10
        api::client::sync::sync_events::v5,
96fdf90aacd6atagen2026-09-08 08:24 UTC11
        events::{GlobalAccountDataEventType, RoomAccountDataEventType},
96fdf90aacd6atagen2026-09-08 08:24 UTC12
        room_id,
96fdf90aacd6atagen2026-09-08 08:24 UTC13
        serde::Raw,
96fdf90aacd6atagen2026-09-08 08:24 UTC14
        server_name, user_id,
96fdf90aacd6atagen2026-09-08 08:24 UTC15
    },
96fdf90aacd6atagen2026-09-08 08:24 UTC16
    test_utils::mocks::MatrixMockServer,
96fdf90aacd6atagen2026-09-08 08:24 UTC17
};
96fdf90aacd6atagen2026-09-08 08:24 UTC18
use matrix_sdk_test::{InvitedRoomBuilder, JoinedRoomBuilder};
96fdf90aacd6atagen2026-09-08 08:24 UTC19
use serde_json::json;
96fdf90aacd6atagen2026-09-08 08:24 UTC20
use tokio::task::JoinHandle;
96fdf90aacd6atagen2026-09-08 08:24 UTC21
use wiremock::{
96fdf90aacd6atagen2026-09-08 08:24 UTC22
    Mock, ResponseTemplate,
96fdf90aacd6atagen2026-09-08 08:24 UTC23
    matchers::{body_partial_json, method, path, path_regex, query_param},
96fdf90aacd6atagen2026-09-08 08:24 UTC24
};
96fdf90aacd6atagen2026-09-08 08:24 UTC25
96fdf90aacd6atagen2026-09-08 08:24 UTC26
#[tokio::test]
96fdf90aacd6atagen2026-09-08 08:24 UTC27
async fn joining_by_room_id_forwards_via_servers() {
96fdf90aacd6atagen2026-09-08 08:24 UTC28
    let room_id = room_id!("!join:localhost");
96fdf90aacd6atagen2026-09-08 08:24 UTC29
    let (server, handle, mut events, running) = start_engine().await;
96fdf90aacd6atagen2026-09-08 08:24 UTC30
    Mock::given(method("POST"))
96fdf90aacd6atagen2026-09-08 08:24 UTC31
        .and(path(format!("/_matrix/client/v3/join/{room_id}")))
96fdf90aacd6atagen2026-09-08 08:24 UTC32
        .and(query_param("via", "relay.example"))
96fdf90aacd6atagen2026-09-08 08:24 UTC33
        .respond_with(ResponseTemplate::new(200).set_body_json(json!({ "room_id": room_id })))
96fdf90aacd6atagen2026-09-08 08:24 UTC34
        .expect(1)
96fdf90aacd6atagen2026-09-08 08:24 UTC35
        .mount(server.server())
96fdf90aacd6atagen2026-09-08 08:24 UTC36
        .await;
96fdf90aacd6atagen2026-09-08 08:24 UTC37
96fdf90aacd6atagen2026-09-08 08:24 UTC38
    let joined_room = handle
96fdf90aacd6atagen2026-09-08 08:24 UTC39
        .join_room(
96fdf90aacd6atagen2026-09-08 08:24 UTC40
            room_id.to_owned().into(),
96fdf90aacd6atagen2026-09-08 08:24 UTC41
            vec![server_name!("relay.example").to_owned()],
96fdf90aacd6atagen2026-09-08 08:24 UTC42
        )
96fdf90aacd6atagen2026-09-08 08:24 UTC43
        .await
96fdf90aacd6atagen2026-09-08 08:24 UTC44
        .unwrap();
96fdf90aacd6atagen2026-09-08 08:24 UTC45
    assert_eq!(joined_room, room_id);
96fdf90aacd6atagen2026-09-08 08:24 UTC46
    stop_engine(handle, &mut events, running).await;
96fdf90aacd6atagen2026-09-08 08:24 UTC47
}
96fdf90aacd6atagen2026-09-08 08:24 UTC48
96fdf90aacd6atagen2026-09-08 08:24 UTC49
#[tokio::test]
96fdf90aacd6atagen2026-09-08 08:24 UTC50
async fn leave_and_forget_report_correlated_success() {
96fdf90aacd6atagen2026-09-08 08:24 UTC51
    let room_id = room_id!("!leave:localhost");
96fdf90aacd6atagen2026-09-08 08:24 UTC52
    let (server, client) = configured_client().await;
96fdf90aacd6atagen2026-09-08 08:24 UTC53
    server.sync_joined_room(&client, room_id).await;
96fdf90aacd6atagen2026-09-08 08:24 UTC54
    server.mock_room_leave().ok(room_id).expect(1).mount().await;
96fdf90aacd6atagen2026-09-08 08:24 UTC55
    server.mock_room_forget().ok().expect(1).mount().await;
96fdf90aacd6atagen2026-09-08 08:24 UTC56
    let (handle, mut events, running) = run_client(client).await;
96fdf90aacd6atagen2026-09-08 08:24 UTC57
96fdf90aacd6atagen2026-09-08 08:24 UTC58
    let leave_id = handle.leave_room(room_id.to_owned()).await.unwrap();
96fdf90aacd6atagen2026-09-08 08:24 UTC59
    receive_room_success(&mut events, leave_id).await;
96fdf90aacd6atagen2026-09-08 08:24 UTC60
    let forget_id = handle
96fdf90aacd6atagen2026-09-08 08:24 UTC61
        .send(Command::Room(RoomCommand::Forget(room_id.to_owned())))
96fdf90aacd6atagen2026-09-08 08:24 UTC62
        .await
96fdf90aacd6atagen2026-09-08 08:24 UTC63
        .unwrap();
96fdf90aacd6atagen2026-09-08 08:24 UTC64
    receive_room_success(&mut events, forget_id).await;
96fdf90aacd6atagen2026-09-08 08:24 UTC65
    assert_ne!(leave_id, forget_id);
96fdf90aacd6atagen2026-09-08 08:24 UTC66
    stop_engine(handle, &mut events, running).await;
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 accepting_and_rejecting_invites_use_the_expected_endpoints() {
96fdf90aacd6atagen2026-09-08 08:24 UTC71
    let accepted = room_id!("!accepted:localhost");
96fdf90aacd6atagen2026-09-08 08:24 UTC72
    let rejected = room_id!("!rejected:localhost");
96fdf90aacd6atagen2026-09-08 08:24 UTC73
    let (server, client) = configured_client().await;
96fdf90aacd6atagen2026-09-08 08:24 UTC74
    server
96fdf90aacd6atagen2026-09-08 08:24 UTC75
        .sync_room(&client, InvitedRoomBuilder::new(accepted))
96fdf90aacd6atagen2026-09-08 08:24 UTC76
        .await;
96fdf90aacd6atagen2026-09-08 08:24 UTC77
    server
96fdf90aacd6atagen2026-09-08 08:24 UTC78
        .sync_room(&client, InvitedRoomBuilder::new(rejected))
96fdf90aacd6atagen2026-09-08 08:24 UTC79
        .await;
96fdf90aacd6atagen2026-09-08 08:24 UTC80
    server.mock_room_join(accepted).ok().expect(1).mount().await;
96fdf90aacd6atagen2026-09-08 08:24 UTC81
    server
96fdf90aacd6atagen2026-09-08 08:24 UTC82
        .mock_room_leave()
96fdf90aacd6atagen2026-09-08 08:24 UTC83
        .ok(rejected)
96fdf90aacd6atagen2026-09-08 08:24 UTC84
        .expect(1)
96fdf90aacd6atagen2026-09-08 08:24 UTC85
        .mount()
96fdf90aacd6atagen2026-09-08 08:24 UTC86
        .await;
96fdf90aacd6atagen2026-09-08 08:24 UTC87
    let (handle, mut events, running) = run_client(client).await;
96fdf90aacd6atagen2026-09-08 08:24 UTC88
96fdf90aacd6atagen2026-09-08 08:24 UTC89
    let accept_id = handle.accept_invite(accepted.to_owned()).await.unwrap();
96fdf90aacd6atagen2026-09-08 08:24 UTC90
    receive_room_success(&mut events, accept_id).await;
96fdf90aacd6atagen2026-09-08 08:24 UTC91
    let reject_id = handle.reject_invite(rejected.to_owned()).await.unwrap();
96fdf90aacd6atagen2026-09-08 08:24 UTC92
    receive_room_success(&mut events, reject_id).await;
96fdf90aacd6atagen2026-09-08 08:24 UTC93
    stop_engine(handle, &mut events, running).await;
96fdf90aacd6atagen2026-09-08 08:24 UTC94
}
96fdf90aacd6atagen2026-09-08 08:24 UTC95
96fdf90aacd6atagen2026-09-08 08:24 UTC96
#[tokio::test]
96fdf90aacd6atagen2026-09-08 08:24 UTC97
async fn creating_a_room_forwards_options() {
96fdf90aacd6atagen2026-09-08 08:24 UTC98
    let (server, handle, mut events, running) = start_engine().await;
96fdf90aacd6atagen2026-09-08 08:24 UTC99
    Mock::given(method("POST"))
96fdf90aacd6atagen2026-09-08 08:24 UTC100
        .and(path("/_matrix/client/v3/createRoom"))
96fdf90aacd6atagen2026-09-08 08:24 UTC101
        .and(body_partial_json(json!({
96fdf90aacd6atagen2026-09-08 08:24 UTC102
            "name": "Planning",
96fdf90aacd6atagen2026-09-08 08:24 UTC103
            "topic": "Next release",
96fdf90aacd6atagen2026-09-08 08:24 UTC104
            "room_alias_name": "planning",
96fdf90aacd6atagen2026-09-08 08:24 UTC105
            "invite": ["@partner:localhost"],
96fdf90aacd6atagen2026-09-08 08:24 UTC106
            "visibility": "public",
96fdf90aacd6atagen2026-09-08 08:24 UTC107
            "preset": "public_chat"
96fdf90aacd6atagen2026-09-08 08:24 UTC108
        })))
96fdf90aacd6atagen2026-09-08 08:24 UTC109
        .respond_with(
96fdf90aacd6atagen2026-09-08 08:24 UTC110
            ResponseTemplate::new(200).set_body_json(json!({ "room_id": "!created:localhost" })),
96fdf90aacd6atagen2026-09-08 08:24 UTC111
        )
96fdf90aacd6atagen2026-09-08 08:24 UTC112
        .expect(1)
96fdf90aacd6atagen2026-09-08 08:24 UTC113
        .mount(server.server())
96fdf90aacd6atagen2026-09-08 08:24 UTC114
        .await;
96fdf90aacd6atagen2026-09-08 08:24 UTC115
96fdf90aacd6atagen2026-09-08 08:24 UTC116
    let created_room = handle
96fdf90aacd6atagen2026-09-08 08:24 UTC117
        .create_room(CreateRoomOptions {
96fdf90aacd6atagen2026-09-08 08:24 UTC118
            name: Some("Planning".to_owned()),
96fdf90aacd6atagen2026-09-08 08:24 UTC119
            topic: Some("Next release".to_owned()),
96fdf90aacd6atagen2026-09-08 08:24 UTC120
            alias_localpart: Some("planning".to_owned()),
96fdf90aacd6atagen2026-09-08 08:24 UTC121
            invite: vec![user_id!("@partner:localhost").to_owned()],
96fdf90aacd6atagen2026-09-08 08:24 UTC122
            visibility: RoomVisibility::Public,
96fdf90aacd6atagen2026-09-08 08:24 UTC123
            preset: Some(RoomPreset::PublicChat),
96fdf90aacd6atagen2026-09-08 08:24 UTC124
            ..Default::default()
96fdf90aacd6atagen2026-09-08 08:24 UTC125
        })
96fdf90aacd6atagen2026-09-08 08:24 UTC126
        .await
96fdf90aacd6atagen2026-09-08 08:24 UTC127
        .unwrap();
96fdf90aacd6atagen2026-09-08 08:24 UTC128
    assert_eq!(created_room, room_id!("!created:localhost").to_owned());
96fdf90aacd6atagen2026-09-08 08:24 UTC129
    stop_engine(handle, &mut events, running).await;
96fdf90aacd6atagen2026-09-08 08:24 UTC130
}
96fdf90aacd6atagen2026-09-08 08:24 UTC131
96fdf90aacd6atagen2026-09-08 08:24 UTC132
#[tokio::test]
96fdf90aacd6atagen2026-09-08 08:24 UTC133
async fn creating_a_direct_room_invites_the_other_user() {
96fdf90aacd6atagen2026-09-08 08:24 UTC134
    let (server, handle, mut events, running) = start_engine().await;
96fdf90aacd6atagen2026-09-08 08:24 UTC135
    Mock::given(method("POST"))
96fdf90aacd6atagen2026-09-08 08:24 UTC136
        .and(path("/_matrix/client/v3/createRoom"))
96fdf90aacd6atagen2026-09-08 08:24 UTC137
        .and(body_partial_json(json!({
96fdf90aacd6atagen2026-09-08 08:24 UTC138
            "invite": ["@partner:localhost"],
96fdf90aacd6atagen2026-09-08 08:24 UTC139
            "is_direct": true,
96fdf90aacd6atagen2026-09-08 08:24 UTC140
            "preset": "trusted_private_chat"
96fdf90aacd6atagen2026-09-08 08:24 UTC141
        })))
96fdf90aacd6atagen2026-09-08 08:24 UTC142
        .respond_with(
96fdf90aacd6atagen2026-09-08 08:24 UTC143
            ResponseTemplate::new(200).set_body_json(json!({ "room_id": "!direct:localhost" })),
96fdf90aacd6atagen2026-09-08 08:24 UTC144
        )
96fdf90aacd6atagen2026-09-08 08:24 UTC145
        .expect(1)
96fdf90aacd6atagen2026-09-08 08:24 UTC146
        .mount(server.server())
96fdf90aacd6atagen2026-09-08 08:24 UTC147
        .await;
96fdf90aacd6atagen2026-09-08 08:24 UTC148
    Mock::given(method("GET"))
96fdf90aacd6atagen2026-09-08 08:24 UTC149
        .and(path_regex(
96fdf90aacd6atagen2026-09-08 08:24 UTC150
            r"^/_matrix/client/v3/user/.*/account_data/m\.direct$",
96fdf90aacd6atagen2026-09-08 08:24 UTC151
        ))
96fdf90aacd6atagen2026-09-08 08:24 UTC152
        .respond_with(ResponseTemplate::new(200).set_body_json(json!({})))
96fdf90aacd6atagen2026-09-08 08:24 UTC153
        .expect(1)
96fdf90aacd6atagen2026-09-08 08:24 UTC154
        .mount(server.server())
96fdf90aacd6atagen2026-09-08 08:24 UTC155
        .await;
96fdf90aacd6atagen2026-09-08 08:24 UTC156
    Mock::given(method("PUT"))
96fdf90aacd6atagen2026-09-08 08:24 UTC157
        .and(path_regex(
96fdf90aacd6atagen2026-09-08 08:24 UTC158
            r"^/_matrix/client/v3/user/.*/account_data/m\.direct$",
96fdf90aacd6atagen2026-09-08 08:24 UTC159
        ))
96fdf90aacd6atagen2026-09-08 08:24 UTC160
        .respond_with(ResponseTemplate::new(200).set_body_json(json!({})))
96fdf90aacd6atagen2026-09-08 08:24 UTC161
        .expect(1)
96fdf90aacd6atagen2026-09-08 08:24 UTC162
        .mount(server.server())
96fdf90aacd6atagen2026-09-08 08:24 UTC163
        .await;
96fdf90aacd6atagen2026-09-08 08:24 UTC164
96fdf90aacd6atagen2026-09-08 08:24 UTC165
    let direct_room = handle
96fdf90aacd6atagen2026-09-08 08:24 UTC166
        .create_direct(user_id!("@partner:localhost").to_owned())
96fdf90aacd6atagen2026-09-08 08:24 UTC167
        .await
96fdf90aacd6atagen2026-09-08 08:24 UTC168
        .unwrap();
96fdf90aacd6atagen2026-09-08 08:24 UTC169
    assert_eq!(direct_room, room_id!("!direct:localhost").to_owned());
96fdf90aacd6atagen2026-09-08 08:24 UTC170
    stop_engine(handle, &mut events, running).await;
96fdf90aacd6atagen2026-09-08 08:24 UTC171
}
96fdf90aacd6atagen2026-09-08 08:24 UTC172
96fdf90aacd6atagen2026-09-08 08:24 UTC173
#[tokio::test]
96fdf90aacd6atagen2026-09-08 08:24 UTC174
async fn unknown_rooms_fail_membership_commands_with_their_ids() {
96fdf90aacd6atagen2026-09-08 08:24 UTC175
    let (server, handle, mut events, running) = start_engine().await;
96fdf90aacd6atagen2026-09-08 08:24 UTC176
    let unknown = room_id!("!unknown:localhost");
96fdf90aacd6atagen2026-09-08 08:24 UTC177
    for command in [
96fdf90aacd6atagen2026-09-08 08:24 UTC178
        RoomCommand::Leave(unknown.to_owned()),
96fdf90aacd6atagen2026-09-08 08:24 UTC179
        RoomCommand::Forget(unknown.to_owned()),
96fdf90aacd6atagen2026-09-08 08:24 UTC180
        RoomCommand::AcceptInvite(unknown.to_owned()),
96fdf90aacd6atagen2026-09-08 08:24 UTC181
        RoomCommand::RejectInvite(unknown.to_owned()),
96fdf90aacd6atagen2026-09-08 08:24 UTC182
    ] {
96fdf90aacd6atagen2026-09-08 08:24 UTC183
        let id = handle.send(Command::Room(command)).await.unwrap();
96fdf90aacd6atagen2026-09-08 08:24 UTC184
        receive_room_failure(&mut events, id, CommandErrorKind::RoomUnknown).await;
96fdf90aacd6atagen2026-09-08 08:24 UTC185
    }
96fdf90aacd6atagen2026-09-08 08:24 UTC186
    stop_engine(handle, &mut events, running).await;
96fdf90aacd6atagen2026-09-08 08:24 UTC187
    drop(server);
96fdf90aacd6atagen2026-09-08 08:24 UTC188
}
96fdf90aacd6atagen2026-09-08 08:24 UTC189
96fdf90aacd6atagen2026-09-08 08:24 UTC190
#[tokio::test]
96fdf90aacd6atagen2026-09-08 08:24 UTC191
async fn account_data_reads_synced_values_and_writes_raw_content() {
96fdf90aacd6atagen2026-09-08 08:24 UTC192
    let room_id = room_id!("!account-data:localhost");
96fdf90aacd6atagen2026-09-08 08:24 UTC193
    let (server, client) = configured_client().await;
96fdf90aacd6atagen2026-09-08 08:24 UTC194
    server
96fdf90aacd6atagen2026-09-08 08:24 UTC195
        .sync_room(
96fdf90aacd6atagen2026-09-08 08:24 UTC196
            &client,
96fdf90aacd6atagen2026-09-08 08:24 UTC197
            JoinedRoomBuilder::new(room_id).add_account_data(
96fdf90aacd6atagen2026-09-08 08:24 UTC198
                Raw::new(&json!({ "type": "m.fully_read", "content": { "event_id": "$seen:localhost" } }))
96fdf90aacd6atagen2026-09-08 08:24 UTC199
                    .unwrap()
96fdf90aacd6atagen2026-09-08 08:24 UTC200
                    .cast_unchecked(),
96fdf90aacd6atagen2026-09-08 08:24 UTC201
            ),
96fdf90aacd6atagen2026-09-08 08:24 UTC202
        )
96fdf90aacd6atagen2026-09-08 08:24 UTC203
        .await;
96fdf90aacd6atagen2026-09-08 08:24 UTC204
    server
96fdf90aacd6atagen2026-09-08 08:24 UTC205
        .mock_sync()
96fdf90aacd6atagen2026-09-08 08:24 UTC206
        .ok_and_run(&client, |builder| {
96fdf90aacd6atagen2026-09-08 08:24 UTC207
            builder.add_custom_global_account_data(json!({
96fdf90aacd6atagen2026-09-08 08:24 UTC208
                "type": "com.example.preferences",
96fdf90aacd6atagen2026-09-08 08:24 UTC209
                "content": { "theme": "dark" }
96fdf90aacd6atagen2026-09-08 08:24 UTC210
            }));
96fdf90aacd6atagen2026-09-08 08:24 UTC211
        })
96fdf90aacd6atagen2026-09-08 08:24 UTC212
        .await;
96fdf90aacd6atagen2026-09-08 08:24 UTC213
    let (engine, handle, mut events) = Engine::from_client(client, EngineConfig::default())
96fdf90aacd6atagen2026-09-08 08:24 UTC214
        .await
96fdf90aacd6atagen2026-09-08 08:24 UTC215
        .unwrap();
96fdf90aacd6atagen2026-09-08 08:24 UTC216
    let running = tokio::spawn(engine.run());
96fdf90aacd6atagen2026-09-08 08:24 UTC217
    let global_type = GlobalAccountDataEventType::from("com.example.preferences".to_owned());
96fdf90aacd6atagen2026-09-08 08:24 UTC218
    let room_type = RoomAccountDataEventType::FullyRead;
96fdf90aacd6atagen2026-09-08 08:24 UTC219
96fdf90aacd6atagen2026-09-08 08:24 UTC220
    let global = handle
96fdf90aacd6atagen2026-09-08 08:24 UTC221
        .global_account_data(global_type.clone())
96fdf90aacd6atagen2026-09-08 08:24 UTC222
        .await
96fdf90aacd6atagen2026-09-08 08:24 UTC223
        .unwrap()
96fdf90aacd6atagen2026-09-08 08:24 UTC224
        .unwrap();
96fdf90aacd6atagen2026-09-08 08:24 UTC225
    assert_eq!(
96fdf90aacd6atagen2026-09-08 08:24 UTC226
        global.deserialize_as::<serde_json::Value>().unwrap()["theme"],
96fdf90aacd6atagen2026-09-08 08:24 UTC227
        "dark"
96fdf90aacd6atagen2026-09-08 08:24 UTC228
    );
96fdf90aacd6atagen2026-09-08 08:24 UTC229
    let room = handle
96fdf90aacd6atagen2026-09-08 08:24 UTC230
        .room_account_data(room_id, room_type.clone())
96fdf90aacd6atagen2026-09-08 08:24 UTC231
        .await
96fdf90aacd6atagen2026-09-08 08:24 UTC232
        .unwrap()
96fdf90aacd6atagen2026-09-08 08:24 UTC233
        .unwrap();
96fdf90aacd6atagen2026-09-08 08:24 UTC234
    assert_eq!(
96fdf90aacd6atagen2026-09-08 08:24 UTC235
        room.deserialize_as::<serde_json::Value>().unwrap()["content"]["event_id"],
96fdf90aacd6atagen2026-09-08 08:24 UTC236
        "$seen:localhost"
96fdf90aacd6atagen2026-09-08 08:24 UTC237
    );
96fdf90aacd6atagen2026-09-08 08:24 UTC238
    assert!(matches!(
96fdf90aacd6atagen2026-09-08 08:24 UTC239
        handle
96fdf90aacd6atagen2026-09-08 08:24 UTC240
            .room_account_data(room_id!("!missing:localhost"), room_type.clone())
96fdf90aacd6atagen2026-09-08 08:24 UTC241
            .await,
96fdf90aacd6atagen2026-09-08 08:24 UTC242
        Err(ClientQueryError::RoomUnknown(_))
96fdf90aacd6atagen2026-09-08 08:24 UTC243
    ));
96fdf90aacd6atagen2026-09-08 08:24 UTC244
96fdf90aacd6atagen2026-09-08 08:24 UTC245
    Mock::given(method("PUT"))
96fdf90aacd6atagen2026-09-08 08:24 UTC246
        .and(path(
96fdf90aacd6atagen2026-09-08 08:24 UTC247
            "/_matrix/client/v3/user/@example:localhost/account_data/com.example.preferences",
96fdf90aacd6atagen2026-09-08 08:24 UTC248
        ))
96fdf90aacd6atagen2026-09-08 08:24 UTC249
        .and(body_partial_json(json!({ "theme": "light" })))
96fdf90aacd6atagen2026-09-08 08:24 UTC250
        .respond_with(ResponseTemplate::new(200).set_body_json(json!({})))
96fdf90aacd6atagen2026-09-08 08:24 UTC251
        .expect(1)
96fdf90aacd6atagen2026-09-08 08:24 UTC252
        .mount(server.server())
96fdf90aacd6atagen2026-09-08 08:24 UTC253
        .await;
96fdf90aacd6atagen2026-09-08 08:24 UTC254
    server
96fdf90aacd6atagen2026-09-08 08:24 UTC255
        .mock_set_room_account_data(room_type.clone())
96fdf90aacd6atagen2026-09-08 08:24 UTC256
        .ok()
96fdf90aacd6atagen2026-09-08 08:24 UTC257
        .expect(1)
96fdf90aacd6atagen2026-09-08 08:24 UTC258
        .mount()
96fdf90aacd6atagen2026-09-08 08:24 UTC259
        .await;
96fdf90aacd6atagen2026-09-08 08:24 UTC260
    handle
96fdf90aacd6atagen2026-09-08 08:24 UTC261
        .set_global_account_data(
96fdf90aacd6atagen2026-09-08 08:24 UTC262
            global_type,
96fdf90aacd6atagen2026-09-08 08:24 UTC263
            Raw::new(&json!({ "theme": "light" }))
96fdf90aacd6atagen2026-09-08 08:24 UTC264
                .unwrap()
96fdf90aacd6atagen2026-09-08 08:24 UTC265
                .cast_unchecked(),
96fdf90aacd6atagen2026-09-08 08:24 UTC266
        )
96fdf90aacd6atagen2026-09-08 08:24 UTC267
        .await
96fdf90aacd6atagen2026-09-08 08:24 UTC268
        .unwrap();
96fdf90aacd6atagen2026-09-08 08:24 UTC269
    handle
96fdf90aacd6atagen2026-09-08 08:24 UTC270
        .set_room_account_data(
96fdf90aacd6atagen2026-09-08 08:24 UTC271
            room_id,
96fdf90aacd6atagen2026-09-08 08:24 UTC272
            room_type,
96fdf90aacd6atagen2026-09-08 08:24 UTC273
            Raw::new(&json!({ "event_id": "$later:localhost" }))
96fdf90aacd6atagen2026-09-08 08:24 UTC274
                .unwrap()
96fdf90aacd6atagen2026-09-08 08:24 UTC275
                .cast_unchecked(),
96fdf90aacd6atagen2026-09-08 08:24 UTC276
        )
96fdf90aacd6atagen2026-09-08 08:24 UTC277
        .await
96fdf90aacd6atagen2026-09-08 08:24 UTC278
        .unwrap();
96fdf90aacd6atagen2026-09-08 08:24 UTC279
    assert!(matches!(
96fdf90aacd6atagen2026-09-08 08:24 UTC280
        handle
96fdf90aacd6atagen2026-09-08 08:24 UTC281
            .set_room_account_data(
96fdf90aacd6atagen2026-09-08 08:24 UTC282
                room_id!("!missing:localhost"),
96fdf90aacd6atagen2026-09-08 08:24 UTC283
                RoomAccountDataEventType::FullyRead,
96fdf90aacd6atagen2026-09-08 08:24 UTC284
                Raw::new(&json!({ "event_id": "$later:localhost" }))
96fdf90aacd6atagen2026-09-08 08:24 UTC285
                    .unwrap()
96fdf90aacd6atagen2026-09-08 08:24 UTC286
                    .cast_unchecked()
96fdf90aacd6atagen2026-09-08 08:24 UTC287
            )
96fdf90aacd6atagen2026-09-08 08:24 UTC288
            .await,
96fdf90aacd6atagen2026-09-08 08:24 UTC289
        Err(ClientQueryError::RoomUnknown(_))
96fdf90aacd6atagen2026-09-08 08:24 UTC290
    ));
96fdf90aacd6atagen2026-09-08 08:24 UTC291
    stop_engine(handle, &mut events, running).await;
96fdf90aacd6atagen2026-09-08 08:24 UTC292
}
96fdf90aacd6atagen2026-09-08 08:24 UTC293
96fdf90aacd6atagen2026-09-08 08:24 UTC294
async fn configured_client() -> (MatrixMockServer, matrix_sdk::Client) {
96fdf90aacd6atagen2026-09-08 08:24 UTC295
    let server = MatrixMockServer::new().await;
96fdf90aacd6atagen2026-09-08 08:24 UTC296
    server
96fdf90aacd6atagen2026-09-08 08:24 UTC297
        .mock_sliding_sync()
96fdf90aacd6atagen2026-09-08 08:24 UTC298
        .ok(v5::Response::new("1".to_owned()))
96fdf90aacd6atagen2026-09-08 08:24 UTC299
        .mount()
96fdf90aacd6atagen2026-09-08 08:24 UTC300
        .await;
96fdf90aacd6atagen2026-09-08 08:24 UTC301
    let client = server.client_builder().build().await;
96fdf90aacd6atagen2026-09-08 08:24 UTC302
    server.mock_room_state_encryption().plain().mount().await;
96fdf90aacd6atagen2026-09-08 08:24 UTC303
    (server, client)
96fdf90aacd6atagen2026-09-08 08:24 UTC304
}
96fdf90aacd6atagen2026-09-08 08:24 UTC305
96fdf90aacd6atagen2026-09-08 08:24 UTC306
async fn start_engine() -> (
96fdf90aacd6atagen2026-09-08 08:24 UTC307
    MatrixMockServer,
96fdf90aacd6atagen2026-09-08 08:24 UTC308
    ClientHandle,
96fdf90aacd6atagen2026-09-08 08:24 UTC309
    EventStream,
96fdf90aacd6atagen2026-09-08 08:24 UTC310
    JoinHandle<Result<(), EngineError>>,
96fdf90aacd6atagen2026-09-08 08:24 UTC311
) {
96fdf90aacd6atagen2026-09-08 08:24 UTC312
    let (server, client) = configured_client().await;
96fdf90aacd6atagen2026-09-08 08:24 UTC313
    let (handle, events, running) = run_client(client).await;
96fdf90aacd6atagen2026-09-08 08:24 UTC314
    (server, handle, events, running)
96fdf90aacd6atagen2026-09-08 08:24 UTC315
}
96fdf90aacd6atagen2026-09-08 08:24 UTC316
96fdf90aacd6atagen2026-09-08 08:24 UTC317
async fn run_client(
96fdf90aacd6atagen2026-09-08 08:24 UTC318
    client: matrix_sdk::Client,
96fdf90aacd6atagen2026-09-08 08:24 UTC319
) -> (
96fdf90aacd6atagen2026-09-08 08:24 UTC320
    ClientHandle,
96fdf90aacd6atagen2026-09-08 08:24 UTC321
    EventStream,
96fdf90aacd6atagen2026-09-08 08:24 UTC322
    JoinHandle<Result<(), EngineError>>,
96fdf90aacd6atagen2026-09-08 08:24 UTC323
) {
96fdf90aacd6atagen2026-09-08 08:24 UTC324
    let config = EngineConfig::default().with_initial_history_limit(0);
96fdf90aacd6atagen2026-09-08 08:24 UTC325
    let (engine, handle, events) = Engine::from_client(client, config).await.unwrap();
96fdf90aacd6atagen2026-09-08 08:24 UTC326
    let running = tokio::spawn(engine.run());
96fdf90aacd6atagen2026-09-08 08:24 UTC327
    (handle, events, running)
96fdf90aacd6atagen2026-09-08 08:24 UTC328
}
96fdf90aacd6atagen2026-09-08 08:24 UTC329
96fdf90aacd6atagen2026-09-08 08:24 UTC330
async fn receive_room_success(events: &mut EventStream, expected_id: CommandId) {
96fdf90aacd6atagen2026-09-08 08:24 UTC331
    tokio::time::timeout(Duration::from_secs(3), async {
96fdf90aacd6atagen2026-09-08 08:24 UTC332
        loop {
96fdf90aacd6atagen2026-09-08 08:24 UTC333
            match events.recv().await.expect("event stream closed") {
96fdf90aacd6atagen2026-09-08 08:24 UTC334
                Event::CommandSucceeded {
96fdf90aacd6atagen2026-09-08 08:24 UTC335
                    id,
96fdf90aacd6atagen2026-09-08 08:24 UTC336
                    command: CommandKind::Room,
96fdf90aacd6atagen2026-09-08 08:24 UTC337
                } if id == expected_id => break,
96fdf90aacd6atagen2026-09-08 08:24 UTC338
                Event::CommandFailed(failure) if failure.id == expected_id => {
96fdf90aacd6atagen2026-09-08 08:24 UTC339
                    panic!("room command failed {failure:?}")
96fdf90aacd6atagen2026-09-08 08:24 UTC340
                }
96fdf90aacd6atagen2026-09-08 08:24 UTC341
                _ => {}
96fdf90aacd6atagen2026-09-08 08:24 UTC342
            }
96fdf90aacd6atagen2026-09-08 08:24 UTC343
        }
96fdf90aacd6atagen2026-09-08 08:24 UTC344
    })
96fdf90aacd6atagen2026-09-08 08:24 UTC345
    .await
96fdf90aacd6atagen2026-09-08 08:24 UTC346
    .expect("room command did not complete");
96fdf90aacd6atagen2026-09-08 08:24 UTC347
}
96fdf90aacd6atagen2026-09-08 08:24 UTC348
96fdf90aacd6atagen2026-09-08 08:24 UTC349
async fn receive_room_failure(
96fdf90aacd6atagen2026-09-08 08:24 UTC350
    events: &mut EventStream,
96fdf90aacd6atagen2026-09-08 08:24 UTC351
    expected_id: CommandId,
96fdf90aacd6atagen2026-09-08 08:24 UTC352
    expected_kind: CommandErrorKind,
96fdf90aacd6atagen2026-09-08 08:24 UTC353
) {
96fdf90aacd6atagen2026-09-08 08:24 UTC354
    tokio::time::timeout(Duration::from_secs(3), async {
96fdf90aacd6atagen2026-09-08 08:24 UTC355
        loop {
96fdf90aacd6atagen2026-09-08 08:24 UTC356
            match events.recv().await.expect("event stream closed") {
96fdf90aacd6atagen2026-09-08 08:24 UTC357
                Event::CommandFailed(failure) if failure.id == expected_id => {
96fdf90aacd6atagen2026-09-08 08:24 UTC358
                    assert_eq!(failure.command, CommandKind::Room);
96fdf90aacd6atagen2026-09-08 08:24 UTC359
                    assert_eq!(failure.kind, expected_kind);
96fdf90aacd6atagen2026-09-08 08:24 UTC360
                    break;
96fdf90aacd6atagen2026-09-08 08:24 UTC361
                }
96fdf90aacd6atagen2026-09-08 08:24 UTC362
                Event::CommandSucceeded { id, .. } if id == expected_id => {
96fdf90aacd6atagen2026-09-08 08:24 UTC363
                    panic!("room command unexpectedly succeeded")
96fdf90aacd6atagen2026-09-08 08:24 UTC364
                }
96fdf90aacd6atagen2026-09-08 08:24 UTC365
                _ => {}
96fdf90aacd6atagen2026-09-08 08:24 UTC366
            }
96fdf90aacd6atagen2026-09-08 08:24 UTC367
        }
96fdf90aacd6atagen2026-09-08 08:24 UTC368
    })
96fdf90aacd6atagen2026-09-08 08:24 UTC369
    .await
96fdf90aacd6atagen2026-09-08 08:24 UTC370
    .expect("room command did not fail");
96fdf90aacd6atagen2026-09-08 08:24 UTC371
}
96fdf90aacd6atagen2026-09-08 08:24 UTC372
96fdf90aacd6atagen2026-09-08 08:24 UTC373
async fn stop_engine(
96fdf90aacd6atagen2026-09-08 08:24 UTC374
    handle: ClientHandle,
96fdf90aacd6atagen2026-09-08 08:24 UTC375
    events: &mut EventStream,
96fdf90aacd6atagen2026-09-08 08:24 UTC376
    running: JoinHandle<Result<(), EngineError>>,
96fdf90aacd6atagen2026-09-08 08:24 UTC377
) {
96fdf90aacd6atagen2026-09-08 08:24 UTC378
    let id = handle
96fdf90aacd6atagen2026-09-08 08:24 UTC379
        .send(Command::Lifecycle(LifecycleCommand::Shutdown))
96fdf90aacd6atagen2026-09-08 08:24 UTC380
        .await
96fdf90aacd6atagen2026-09-08 08:24 UTC381
        .unwrap();
96fdf90aacd6atagen2026-09-08 08:24 UTC382
    tokio::time::timeout(Duration::from_secs(3), async {
96fdf90aacd6atagen2026-09-08 08:24 UTC383
        let mut succeeded = false;
96fdf90aacd6atagen2026-09-08 08:24 UTC384
        while let Some(event) = events.recv().await {
96fdf90aacd6atagen2026-09-08 08:24 UTC385
            match event {
96fdf90aacd6atagen2026-09-08 08:24 UTC386
                Event::CommandSucceeded {
96fdf90aacd6atagen2026-09-08 08:24 UTC387
                    id: completed,
96fdf90aacd6atagen2026-09-08 08:24 UTC388
                    command: CommandKind::Lifecycle,
96fdf90aacd6atagen2026-09-08 08:24 UTC389
                } if completed == id => succeeded = true,
96fdf90aacd6atagen2026-09-08 08:24 UTC390
                Event::Stopped => {
96fdf90aacd6atagen2026-09-08 08:24 UTC391
                    assert!(succeeded);
96fdf90aacd6atagen2026-09-08 08:24 UTC392
                    break;
96fdf90aacd6atagen2026-09-08 08:24 UTC393
                }
96fdf90aacd6atagen2026-09-08 08:24 UTC394
                _ => {}
96fdf90aacd6atagen2026-09-08 08:24 UTC395
            }
96fdf90aacd6atagen2026-09-08 08:24 UTC396
        }
96fdf90aacd6atagen2026-09-08 08:24 UTC397
    })
96fdf90aacd6atagen2026-09-08 08:24 UTC398
    .await
96fdf90aacd6atagen2026-09-08 08:24 UTC399
    .expect("engine did not stop");
96fdf90aacd6atagen2026-09-08 08:24 UTC400
    running.await.unwrap().unwrap();
96fdf90aacd6atagen2026-09-08 08:24 UTC401
}