this repo has no description
1mod common; 2mod helpers; 3use common::*; 4use chrono::Utc; 5use reqwest::StatusCode; 6use serde_json::{Value, json}; 7use sqlx::PgPool; 8async fn get_pool() -> PgPool { 9 let conn_str = get_db_connection_string().await; 10 sqlx::postgres::PgPoolOptions::new() 11 .max_connections(5) 12 .connect(&conn_str) 13 .await 14 .expect("Failed to connect to test database") 15} 16async fn create_verified_account(client: &reqwest::Client, base_url: &str, handle: &str, email: &str, password: &str) -> (String, String) { 17 let res = client 18 .post(format!("{}/xrpc/com.atproto.server.createAccount", base_url)) 19 .json(&json!({ 20 "handle": handle, 21 "email": email, 22 "password": password 23 })) 24 .send() 25 .await 26 .expect("Failed to create account"); 27 assert_eq!(res.status(), StatusCode::OK); 28 let body: Value = res.json().await.expect("Invalid JSON"); 29 let did = body["did"].as_str().expect("No did").to_string(); 30 let jwt = verify_new_account(client, &did).await; 31 (did, jwt) 32} 33#[tokio::test] 34async fn test_delete_account_full_flow() { 35 let client = client(); 36 let base_url = base_url().await; 37 let ts = Utc::now().timestamp_millis(); 38 let handle = format!("delete-test-{}.test", ts); 39 let email = format!("delete-test-{}@test.com", ts); 40 let password = "delete-password-123"; 41 let (did, jwt) = create_verified_account(&client, &base_url, &handle, &email, password).await; 42 let request_delete_res = client 43 .post(format!( 44 "{}/xrpc/com.atproto.server.requestAccountDelete", 45 base_url 46 )) 47 .bearer_auth(&jwt) 48 .send() 49 .await 50 .expect("Failed to request account deletion"); 51 assert_eq!(request_delete_res.status(), StatusCode::OK); 52 let pool = get_pool().await; 53 let row = sqlx::query!("SELECT token FROM account_deletion_requests WHERE did = $1", did) 54 .fetch_one(&pool) 55 .await 56 .expect("Failed to query deletion token"); 57 let token = row.token; 58 let delete_payload = json!({ 59 "did": did, 60 "password": password, 61 "token": token 62 }); 63 let delete_res = client 64 .post(format!( 65 "{}/xrpc/com.atproto.server.deleteAccount", 66 base_url 67 )) 68 .json(&delete_payload) 69 .send() 70 .await 71 .expect("Failed to delete account"); 72 assert_eq!(delete_res.status(), StatusCode::OK); 73 let user_row = sqlx::query!("SELECT id FROM users WHERE did = $1", did) 74 .fetch_optional(&pool) 75 .await 76 .expect("Failed to query user"); 77 assert!(user_row.is_none(), "User should be deleted from database"); 78 let session_res = client 79 .get(format!( 80 "{}/xrpc/com.atproto.server.getSession", 81 base_url 82 )) 83 .bearer_auth(&jwt) 84 .send() 85 .await 86 .expect("Failed to check session"); 87 assert_eq!(session_res.status(), StatusCode::UNAUTHORIZED); 88} 89#[tokio::test] 90async fn test_delete_account_wrong_password() { 91 let client = client(); 92 let base_url = base_url().await; 93 let ts = Utc::now().timestamp_millis(); 94 let handle = format!("delete-wrongpw-{}.test", ts); 95 let email = format!("delete-wrongpw-{}@test.com", ts); 96 let password = "correct-password"; 97 let (did, jwt) = create_verified_account(&client, &base_url, &handle, &email, password).await; 98 let request_delete_res = client 99 .post(format!( 100 "{}/xrpc/com.atproto.server.requestAccountDelete", 101 base_url 102 )) 103 .bearer_auth(&jwt) 104 .send() 105 .await 106 .expect("Failed to request account deletion"); 107 assert_eq!(request_delete_res.status(), StatusCode::OK); 108 let pool = get_pool().await; 109 let row = sqlx::query!("SELECT token FROM account_deletion_requests WHERE did = $1", did) 110 .fetch_one(&pool) 111 .await 112 .expect("Failed to query deletion token"); 113 let token = row.token; 114 let delete_payload = json!({ 115 "did": did, 116 "password": "wrong-password", 117 "token": token 118 }); 119 let delete_res = client 120 .post(format!( 121 "{}/xrpc/com.atproto.server.deleteAccount", 122 base_url 123 )) 124 .json(&delete_payload) 125 .send() 126 .await 127 .expect("Failed to send delete request"); 128 assert_eq!(delete_res.status(), StatusCode::UNAUTHORIZED); 129 let body: Value = delete_res.json().await.unwrap(); 130 assert_eq!(body["error"], "AuthenticationFailed"); 131} 132#[tokio::test] 133async fn test_delete_account_invalid_token() { 134 let client = client(); 135 let base_url = base_url().await; 136 let ts = Utc::now().timestamp_millis(); 137 let handle = format!("delete-badtoken-{}.test", ts); 138 let email = format!("delete-badtoken-{}@test.com", ts); 139 let password = "delete-password"; 140 let create_res = client 141 .post(format!( 142 "{}/xrpc/com.atproto.server.createAccount", 143 base_url 144 )) 145 .json(&json!({ 146 "handle": handle, 147 "email": email, 148 "password": password 149 })) 150 .send() 151 .await 152 .expect("Failed to create account"); 153 assert_eq!(create_res.status(), StatusCode::OK); 154 let create_body: Value = create_res.json().await.unwrap(); 155 let did = create_body["did"].as_str().unwrap().to_string(); 156 let delete_payload = json!({ 157 "did": did, 158 "password": password, 159 "token": "invalid-token-12345" 160 }); 161 let delete_res = client 162 .post(format!( 163 "{}/xrpc/com.atproto.server.deleteAccount", 164 base_url 165 )) 166 .json(&delete_payload) 167 .send() 168 .await 169 .expect("Failed to send delete request"); 170 assert_eq!(delete_res.status(), StatusCode::BAD_REQUEST); 171 let body: Value = delete_res.json().await.unwrap(); 172 assert_eq!(body["error"], "InvalidToken"); 173} 174#[tokio::test] 175async fn test_delete_account_expired_token() { 176 let client = client(); 177 let base_url = base_url().await; 178 let ts = Utc::now().timestamp_millis(); 179 let handle = format!("delete-expired-{}.test", ts); 180 let email = format!("delete-expired-{}@test.com", ts); 181 let password = "delete-password"; 182 let (did, jwt) = create_verified_account(&client, &base_url, &handle, &email, password).await; 183 let request_delete_res = client 184 .post(format!( 185 "{}/xrpc/com.atproto.server.requestAccountDelete", 186 base_url 187 )) 188 .bearer_auth(&jwt) 189 .send() 190 .await 191 .expect("Failed to request account deletion"); 192 assert_eq!(request_delete_res.status(), StatusCode::OK); 193 let pool = get_pool().await; 194 let row = sqlx::query!("SELECT token FROM account_deletion_requests WHERE did = $1", did) 195 .fetch_one(&pool) 196 .await 197 .expect("Failed to query deletion token"); 198 let token = row.token; 199 sqlx::query!( 200 "UPDATE account_deletion_requests SET expires_at = NOW() - INTERVAL '1 hour' WHERE token = $1", 201 token 202 ) 203 .execute(&pool) 204 .await 205 .expect("Failed to expire token"); 206 let delete_payload = json!({ 207 "did": did, 208 "password": password, 209 "token": token 210 }); 211 let delete_res = client 212 .post(format!( 213 "{}/xrpc/com.atproto.server.deleteAccount", 214 base_url 215 )) 216 .json(&delete_payload) 217 .send() 218 .await 219 .expect("Failed to send delete request"); 220 assert_eq!(delete_res.status(), StatusCode::BAD_REQUEST); 221 let body: Value = delete_res.json().await.unwrap(); 222 assert_eq!(body["error"], "ExpiredToken"); 223} 224#[tokio::test] 225async fn test_delete_account_token_mismatch() { 226 let client = client(); 227 let base_url = base_url().await; 228 let ts = Utc::now().timestamp_millis(); 229 let handle1 = format!("delete-user1-{}.test", ts); 230 let email1 = format!("delete-user1-{}@test.com", ts); 231 let password1 = "user1-password"; 232 let (did1, jwt1) = create_verified_account(&client, &base_url, &handle1, &email1, password1).await; 233 let handle2 = format!("delete-user2-{}.test", ts); 234 let email2 = format!("delete-user2-{}@test.com", ts); 235 let password2 = "user2-password"; 236 let (did2, _) = create_verified_account(&client, &base_url, &handle2, &email2, password2).await; 237 let request_delete_res = client 238 .post(format!( 239 "{}/xrpc/com.atproto.server.requestAccountDelete", 240 base_url 241 )) 242 .bearer_auth(&jwt1) 243 .send() 244 .await 245 .expect("Failed to request account deletion"); 246 assert_eq!(request_delete_res.status(), StatusCode::OK); 247 let pool = get_pool().await; 248 let row = sqlx::query!("SELECT token FROM account_deletion_requests WHERE did = $1", did1) 249 .fetch_one(&pool) 250 .await 251 .expect("Failed to query deletion token"); 252 let token = row.token; 253 let delete_payload = json!({ 254 "did": did2, 255 "password": password2, 256 "token": token 257 }); 258 let delete_res = client 259 .post(format!( 260 "{}/xrpc/com.atproto.server.deleteAccount", 261 base_url 262 )) 263 .json(&delete_payload) 264 .send() 265 .await 266 .expect("Failed to send delete request"); 267 assert_eq!(delete_res.status(), StatusCode::BAD_REQUEST); 268 let body: Value = delete_res.json().await.unwrap(); 269 assert_eq!(body["error"], "InvalidToken"); 270} 271#[tokio::test] 272async fn test_delete_account_with_app_password() { 273 let client = client(); 274 let base_url = base_url().await; 275 let ts = Utc::now().timestamp_millis(); 276 let handle = format!("delete-apppw-{}.test", ts); 277 let email = format!("delete-apppw-{}@test.com", ts); 278 let main_password = "main-password-123"; 279 let (did, jwt) = create_verified_account(&client, &base_url, &handle, &email, main_password).await; 280 let app_password_res = client 281 .post(format!( 282 "{}/xrpc/com.atproto.server.createAppPassword", 283 base_url 284 )) 285 .bearer_auth(&jwt) 286 .json(&json!({ "name": "delete-test-app" })) 287 .send() 288 .await 289 .expect("Failed to create app password"); 290 assert_eq!(app_password_res.status(), StatusCode::OK); 291 let app_password_body: Value = app_password_res.json().await.unwrap(); 292 let app_password = app_password_body["password"].as_str().unwrap().to_string(); 293 let request_delete_res = client 294 .post(format!( 295 "{}/xrpc/com.atproto.server.requestAccountDelete", 296 base_url 297 )) 298 .bearer_auth(&jwt) 299 .send() 300 .await 301 .expect("Failed to request account deletion"); 302 assert_eq!(request_delete_res.status(), StatusCode::OK); 303 let pool = get_pool().await; 304 let row = sqlx::query!("SELECT token FROM account_deletion_requests WHERE did = $1", did) 305 .fetch_one(&pool) 306 .await 307 .expect("Failed to query deletion token"); 308 let token = row.token; 309 let delete_payload = json!({ 310 "did": did, 311 "password": app_password, 312 "token": token 313 }); 314 let delete_res = client 315 .post(format!( 316 "{}/xrpc/com.atproto.server.deleteAccount", 317 base_url 318 )) 319 .json(&delete_payload) 320 .send() 321 .await 322 .expect("Failed to delete account"); 323 assert_eq!(delete_res.status(), StatusCode::OK); 324 let user_row = sqlx::query!("SELECT id FROM users WHERE did = $1", did) 325 .fetch_optional(&pool) 326 .await 327 .expect("Failed to query user"); 328 assert!(user_row.is_none(), "User should be deleted from database"); 329} 330#[tokio::test] 331async fn test_delete_account_missing_fields() { 332 let client = client(); 333 let base_url = base_url().await; 334 let res1 = client 335 .post(format!( 336 "{}/xrpc/com.atproto.server.deleteAccount", 337 base_url 338 )) 339 .json(&json!({ 340 "password": "test", 341 "token": "test" 342 })) 343 .send() 344 .await 345 .expect("Failed to send request"); 346 assert_eq!(res1.status(), StatusCode::UNPROCESSABLE_ENTITY); 347 let res2 = client 348 .post(format!( 349 "{}/xrpc/com.atproto.server.deleteAccount", 350 base_url 351 )) 352 .json(&json!({ 353 "did": "did:web:test", 354 "token": "test" 355 })) 356 .send() 357 .await 358 .expect("Failed to send request"); 359 assert_eq!(res2.status(), StatusCode::UNPROCESSABLE_ENTITY); 360 let res3 = client 361 .post(format!( 362 "{}/xrpc/com.atproto.server.deleteAccount", 363 base_url 364 )) 365 .json(&json!({ 366 "did": "did:web:test", 367 "password": "test" 368 })) 369 .send() 370 .await 371 .expect("Failed to send request"); 372 assert_eq!(res3.status(), StatusCode::UNPROCESSABLE_ENTITY); 373} 374#[tokio::test] 375async fn test_delete_account_nonexistent_user() { 376 let client = client(); 377 let base_url = base_url().await; 378 let delete_payload = json!({ 379 "did": "did:web:nonexistent.user", 380 "password": "any-password", 381 "token": "any-token" 382 }); 383 let delete_res = client 384 .post(format!( 385 "{}/xrpc/com.atproto.server.deleteAccount", 386 base_url 387 )) 388 .json(&delete_payload) 389 .send() 390 .await 391 .expect("Failed to send delete request"); 392 assert_eq!(delete_res.status(), StatusCode::BAD_REQUEST); 393 let body: Value = delete_res.json().await.unwrap(); 394 assert_eq!(body["error"], "AccountNotFound"); 395}