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}