atagen

macltok

public
Star0
Watch0
55a1b82528146aa58ea1bc612190c22e1915289e
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
713205a95af2atagen2026-09-26 09:24 UTC4
use std::{path::PathBuf, 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"),
713205a95af2atagen2026-09-26 09:24 UTC40
        &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
713205a95af2atagen2026-09-26 09:24 UTC59
    let sessions = sessions();
6f1d6591c10datagen2026-09-26 09:52 UTC60
    let Some(Restore::Resumed(session)) = Session::restore(&sessions).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());
713205a95af2atagen2026-09-26 09:24 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"),
6f1d6591c10datagen2026-09-26 09:52 UTC93
        &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
6f1d6591c10datagen2026-09-26 09:52 UTC99
    let Some(Restore::SoftLoggedOut(soft)) = Session::restore(&sessions()).await.unwrap() else {
6f1d6591c10datagen2026-09-26 09:52 UTC100
        panic!("a soft logged out record came back as something else");
6f1d6591c10datagen2026-09-26 09:52 UTC101
    };
6f1d6591c10datagen2026-09-26 09:52 UTC102
    // the device matrix-sdk-test's canned login response hands out
6f1d6591c10datagen2026-09-26 09:52 UTC103
    assert_eq!(soft.device_id, "GHTYAJCE");
6f1d6591c10datagen2026-09-26 09:52 UTC104
    Session::login_password(soft.password_login("wordpass"), &sessions())
6f1d6591c10datagen2026-09-26 09:52 UTC105
        .await
6f1d6591c10datagen2026-09-26 09:52 UTC106
        .unwrap();
6f1d6591c10datagen2026-09-26 09:52 UTC107
    let requests = server.server().received_requests().await.unwrap();
6f1d6591c10datagen2026-09-26 09:52 UTC108
    let relogin: serde_json::Value = requests
6f1d6591c10datagen2026-09-26 09:52 UTC109
        .iter()
6f1d6591c10datagen2026-09-26 09:52 UTC110
        .rfind(|request| request.url.path().ends_with("/login"))
6f1d6591c10datagen2026-09-26 09:52 UTC111
        .unwrap()
6f1d6591c10datagen2026-09-26 09:52 UTC112
        .body_json()
6f1d6591c10datagen2026-09-26 09:52 UTC113
        .unwrap();
6f1d6591c10datagen2026-09-26 09:52 UTC114
    assert_eq!(relogin["device_id"], "GHTYAJCE");
6f1d6591c10datagen2026-09-26 09:52 UTC115
    assert!(matches!(
6f1d6591c10datagen2026-09-26 09:52 UTC116
        Session::restore(&sessions()).await.unwrap(),
6f1d6591c10datagen2026-09-26 09:52 UTC117
        Some(Restore::Resumed(_))
6f1d6591c10datagen2026-09-26 09:52 UTC118
    ));
6f1d6591c10datagen2026-09-26 09:52 UTC119
    std::fs::remove_dir_all(root).unwrap();
