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}