atagen

macltok

public
Star0
Watch0
f12377bce9b9427ca9b557bbf34679a4bd6fae14
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::{
6f1d6591c10datagen2026-09-26 09:52 UTC7
    EncryptedSessionStore, EngineConfig, Event, PasswordLogin, Restore, Session, SessionRecord,
713205a95af2atagen2026-09-26 09:24 UTC8
    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]
713205a95af2atagen2026-09-26 09:24 UTC124
async fn a_torn_or_failed_save_leaves_the_old_record() {
713205a95af2atagen2026-09-26 09:24 UTC125
    let root = temporary_path();
713205a95af2atagen2026-09-26 09:24 UTC126
    let record_path = root.join("session");
713205a95af2atagen2026-09-26 09:24 UTC127
    let sessions = || {
713205a95af2atagen2026-09-26 09:24 UTC128
        EncryptedSessionStore::file(
713205a95af2atagen2026-09-26 09:24 UTC129
            &record_path,
713205a95af2atagen2026-09-26 09:24 UTC130
            StoreConfig::sqlite(root.join("store"), "test store passphrase"),
713205a95af2atagen2026-09-26 09:24 UTC131
        )
713205a95af2atagen2026-09-26 09:24 UTC132
    };
713205a95af2atagen2026-09-26 09:24 UTC133
    let temporary = root.join("session.tmp");
713205a95af2atagen2026-09-26 09:24 UTC134
    sessions()
713205a95af2atagen2026-09-26 09:24 UTC135
        .save(&record("https://old.example"))
713205a95af2atagen2026-09-26 09:24 UTC136
        .await
713205a95af2atagen2026-09-26 09:24 UTC137
        .unwrap();
713205a95af2atagen2026-09-26 09:24 UTC138
713205a95af2atagen2026-09-26 09:24 UTC139
    std::fs::write(&temporary, b"half a rec").unwrap();
713205a95af2atagen2026-09-26 09:24 UTC140
    let loaded = sessions().load().await.unwrap().unwrap();
713205a95af2atagen2026-09-26 09:24 UTC141
    assert_eq!(loaded.homeserver(), "https://old.example");
713205a95af2atagen2026-09-26 09:24 UTC142
713205a95af2atagen2026-09-26 09:24 UTC143
    std::fs::remove_file(&temporary).unwrap();
713205a95af2atagen2026-09-26 09:24 UTC144
    std::fs::create_dir(&temporary).unwrap();
713205a95af2atagen2026-09-26 09:24 UTC145
    assert!(
713205a95af2atagen2026-09-26 09:24 UTC146
        sessions()
713205a95af2atagen2026-09-26 09:24 UTC147
            .save(&record("https://new.example"))
713205a95af2atagen2026-09-26 09:24 UTC148
            .await
713205a95af2atagen2026-09-26 09:24 UTC149
            .is_err()
713205a95af2atagen2026-09-26 09:24 UTC150
    );
713205a95af2atagen2026-09-26 09:24 UTC151
    let loaded = sessions().load().await.unwrap().unwrap();
713205a95af2atagen2026-09-26 09:24 UTC152
    assert_eq!(loaded.homeserver(), "https://old.example");
713205a95af2atagen2026-09-26 09:24 UTC153
713205a95af2atagen2026-09-26 09:24 UTC154
    std::fs::remove_dir_all(root).unwrap();
713205a95af2atagen2026-09-26 09:24 UTC155
}
96fdf90aacd6atagen2026-09-08 08:24 UTC156
f12377bce9b9atagen2026-09-26 12:32 UTC157
#[tokio::test]
f12377bce9b9atagen2026-09-26 12:32 UTC158
async fn a_refresh_is_saved_before_anyone_hears_of_it() {
f12377bce9b9atagen2026-09-26 12:32 UTC159
    use matrix_sdk_test::test_json;
f12377bce9b9atagen2026-09-26 12:32 UTC160
    use wiremock::{
f12377bce9b9atagen2026-09-26 12:32 UTC161
        Mock, ResponseTemplate,
f12377bce9b9atagen2026-09-26 12:32 UTC162
        matchers::{header, method, path, path_regex},
f12377bce9b9atagen2026-09-26 12:32 UTC163
    };
f12377bce9b9atagen2026-09-26 12:32 UTC164
f12377bce9b9atagen2026-09-26 12:32 UTC165
    let server = MatrixMockServer::new().await;
f12377bce9b9atagen2026-09-26 12:32 UTC166
    server.mock_versions().ok().mount().await;
f12377bce9b9atagen2026-09-26 12:32 UTC167
    server
f12377bce9b9atagen2026-09-26 12:32 UTC168
        .mock_login()
f12377bce9b9atagen2026-09-26 12:32 UTC169
        .respond_with(
f12377bce9b9atagen2026-09-26 12:32 UTC170
            ResponseTemplate::new(200).set_body_json(&*test_json::LOGIN_WITH_REFRESH_TOKEN),
f12377bce9b9atagen2026-09-26 12:32 UTC171
        )
f12377bce9b9atagen2026-09-26 12:32 UTC172
        .mount()
f12377bce9b9atagen2026-09-26 12:32 UTC173
        .await;
f12377bce9b9atagen2026-09-26 12:32 UTC174
    Mock::given(method("POST"))
f12377bce9b9atagen2026-09-26 12:32 UTC175
        .and(path_regex("sync"))
f12377bce9b9atagen2026-09-26 12:32 UTC176
        .and(header("authorization", "Bearer abc123"))
f12377bce9b9atagen2026-09-26 12:32 UTC177
        .respond_with(ResponseTemplate::new(401).set_body_json(serde_json::json!({
f12377bce9b9atagen2026-09-26 12:32 UTC178
            "errcode": "M_UNKNOWN_TOKEN",
f12377bce9b9atagen2026-09-26 12:32 UTC179
            "error": "expired",
f12377bce9b9atagen2026-09-26 12:32 UTC180
            "soft_logout": false,
f12377bce9b9atagen2026-09-26 12:32 UTC181
        })))
f12377bce9b9atagen2026-09-26 12:32 UTC182
        .with_priority(1)
f12377bce9b9atagen2026-09-26 12:32 UTC183
        .mount(server.server())
f12377bce9b9atagen2026-09-26 12:32 UTC184
        .await;
f12377bce9b9atagen2026-09-26 12:32 UTC185
    Mock::given(method("POST"))
f12377bce9b9atagen2026-09-26 12:32 UTC186
        .and(path("/_matrix/client/v3/refresh"))
f12377bce9b9atagen2026-09-26 12:32 UTC187
        .respond_with(
f12377bce9b9atagen2026-09-26 12:32 UTC188
            ResponseTemplate::new(200).set_body_json(&*test_json::REFRESH_TOKEN_WITH_REFRESH_TOKEN),
f12377bce9b9atagen2026-09-26 12:32 UTC189
        )
f12377bce9b9atagen2026-09-26 12:32 UTC190
        .mount(server.server())
f12377bce9b9atagen2026-09-26 12:32 UTC191
        .await;
f12377bce9b9atagen2026-09-26 12:32 UTC192
    server
f12377bce9b9atagen2026-09-26 12:32 UTC193
        .mock_sliding_sync()
f12377bce9b9atagen2026-09-26 12:32 UTC194
        .ok(v5::Response::new("1".to_owned()))
f12377bce9b9atagen2026-09-26 12:32 UTC195
        .mount()
f12377bce9b9atagen2026-09-26 12:32 UTC196
        .await;
f12377bce9b9atagen2026-09-26 12:32 UTC197
    let root = temporary_path();
f12377bce9b9atagen2026-09-26 12:32 UTC198
    let sessions = Arc::new(EncryptedSessionStore::file(
f12377bce9b9atagen2026-09-26 12:32 UTC199
        root.join("session"),
f12377bce9b9atagen2026-09-26 12:32 UTC200
        StoreConfig::sqlite(root.join("store"), "test store passphrase"),
f12377bce9b9atagen2026-09-26 12:32 UTC201
    ));
f12377bce9b9atagen2026-09-26 12:32 UTC202
    let session = Session::login_password(
f12377bce9b9atagen2026-09-26 12:32 UTC203
        PasswordLogin::new(server.uri(), "example", "wordpass"),
f12377bce9b9atagen2026-09-26 12:32 UTC204
        sessions.clone(),
f12377bce9b9atagen2026-09-26 12:32 UTC205
    )
f12377bce9b9atagen2026-09-26 12:32 UTC206
    .await
f12377bce9b9atagen2026-09-26 12:32 UTC207
    .unwrap();
f12377bce9b9atagen2026-09-26 12:32 UTC208
    let (engine, handle, mut events) = session
f12377bce9b9atagen2026-09-26 12:32 UTC209
        .build(EngineConfig::default().with_initial_history_limit(0))
f12377bce9b9atagen2026-09-26 12:32 UTC210
        .await
f12377bce9b9atagen2026-09-26 12:32 UTC211
        .unwrap();
f12377bce9b9atagen2026-09-26 12:32 UTC212
    let running = tokio::spawn(engine.run());
f12377bce9b9atagen2026-09-26 12:32 UTC213
    support::receive_until(&mut events, |event| {
f12377bce9b9atagen2026-09-26 12:32 UTC214
        matches!(event, Event::SessionTokensChanged)
f12377bce9b9atagen2026-09-26 12:32 UTC215
    })
f12377bce9b9atagen2026-09-26 12:32 UTC216
    .await;
f12377bce9b9atagen2026-09-26 12:32 UTC217
    let saved = serde_json::to_value(sessions.load().await.unwrap().unwrap()).unwrap();
f12377bce9b9atagen2026-09-26 12:32 UTC218
    assert_eq!(saved["authentication"]["session"]["refresh_token"], "wxyz");
f12377bce9b9atagen2026-09-26 12:32 UTC219
    support::shut_down(&handle, &mut events, running).await;
f12377bce9b9atagen2026-09-26 12:32 UTC220
    drop(handle);
f12377bce9b9atagen2026-09-26 12:32 UTC221
    std::fs::remove_dir_all(root).unwrap();
f12377bce9b9atagen2026-09-26 12:32 UTC222
}
f12377bce9b9atagen2026-09-26 12:32 UTC223
713205a95af2atagen2026-09-26 09:24 UTC224
fn record(homeserver: &str) -> SessionRecord {
713205a95af2atagen2026-09-26 09:24 UTC225
    serde_json::from_value(serde_json::json!({
713205a95af2atagen2026-09-26 09:24 UTC226
        "homeserver": homeserver,
713205a95af2atagen2026-09-26 09:24 UTC227
        "authentication": {
713205a95af2atagen2026-09-26 09:24 UTC228
            "type": "matrix",
713205a95af2atagen2026-09-26 09:24 UTC229
            "session": {
713205a95af2atagen2026-09-26 09:24 UTC230
                "user_id": "@example:example.org",
713205a95af2atagen2026-09-26 09:24 UTC231
                "device_id": "DEVICE",
713205a95af2atagen2026-09-26 09:24 UTC232
                "access_token": "secret",
713205a95af2atagen2026-09-26 09:24 UTC233
            },
713205a95af2atagen2026-09-26 09:24 UTC234
        },
713205a95af2atagen2026-09-26 09:24 UTC235
    }))
713205a95af2atagen2026-09-26 09:24 UTC236
    .unwrap()
96fdf90aacd6atagen2026-09-08 08:24 UTC237
}
96fdf90aacd6atagen2026-09-08 08:24 UTC238
713205a95af2atagen2026-09-26 09:24 UTC239
fn temporary_path() -> PathBuf {
96fdf90aacd6atagen2026-09-08 08:24 UTC240
    let nonce = SystemTime::now()
96fdf90aacd6atagen2026-09-08 08:24 UTC241
        .duration_since(SystemTime::UNIX_EPOCH)
96fdf90aacd6atagen2026-09-08 08:24 UTC242
        .unwrap()
96fdf90aacd6atagen2026-09-08 08:24 UTC243
        .as_nanos();
713205a95af2atagen2026-09-26 09:24 UTC244
    PathBuf::from(env!("CARGO_TARGET_TMPDIR")).join(format!("macltok-account-{nonce}"))
96fdf90aacd6atagen2026-09-08 08:24 UTC245
}