this repo has no description
1mod common; 2mod helpers; 3 4use common::*; 5 6use chrono::Utc; 7use reqwest::StatusCode; 8use serde_json::{Value, json}; 9 10#[tokio::test] 11async fn test_delete_account_full_flow() { 12 let client = client(); 13 let ts = Utc::now().timestamp_millis(); 14 let handle = format!("delete-test-{}.test", ts); 15 let email = format!("delete-test-{}@test.com", ts); 16 let password = "delete-password-123"; 17 18 let create_payload = json!({ 19 "handle": handle, 20 "email": email, 21 "password": password 22 }); 23 let create_res = client 24 .post(format!( 25 "{}/xrpc/com.atproto.server.createAccount", 26 base_url().await 27 )) 28 .json(&create_payload) 29 .send() 30 .await 31 .expect("Failed to create account"); 32 assert_eq!(create_res.status(), StatusCode::OK); 33 let create_body: Value = create_res.json().await.unwrap(); 34 let did = create_body["did"].as_str().unwrap().to_string(); 35 let jwt = create_body["accessJwt"].as_str().unwrap().to_string(); 36 37 let request_delete_res = client 38 .post(format!( 39 "{}/xrpc/com.atproto.server.requestAccountDelete", 40 base_url().await 41 )) 42 .bearer_auth(&jwt) 43 .send() 44 .await 45 .expect("Failed to request account deletion"); 46 assert_eq!(request_delete_res.status(), StatusCode::OK); 47 48 let db_url = get_db_connection_string().await; 49 let pool = sqlx::PgPool::connect(&db_url).await.expect("Failed to connect to test DB"); 50 51 let row = sqlx::query!("SELECT token FROM account_deletion_requests WHERE did = $1", did) 52 .fetch_one(&pool) 53 .await 54 .expect("Failed to query deletion token"); 55 let token = row.token; 56 57 let delete_payload = json!({ 58 "did": did, 59 "password": password, 60 "token": token 61 }); 62 let delete_res = client 63 .post(format!( 64 "{}/xrpc/com.atproto.server.deleteAccount", 65 base_url().await 66 )) 67 .json(&delete_payload) 68 .send() 69 .await 70 .expect("Failed to delete account"); 71 assert_eq!(delete_res.status(), StatusCode::OK); 72 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 79 let session_res = client 80 .get(format!( 81 "{}/xrpc/com.atproto.server.getSession", 82 base_url().await 83 )) 84 .bearer_auth(&jwt) 85 .send() 86 .await 87 .expect("Failed to check session"); 88 assert_eq!(session_res.status(), StatusCode::UNAUTHORIZED); 89} 90 91#[tokio::test] 92async fn test_delete_account_wrong_password() { 93 let client = client(); 94 let ts = Utc::now().timestamp_millis(); 95 let handle = format!("delete-wrongpw-{}.test", ts); 96 let email = format!("delete-wrongpw-{}@test.com", ts); 97 let password = "correct-password"; 98 99 let create_payload = json!({ 100 "handle": handle, 101 "email": email, 102 "password": password 103 }); 104 let create_res = client 105 .post(format!( 106 "{}/xrpc/com.atproto.server.createAccount", 107 base_url().await 108 )) 109 .json(&create_payload) 110 .send() 111 .await 112 .expect("Failed to create account"); 113 assert_eq!(create_res.status(), StatusCode::OK); 114 let create_body: Value = create_res.json().await.unwrap(); 115 let did = create_body["did"].as_str().unwrap().to_string(); 116 let jwt = create_body["accessJwt"].as_str().unwrap().to_string(); 117 118 let request_delete_res = client 119 .post(format!( 120 "{}/xrpc/com.atproto.server.requestAccountDelete", 121 base_url().await 122 )) 123 .bearer_auth(&jwt) 124 .send() 125 .await 126 .expect("Failed to request account deletion"); 127 assert_eq!(request_delete_res.status(), StatusCode::OK); 128 129 let db_url = get_db_connection_string().await; 130 let pool = sqlx::PgPool::connect(&db_url).await.expect("Failed to connect to test DB"); 131 132 let row = sqlx::query!("SELECT token FROM account_deletion_requests WHERE did = $1", did) 133 .fetch_one(&pool) 134 .await 135 .expect("Failed to query deletion token"); 136 let token = row.token; 137 138 let delete_payload = json!({ 139 "did": did, 140 "password": "wrong-password", 141 "token": token 142 }); 143 let delete_res = client 144 .post(format!( 145 "{}/xrpc/com.atproto.server.deleteAccount", 146 base_url().await 147 )) 148 .json(&delete_payload) 149 .send() 150 .await 151 .expect("Failed to send delete request"); 152 assert_eq!(delete_res.status(), StatusCode::UNAUTHORIZED); 153 154 let body: Value = delete_res.json().await.unwrap(); 155 assert_eq!(body["error"], "AuthenticationFailed"); 156} 157 158#[tokio::test] 159async fn test_delete_account_invalid_token() { 160 let client = client(); 161 let ts = Utc::now().timestamp_millis(); 162 let handle = format!("delete-badtoken-{}.test", ts); 163 let email = format!("delete-badtoken-{}@test.com", ts); 164 let password = "delete-password"; 165 166 let create_payload = json!({ 167 "handle": handle, 168 "email": email, 169 "password": password 170 }); 171 let create_res = client 172 .post(format!( 173 "{}/xrpc/com.atproto.server.createAccount", 174 base_url().await 175 )) 176 .json(&create_payload) 177 .send() 178 .await 179 .expect("Failed to create account"); 180 assert_eq!(create_res.status(), StatusCode::OK); 181 let create_body: Value = create_res.json().await.unwrap(); 182 let did = create_body["did"].as_str().unwrap().to_string(); 183 184 let delete_payload = json!({ 185 "did": did, 186 "password": password, 187 "token": "invalid-token-12345" 188 }); 189 let delete_res = client 190 .post(format!( 191 "{}/xrpc/com.atproto.server.deleteAccount", 192 base_url().await 193 )) 194 .json(&delete_payload) 195 .send() 196 .await 197 .expect("Failed to send delete request"); 198 assert_eq!(delete_res.status(), StatusCode::BAD_REQUEST); 199 200 let body: Value = delete_res.json().await.unwrap(); 201 assert_eq!(body["error"], "InvalidToken"); 202} 203 204#[tokio::test] 205async fn test_delete_account_expired_token() { 206 let client = client(); 207 let ts = Utc::now().timestamp_millis(); 208 let handle = format!("delete-expired-{}.test", ts); 209 let email = format!("delete-expired-{}@test.com", ts); 210 let password = "delete-password"; 211 212 let create_payload = json!({ 213 "handle": handle, 214 "email": email, 215 "password": password 216 }); 217 let create_res = client 218 .post(format!( 219 "{}/xrpc/com.atproto.server.createAccount", 220 base_url().await 221 )) 222 .json(&create_payload) 223 .send() 224 .await 225 .expect("Failed to create account"); 226 assert_eq!(create_res.status(), StatusCode::OK); 227 let create_body: Value = create_res.json().await.unwrap(); 228 let did = create_body["did"].as_str().unwrap().to_string(); 229 let jwt = create_body["accessJwt"].as_str().unwrap().to_string(); 230 231 let request_delete_res = client 232 .post(format!( 233 "{}/xrpc/com.atproto.server.requestAccountDelete", 234 base_url().await 235 )) 236 .bearer_auth(&jwt) 237 .send() 238 .await 239 .expect("Failed to request account deletion"); 240 assert_eq!(request_delete_res.status(), StatusCode::OK); 241 242 let db_url = get_db_connection_string().await; 243 let pool = sqlx::PgPool::connect(&db_url).await.expect("Failed to connect to test DB"); 244 245 let row = sqlx::query!("SELECT token FROM account_deletion_requests WHERE did = $1", did) 246 .fetch_one(&pool) 247 .await 248 .expect("Failed to query deletion token"); 249 let token = row.token; 250 251 sqlx::query!( 252 "UPDATE account_deletion_requests SET expires_at = NOW() - INTERVAL '1 hour' WHERE token = $1", 253 token 254 ) 255 .execute(&pool) 256 .await 257 .expect("Failed to expire token"); 258 259 let delete_payload = json!({ 260 "did": did, 261 "password": password, 262 "token": token 263 }); 264 let delete_res = client 265 .post(format!( 266 "{}/xrpc/com.atproto.server.deleteAccount", 267 base_url().await 268 )) 269 .json(&delete_payload) 270 .send() 271 .await 272 .expect("Failed to send delete request"); 273 assert_eq!(delete_res.status(), StatusCode::BAD_REQUEST); 274 275 let body: Value = delete_res.json().await.unwrap(); 276 assert_eq!(body["error"], "ExpiredToken"); 277} 278 279#[tokio::test] 280async fn test_delete_account_token_mismatch() { 281 let client = client(); 282 let ts = Utc::now().timestamp_millis(); 283 284 let handle1 = format!("delete-user1-{}.test", ts); 285 let email1 = format!("delete-user1-{}@test.com", ts); 286 let password1 = "user1-password"; 287 288 let create1_res = client 289 .post(format!( 290 "{}/xrpc/com.atproto.server.createAccount", 291 base_url().await 292 )) 293 .json(&json!({ 294 "handle": handle1, 295 "email": email1, 296 "password": password1 297 })) 298 .send() 299 .await 300 .expect("Failed to create account 1"); 301 assert_eq!(create1_res.status(), StatusCode::OK); 302 let create1_body: Value = create1_res.json().await.unwrap(); 303 let did1 = create1_body["did"].as_str().unwrap().to_string(); 304 let jwt1 = create1_body["accessJwt"].as_str().unwrap().to_string(); 305 306 let handle2 = format!("delete-user2-{}.test", ts); 307 let email2 = format!("delete-user2-{}@test.com", ts); 308 let password2 = "user2-password"; 309 310 let create2_res = client 311 .post(format!( 312 "{}/xrpc/com.atproto.server.createAccount", 313 base_url().await 314 )) 315 .json(&json!({ 316 "handle": handle2, 317 "email": email2, 318 "password": password2 319 })) 320 .send() 321 .await 322 .expect("Failed to create account 2"); 323 assert_eq!(create2_res.status(), StatusCode::OK); 324 let create2_body: Value = create2_res.json().await.unwrap(); 325 let did2 = create2_body["did"].as_str().unwrap().to_string(); 326 327 let request_delete_res = client 328 .post(format!( 329 "{}/xrpc/com.atproto.server.requestAccountDelete", 330 base_url().await 331 )) 332 .bearer_auth(&jwt1) 333 .send() 334 .await 335 .expect("Failed to request account deletion"); 336 assert_eq!(request_delete_res.status(), StatusCode::OK); 337 338 let db_url = get_db_connection_string().await; 339 let pool = sqlx::PgPool::connect(&db_url).await.expect("Failed to connect to test DB"); 340 341 let row = sqlx::query!("SELECT token FROM account_deletion_requests WHERE did = $1", did1) 342 .fetch_one(&pool) 343 .await 344 .expect("Failed to query deletion token"); 345 let token = row.token; 346 347 let delete_payload = json!({ 348 "did": did2, 349 "password": password2, 350 "token": token 351 }); 352 let delete_res = client 353 .post(format!( 354 "{}/xrpc/com.atproto.server.deleteAccount", 355 base_url().await 356 )) 357 .json(&delete_payload) 358 .send() 359 .await 360 .expect("Failed to send delete request"); 361 assert_eq!(delete_res.status(), StatusCode::BAD_REQUEST); 362 363 let body: Value = delete_res.json().await.unwrap(); 364 assert_eq!(body["error"], "InvalidToken"); 365} 366 367#[tokio::test] 368async fn test_delete_account_with_app_password() { 369 let client = client(); 370 let ts = Utc::now().timestamp_millis(); 371 let handle = format!("delete-apppw-{}.test", ts); 372 let email = format!("delete-apppw-{}@test.com", ts); 373 let main_password = "main-password-123"; 374 375 let create_payload = json!({ 376 "handle": handle, 377 "email": email, 378 "password": main_password 379 }); 380 let create_res = client 381 .post(format!( 382 "{}/xrpc/com.atproto.server.createAccount", 383 base_url().await 384 )) 385 .json(&create_payload) 386 .send() 387 .await 388 .expect("Failed to create account"); 389 assert_eq!(create_res.status(), StatusCode::OK); 390 let create_body: Value = create_res.json().await.unwrap(); 391 let did = create_body["did"].as_str().unwrap().to_string(); 392 let jwt = create_body["accessJwt"].as_str().unwrap().to_string(); 393 394 let app_password_res = client 395 .post(format!( 396 "{}/xrpc/com.atproto.server.createAppPassword", 397 base_url().await 398 )) 399 .bearer_auth(&jwt) 400 .json(&json!({ "name": "delete-test-app" })) 401 .send() 402 .await 403 .expect("Failed to create app password"); 404 assert_eq!(app_password_res.status(), StatusCode::OK); 405 let app_password_body: Value = app_password_res.json().await.unwrap(); 406 let app_password = app_password_body["password"].as_str().unwrap().to_string(); 407 408 let request_delete_res = client 409 .post(format!( 410 "{}/xrpc/com.atproto.server.requestAccountDelete", 411 base_url().await 412 )) 413 .bearer_auth(&jwt) 414 .send() 415 .await 416 .expect("Failed to request account deletion"); 417 assert_eq!(request_delete_res.status(), StatusCode::OK); 418 419 let db_url = get_db_connection_string().await; 420 let pool = sqlx::PgPool::connect(&db_url).await.expect("Failed to connect to test DB"); 421 422 let row = sqlx::query!("SELECT token FROM account_deletion_requests WHERE did = $1", did) 423 .fetch_one(&pool) 424 .await 425 .expect("Failed to query deletion token"); 426 let token = row.token; 427 428 let delete_payload = json!({ 429 "did": did, 430 "password": app_password, 431 "token": token 432 }); 433 let delete_res = client 434 .post(format!( 435 "{}/xrpc/com.atproto.server.deleteAccount", 436 base_url().await 437 )) 438 .json(&delete_payload) 439 .send() 440 .await 441 .expect("Failed to delete account"); 442 assert_eq!(delete_res.status(), StatusCode::OK); 443 444 let user_row = sqlx::query!("SELECT id FROM users WHERE did = $1", did) 445 .fetch_optional(&pool) 446 .await 447 .expect("Failed to query user"); 448 assert!(user_row.is_none(), "User should be deleted from database"); 449} 450 451#[tokio::test] 452async fn test_delete_account_missing_fields() { 453 let client = client(); 454 455 let res1 = client 456 .post(format!( 457 "{}/xrpc/com.atproto.server.deleteAccount", 458 base_url().await 459 )) 460 .json(&json!({ 461 "password": "test", 462 "token": "test" 463 })) 464 .send() 465 .await 466 .expect("Failed to send request"); 467 assert_eq!(res1.status(), StatusCode::UNPROCESSABLE_ENTITY); 468 469 let res2 = client 470 .post(format!( 471 "{}/xrpc/com.atproto.server.deleteAccount", 472 base_url().await 473 )) 474 .json(&json!({ 475 "did": "did:web:test", 476 "token": "test" 477 })) 478 .send() 479 .await 480 .expect("Failed to send request"); 481 assert_eq!(res2.status(), StatusCode::UNPROCESSABLE_ENTITY); 482 483 let res3 = client 484 .post(format!( 485 "{}/xrpc/com.atproto.server.deleteAccount", 486 base_url().await 487 )) 488 .json(&json!({ 489 "did": "did:web:test", 490 "password": "test" 491 })) 492 .send() 493 .await 494 .expect("Failed to send request"); 495 assert_eq!(res3.status(), StatusCode::UNPROCESSABLE_ENTITY); 496} 497 498#[tokio::test] 499async fn test_delete_account_nonexistent_user() { 500 let client = client(); 501 502 let delete_payload = json!({ 503 "did": "did:web:nonexistent.user", 504 "password": "any-password", 505 "token": "any-token" 506 }); 507 let delete_res = client 508 .post(format!( 509 "{}/xrpc/com.atproto.server.deleteAccount", 510 base_url().await 511 )) 512 .json(&delete_payload) 513 .send() 514 .await 515 .expect("Failed to send delete request"); 516 assert_eq!(delete_res.status(), StatusCode::BAD_REQUEST); 517 518 let body: Value = delete_res.json().await.unwrap(); 519 assert_eq!(body["error"], "AccountNotFound"); 520}