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