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