6f1d6591c10datagen2026-09-26 09:52 UTC120
}
6f1d6591c10datagen2026-09-26 09:52 UTC121
6f1d6591c10datagen2026-09-26 09:52 UTC122
#[tokio::test]
713205a95af2atagen2026-09-26 09:24 UTC123
async fn a_torn_or_failed_save_leaves_the_old_record() {
713205a95af2atagen2026-09-26 09:24 UTC124
    let root = temporary_path();
713205a95af2atagen2026-09-26 09:24 UTC125
    let record_path = root.join("session");
713205a95af2atagen2026-09-26 09:24 UTC126
    let sessions = || {
713205a95af2atagen2026-09-26 09:24 UTC127
        EncryptedSessionStore::file(
713205a95af2atagen2026-09-26 09:24 UTC128
            &record_path,
713205a95af2atagen2026-09-26 09:24 UTC129
            StoreConfig::sqlite(root.join("store"), "test store passphrase"),
713205a95af2atagen2026-09-26 09:24 UTC130
        )
713205a95af2atagen2026-09-26 09:24 UTC131
    };
713205a95af2atagen2026-09-26 09:24 UTC132
    let temporary = root.join("session.tmp");
713205a95af2atagen2026-09-26 09:24 UTC133
    sessions()
713205a95af2atagen2026-09-26 09:24 UTC134
        .save(&record("https://old.example"))
713205a95af2atagen2026-09-26 09:24 UTC135
        .await
713205a95af2atagen2026-09-26 09:24 UTC136
        .unwrap();
713205a95af2atagen2026-09-26 09:24 UTC137
713205a95af2atagen2026-09-26 09:24 UTC138
    std::fs::write(&temporary, b"half a rec").unwrap();
713205a95af2atagen2026-09-26 09:24 UTC139
    let loaded = sessions().load().await.unwrap().unwrap();
713205a95af2atagen2026-09-26 09:24 UTC140
    assert_eq!(loaded.homeserver(), "https://old.example");
713205a95af2atagen2026-09-26 09:24 UTC141
713205a95af2atagen2026-09-26 09:24 UTC142
    std::fs::remove_file(&temporary).unwrap();
713205a95af2atagen2026-09-26 09:24 UTC143
    std::fs::create_dir(&temporary).unwrap();
713205a95af2atagen2026-09-26 09:24 UTC144
    assert!(
713205a95af2atagen2026-09-26 09:24 UTC145
        sessions()
713205a95af2atagen2026-09-26 09:24 UTC146
            .save(&record("https://new.example"))
713205a95af2atagen2026-09-26 09:24 UTC147
            .await
713205a95af2atagen2026-09-26 09:24 UTC148
            .is_err()
713205a95af2atagen2026-09-26 09:24 UTC149
    );
713205a95af2atagen2026-09-26 09:24 UTC150
    let loaded = sessions().load().await.unwrap().unwrap();
713205a95af2atagen2026-09-26 09:24 UTC151
    assert_eq!(loaded.homeserver(), "https://old.example");
713205a95af2atagen2026-09-26 09:24 UTC152
713205a95af2atagen2026-09-26 09:24 UTC153
    std::fs::remove_dir_all(root).unwrap();
713205a95af2atagen2026-09-26 09:24 UTC154
}
96fdf90aacd6atagen2026-09-08 08:24 UTC155
713205a95af2atagen2026-09-26 09:24 UTC156
fn record(homeserver: &str) -> SessionRecord {
713205a95af2atagen2026-09-26 09:24 UTC157
    serde_json::from_value(serde_json::json!({
713205a95af2atagen2026-09-26 09:24 UTC158
        "homeserver": homeserver,
713205a95af2atagen2026-09-26 09:24 UTC159
        "authentication": {
713205a95af2atagen2026-09-26 09:24 UTC160
            "type": "matrix",
713205a95af2atagen2026-09-26 09:24 UTC161
            "session": {
713205a95af2atagen2026-09-26 09:24 UTC162
                "user_id": "@example:example.org",
713205a95af2atagen2026-09-26 09:24 UTC163
                "device_id": "DEVICE",
713205a95af2atagen2026-09-26 09:24 UTC164
                "access_token": "secret",
713205a95af2atagen2026-09-26 09:24 UTC165
            },
713205a95af2atagen2026-09-26 09:24 UTC166
        },
713205a95af2atagen2026-09-26 09:24 UTC167
    }))
713205a95af2atagen2026-09-26 09:24 UTC168
    .unwrap()
96fdf90aacd6atagen2026-09-08 08:24 UTC169
}
96fdf90aacd6atagen2026-09-08 08:24 UTC170
713205a95af2atagen2026-09-26 09:24 UTC171
fn temporary_path() -> PathBuf {
96fdf90aacd6atagen2026-09-08 08:24 UTC172
    let nonce = SystemTime::now()
96fdf90aacd6atagen2026-09-08 08:24 UTC173
        .duration_since(SystemTime::UNIX_EPOCH)
96fdf90aacd6atagen2026-09-08 08:24 UTC174
        .unwrap()
96fdf90aacd6atagen2026-09-08 08:24 UTC175
        .as_nanos();
713205a95af2atagen2026-09-26 09:24 UTC176
    PathBuf::from(env!("CARGO_TARGET_TMPDIR")).join(format!("macltok-account-{nonce}"))
96fdf90aacd6atagen2026-09-08 08:24 UTC177
}