atagen

macltok

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

Blame: tests/account.rs

96fdf90aacd6atagen2026-09-08 08:24 UTC1
#![cfg(feature = "sqlite")]
31ce2453c82fatagen2026-09-24 13:07 UTC2
mod support;
96fdf90aacd6atagen2026-09-08 08:24 UTC3
f12377bce9b9atagen2026-09-26 12:32 UTC4
use std::{path::PathBuf, sync::Arc, time::SystemTime};
31ce2453c82fatagen2026-09-24 13:07 UTC5
713205a95af2atagen2026-09-26 09:24 UTC6
use macltok::{
a250c7fa43fbatagen2026-09-26 13:35 UTC7
    AccountError, EncryptedSessionStore, EngineConfig, Event, PasswordLogin, Restore, Session,
a250c7fa43fbatagen2026-09-26 13:35 UTC8
    SessionRecord, SessionStore, StoreConfig,
713205a95af2atagen2026-09-26 09:24 UTC9
};
96fdf90aacd6atagen2026-09-08 08:24 UTC10
use matrix_sdk::{ruma::api::client::sync::sync_events::v5, test_utils::mocks::MatrixMockServer};
96fdf90aacd6atagen2026-09-08 08:24 UTC11
96fdf90aacd6atagen2026-09-08 08:24 UTC12
#[tokio::test]
713205a95af2atagen2026-09-26 09:24 UTC13
async fn password_session_round_trips_and_discards_with_its_store() {
96fdf90aacd6atagen2026-09-08 08:24 UTC14
    let server = MatrixMockServer::new().await;
96fdf90aacd6atagen2026-09-08 08:24 UTC15
    server
96fdf90aacd6atagen2026-09-08 08:24 UTC16
        .mock_versions()
96fdf90aacd6atagen2026-09-08 08:24 UTC17
        .with_simplified_sliding_sync()
96fdf90aacd6atagen2026-09-08 08:24 UTC18
        .ok()
96fdf90aacd6atagen2026-09-08 08:24 UTC19
        .mount()
96fdf90aacd6atagen2026-09-08 08:24 UTC20
        .await;
96fdf90aacd6atagen2026-09-08 08:24 UTC21
    server.mock_login().ok().mount().await;
96fdf90aacd6atagen2026-09-08 08:24 UTC22
    server
96fdf90aacd6atagen2026-09-08 08:24 UTC23
        .mock_sliding_sync()
96fdf90aacd6atagen2026-09-08 08:24 UTC24
        .ok(v5::Response::new("1".to_owned()))
96fdf90aacd6atagen2026-09-08 08:24 UTC25
        .mount()
96fdf90aacd6atagen2026-09-08 08:24 UTC26
        .await;
713205a95af2atagen2026-09-26 09:24 UTC27
    let root = temporary_path();
713205a95af2atagen2026-09-26 09:24 UTC28
    let store_path = root.join("store");
713205a95af2atagen2026-09-26 09:24 UTC29
    let record_path = root.join("records").join("session");
713205a95af2atagen2026-09-26 09:24 UTC30
    let sessions = || {
713205a95af2atagen2026-09-26 09:24 UTC31
        EncryptedSessionStore::file(
713205a95af2atagen2026-09-26 09:24 UTC32
            &record_path,
713205a95af2atagen2026-09-26 09:24 UTC33
            StoreConfig::sqlite(&store_path, "test store passphrase"),
713205a95af2atagen2026-09-26 09:24 UTC34
        )
713205a95af2atagen2026-09-26 09:24 UTC35
    };
96fdf90aacd6atagen2026-09-08 08:24 UTC36
    let config = EngineConfig::default().with_initial_history_limit(0);
96fdf90aacd6atagen2026-09-08 08:24 UTC37
    let session = Session::login_password(
31ce2453c82fatagen2026-09-24 13:07 UTC38
        PasswordLogin::new(server.uri(), "example", "wordpass")
31ce2453c82fatagen2026-09-24 13:07 UTC39
            .with_device_display_name("macltok test"),
f12377bce9b9atagen2026-09-26 12:32 UTC40
        Arc::new(sessions()),
96fdf90aacd6atagen2026-09-08 08:24 UTC41
    )
96fdf90aacd6atagen2026-09-08 08:24 UTC42
    .await
96fdf90aacd6atagen2026-09-08 08:24 UTC43
    .unwrap();
96fdf90aacd6atagen2026-09-08 08:24 UTC44
    let (engine, handle, mut events) = session.build(config.clone()).await.unwrap();
96fdf90aacd6atagen2026-09-08 08:24 UTC45
    let running = tokio::spawn(engine.run());
31ce2453c82fatagen2026-09-24 13:07 UTC46
    support::receive_until(&mut events, |event| {
31ce2453c82fatagen2026-09-24 13:07 UTC47
        matches!(event, Event::Authenticated { .. })
31ce2453c82fatagen2026-09-24 13:07 UTC48
    })
31ce2453c82fatagen2026-09-24 13:07 UTC49
    .await;
31ce2453c82fatagen2026-09-24 13:07 UTC50
    support::shut_down(&handle, &mut events, running).await;
713205a95af2atagen2026-09-26 09:24 UTC51
    drop(handle);
96fdf90aacd6atagen2026-09-08 08:24 UTC52
713205a95af2atagen2026-09-26 09:24 UTC53
    let wrong = EncryptedSessionStore::file(
713205a95af2atagen2026-09-26 09:24 UTC54
        &record_path,
713205a95af2atagen2026-09-26 09:24 UTC55
        StoreConfig::sqlite(&store_path, "someone else's passphrase"),
713205a95af2atagen2026-09-26 09:24 UTC56
    );
713205a95af2atagen2026-09-26 09:24 UTC57
    assert!(wrong.load().await.is_err());
713205a95af2atagen2026-09-26 09:24 UTC58
f12377bce9b9atagen2026-09-26 12:32 UTC59
    let sessions = Arc::new(sessions());
f12377bce9b9atagen2026-09-26 12:32 UTC60
    let Some(Restore::Resumed(session)) = Session::restore(sessions.clone()).await.unwrap() else {
6f1d6591c10datagen2026-09-26 09:52 UTC61
        panic!("the saved login didn't resume");
6f1d6591c10datagen2026-09-26 09:52 UTC62
    };
96fdf90aacd6atagen2026-09-08 08:24 UTC63
    let (engine, handle, mut events) = session.build(config).await.unwrap();
96fdf90aacd6atagen2026-09-08 08:24 UTC64
    let running = tokio::spawn(engine.run());
31ce2453c82fatagen2026-09-24 13:07 UTC65
    support::receive_until(&mut events, |event| {
31ce2453c82fatagen2026-09-24 13:07 UTC66
        matches!(event, Event::Authenticated { .. })
31ce2453c82fatagen2026-09-24 13:07 UTC67
    })
31ce2453c82fatagen2026-09-24 13:07 UTC68
    .await;
31ce2453c82fatagen2026-09-24 13:07 UTC69
    support::shut_down(&handle, &mut events, running).await;
713205a95af2atagen2026-09-26 09:24 UTC70
    drop(handle);
713205a95af2atagen2026-09-26 09:24 UTC71
713205a95af2atagen2026-09-26 09:24 UTC72
    sessions.discard().await.unwrap();
713205a95af2atagen2026-09-26 09:24 UTC73
    assert!(!record_path.exists());
713205a95af2atagen2026-09-26 09:24 UTC74
    assert!(!store_path.exists());
f12377bce9b9atagen2026-09-26 12:32 UTC75
    assert!(Session::restore(sessions).await.unwrap().is_none());
713205a95af2atagen2026-09-26 09:24 UTC76
    std::fs::remove_dir_all(root).unwrap();
713205a95af2atagen2026-09-26 09:24 UTC77
}
713205a95af2atagen2026-09-26 09:24 UTC78
713205a95af2atagen2026-09-26 09:24 UTC79
#[tokio::test]
6f1d6591c10datagen2026-09-26 09:52 UTC80
async fn a_restart_after_a_soft_logout_still_knows_the_device() {
6f1d6591c10datagen2026-09-26 09:52 UTC81
    let server = MatrixMockServer::new().await;
6f1d6591c10datagen2026-09-26 09:52 UTC82
    server.mock_versions().ok().mount().await;
6f1d6591c10datagen2026-09-26 09:52 UTC83
    server.mock_login().ok().mount().await;
6f1d6591c10datagen2026-09-26 09:52 UTC84
    let root = temporary_path();
6f1d6591c10datagen2026-09-26 09:52 UTC85
    let sessions = || {
6f1d6591c10datagen2026-09-26 09:52 UTC86
        EncryptedSessionStore::file(
6f1d6591c10datagen2026-09-26 09:52 UTC87
            root.join("session"),
6f1d6591c10datagen2026-09-26 09:52 UTC88
            StoreConfig::sqlite(root.join("store"), "test store passphrase"),
6f1d6591c10datagen2026-09-26 09:52 UTC89
        )
6f1d6591c10datagen2026-09-26 09:52 UTC90
    };
6f1d6591c10datagen2026-09-26 09:52 UTC91
    Session::login_password(
6f1d6591c10datagen2026-09-26 09:52 UTC92
        PasswordLogin::new(server.uri(), "example", "wordpass"),
f12377bce9b9atagen2026-09-26 12:32 UTC93
        Arc::new(sessions()),
6f1d6591c10datagen2026-09-26 09:52 UTC94
    )
6f1d6591c10datagen2026-09-26 09:52 UTC95
    .await
6f1d6591c10datagen2026-09-26 09:52 UTC96
    .unwrap();
6f1d6591c10datagen2026-09-26 09:52 UTC97
    sessions().soft_log_out().await.unwrap();
6f1d6591c10datagen2026-09-26 09:52 UTC98
f12377bce9b9atagen2026-09-26 12:32 UTC99
    let Some(Restore::SoftLoggedOut(soft)) = Session::restore(Arc::new(sessions())).await.unwrap()
f12377bce9b9atagen2026-09-26 12:32 UTC100
    else {
6f1d6591c10datagen2026-09-26 09:52 UTC101
        panic!("a soft logged out record came back as something else");
6f1d6591c10datagen2026-09-26 09:52 UTC102
    };
6f1d6591c10datagen2026-09-26 09:52 UTC103
    // the device matrix-sdk-test's canned login response hands out
6f1d6591c10datagen2026-09-26 09:52 UTC104
    assert_eq!(soft.device_id, "GHTYAJCE");
f12377bce9b9atagen2026-09-26 12:32 UTC105
    Session::login_password(soft.password_login("wordpass"), Arc::new(sessions()))
6f1d6591c10datagen2026-09-26 09:52 UTC106
        .await
6f1d6591c10datagen2026-09-26 09:52 UTC107
        .unwrap();
6f1d6591c10datagen2026-09-26 09:52 UTC108
    let requests = server.server().received_requests().await.unwrap();
6f1d6591c10datagen2026-09-26 09:52 UTC109
    let relogin: serde_json::Value = requests
6f1d6591c10datagen2026-09-26 09:52 UTC110
        .iter()
6f1d6591c10datagen2026-09-26 09:52 UTC111
        .rfind(|request| request.url.path().ends_with("/login"))
6f1d6591c10datagen2026-09-26 09:52 UTC112
        .unwrap()
6f1d6591c10datagen2026-09-26 09:52 UTC113
        .body_json()
6f1d6591c10datagen2026-09-26 09:52 UTC114
        .unwrap();
6f1d6591c10datagen2026-09-26 09:52 UTC115
    assert_eq!(relogin["device_id"], "GHTYAJCE");
6f1d6591c10datagen2026-09-26 09:52 UTC116
    assert!(matches!(
f12377bce9b9atagen2026-09-26 12:32 UTC117
        Session::restore(Arc::new(sessions())).await.unwrap(),
6f1d6591c10datagen2026-09-26 09:52 UTC118
        Some(Restore::Resumed(_))
6f1d6591c10datagen2026-09-26 09:52 UTC119
    ));
6f1d6591c10datagen2026-09-26 09:52 UTC120
    std::fs::remove_dir_all(root).unwrap();
6f1d6591c10datagen2026-09-26 09:52 UTC121
}
6f1d6591c10datagen2026-09-26 09:52 UTC122
6f1d6591c10datagen2026-09-26 09:52 UTC123
#[tokio::test]
a250c7fa43fbatagen2026-09-26 13:35 UTC124
async fn a_login_whose_first_save_fails_can_still_be_saved() {
a250c7fa43fbatagen2026-09-26 13:35 UTC125
    let server = MatrixMockServer::new().await;
a250c7fa43fbatagen2026-09-26 13:35 UTC126
    server.mock_versions().ok().mount().await;
a250c7fa43fbatagen2026-09-26 13:35 UTC127
    server.mock_login().ok().mount().await;
a250c7fa43fbatagen2026-09-26 13:35 UTC128
    let root = temporary_path();
a250c7fa43fbatagen2026-09-26 13:35 UTC129
    let records = root.join("records");
a250c7fa43fbatagen2026-09-26 13:35 UTC130
    std::fs::create_dir_all(&records).unwrap();
a250c7fa43fbatagen2026-09-26 13:35 UTC131
    let sessions = || {
a250c7fa43fbatagen2026-09-26 13:35 UTC132
        EncryptedSessionStore::file(
a250c7fa43fbatagen2026-09-26 13:35 UTC133
            records.join("session"),
a250c7fa43fbatagen2026-09-26 13:35 UTC134
            StoreConfig::sqlite(root.join("store"), "test store passphrase"),
a250c7fa43fbatagen2026-09-26 13:35 UTC135
        )
a250c7fa43fbatagen2026-09-26 13:35 UTC136
    };
a250c7fa43fbatagen2026-09-26 13:35 UTC137
    let writable = std::fs::metadata(&records).unwrap().permissions();
a250c7fa43fbatagen2026-09-26 13:35 UTC138
    let mut read_only = writable.clone();
a250c7fa43fbatagen2026-09-26 13:35 UTC139
    read_only.set_readonly(true);
a250c7fa43fbatagen2026-09-26 13:35 UTC140
    std::fs::set_permissions(&records, read_only).unwrap();
a250c7fa43fbatagen2026-09-26 13:35 UTC141
a250c7fa43fbatagen2026-09-26 13:35 UTC142
    let login = Session::login_password(
a250c7fa43fbatagen2026-09-26 13:35 UTC143
        PasswordLogin::new(server.uri(), "example", "wordpass"),
a250c7fa43fbatagen2026-09-26 13:35 UTC144
        Arc::new(sessions()),
a250c7fa43fbatagen2026-09-26 13:35 UTC145
    )
a250c7fa43fbatagen2026-09-26 13:35 UTC146
    .await;
a250c7fa43fbatagen2026-09-26 13:35 UTC147
    let Err(AccountError::Unsaved { session, .. }) = login else {
a250c7fa43fbatagen2026-09-26 13:35 UTC148
        panic!("a login that couldn't save its record didn't hand the session back");
a250c7fa43fbatagen2026-09-26 13:35 UTC149
    };
a250c7fa43fbatagen2026-09-26 13:35 UTC150
    std::fs::set_permissions(&records, writable).unwrap();
a250c7fa43fbatagen2026-09-26 13:35 UTC151
    drop(session.save().await.unwrap());
a250c7fa43fbatagen2026-09-26 13:35 UTC152
    assert!(matches!(
a250c7fa43fbatagen2026-09-26 13:35 UTC153
        Session::restore(Arc::new(sessions())).await.unwrap(),
a250c7fa43fbatagen2026-09-26 13:35 UTC154
        Some(Restore::Resumed(_))
a250c7fa43fbatagen2026-09-26 13:35 UTC155
    ));
a250c7fa43fbatagen2026-09-26 13:35 UTC156
    std::fs::remove_dir_all(root).unwrap();
a250c7fa43fbatagen2026-09-26 13:35 UTC157
}
a250c7fa43fbatagen2026-09-26 13:35 UTC158
a250c7fa43fbatagen2026-09-26 13:35 UTC159
#[tokio::test]
a250c7fa43fbatagen2026-09-26 13:35 UTC160
async fn a_store_takes_one_session_at_a_time() {
a250c7fa43fbatagen2026-09-26 13:35 UTC161
    let server = MatrixMockServer::new().await;
a250c7fa43fbatagen2026-09-26 13:35 UTC162
    server.mock_versions().ok().mount().await;
a250c7fa43fbatagen2026-09-26 13:35 UTC163
    server.mock_login().ok().mount().await;
a250c7fa43fbatagen2026-09-26 13:35 UTC164
    let root = temporary_path();
a250c7fa43fbatagen2026-09-26 13:35 UTC165
    let sessions = || {
a250c7fa43fbatagen2026-09-26 13:35 UTC166
        EncryptedSessionStore::file(
a250c7fa43fbatagen2026-09-26 13:35 UTC167
            root.join("session"),
a250c7fa43fbatagen2026-09-26 13:35 UTC168
            StoreConfig::sqlite(root.join("store"), "test store passphrase"),
a250c7fa43fbatagen2026-09-26 13:35 UTC169
        )
a250c7fa43fbatagen2026-09-26 13:35 UTC170
    };
a250c7fa43fbatagen2026-09-26 13:35 UTC171
    let (engine, handle, events) = Session::login_password(
a250c7fa43fbatagen2026-09-26 13:35 UTC172
        PasswordLogin::new(server.uri(), "example", "wordpass"),
a250c7fa43fbatagen2026-09-26 13:35 UTC173
        Arc::new(sessions()),
a250c7fa43fbatagen2026-09-26 13:35 UTC174
    )
a250c7fa43fbatagen2026-09-26 13:35 UTC175
    .await
a250c7fa43fbatagen2026-09-26 13:35 UTC176
    .unwrap()
a250c7fa43fbatagen2026-09-26 13:35 UTC177
    .build(EngineConfig::default())
a250c7fa43fbatagen2026-09-26 13:35 UTC178
    .await
a250c7fa43fbatagen2026-09-26 13:35 UTC179
    .unwrap();
a250c7fa43fbatagen2026-09-26 13:35 UTC180
a250c7fa43fbatagen2026-09-26 13:35 UTC181
    assert!(matches!(
a250c7fa43fbatagen2026-09-26 13:35 UTC182
        Session::restore(Arc::new(sessions())).await,
a250c7fa43fbatagen2026-09-26 13:35 UTC183
        Err(AccountError::StoreInUse)
a250c7fa43fbatagen2026-09-26 13:35 UTC184
    ));
a250c7fa43fbatagen2026-09-26 13:35 UTC185
    drop((engine, events));
a250c7fa43fbatagen2026-09-26 13:35 UTC186
    assert!(matches!(
a250c7fa43fbatagen2026-09-26 13:35 UTC187
        Session::restore(Arc::new(sessions())).await,
a250c7fa43fbatagen2026-09-26 13:35 UTC188
        Err(AccountError::StoreInUse)
a250c7fa43fbatagen2026-09-26 13:35 UTC189
    ));
a250c7fa43fbatagen2026-09-26 13:35 UTC190
    drop(handle);
a250c7fa43fbatagen2026-09-26 13:35 UTC191
    let resumed = Session::restore(Arc::new(sessions())).await.unwrap();
a250c7fa43fbatagen2026-09-26 13:35 UTC192
    assert!(matches!(resumed, Some(Restore::Resumed(_))));
a250c7fa43fbatagen2026-09-26 13:35 UTC193
    drop(resumed);
a250c7fa43fbatagen2026-09-26 13:35 UTC194
    sessions().discard().await.unwrap();
a250c7fa43fbatagen2026-09-26 13:35 UTC195
    assert!(!root.join("store").exists());
a250c7fa43fbatagen2026-09-26 13:35 UTC196
    std::fs::remove_dir_all(root).unwrap();
a250c7fa43fbatagen2026-09-26 13:35 UTC197
}
a250c7fa43fbatagen2026-09-26 13:35 UTC198
a250c7fa43fbatagen2026-09-26 13:35 UTC199
#[tokio::test]
713205a95af2atagen2026-09-26 09:24 UTC200
async fn a_torn_or_failed_save_leaves_the_old_record() {
713205a95af2atagen2026-09-26 09:24 UTC201
    let root = temporary_path();
713205a95af2atagen2026-09-26 09:24 UTC202
    let record_path = root.join("session");
713205a95af2atagen2026-09-26 09:24 UTC203
    let sessions = || {
713205a95af2atagen2026-09-26 09:24 UTC204
        EncryptedSessionStore::file(
713205a95af2atagen2026-09-26 09:24 UTC205
            &record_path,
713205a95af2atagen2026-09-26 09:24 UTC206
            StoreConfig::sqlite(root.join("store"), "test store passphrase"),
713205a95af2atagen2026-09-26 09:24 UTC207
        )
713205a95af2atagen2026-09-26 09:24 UTC208
    };
6db49c0ecf71atagen2026-09-26 12:41 UTC209
    let temporary = root.join("session.1.0.tmp");
713205a95af2atagen2026-09-26 09:24 UTC210
    sessions()
713205a95af2atagen2026-09-26 09:24 UTC211
        .save(&record("https://old.example"))
713205a95af2atagen2026-09-26 09:24 UTC212
        .await
713205a95af2atagen2026-09-26 09:24 UTC213
        .unwrap();
713205a95af2atagen2026-09-26 09:24 UTC214
713205a95af2atagen2026-09-26 09:24 UTC215
    std::fs::write(&temporary, b"half a rec").unwrap();
713205a95af2atagen2026-09-26 09:24 UTC216
    let loaded = sessions().load().await.unwrap().unwrap();
713205a95af2atagen2026-09-26 09:24 UTC217
    assert_eq!(loaded.homeserver(), "https://old.example");
713205a95af2atagen2026-09-26 09:24 UTC218
6db49c0ecf71atagen2026-09-26 12:41 UTC219
    let writable = std::fs::metadata(&root).unwrap().permissions();
6db49c0ecf71atagen2026-09-26 12:41 UTC220
    let mut read_only = writable.clone();
6db49c0ecf71atagen2026-09-26 12:41 UTC221
    read_only.set_readonly(true);
6db49c0ecf71atagen2026-09-26 12:41 UTC222
    std::fs::set_permissions(&root, read_only).unwrap();
713205a95af2atagen2026-09-26 09:24 UTC223
    assert!(
713205a95af2atagen2026-09-26 09:24 UTC224
        sessions()
713205a95af2atagen2026-09-26 09:24 UTC225
            .save(&record("https://new.example"))
713205a95af2atagen2026-09-26 09:24 UTC226
            .await
713205a95af2atagen2026-09-26 09:24 UTC227
            .is_err()
713205a95af2atagen2026-09-26 09:24 UTC228
    );
713205a95af2atagen2026-09-26 09:24 UTC229
    let loaded = sessions().load().await.unwrap().unwrap();
713205a95af2atagen2026-09-26 09:24 UTC230
    assert_eq!(loaded.homeserver(), "https://old.example");
713205a95af2atagen2026-09-26 09:24 UTC231
6db49c0ecf71atagen2026-09-26 12:41 UTC232
    std::fs::set_permissions(&root, writable).unwrap();
713205a95af2atagen2026-09-26 09:24 UTC233
    std::fs::remove_dir_all(root).unwrap();
713205a95af2atagen2026-09-26 09:24 UTC234
}
96fdf90aacd6atagen2026-09-08 08:24 UTC235
f12377bce9b9atagen2026-09-26 12:32 UTC236
#[tokio::test]
f12377bce9b9atagen2026-09-26 12:32 UTC237
async fn a_refresh_is_saved_before_anyone_hears_of_it() {
f12377bce9b9atagen2026-09-26 12:32 UTC238
    use matrix_sdk_test::test_json;
f12377bce9b9atagen2026-09-26 12:32 UTC239
    use wiremock::{
f12377bce9b9atagen2026-09-26 12:32 UTC240
        Mock, ResponseTemplate,
f12377bce9b9atagen2026-09-26 12:32 UTC241
        matchers::{header, method, path, path_regex},
f12377bce9b9atagen2026-09-26 12:32 UTC242
    };
f12377bce9b9atagen2026-09-26 12:32 UTC243
f12377bce9b9atagen2026-09-26 12:32 UTC244
    let server = MatrixMockServer::new().await;
f12377bce9b9atagen2026-09-26 12:32 UTC245
    server.mock_versions().ok().mount().await;
f12377bce9b9atagen2026-09-26 12:32 UTC246
    server
f12377bce9b9atagen2026-09-26 12:32 UTC247
        .mock_login()
f12377bce9b9atagen2026-09-26 12:32 UTC248
        .respond_with(
f12377bce9b9atagen2026-09-26 12:32 UTC249
            ResponseTemplate::new(200).set_body_json(&*test_json::LOGIN_WITH_REFRESH_TOKEN),
f12377bce9b9atagen2026-09-26 12:32 UTC250
        )
f12377bce9b9atagen2026-09-26 12:32 UTC251
        .mount()
f12377bce9b9atagen2026-09-26 12:32 UTC252
        .await;
f12377bce9b9atagen2026-09-26 12:32 UTC253
    Mock::given(method("POST"))
f12377bce9b9atagen2026-09-26 12:32 UTC254
        .and(path_regex("sync"))
f12377bce9b9atagen2026-09-26 12:32 UTC255
        .and(header("authorization", "Bearer abc123"))
f12377bce9b9atagen2026-09-26 12:32 UTC256
        .respond_with(ResponseTemplate::new(401).set_body_json(serde_json::json!({
f12377bce9b9atagen2026-09-26 12:32 UTC257
            "errcode": "M_UNKNOWN_TOKEN",
f12377bce9b9atagen2026-09-26 12:32 UTC258
            "error": "expired",
f12377bce9b9atagen2026-09-26 12:32 UTC259
            "soft_logout": false,
f12377bce9b9atagen2026-09-26 12:32 UTC260
        })))
f12377bce9b9atagen2026-09-26 12:32 UTC261
        .with_priority(1)
f12377bce9b9atagen2026-09-26 12:32 UTC262
        .mount(server.server())
f12377bce9b9atagen2026-09-26 12:32 UTC263
        .await;
f12377bce9b9atagen2026-09-26 12:32 UTC264
    Mock::given(method("POST"))
f12377bce9b9atagen2026-09-26 12:32 UTC265
        .and(path("/_matrix/client/v3/refresh"))
f12377bce9b9atagen2026-09-26 12:32 UTC266
        .respond_with(
f12377bce9b9atagen2026-09-26 12:32 UTC267
            ResponseTemplate::new(200).set_body_json(&*test_json::REFRESH_TOKEN_WITH_REFRESH_TOKEN),
f12377bce9b9atagen2026-09-26 12:32 UTC268
        )
f12377bce9b9atagen2026-09-26 12:32 UTC269
        .mount(server.server())
f12377bce9b9atagen2026-09-26 12:32 UTC270
        .await;
f12377bce9b9atagen2026-09-26 12:32 UTC271
    server
f12377bce9b9atagen2026-09-26 12:32 UTC272
        .mock_sliding_sync()
f12377bce9b9atagen2026-09-26 12:32 UTC273
        .ok(v5::Response::new("1".to_owned()))
f12377bce9b9atagen2026-09-26 12:32 UTC274
        .mount()
f12377bce9b9atagen2026-09-26 12:32 UTC275
        .await;
f12377bce9b9atagen2026-09-26 12:32 UTC276
    let root = temporary_path();
f12377bce9b9atagen2026-09-26 12:32 UTC277
    let sessions = Arc::new(EncryptedSessionStore::file(
f12377bce9b9atagen2026-09-26 12:32 UTC278
        root.join("session"),
f12377bce9b9atagen2026-09-26 12:32 UTC279
        StoreConfig::sqlite(root.join("store"), "test store passphrase"),
f12377bce9b9atagen2026-09-26 12:32 UTC280
    ));
f12377bce9b9atagen2026-09-26 12:32 UTC281
    let session = Session::login_password(
f12377bce9b9atagen2026-09-26 12:32 UTC282
        PasswordLogin::new(server.uri(), "example", "wordpass"),
f12377bce9b9atagen2026-09-26 12:32 UTC283
        sessions.clone(),
f12377bce9b9atagen2026-09-26 12:32 UTC284
    )
f12377bce9b9atagen2026-09-26 12:32 UTC285
    .await
f12377bce9b9atagen2026-09-26 12:32 UTC286
    .unwrap();
f12377bce9b9atagen2026-09-26 12:32 UTC287
    let (engine, handle, mut events) = session
f12377bce9b9atagen2026-09-26 12:32 UTC288
        .build(EngineConfig::default().with_initial_history_limit(0))
f12377bce9b9atagen2026-09-26 12:32 UTC289
        .await
f12377bce9b9atagen2026-09-26 12:32 UTC290
        .unwrap();
f12377bce9b9atagen2026-09-26 12:32 UTC291
    let running = tokio::spawn(engine.run());
f12377bce9b9atagen2026-09-26 12:32 UTC292
    support::receive_until(&mut events, |event| {
f12377bce9b9atagen2026-09-26 12:32 UTC293
        matches!(event, Event::SessionTokensChanged)
f12377bce9b9atagen2026-09-26 12:32 UTC294
    })
f12377bce9b9atagen2026-09-26 12:32 UTC295
    .await;
f12377bce9b9atagen2026-09-26 12:32 UTC296
    let saved = serde_json::to_value(sessions.load().await.unwrap().unwrap()).unwrap();
f12377bce9b9atagen2026-09-26 12:32 UTC297
    assert_eq!(saved["authentication"]["session"]["refresh_token"], "wxyz");
f12377bce9b9atagen2026-09-26 12:32 UTC298
    support::shut_down(&handle, &mut events, running).await;
f12377bce9b9atagen2026-09-26 12:32 UTC299
    drop(handle);
f12377bce9b9atagen2026-09-26 12:32 UTC300
    std::fs::remove_dir_all(root).unwrap();
f12377bce9b9atagen2026-09-26 12:32 UTC301
}
f12377bce9b9atagen2026-09-26 12:32 UTC302
713205a95af2atagen2026-09-26 09:24 UTC303
fn record(homeserver: &str) -> SessionRecord {
713205a95af2atagen2026-09-26 09:24 UTC304
    serde_json::from_value(serde_json::json!({
713205a95af2atagen2026-09-26 09:24 UTC305
        "homeserver": homeserver,
713205a95af2atagen2026-09-26 09:24 UTC306
        "authentication": {
713205a95af2atagen2026-09-26 09:24 UTC307
            "type": "matrix",
713205a95af2atagen2026-09-26 09:24 UTC308
            "session": {
713205a95af2atagen2026-09-26 09:24 UTC309
                "user_id": "@example:example.org",
713205a95af2atagen2026-09-26 09:24 UTC310
                "device_id": "DEVICE",
713205a95af2atagen2026-09-26 09:24 UTC311
                "access_token": "secret",
713205a95af2atagen2026-09-26 09:24 UTC312
            },
713205a95af2atagen2026-09-26 09:24 UTC313
        },
713205a95af2atagen2026-09-26 09:24 UTC314
    }))
713205a95af2atagen2026-09-26 09:24 UTC315
    .unwrap()
96fdf90aacd6atagen2026-09-08 08:24 UTC316
}
96fdf90aacd6atagen2026-09-08 08:24 UTC317
713205a95af2atagen2026-09-26 09:24 UTC318
fn temporary_path() -> PathBuf {
96fdf90aacd6atagen2026-09-08 08:24 UTC319
    let nonce = SystemTime::now()
96fdf90aacd6atagen2026-09-08 08:24 UTC320
        .duration_since(SystemTime::UNIX_EPOCH)
96fdf90aacd6atagen2026-09-08 08:24 UTC321
        .unwrap()
96fdf90aacd6atagen2026-09-08 08:24 UTC322
        .as_nanos();
713205a95af2atagen2026-09-26 09:24 UTC323
    PathBuf::from(env!("CARGO_TARGET_TMPDIR")).join(format!("macltok-account-{nonce}"))
96fdf90aacd6atagen2026-09-08 08:24 UTC324
}