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}