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