this repo has no description
1mod common;
2mod helpers;
3use helpers::verify_new_account;
4use reqwest::StatusCode;
5use serde_json::{Value, json};
6
7#[tokio::test]
8async fn test_request_password_reset_creates_code() {
9 let client = common::client();
10 let base_url = common::base_url().await;
11 let pool = common::get_test_db_pool().await;
12 let handle = format!("pr{}", &uuid::Uuid::new_v4().simple().to_string()[..12]);
13 let email = format!("{}@example.com", handle);
14 let payload = json!({
15 "handle": handle,
16 "email": email,
17 "password": "Oldpass123!"
18 });
19 let res = client
20 .post(format!(
21 "{}/xrpc/com.atproto.server.createAccount",
22 base_url
23 ))
24 .json(&payload)
25 .send()
26 .await
27 .expect("Failed to create account");
28 assert_eq!(res.status(), StatusCode::OK);
29 let res = client
30 .post(format!(
31 "{}/xrpc/com.atproto.server.requestPasswordReset",
32 base_url
33 ))
34 .json(&json!({"email": email}))
35 .send()
36 .await
37 .expect("Failed to request password reset");
38 assert_eq!(res.status(), StatusCode::OK);
39 let user = sqlx::query!(
40 "SELECT password_reset_code, password_reset_code_expires_at FROM users WHERE email = $1",
41 email
42 )
43 .fetch_one(pool)
44 .await
45 .expect("User not found");
46 assert!(user.password_reset_code.is_some());
47 assert!(user.password_reset_code_expires_at.is_some());
48 let code = user.password_reset_code.unwrap();
49 assert!(code.contains('-'));
50 assert_eq!(code.len(), 11);
51}
52
53#[tokio::test]
54async fn test_request_password_reset_unknown_email_returns_ok() {
55 let client = common::client();
56 let base_url = common::base_url().await;
57 let res = client
58 .post(format!(
59 "{}/xrpc/com.atproto.server.requestPasswordReset",
60 base_url
61 ))
62 .json(&json!({"email": "nonexistent@example.com"}))
63 .send()
64 .await
65 .expect("Failed to request password reset");
66 assert_eq!(res.status(), StatusCode::OK);
67}
68
69#[tokio::test]
70async fn test_reset_password_with_valid_token() {
71 let client = common::client();
72 let base_url = common::base_url().await;
73 let pool = common::get_test_db_pool().await;
74 let handle = format!("pr2{}", &uuid::Uuid::new_v4().simple().to_string()[..12]);
75 let email = format!("{}@example.com", handle);
76 let old_password = "Oldpass123!";
77 let new_password = "Newpass456!";
78 let payload = json!({
79 "handle": handle,
80 "email": email,
81 "password": old_password
82 });
83 let res = client
84 .post(format!(
85 "{}/xrpc/com.atproto.server.createAccount",
86 base_url
87 ))
88 .json(&payload)
89 .send()
90 .await
91 .expect("Failed to create account");
92 assert_eq!(res.status(), StatusCode::OK);
93 let body: Value = res.json().await.unwrap();
94 let did = body["did"].as_str().unwrap();
95 let _ = verify_new_account(&client, did).await;
96 let res = client
97 .post(format!(
98 "{}/xrpc/com.atproto.server.requestPasswordReset",
99 base_url
100 ))
101 .json(&json!({"email": email}))
102 .send()
103 .await
104 .expect("Failed to request password reset");
105 assert_eq!(res.status(), StatusCode::OK);
106 let user = sqlx::query!(
107 "SELECT password_reset_code FROM users WHERE email = $1",
108 email
109 )
110 .fetch_one(pool)
111 .await
112 .expect("User not found");
113 let token = user.password_reset_code.expect("No reset code");
114 let res = client
115 .post(format!(
116 "{}/xrpc/com.atproto.server.resetPassword",
117 base_url
118 ))
119 .json(&json!({
120 "token": token,
121 "password": new_password
122 }))
123 .send()
124 .await
125 .expect("Failed to reset password");
126 assert_eq!(res.status(), StatusCode::OK);
127 let user = sqlx::query!(
128 "SELECT password_reset_code, password_reset_code_expires_at FROM users WHERE email = $1",
129 email
130 )
131 .fetch_one(pool)
132 .await
133 .expect("User not found");
134 assert!(user.password_reset_code.is_none());
135 assert!(user.password_reset_code_expires_at.is_none());
136 let res = client
137 .post(format!(
138 "{}/xrpc/com.atproto.server.createSession",
139 base_url
140 ))
141 .json(&json!({
142 "identifier": handle,
143 "password": new_password
144 }))
145 .send()
146 .await
147 .expect("Failed to login");
148 assert_eq!(res.status(), StatusCode::OK);
149 let res = client
150 .post(format!(
151 "{}/xrpc/com.atproto.server.createSession",
152 base_url
153 ))
154 .json(&json!({
155 "identifier": handle,
156 "password": old_password
157 }))
158 .send()
159 .await
160 .expect("Failed to login attempt");
161 assert_eq!(res.status(), StatusCode::UNAUTHORIZED);
162}
163
164#[tokio::test]
165async fn test_reset_password_with_invalid_token() {
166 let client = common::client();
167 let base_url = common::base_url().await;
168 let res = client
169 .post(format!(
170 "{}/xrpc/com.atproto.server.resetPassword",
171 base_url
172 ))
173 .json(&json!({
174 "token": "invalid-token",
175 "password": "Newpass123!"
176 }))
177 .send()
178 .await
179 .expect("Failed to reset password");
180 assert_eq!(res.status(), StatusCode::BAD_REQUEST);
181 let body: Value = res.json().await.expect("Invalid JSON");
182 assert_eq!(body["error"], "InvalidToken");
183}
184
185#[tokio::test]
186async fn test_reset_password_with_expired_token() {
187 let client = common::client();
188 let base_url = common::base_url().await;
189 let pool = common::get_test_db_pool().await;
190 let handle = format!("pr3{}", &uuid::Uuid::new_v4().simple().to_string()[..12]);
191 let email = format!("{}@example.com", handle);
192 let payload = json!({
193 "handle": handle,
194 "email": email,
195 "password": "Oldpass123!"
196 });
197 let res = client
198 .post(format!(
199 "{}/xrpc/com.atproto.server.createAccount",
200 base_url
201 ))
202 .json(&payload)
203 .send()
204 .await
205 .expect("Failed to create account");
206 assert_eq!(res.status(), StatusCode::OK);
207 let res = client
208 .post(format!(
209 "{}/xrpc/com.atproto.server.requestPasswordReset",
210 base_url
211 ))
212 .json(&json!({"email": email}))
213 .send()
214 .await
215 .expect("Failed to request password reset");
216 assert_eq!(res.status(), StatusCode::OK);
217 let user = sqlx::query!(
218 "SELECT password_reset_code FROM users WHERE email = $1",
219 email
220 )
221 .fetch_one(pool)
222 .await
223 .expect("User not found");
224 let token = user.password_reset_code.expect("No reset code");
225 sqlx::query!(
226 "UPDATE users SET password_reset_code_expires_at = NOW() - INTERVAL '1 hour' WHERE email = $1",
227 email
228 )
229 .execute(pool)
230 .await
231 .expect("Failed to expire token");
232 let res = client
233 .post(format!(
234 "{}/xrpc/com.atproto.server.resetPassword",
235 base_url
236 ))
237 .json(&json!({
238 "token": token,
239 "password": "Newpass123!"
240 }))
241 .send()
242 .await
243 .expect("Failed to reset password");
244 assert_eq!(res.status(), StatusCode::BAD_REQUEST);
245 let body: Value = res.json().await.expect("Invalid JSON");
246 assert_eq!(body["error"], "ExpiredToken");
247}
248
249#[tokio::test]
250async fn test_reset_password_invalidates_sessions() {
251 let client = common::client();
252 let base_url = common::base_url().await;
253 let pool = common::get_test_db_pool().await;
254 let handle = format!("pr4{}", &uuid::Uuid::new_v4().simple().to_string()[..12]);
255 let email = format!("{}@example.com", handle);
256 let payload = json!({
257 "handle": handle,
258 "email": email,
259 "password": "Oldpass123!"
260 });
261 let res = client
262 .post(format!(
263 "{}/xrpc/com.atproto.server.createAccount",
264 base_url
265 ))
266 .json(&payload)
267 .send()
268 .await
269 .expect("Failed to create account");
270 assert_eq!(res.status(), StatusCode::OK);
271 let body: Value = res.json().await.expect("Invalid JSON");
272 let did = body["did"].as_str().expect("No did");
273 let original_token = verify_new_account(&client, did).await;
274 let res = client
275 .get(format!("{}/xrpc/com.atproto.server.getSession", base_url))
276 .bearer_auth(&original_token)
277 .send()
278 .await
279 .expect("Failed to get session");
280 assert_eq!(res.status(), StatusCode::OK);
281 let res = client
282 .post(format!(
283 "{}/xrpc/com.atproto.server.requestPasswordReset",
284 base_url
285 ))
286 .json(&json!({"email": email}))
287 .send()
288 .await
289 .expect("Failed to request password reset");
290 assert_eq!(res.status(), StatusCode::OK);
291 let user = sqlx::query!(
292 "SELECT password_reset_code FROM users WHERE email = $1",
293 email
294 )
295 .fetch_one(pool)
296 .await
297 .expect("User not found");
298 let token = user.password_reset_code.expect("No reset code");
299 let res = client
300 .post(format!(
301 "{}/xrpc/com.atproto.server.resetPassword",
302 base_url
303 ))
304 .json(&json!({
305 "token": token,
306 "password": "Newpass123!"
307 }))
308 .send()
309 .await
310 .expect("Failed to reset password");
311 assert_eq!(res.status(), StatusCode::OK);
312 let res = client
313 .get(format!("{}/xrpc/com.atproto.server.getSession", base_url))
314 .bearer_auth(&original_token)
315 .send()
316 .await
317 .expect("Failed to get session");
318 assert_eq!(res.status(), StatusCode::UNAUTHORIZED);
319}
320
321#[tokio::test]
322async fn test_request_password_reset_empty_email() {
323 let client = common::client();
324 let base_url = common::base_url().await;
325 let res = client
326 .post(format!(
327 "{}/xrpc/com.atproto.server.requestPasswordReset",
328 base_url
329 ))
330 .json(&json!({"email": ""}))
331 .send()
332 .await
333 .expect("Failed to request password reset");
334 assert_eq!(res.status(), StatusCode::BAD_REQUEST);
335 let body: Value = res.json().await.expect("Invalid JSON");
336 assert_eq!(body["error"], "InvalidRequest");
337}
338
339#[tokio::test]
340async fn test_reset_password_creates_notification() {
341 let pool = common::get_test_db_pool().await;
342 let client = common::client();
343 let base_url = common::base_url().await;
344 let handle = format!("pr5{}", &uuid::Uuid::new_v4().simple().to_string()[..12]);
345 let email = format!("{}@example.com", handle);
346 let payload = json!({
347 "handle": handle,
348 "email": email,
349 "password": "Oldpass123!"
350 });
351 let res = client
352 .post(format!(
353 "{}/xrpc/com.atproto.server.createAccount",
354 base_url
355 ))
356 .json(&payload)
357 .send()
358 .await
359 .expect("Failed to create account");
360 assert_eq!(res.status(), StatusCode::OK);
361 let user = sqlx::query!("SELECT id FROM users WHERE email = $1", email)
362 .fetch_one(pool)
363 .await
364 .expect("User not found");
365 let initial_count: i64 = sqlx::query_scalar!(
366 "SELECT COUNT(*) FROM comms_queue WHERE user_id = $1 AND comms_type = 'password_reset'",
367 user.id
368 )
369 .fetch_one(pool)
370 .await
371 .expect("Failed to count")
372 .unwrap_or(0);
373 let res = client
374 .post(format!(
375 "{}/xrpc/com.atproto.server.requestPasswordReset",
376 base_url
377 ))
378 .json(&json!({"email": email}))
379 .send()
380 .await
381 .expect("Failed to request password reset");
382 assert_eq!(res.status(), StatusCode::OK);
383 let final_count: i64 = sqlx::query_scalar!(
384 "SELECT COUNT(*) FROM comms_queue WHERE user_id = $1 AND comms_type = 'password_reset'",
385 user.id
386 )
387 .fetch_one(pool)
388 .await
389 .expect("Failed to count")
390 .unwrap_or(0);
391 assert_eq!(final_count - initial_count, 1);
392}