mirror of
https://tangled.org/tranquil.farm/tranquil-pds
synced 2026-09-19 08:44:13 +00:00
Account lifecycle conf. vs ref
This commit is contained in:
@@ -0,0 +1,279 @@
|
||||
mod common;
|
||||
mod helpers;
|
||||
use common::*;
|
||||
use reqwest::StatusCode;
|
||||
use serde_json::{Value, json};
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_check_account_status_returns_correct_block_count() {
|
||||
let client = client();
|
||||
let base = base_url().await;
|
||||
let (access_jwt, did) = create_account_and_login(&client).await;
|
||||
|
||||
let status1 = client
|
||||
.get(format!("{}/xrpc/com.atproto.server.checkAccountStatus", base))
|
||||
.bearer_auth(&access_jwt)
|
||||
.send()
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(status1.status(), StatusCode::OK);
|
||||
let body1: Value = status1.json().await.unwrap();
|
||||
let initial_blocks = body1["repoBlocks"].as_i64().unwrap();
|
||||
assert!(initial_blocks >= 2, "New account should have at least 2 blocks (commit + empty MST)");
|
||||
|
||||
let create_res = client
|
||||
.post(format!("{}/xrpc/com.atproto.repo.createRecord", base))
|
||||
.bearer_auth(&access_jwt)
|
||||
.json(&json!({
|
||||
"repo": did,
|
||||
"collection": "app.bsky.feed.post",
|
||||
"record": {
|
||||
"$type": "app.bsky.feed.post",
|
||||
"text": "Test post for block counting",
|
||||
"createdAt": chrono::Utc::now().to_rfc3339()
|
||||
}
|
||||
}))
|
||||
.send()
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(create_res.status(), StatusCode::OK);
|
||||
let create_body: Value = create_res.json().await.unwrap();
|
||||
let rkey = create_body["uri"].as_str().unwrap().split('/').last().unwrap().to_string();
|
||||
|
||||
let status2 = client
|
||||
.get(format!("{}/xrpc/com.atproto.server.checkAccountStatus", base))
|
||||
.bearer_auth(&access_jwt)
|
||||
.send()
|
||||
.await
|
||||
.unwrap();
|
||||
let body2: Value = status2.json().await.unwrap();
|
||||
let after_create_blocks = body2["repoBlocks"].as_i64().unwrap();
|
||||
assert!(after_create_blocks > initial_blocks, "Block count should increase after creating a record");
|
||||
|
||||
let delete_res = client
|
||||
.post(format!("{}/xrpc/com.atproto.repo.deleteRecord", base))
|
||||
.bearer_auth(&access_jwt)
|
||||
.json(&json!({
|
||||
"repo": did,
|
||||
"collection": "app.bsky.feed.post",
|
||||
"rkey": rkey
|
||||
}))
|
||||
.send()
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(delete_res.status(), StatusCode::OK);
|
||||
|
||||
let status3 = client
|
||||
.get(format!("{}/xrpc/com.atproto.server.checkAccountStatus", base))
|
||||
.bearer_auth(&access_jwt)
|
||||
.send()
|
||||
.await
|
||||
.unwrap();
|
||||
let body3: Value = status3.json().await.unwrap();
|
||||
let after_delete_blocks = body3["repoBlocks"].as_i64().unwrap();
|
||||
assert!(
|
||||
after_delete_blocks >= after_create_blocks,
|
||||
"Block count should not decrease after deleting a record (was {}, now {})",
|
||||
after_create_blocks,
|
||||
after_delete_blocks
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_check_account_status_returns_valid_repo_rev() {
|
||||
let client = client();
|
||||
let base = base_url().await;
|
||||
let (access_jwt, _) = create_account_and_login(&client).await;
|
||||
|
||||
let status = client
|
||||
.get(format!("{}/xrpc/com.atproto.server.checkAccountStatus", base))
|
||||
.bearer_auth(&access_jwt)
|
||||
.send()
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(status.status(), StatusCode::OK);
|
||||
let body: Value = status.json().await.unwrap();
|
||||
|
||||
let repo_rev = body["repoRev"].as_str().unwrap();
|
||||
assert!(!repo_rev.is_empty(), "repoRev should not be empty");
|
||||
assert!(repo_rev.chars().all(|c| c.is_alphanumeric()), "repoRev should be alphanumeric TID");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_check_account_status_valid_did_is_true_for_active_account() {
|
||||
let client = client();
|
||||
let base = base_url().await;
|
||||
let (access_jwt, _) = create_account_and_login(&client).await;
|
||||
|
||||
let status = client
|
||||
.get(format!("{}/xrpc/com.atproto.server.checkAccountStatus", base))
|
||||
.bearer_auth(&access_jwt)
|
||||
.send()
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(status.status(), StatusCode::OK);
|
||||
let body: Value = status.json().await.unwrap();
|
||||
|
||||
assert_eq!(body["validDid"], true, "validDid should be true for active account with correct DID document");
|
||||
assert_eq!(body["activated"], true, "activated should be true for active account");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_deactivate_account_with_delete_after() {
|
||||
let client = client();
|
||||
let base = base_url().await;
|
||||
let (access_jwt, _) = create_account_and_login(&client).await;
|
||||
|
||||
let future_time = chrono::Utc::now() + chrono::Duration::hours(24);
|
||||
let delete_after = future_time.to_rfc3339();
|
||||
|
||||
let deactivate = client
|
||||
.post(format!("{}/xrpc/com.atproto.server.deactivateAccount", base))
|
||||
.bearer_auth(&access_jwt)
|
||||
.json(&json!({
|
||||
"deleteAfter": delete_after
|
||||
}))
|
||||
.send()
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(deactivate.status(), StatusCode::OK);
|
||||
|
||||
let status = client
|
||||
.get(format!("{}/xrpc/com.atproto.server.checkAccountStatus", base))
|
||||
.bearer_auth(&access_jwt)
|
||||
.send()
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(status.status(), StatusCode::OK);
|
||||
let body: Value = status.json().await.unwrap();
|
||||
assert_eq!(body["activated"], false, "Account should be deactivated");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_create_account_returns_did_doc() {
|
||||
let client = client();
|
||||
let base = base_url().await;
|
||||
|
||||
let handle = format!("diddoctest-{}", uuid::Uuid::new_v4());
|
||||
let payload = json!({
|
||||
"handle": handle,
|
||||
"email": format!("{}@example.com", handle),
|
||||
"password": "Testpass123!"
|
||||
});
|
||||
|
||||
let create_res = client
|
||||
.post(format!("{}/xrpc/com.atproto.server.createAccount", base))
|
||||
.json(&payload)
|
||||
.send()
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(create_res.status(), StatusCode::OK);
|
||||
let body: Value = create_res.json().await.unwrap();
|
||||
|
||||
assert!(body["accessJwt"].is_string(), "accessJwt should always be returned");
|
||||
assert!(body["refreshJwt"].is_string(), "refreshJwt should always be returned");
|
||||
assert!(body["did"].is_string(), "did should be returned");
|
||||
|
||||
if body["didDoc"].is_object() {
|
||||
let did_doc = &body["didDoc"];
|
||||
assert!(did_doc["id"].is_string(), "didDoc should have id field");
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_create_account_always_returns_tokens() {
|
||||
let client = client();
|
||||
let base = base_url().await;
|
||||
|
||||
let handle = format!("tokentest-{}", uuid::Uuid::new_v4());
|
||||
let payload = json!({
|
||||
"handle": handle,
|
||||
"email": format!("{}@example.com", handle),
|
||||
"password": "Testpass123!"
|
||||
});
|
||||
|
||||
let create_res = client
|
||||
.post(format!("{}/xrpc/com.atproto.server.createAccount", base))
|
||||
.json(&payload)
|
||||
.send()
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(create_res.status(), StatusCode::OK);
|
||||
let body: Value = create_res.json().await.unwrap();
|
||||
|
||||
let access_jwt = body["accessJwt"].as_str().expect("accessJwt should be present");
|
||||
let refresh_jwt = body["refreshJwt"].as_str().expect("refreshJwt should be present");
|
||||
|
||||
assert!(!access_jwt.is_empty(), "accessJwt should not be empty");
|
||||
assert!(!refresh_jwt.is_empty(), "refreshJwt should not be empty");
|
||||
|
||||
let parts: Vec<&str> = access_jwt.split('.').collect();
|
||||
assert_eq!(parts.len(), 3, "accessJwt should be a valid JWT with 3 parts");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_describe_server_has_links_and_contact() {
|
||||
let client = client();
|
||||
let base = base_url().await;
|
||||
|
||||
let describe = client
|
||||
.get(format!("{}/xrpc/com.atproto.server.describeServer", base))
|
||||
.send()
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(describe.status(), StatusCode::OK);
|
||||
let body: Value = describe.json().await.unwrap();
|
||||
|
||||
assert!(body.get("links").is_some(), "describeServer should include links object");
|
||||
assert!(body.get("contact").is_some(), "describeServer should include contact object");
|
||||
|
||||
let links = &body["links"];
|
||||
assert!(links.get("privacyPolicy").is_some() || links["privacyPolicy"].is_null(),
|
||||
"links should have privacyPolicy field (can be null)");
|
||||
assert!(links.get("termsOfService").is_some() || links["termsOfService"].is_null(),
|
||||
"links should have termsOfService field (can be null)");
|
||||
|
||||
let contact = &body["contact"];
|
||||
assert!(contact.get("email").is_some() || contact["email"].is_null(),
|
||||
"contact should have email field (can be null)");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_delete_account_password_max_length() {
|
||||
let client = client();
|
||||
let base = base_url().await;
|
||||
|
||||
let handle = format!("pwdlentest-{}", uuid::Uuid::new_v4());
|
||||
let payload = json!({
|
||||
"handle": handle,
|
||||
"email": format!("{}@example.com", handle),
|
||||
"password": "Testpass123!"
|
||||
});
|
||||
|
||||
let create_res = client
|
||||
.post(format!("{}/xrpc/com.atproto.server.createAccount", base))
|
||||
.json(&payload)
|
||||
.send()
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(create_res.status(), StatusCode::OK);
|
||||
let body: Value = create_res.json().await.unwrap();
|
||||
let did = body["did"].as_str().unwrap();
|
||||
|
||||
let too_long_password = "a".repeat(600);
|
||||
let delete_res = client
|
||||
.post(format!("{}/xrpc/com.atproto.server.deleteAccount", base))
|
||||
.json(&json!({
|
||||
"did": did,
|
||||
"password": too_long_password,
|
||||
"token": "fake-token"
|
||||
}))
|
||||
.send()
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(delete_res.status(), StatusCode::BAD_REQUEST);
|
||||
let error_body: Value = delete_res.json().await.unwrap();
|
||||
assert!(error_body["message"].as_str().unwrap().contains("password length")
|
||||
|| error_body["error"].as_str().unwrap() == "InvalidRequest");
|
||||
}
|
||||
+4
-1
@@ -466,8 +466,11 @@ async fn create_account_and_login_internal(client: &Client, make_admin: bool) ->
|
||||
.await
|
||||
.expect("Failed to mark user as admin");
|
||||
}
|
||||
let verification_required = body["verificationRequired"].as_bool().unwrap_or(true);
|
||||
if let Some(access_jwt) = body["accessJwt"].as_str() {
|
||||
return (access_jwt.to_string(), did);
|
||||
if !verification_required {
|
||||
return (access_jwt.to_string(), did);
|
||||
}
|
||||
}
|
||||
let body_text: String = sqlx::query_scalar!(
|
||||
"SELECT body FROM comms_queue WHERE user_id = (SELECT id FROM users WHERE did = $1) AND comms_type = 'email_verification' ORDER BY created_at DESC LIMIT 1",
|
||||
|
||||
+2
-1
@@ -8,6 +8,7 @@ use serde_json::Value;
|
||||
async fn test_list_blobs_success() {
|
||||
let client = client();
|
||||
let (access_jwt, did) = create_account_and_login(&client).await;
|
||||
let unique_content = format!("test blob content {}", uuid::Uuid::new_v4());
|
||||
let blob_res = client
|
||||
.post(format!(
|
||||
"{}/xrpc/com.atproto.repo.uploadBlob",
|
||||
@@ -15,7 +16,7 @@ async fn test_list_blobs_success() {
|
||||
))
|
||||
.header(header::CONTENT_TYPE, "text/plain")
|
||||
.bearer_auth(&access_jwt)
|
||||
.body("test blob content")
|
||||
.body(unique_content)
|
||||
.send()
|
||||
.await
|
||||
.expect("Failed to upload blob");
|
||||
|
||||
@@ -162,7 +162,7 @@ async fn test_list_repos_shows_status_field() {
|
||||
|
||||
let res = client
|
||||
.get(format!(
|
||||
"{}/xrpc/com.atproto.sync.listRepos",
|
||||
"{}/xrpc/com.atproto.sync.listRepos?limit=1000",
|
||||
base_url().await
|
||||
))
|
||||
.send()
|
||||
|
||||
+4
-3
@@ -552,7 +552,8 @@ async fn test_sync_repo_export_lifecycle() {
|
||||
tokio::time::sleep(std::time::Duration::from_millis(100)).await;
|
||||
create_post(&client, &did, &jwt, &format!("Export test post {}", i)).await;
|
||||
}
|
||||
let blob_data = b"blob data for sync export test";
|
||||
let blob_data = format!("blob data for sync export test {}", uuid::Uuid::new_v4());
|
||||
let blob_bytes = blob_data.as_bytes().to_vec();
|
||||
let upload_res = client
|
||||
.post(format!(
|
||||
"{}/xrpc/com.atproto.repo.uploadBlob",
|
||||
@@ -560,7 +561,7 @@ async fn test_sync_repo_export_lifecycle() {
|
||||
))
|
||||
.header(header::CONTENT_TYPE, "application/octet-stream")
|
||||
.bearer_auth(&jwt)
|
||||
.body(blob_data.to_vec())
|
||||
.body(blob_bytes.clone())
|
||||
.send()
|
||||
.await
|
||||
.expect("Failed to upload blob");
|
||||
@@ -631,7 +632,7 @@ async fn test_sync_repo_export_lifecycle() {
|
||||
let retrieved_blob = get_blob_res.bytes().await.unwrap();
|
||||
assert_eq!(
|
||||
retrieved_blob.as_ref(),
|
||||
blob_data,
|
||||
blob_bytes.as_slice(),
|
||||
"Retrieved blob should match uploaded data"
|
||||
);
|
||||
let latest_commit_res = client
|
||||
|
||||
Reference in New Issue
Block a user