mirror of
https://tangled.org/tranquil.farm/tranquil-pds
synced 2026-09-28 05:04:14 +00:00
Backups, adversarial migrations
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
@@ -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);
|
||||
}
|
||||
@@ -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
@@ -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,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};
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user