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