Backups, adversarial migrations

This commit is contained in:
lewis
2026-01-02 00:24:32 +02:00
parent 2fb59b41ef
commit df2135b5e1
107 changed files with 7569 additions and 2878 deletions
+10 -40
View File
@@ -27,10 +27,7 @@ async fn test_get_notification_history() {
}
let resp = client
.get(format!(
"{}/xrpc/com.tranquil.account.getNotificationHistory",
base
))
.get(format!("{}/xrpc/_account.getNotificationHistory", base))
.header("Authorization", format!("Bearer {}", token))
.send()
.await
@@ -56,10 +53,7 @@ async fn test_verify_channel_discord() {
"discordId": "123456789"
});
let resp = client
.post(format!(
"{}/xrpc/com.tranquil.account.updateNotificationPrefs",
base
))
.post(format!("{}/xrpc/_account.updateNotificationPrefs", base))
.header("Authorization", format!("Bearer {}", token))
.json(&prefs)
.send()
@@ -101,10 +95,7 @@ async fn test_verify_channel_discord() {
"code": code
});
let resp = client
.post(format!(
"{}/xrpc/com.tranquil.account.confirmChannelVerification",
base
))
.post(format!("{}/xrpc/_account.confirmChannelVerification", base))
.header("Authorization", format!("Bearer {}", token))
.json(&input)
.send()
@@ -113,10 +104,7 @@ async fn test_verify_channel_discord() {
assert_eq!(resp.status(), 200);
let resp = client
.get(format!(
"{}/xrpc/com.tranquil.account.getNotificationPrefs",
base
))
.get(format!("{}/xrpc/_account.getNotificationPrefs", base))
.header("Authorization", format!("Bearer {}", token))
.send()
.await
@@ -136,10 +124,7 @@ async fn test_verify_channel_invalid_code() {
"telegramUsername": "testuser"
});
let resp = client
.post(format!(
"{}/xrpc/com.tranquil.account.updateNotificationPrefs",
base
))
.post(format!("{}/xrpc/_account.updateNotificationPrefs", base))
.header("Authorization", format!("Bearer {}", token))
.json(&prefs)
.send()
@@ -153,10 +138,7 @@ async fn test_verify_channel_invalid_code() {
"code": "XXXX-XXXX-XXXX-XXXX"
});
let resp = client
.post(format!(
"{}/xrpc/com.tranquil.account.confirmChannelVerification",
base
))
.post(format!("{}/xrpc/_account.confirmChannelVerification", base))
.header("Authorization", format!("Bearer {}", token))
.json(&input)
.send()
@@ -181,10 +163,7 @@ async fn test_verify_channel_not_set() {
"code": "XXXX-XXXX-XXXX-XXXX"
});
let resp = client
.post(format!(
"{}/xrpc/com.tranquil.account.confirmChannelVerification",
base
))
.post(format!("{}/xrpc/_account.confirmChannelVerification", base))
.header("Authorization", format!("Bearer {}", token))
.json(&input)
.send()
@@ -209,10 +188,7 @@ async fn test_update_email_via_notification_prefs() {
"email": unique_email
});
let resp = client
.post(format!(
"{}/xrpc/com.tranquil.account.updateNotificationPrefs",
base
))
.post(format!("{}/xrpc/_account.updateNotificationPrefs", base))
.header("Authorization", format!("Bearer {}", token))
.json(&prefs)
.send()
@@ -263,10 +239,7 @@ async fn test_update_email_via_notification_prefs() {
"code": code
});
let resp = client
.post(format!(
"{}/xrpc/com.tranquil.account.confirmChannelVerification",
base
))
.post(format!("{}/xrpc/_account.confirmChannelVerification", base))
.header("Authorization", format!("Bearer {}", token))
.json(&input)
.send()
@@ -275,10 +248,7 @@ async fn test_update_email_via_notification_prefs() {
assert_eq!(resp.status(), 200);
let resp = client
.get(format!(
"{}/xrpc/com.tranquil.account.getNotificationPrefs",
base
))
.get(format!("{}/xrpc/_account.getNotificationPrefs", base))
.header("Authorization", format!("Bearer {}", token))
.send()
.await
+2 -2
View File
@@ -11,7 +11,7 @@ async fn test_get_server_stats() {
let (_, _) = create_admin_account_and_login(&client).await;
let resp = client
.get(format!("{}/xrpc/com.tranquil.admin.getServerStats", base))
.get(format!("{}/xrpc/_admin.getServerStats", base))
.header("Authorization", format!("Bearer {}", token1))
.send()
.await
@@ -33,7 +33,7 @@ async fn test_get_server_stats_no_auth() {
let client = client();
let base = base_url().await;
let resp = client
.get(format!("{}/xrpc/com.tranquil.admin.getServerStats", base))
.get(format!("{}/xrpc/_admin.getServerStats", base))
.send()
.await
.unwrap();
+325
View File
@@ -0,0 +1,325 @@
mod common;
mod helpers;
use common::*;
use reqwest::{StatusCode, header};
use serde_json::{Value, json};
#[tokio::test]
async fn test_list_backups_empty() {
let client = client();
let (token, _did) = create_account_and_login(&client).await;
let res = client
.get(format!("{}/xrpc/_backup.listBackups", base_url().await))
.bearer_auth(&token)
.send()
.await
.expect("listBackups request failed");
assert_eq!(res.status(), StatusCode::OK);
let body: Value = res.json().await.expect("Invalid JSON");
assert!(body["backups"].is_array());
assert_eq!(body["backups"].as_array().unwrap().len(), 0);
assert!(body["backupEnabled"].as_bool().unwrap_or(false));
}
#[tokio::test]
async fn test_create_and_list_backup() {
let client = client();
let (token, _did) = create_account_and_login(&client).await;
let create_res = client
.post(format!("{}/xrpc/_backup.createBackup", base_url().await))
.bearer_auth(&token)
.send()
.await
.expect("createBackup request failed");
assert_eq!(create_res.status(), StatusCode::OK, "createBackup failed");
let create_body: Value = create_res.json().await.expect("Invalid JSON");
assert!(create_body["id"].is_string());
assert!(create_body["repoRev"].is_string());
assert!(create_body["sizeBytes"].is_i64());
assert!(create_body["blockCount"].is_i64());
let list_res = client
.get(format!("{}/xrpc/_backup.listBackups", base_url().await))
.bearer_auth(&token)
.send()
.await
.expect("listBackups request failed");
assert_eq!(list_res.status(), StatusCode::OK);
let list_body: Value = list_res.json().await.expect("Invalid JSON");
let backups = list_body["backups"].as_array().unwrap();
assert!(backups.len() >= 1);
}
#[tokio::test]
async fn test_download_backup() {
let client = client();
let (token, _did) = create_account_and_login(&client).await;
let create_res = client
.post(format!("{}/xrpc/_backup.createBackup", base_url().await))
.bearer_auth(&token)
.send()
.await
.expect("createBackup request failed");
assert_eq!(create_res.status(), StatusCode::OK);
let create_body: Value = create_res.json().await.expect("Invalid JSON");
let backup_id = create_body["id"].as_str().unwrap();
let get_res = client
.get(format!(
"{}/xrpc/_backup.getBackup?id={}",
base_url().await,
backup_id
))
.bearer_auth(&token)
.send()
.await
.expect("getBackup request failed");
assert_eq!(get_res.status(), StatusCode::OK);
let content_type = get_res.headers().get(header::CONTENT_TYPE).unwrap();
assert_eq!(content_type, "application/vnd.ipld.car");
let bytes = get_res.bytes().await.expect("Failed to read body");
assert!(bytes.len() > 100, "CAR file should have content");
assert_eq!(
bytes[1], 0xa2,
"CAR file should have valid header structure"
);
}
#[tokio::test]
async fn test_delete_backup() {
let client = client();
let (token, _did) = create_account_and_login(&client).await;
let create_res = client
.post(format!("{}/xrpc/_backup.createBackup", base_url().await))
.bearer_auth(&token)
.send()
.await
.expect("createBackup request failed");
assert_eq!(create_res.status(), StatusCode::OK);
let create_body: Value = create_res.json().await.expect("Invalid JSON");
let backup_id = create_body["id"].as_str().unwrap();
let delete_res = client
.post(format!(
"{}/xrpc/_backup.deleteBackup?id={}",
base_url().await,
backup_id
))
.bearer_auth(&token)
.send()
.await
.expect("deleteBackup request failed");
assert_eq!(delete_res.status(), StatusCode::OK);
let get_res = client
.get(format!(
"{}/xrpc/_backup.getBackup?id={}",
base_url().await,
backup_id
))
.bearer_auth(&token)
.send()
.await
.expect("getBackup request failed");
assert_eq!(get_res.status(), StatusCode::NOT_FOUND);
}
#[tokio::test]
async fn test_toggle_backup_enabled() {
let client = client();
let (token, _did) = create_account_and_login(&client).await;
let list_res = client
.get(format!("{}/xrpc/_backup.listBackups", base_url().await))
.bearer_auth(&token)
.send()
.await
.expect("listBackups request failed");
assert_eq!(list_res.status(), StatusCode::OK);
let list_body: Value = list_res.json().await.expect("Invalid JSON");
assert!(list_body["backupEnabled"].as_bool().unwrap());
let disable_res = client
.post(format!("{}/xrpc/_backup.setEnabled", base_url().await))
.bearer_auth(&token)
.json(&json!({"enabled": false}))
.send()
.await
.expect("setEnabled request failed");
assert_eq!(disable_res.status(), StatusCode::OK);
let disable_body: Value = disable_res.json().await.expect("Invalid JSON");
assert!(!disable_body["enabled"].as_bool().unwrap());
let list_res2 = client
.get(format!("{}/xrpc/_backup.listBackups", base_url().await))
.bearer_auth(&token)
.send()
.await
.expect("listBackups request failed");
let list_body2: Value = list_res2.json().await.expect("Invalid JSON");
assert!(!list_body2["backupEnabled"].as_bool().unwrap());
let enable_res = client
.post(format!("{}/xrpc/_backup.setEnabled", base_url().await))
.bearer_auth(&token)
.json(&json!({"enabled": true}))
.send()
.await
.expect("setEnabled request failed");
assert_eq!(enable_res.status(), StatusCode::OK);
}
#[tokio::test]
async fn test_backup_includes_blobs() {
let client = client();
let (token, did) = create_account_and_login(&client).await;
let blob_data = b"Hello, this is test blob data for backup testing!";
let upload_res = client
.post(format!(
"{}/xrpc/com.atproto.repo.uploadBlob",
base_url().await
))
.header(header::CONTENT_TYPE, "text/plain")
.bearer_auth(&token)
.body(blob_data.to_vec())
.send()
.await
.expect("uploadBlob request failed");
assert_eq!(upload_res.status(), StatusCode::OK);
let upload_body: Value = upload_res.json().await.expect("Invalid JSON");
let blob = &upload_body["blob"];
let record = json!({
"$type": "app.bsky.feed.post",
"text": "Test post with blob",
"createdAt": chrono::Utc::now().to_rfc3339(),
"embed": {
"$type": "app.bsky.embed.images",
"images": [{
"alt": "test image",
"image": blob
}]
}
});
let create_record_res = client
.post(format!(
"{}/xrpc/com.atproto.repo.createRecord",
base_url().await
))
.bearer_auth(&token)
.json(&json!({
"repo": did,
"collection": "app.bsky.feed.post",
"record": record
}))
.send()
.await
.expect("createRecord request failed");
assert_eq!(create_record_res.status(), StatusCode::OK);
let create_backup_res = client
.post(format!("{}/xrpc/_backup.createBackup", base_url().await))
.bearer_auth(&token)
.send()
.await
.expect("createBackup request failed");
assert_eq!(create_backup_res.status(), StatusCode::OK);
let backup_body: Value = create_backup_res.json().await.expect("Invalid JSON");
let backup_id = backup_body["id"].as_str().unwrap();
let get_backup_res = client
.get(format!(
"{}/xrpc/_backup.getBackup?id={}",
base_url().await,
backup_id
))
.bearer_auth(&token)
.send()
.await
.expect("getBackup request failed");
assert_eq!(get_backup_res.status(), StatusCode::OK);
let car_bytes = get_backup_res.bytes().await.expect("Failed to read body");
let blob_cid = blob["ref"]["$link"].as_str().unwrap();
let blob_found = String::from_utf8_lossy(&car_bytes).contains("Hello, this is test blob data");
assert!(
blob_found || car_bytes.len() > 500,
"Backup should contain blob data (cid: {})",
blob_cid
);
}
#[tokio::test]
async fn test_backup_unauthorized() {
let client = client();
let res = client
.get(format!("{}/xrpc/_backup.listBackups", base_url().await))
.send()
.await
.expect("listBackups request failed");
assert_eq!(res.status(), StatusCode::UNAUTHORIZED);
}
#[tokio::test]
async fn test_get_nonexistent_backup() {
let client = client();
let (token, _did) = create_account_and_login(&client).await;
let fake_id = uuid::Uuid::new_v4();
let res = client
.get(format!(
"{}/xrpc/_backup.getBackup?id={}",
base_url().await,
fake_id
))
.bearer_auth(&token)
.send()
.await
.expect("getBackup request failed");
assert_eq!(res.status(), StatusCode::NOT_FOUND);
}
#[tokio::test]
async fn test_backup_invalid_id() {
let client = client();
let (token, _did) = create_account_and_login(&client).await;
let res = client
.get(format!(
"{}/xrpc/_backup.getBackup?id=not-a-uuid",
base_url().await
))
.bearer_auth(&token)
.send()
.await
.expect("getBackup request failed");
assert_eq!(res.status(), StatusCode::BAD_REQUEST);
}
+6 -24
View File
@@ -32,10 +32,7 @@ async fn test_change_password_success() {
let did = create_body["did"].as_str().unwrap();
let jwt = verify_new_account(&client, did).await;
let change_res = client
.post(format!(
"{}/xrpc/com.tranquil.account.changePassword",
base_url().await
))
.post(format!("{}/xrpc/_account.changePassword", base_url().await))
.bearer_auth(&jwt)
.json(&json!({
"currentPassword": old_password,
@@ -86,10 +83,7 @@ async fn test_change_password_wrong_current() {
let client = client();
let (_, jwt) = setup_new_user("change-pw-wrong").await;
let res = client
.post(format!(
"{}/xrpc/com.tranquil.account.changePassword",
base_url().await
))
.post(format!("{}/xrpc/_account.changePassword", base_url().await))
.bearer_auth(&jwt)
.json(&json!({
"currentPassword": "Wrongpass999!",
@@ -129,10 +123,7 @@ async fn test_change_password_too_short() {
let did = create_body["did"].as_str().unwrap();
let jwt = verify_new_account(&client, did).await;
let res = client
.post(format!(
"{}/xrpc/com.tranquil.account.changePassword",
base_url().await
))
.post(format!("{}/xrpc/_account.changePassword", base_url().await))
.bearer_auth(&jwt)
.json(&json!({
"currentPassword": password,
@@ -151,10 +142,7 @@ async fn test_change_password_empty_current() {
let client = client();
let (_, jwt) = setup_new_user("change-pw-empty").await;
let res = client
.post(format!(
"{}/xrpc/com.tranquil.account.changePassword",
base_url().await
))
.post(format!("{}/xrpc/_account.changePassword", base_url().await))
.bearer_auth(&jwt)
.json(&json!({
"currentPassword": "",
@@ -171,10 +159,7 @@ async fn test_change_password_empty_new() {
let client = client();
let (_, jwt) = setup_new_user("change-pw-emptynew").await;
let res = client
.post(format!(
"{}/xrpc/com.tranquil.account.changePassword",
base_url().await
))
.post(format!("{}/xrpc/_account.changePassword", base_url().await))
.bearer_auth(&jwt)
.json(&json!({
"currentPassword": "E2epass123!",
@@ -190,10 +175,7 @@ async fn test_change_password_empty_new() {
async fn test_change_password_requires_auth() {
let client = client();
let res = client
.post(format!(
"{}/xrpc/com.tranquil.account.changePassword",
base_url().await
))
.post(format!("{}/xrpc/_account.changePassword", base_url().await))
.json(&json!({
"currentPassword": "Oldpass123!",
"newPassword": "Newpass123!"
+32 -251
View File
@@ -547,62 +547,10 @@ async fn test_did_web_byod_flow() {
}
#[tokio::test]
async fn test_deactivate_with_migrating_to() {
async fn test_did_web_can_edit_did_document() {
let client = client();
let base = base_url().await;
let handle = format!("mig{}", &uuid::Uuid::new_v4().simple().to_string()[..12]);
let payload = json!({
"handle": handle,
"email": format!("{}@example.com", handle),
"password": "Testpass123!",
"didType": "web"
});
let res = client
.post(format!("{}/xrpc/com.atproto.server.createAccount", base))
.json(&payload)
.send()
.await
.expect("Failed to send request");
assert_eq!(res.status(), StatusCode::OK);
let body: Value = res.json().await.expect("Response was not JSON");
let did = body["did"].as_str().expect("No DID").to_string();
let jwt = verify_new_account(&client, &did).await;
let target_pds = "https://pds2.example.com";
let res = client
.post(format!(
"{}/xrpc/com.atproto.server.deactivateAccount",
base
))
.bearer_auth(&jwt)
.json(&json!({ "migratingTo": target_pds }))
.send()
.await
.expect("Failed to send request");
assert_eq!(res.status(), StatusCode::OK);
let pool = get_test_db_pool().await;
let row = sqlx::query!(
r#"SELECT migrated_to_pds, deactivated_at FROM users WHERE did = $1"#,
&did
)
.fetch_one(pool)
.await
.expect("Failed to query user");
assert_eq!(
row.migrated_to_pds.as_deref(),
Some(target_pds),
"migrated_to_pds should be set to target PDS"
);
assert!(
row.deactivated_at.is_some(),
"deactivated_at should be set for migrated account"
);
}
#[tokio::test]
async fn test_migrated_account_blocked_from_repo_ops() {
let client = client();
let base = base_url().await;
let handle = format!("blk{}", &uuid::Uuid::new_v4().simple().to_string()[..12]);
let handle = format!("doc{}", &uuid::Uuid::new_v4().simple().to_string()[..12]);
let payload = json!({
"handle": handle,
"email": format!("{}@example.com", handle),
@@ -620,131 +568,53 @@ async fn test_migrated_account_blocked_from_repo_ops() {
let did = body["did"].as_str().expect("No DID").to_string();
let jwt = verify_new_account(&client, &did).await;
let res = client
.post(format!("{}/xrpc/com.atproto.repo.createRecord", base))
.get(format!("{}/xrpc/_account.getDidDocument", base))
.bearer_auth(&jwt)
.json(&json!({
"repo": did,
"collection": "app.bsky.feed.post",
"record": {
"$type": "app.bsky.feed.post",
"text": "Pre-migration post",
"createdAt": chrono::Utc::now().to_rfc3339()
}
}))
.send()
.await
.expect("Failed to send request");
assert_eq!(res.status(), StatusCode::OK);
let body: Value = res.json().await.expect("Response was not JSON");
assert!(
body["didDocument"].is_object(),
"Should return DID document"
);
assert_eq!(
body["didDocument"]["id"], did,
"DID document should have correct id"
);
let res = client
.post(format!(
"{}/xrpc/com.atproto.server.deactivateAccount",
base
))
.bearer_auth(&jwt)
.json(&json!({ "migratingTo": "https://pds2.example.com" }))
.send()
.await
.expect("Failed to send request");
assert_eq!(res.status(), StatusCode::OK);
let res = client
.post(format!("{}/xrpc/com.atproto.repo.createRecord", base))
.post(format!("{}/xrpc/_account.updateDidDocument", base))
.bearer_auth(&jwt)
.json(&json!({
"repo": did,
"collection": "app.bsky.feed.post",
"record": {
"$type": "app.bsky.feed.post",
"text": "Post-migration post - should fail",
"createdAt": chrono::Utc::now().to_rfc3339()
}
"alsoKnownAs": ["at://custom.handle.test"]
}))
.send()
.await
.expect("Failed to send request");
assert!(
res.status().is_client_error(),
"createRecord should fail for migrated account: {}",
res.status()
assert_eq!(
res.status(),
StatusCode::OK,
"Non-migrated did:web user should be able to update DID document"
);
let res = client
.post(format!("{}/xrpc/com.atproto.repo.putRecord", base))
.bearer_auth(&jwt)
.json(&json!({
"repo": did,
"collection": "app.bsky.actor.profile",
"rkey": "self",
"record": {
"$type": "app.bsky.actor.profile",
"displayName": "Test"
}
}))
.send()
.await
.expect("Failed to send request");
let body: Value = res.json().await.expect("Response was not JSON");
assert!(body["success"].as_bool().unwrap_or(false));
let also_known_as = body["didDocument"]["alsoKnownAs"]
.as_array()
.expect("alsoKnownAs should be array");
assert!(
res.status().is_client_error(),
"putRecord should fail for migrated account: {}",
res.status()
);
let res = client
.post(format!("{}/xrpc/com.atproto.repo.deleteRecord", base))
.bearer_auth(&jwt)
.json(&json!({
"repo": did,
"collection": "app.bsky.feed.post",
"rkey": "test123"
}))
.send()
.await
.expect("Failed to send request");
assert!(
res.status().is_client_error(),
"deleteRecord should fail for migrated account: {}",
res.status()
);
let res = client
.post(format!("{}/xrpc/com.atproto.repo.applyWrites", base))
.bearer_auth(&jwt)
.json(&json!({
"repo": did,
"writes": [{
"$type": "com.atproto.repo.applyWrites#create",
"collection": "app.bsky.feed.post",
"value": {
"$type": "app.bsky.feed.post",
"text": "Batch post",
"createdAt": chrono::Utc::now().to_rfc3339()
}
}]
}))
.send()
.await
.expect("Failed to send request");
assert!(
res.status().is_client_error(),
"applyWrites should fail for migrated account: {}",
res.status()
);
let res = client
.post(format!("{}/xrpc/com.atproto.repo.uploadBlob", base))
.bearer_auth(&jwt)
.header("Content-Type", "text/plain")
.body("test blob content")
.send()
.await
.expect("Failed to send request");
assert!(
res.status().is_client_error(),
"uploadBlob should fail for migrated account: {}",
res.status()
also_known_as
.iter()
.any(|v| v.as_str() == Some("at://custom.handle.test")),
"alsoKnownAs should contain custom entry"
);
}
#[tokio::test]
async fn test_migrated_session_status() {
async fn test_deactivate_account_basic() {
let client = client();
let base = base_url().await;
let handle = format!("ses{}", &uuid::Uuid::new_v4().simple().to_string()[..12]);
let handle = format!("dea{}", &uuid::Uuid::new_v4().simple().to_string()[..12]);
let payload = json!({
"handle": handle,
"email": format!("{}@example.com", handle),
@@ -761,27 +631,13 @@ async fn test_migrated_session_status() {
let body: Value = res.json().await.expect("Response was not JSON");
let did = body["did"].as_str().expect("No DID").to_string();
let jwt = verify_new_account(&client, &did).await;
let res = client
.get(format!("{}/xrpc/com.atproto.server.getSession", base))
.bearer_auth(&jwt)
.send()
.await
.expect("Failed to send request");
assert_eq!(res.status(), StatusCode::OK);
let body: Value = res.json().await.expect("Response was not JSON");
assert_eq!(body["active"], true);
assert!(
body["status"].is_null() || body["status"] == "active",
"Status should be null or 'active' for normal accounts"
);
let target_pds = "https://pds3.example.com";
let res = client
.post(format!(
"{}/xrpc/com.atproto.server.deactivateAccount",
base
))
.bearer_auth(&jwt)
.json(&json!({ "migratingTo": target_pds }))
.json(&json!({}))
.send()
.await
.expect("Failed to send request");
@@ -794,84 +650,9 @@ async fn test_migrated_session_status() {
.expect("Failed to send request");
assert_eq!(res.status(), StatusCode::OK);
let body: Value = res.json().await.expect("Response was not JSON");
assert_eq!(
body["active"], false,
"Migrated account should not be active"
);
assert_eq!(
body["status"], "migrated",
"Status should be 'migrated' after migration"
);
assert_eq!(
body["migratedToPds"], target_pds,
"migratedToPds should be set to target PDS"
);
}
#[tokio::test]
async fn test_migrating_to_ignored_for_did_plc() {
let client = client();
let base = base_url().await;
let handle = format!("plc{}", &uuid::Uuid::new_v4().simple().to_string()[..12]);
let payload = json!({
"handle": handle,
"email": format!("{}@example.com", handle),
"password": "Testpass123!",
"didType": "plc"
});
let res = client
.post(format!("{}/xrpc/com.atproto.server.createAccount", base))
.json(&payload)
.send()
.await
.expect("Failed to send request");
assert_eq!(res.status(), StatusCode::OK);
let body: Value = res.json().await.expect("Response was not JSON");
let did = body["did"].as_str().expect("No DID").to_string();
assert!(did.starts_with("did:plc:"), "Should be did:plc account");
let jwt = verify_new_account(&client, &did).await;
let res = client
.post(format!(
"{}/xrpc/com.atproto.server.deactivateAccount",
base
))
.bearer_auth(&jwt)
.json(&json!({ "migratingTo": "https://pds2.example.com" }))
.send()
.await
.expect("Failed to send request");
assert_eq!(res.status(), StatusCode::OK);
let pool = get_test_db_pool().await;
let row = sqlx::query!(
r#"SELECT migrated_to_pds, deactivated_at FROM users WHERE did = $1"#,
&did
)
.fetch_one(pool)
.await
.expect("Failed to query user");
assert!(
row.migrated_to_pds.is_none(),
"migrated_to_pds should NOT be set for did:plc accounts"
);
assert!(
row.deactivated_at.is_some(),
"deactivated_at should still be set"
);
let res = client
.get(format!("{}/xrpc/com.atproto.server.getSession", base))
.bearer_auth(&jwt)
.send()
.await
.expect("Failed to send request");
assert_eq!(res.status(), StatusCode::OK);
let body: Value = res.json().await.expect("Response was not JSON");
assert_eq!(body["active"], false);
assert_eq!(body["active"], false, "Account should be deactivated");
assert_eq!(
body["status"], "deactivated",
"Status should be 'deactivated' not 'migrated' for did:plc"
);
assert!(
body["migratedToPds"].is_null(),
"migratedToPds should not be set for did:plc accounts"
"Status should be 'deactivated'"
);
}
-1
View File
@@ -1,7 +1,6 @@
mod common;
mod helpers;
use base64::{Engine as _, engine::general_purpose::URL_SAFE_NO_PAD};
use chrono::Utc;
use common::{base_url, client, get_test_db_pool};
use helpers::verify_new_account;
use reqwest::{StatusCode, redirect};
+1 -4
View File
@@ -1116,10 +1116,7 @@ async fn test_delegation_viewer_scope_cannot_write() {
let delegated_handle = format!("dg{}", suffix);
let delegated_res = http_client
.post(format!(
"{}/xrpc/com.tranquil.delegation.createDelegatedAccount",
url
))
.post(format!("{}/xrpc/_delegation.createDelegatedAccount", url))
.bearer_auth(controller_jwt)
.json(&json!({
"handle": delegated_handle,
+9 -36
View File
@@ -10,10 +10,7 @@ async fn test_list_sessions_returns_current_session() {
let client = client();
let (did, jwt) = setup_new_user("list-sessions").await;
let res = client
.get(format!(
"{}/xrpc/com.tranquil.account.listSessions",
base_url().await
))
.get(format!("{}/xrpc/_account.listSessions", base_url().await))
.bearer_auth(&jwt)
.send()
.await
@@ -83,10 +80,7 @@ async fn test_list_sessions_multiple_sessions() {
let login_body: Value = login_res.json().await.unwrap();
let jwt2 = login_body["accessJwt"].as_str().unwrap();
let list_res = client
.get(format!(
"{}/xrpc/com.tranquil.account.listSessions",
base_url().await
))
.get(format!("{}/xrpc/_account.listSessions", base_url().await))
.bearer_auth(jwt2)
.send()
.await
@@ -106,10 +100,7 @@ async fn test_list_sessions_multiple_sessions() {
async fn test_list_sessions_requires_auth() {
let client = client();
let res = client
.get(format!(
"{}/xrpc/com.tranquil.account.listSessions",
base_url().await
))
.get(format!("{}/xrpc/_account.listSessions", base_url().await))
.send()
.await
.expect("Failed to send request");
@@ -158,10 +149,7 @@ async fn test_revoke_session_success() {
let login_body: Value = login_res.json().await.unwrap();
let jwt2 = login_body["accessJwt"].as_str().unwrap();
let list_res = client
.get(format!(
"{}/xrpc/com.tranquil.account.listSessions",
base_url().await
))
.get(format!("{}/xrpc/_account.listSessions", base_url().await))
.bearer_auth(jwt2)
.send()
.await
@@ -177,10 +165,7 @@ async fn test_revoke_session_success() {
);
let session_id = other_session.unwrap()["id"].as_str().unwrap();
let revoke_res = client
.post(format!(
"{}/xrpc/com.tranquil.account.revokeSession",
base_url().await
))
.post(format!("{}/xrpc/_account.revokeSession", base_url().await))
.bearer_auth(jwt2)
.json(&json!({"sessionId": session_id}))
.send()
@@ -188,10 +173,7 @@ async fn test_revoke_session_success() {
.expect("Failed to revoke session");
assert_eq!(revoke_res.status(), StatusCode::OK);
let list_after_res = client
.get(format!(
"{}/xrpc/com.tranquil.account.listSessions",
base_url().await
))
.get(format!("{}/xrpc/_account.listSessions", base_url().await))
.bearer_auth(jwt2)
.send()
.await
@@ -213,10 +195,7 @@ async fn test_revoke_session_invalid_id() {
let client = client();
let (_, jwt) = setup_new_user("revoke-invalid").await;
let res = client
.post(format!(
"{}/xrpc/com.tranquil.account.revokeSession",
base_url().await
))
.post(format!("{}/xrpc/_account.revokeSession", base_url().await))
.bearer_auth(&jwt)
.json(&json!({"sessionId": "not-a-number"}))
.send()
@@ -230,10 +209,7 @@ async fn test_revoke_session_not_found() {
let client = client();
let (_, jwt) = setup_new_user("revoke-notfound").await;
let res = client
.post(format!(
"{}/xrpc/com.tranquil.account.revokeSession",
base_url().await
))
.post(format!("{}/xrpc/_account.revokeSession", base_url().await))
.bearer_auth(&jwt)
.json(&json!({"sessionId": "jwt:999999999"}))
.send()
@@ -246,10 +222,7 @@ async fn test_revoke_session_not_found() {
async fn test_revoke_session_requires_auth() {
let client = client();
let res = client
.post(format!(
"{}/xrpc/com.tranquil.account.revokeSession",
base_url().await
))
.post(format!("{}/xrpc/_account.revokeSession", base_url().await))
.json(&json!({"sessionId": "1"}))
.send()
.await