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