this repo has no description
1mod common;
2
3use reqwest::StatusCode;
4use serde_json::{json, Value};
5use sqlx::PgPool;
6
7async fn get_pool() -> PgPool {
8 let conn_str = common::get_db_connection_string().await;
9 sqlx::postgres::PgPoolOptions::new()
10 .max_connections(5)
11 .connect(&conn_str)
12 .await
13 .expect("Failed to connect to test database")
14}
15
16#[tokio::test]
17async fn test_email_update_flow_success() {
18 let client = common::client();
19 let base_url = common::base_url().await;
20 let pool = get_pool().await;
21
22 let handle = format!("emailup_{}", uuid::Uuid::new_v4());
23 let email = format!("{}@example.com", handle);
24 let payload = json!({
25 "handle": handle,
26 "email": email,
27 "password": "password"
28 });
29
30 let res = client
31 .post(format!("{}/xrpc/com.atproto.server.createAccount", base_url))
32 .json(&payload)
33 .send()
34 .await
35 .expect("Failed to create account");
36 assert_eq!(res.status(), StatusCode::OK);
37 let body: Value = res.json().await.expect("Invalid JSON");
38 let access_jwt = body["accessJwt"].as_str().expect("No accessJwt");
39
40 let new_email = format!("new_{}@example.com", handle);
41 let res = client
42 .post(format!("{}/xrpc/com.atproto.server.requestEmailUpdate", base_url))
43 .bearer_auth(access_jwt)
44 .json(&json!({"email": new_email}))
45 .send()
46 .await
47 .expect("Failed to request email update");
48 assert_eq!(res.status(), StatusCode::OK);
49 let body: Value = res.json().await.expect("Invalid JSON");
50 assert_eq!(body["tokenRequired"], true);
51
52 let user = sqlx::query!(
53 "SELECT email_pending_verification, email_confirmation_code, email FROM users WHERE handle = $1",
54 handle
55 )
56 .fetch_one(&pool)
57 .await
58 .expect("User not found");
59
60 assert_eq!(user.email_pending_verification.as_deref(), Some(new_email.as_str()));
61 assert!(user.email_confirmation_code.is_some());
62 let code = user.email_confirmation_code.unwrap();
63
64 let res = client
65 .post(format!("{}/xrpc/com.atproto.server.confirmEmail", base_url))
66 .bearer_auth(access_jwt)
67 .json(&json!({
68 "email": new_email,
69 "token": code
70 }))
71 .send()
72 .await
73 .expect("Failed to confirm email");
74 assert_eq!(res.status(), StatusCode::OK);
75
76 let user = sqlx::query!(
77 "SELECT email, email_pending_verification, email_confirmation_code FROM users WHERE handle = $1",
78 handle
79 )
80 .fetch_one(&pool)
81 .await
82 .expect("User not found");
83
84 assert_eq!(user.email, new_email);
85 assert!(user.email_pending_verification.is_none());
86 assert!(user.email_confirmation_code.is_none());
87}
88
89#[tokio::test]
90async fn test_request_email_update_taken_email() {
91 let client = common::client();
92 let base_url = common::base_url().await;
93
94 let handle1 = format!("emailup_taken1_{}", uuid::Uuid::new_v4());
95 let email1 = format!("{}@example.com", handle1);
96 let res = client
97 .post(format!("{}/xrpc/com.atproto.server.createAccount", base_url))
98 .json(&json!({
99 "handle": handle1,
100 "email": email1,
101 "password": "password"
102 }))
103 .send()
104 .await
105 .expect("Failed to create account 1");
106 assert_eq!(res.status(), StatusCode::OK);
107
108 let handle2 = format!("emailup_taken2_{}", uuid::Uuid::new_v4());
109 let email2 = format!("{}@example.com", handle2);
110 let res = client
111 .post(format!("{}/xrpc/com.atproto.server.createAccount", base_url))
112 .json(&json!({
113 "handle": handle2,
114 "email": email2,
115 "password": "password"
116 }))
117 .send()
118 .await
119 .expect("Failed to create account 2");
120 assert_eq!(res.status(), StatusCode::OK);
121 let body: Value = res.json().await.expect("Invalid JSON");
122 let access_jwt2 = body["accessJwt"].as_str().expect("No accessJwt");
123
124 let res = client
125 .post(format!("{}/xrpc/com.atproto.server.requestEmailUpdate", base_url))
126 .bearer_auth(access_jwt2)
127 .json(&json!({"email": email1}))
128 .send()
129 .await
130 .expect("Failed to request email update");
131
132 assert_eq!(res.status(), StatusCode::BAD_REQUEST);
133 let body: Value = res.json().await.expect("Invalid JSON");
134 assert_eq!(body["error"], "EmailTaken");
135}
136
137#[tokio::test]
138async fn test_confirm_email_invalid_token() {
139 let client = common::client();
140 let base_url = common::base_url().await;
141
142 let handle = format!("emailup_inv_{}", uuid::Uuid::new_v4());
143 let email = format!("{}@example.com", handle);
144 let res = client
145 .post(format!("{}/xrpc/com.atproto.server.createAccount", base_url))
146 .json(&json!({
147 "handle": handle,
148 "email": email,
149 "password": "password"
150 }))
151 .send()
152 .await
153 .expect("Failed to create account");
154 assert_eq!(res.status(), StatusCode::OK);
155 let body: Value = res.json().await.expect("Invalid JSON");
156 let access_jwt = body["accessJwt"].as_str().expect("No accessJwt");
157
158 let new_email = format!("new_{}@example.com", handle);
159 let res = client
160 .post(format!("{}/xrpc/com.atproto.server.requestEmailUpdate", base_url))
161 .bearer_auth(access_jwt)
162 .json(&json!({"email": new_email}))
163 .send()
164 .await
165 .expect("Failed to request email update");
166 assert_eq!(res.status(), StatusCode::OK);
167
168 let res = client
169 .post(format!("{}/xrpc/com.atproto.server.confirmEmail", base_url))
170 .bearer_auth(access_jwt)
171 .json(&json!({
172 "email": new_email,
173 "token": "wrong-token"
174 }))
175 .send()
176 .await
177 .expect("Failed to confirm email");
178
179 assert_eq!(res.status(), StatusCode::BAD_REQUEST);
180 let body: Value = res.json().await.expect("Invalid JSON");
181 assert_eq!(body["error"], "InvalidToken");
182}
183
184#[tokio::test]
185async fn test_confirm_email_wrong_email() {
186 let client = common::client();
187 let base_url = common::base_url().await;
188 let pool = get_pool().await;
189
190 let handle = format!("emailup_wrong_{}", uuid::Uuid::new_v4());
191 let email = format!("{}@example.com", handle);
192 let res = client
193 .post(format!("{}/xrpc/com.atproto.server.createAccount", base_url))
194 .json(&json!({
195 "handle": handle,
196 "email": email,
197 "password": "password"
198 }))
199 .send()
200 .await
201 .expect("Failed to create account");
202 assert_eq!(res.status(), StatusCode::OK);
203 let body: Value = res.json().await.expect("Invalid JSON");
204 let access_jwt = body["accessJwt"].as_str().expect("No accessJwt");
205
206 let new_email = format!("new_{}@example.com", handle);
207 let res = client
208 .post(format!("{}/xrpc/com.atproto.server.requestEmailUpdate", base_url))
209 .bearer_auth(access_jwt)
210 .json(&json!({"email": new_email}))
211 .send()
212 .await
213 .expect("Failed to request email update");
214 assert_eq!(res.status(), StatusCode::OK);
215
216 let user = sqlx::query!("SELECT email_confirmation_code FROM users WHERE handle = $1", handle)
217 .fetch_one(&pool)
218 .await
219 .expect("User not found");
220 let code = user.email_confirmation_code.unwrap();
221
222 let res = client
223 .post(format!("{}/xrpc/com.atproto.server.confirmEmail", base_url))
224 .bearer_auth(access_jwt)
225 .json(&json!({
226 "email": "another_random@example.com",
227 "token": code
228 }))
229 .send()
230 .await
231 .expect("Failed to confirm email");
232
233 assert_eq!(res.status(), StatusCode::BAD_REQUEST);
234 let body: Value = res.json().await.expect("Invalid JSON");
235 assert_eq!(body["message"], "Email does not match pending update");
236}
237
238#[tokio::test]
239async fn test_update_email_success_no_token_required() {
240 let client = common::client();
241 let base_url = common::base_url().await;
242 let pool = get_pool().await;
243
244 let handle = format!("emailup_direct_{}", uuid::Uuid::new_v4());
245 let email = format!("{}@example.com", handle);
246 let res = client
247 .post(format!("{}/xrpc/com.atproto.server.createAccount", base_url))
248 .json(&json!({
249 "handle": handle,
250 "email": email,
251 "password": "password"
252 }))
253 .send()
254 .await
255 .expect("Failed to create account");
256 assert_eq!(res.status(), StatusCode::OK);
257 let body: Value = res.json().await.expect("Invalid JSON");
258 let access_jwt = body["accessJwt"].as_str().expect("No accessJwt");
259
260 let new_email = format!("direct_{}@example.com", handle);
261 let res = client
262 .post(format!("{}/xrpc/com.atproto.server.updateEmail", base_url))
263 .bearer_auth(access_jwt)
264 .json(&json!({ "email": new_email }))
265 .send()
266 .await
267 .expect("Failed to update email");
268
269 assert_eq!(res.status(), StatusCode::OK);
270
271 let user = sqlx::query!("SELECT email FROM users WHERE handle = $1", handle)
272 .fetch_one(&pool)
273 .await
274 .expect("User not found");
275 assert_eq!(user.email, new_email);
276}
277
278#[tokio::test]
279async fn test_update_email_same_email_noop() {
280 let client = common::client();
281 let base_url = common::base_url().await;
282
283 let handle = format!("emailup_same_{}", uuid::Uuid::new_v4());
284 let email = format!("{}@example.com", handle);
285 let res = client
286 .post(format!("{}/xrpc/com.atproto.server.createAccount", base_url))
287 .json(&json!({
288 "handle": handle,
289 "email": email,
290 "password": "password"
291 }))
292 .send()
293 .await
294 .expect("Failed to create account");
295 assert_eq!(res.status(), StatusCode::OK);
296 let body: Value = res.json().await.expect("Invalid JSON");
297 let access_jwt = body["accessJwt"].as_str().expect("No accessJwt");
298
299 let res = client
300 .post(format!("{}/xrpc/com.atproto.server.updateEmail", base_url))
301 .bearer_auth(access_jwt)
302 .json(&json!({ "email": email }))
303 .send()
304 .await
305 .expect("Failed to update email");
306
307 assert_eq!(res.status(), StatusCode::OK, "Updating to same email should succeed as no-op");
308}
309
310#[tokio::test]
311async fn test_update_email_requires_token_after_pending() {
312 let client = common::client();
313 let base_url = common::base_url().await;
314
315 let handle = format!("emailup_token_{}", uuid::Uuid::new_v4());
316 let email = format!("{}@example.com", handle);
317 let res = client
318 .post(format!("{}/xrpc/com.atproto.server.createAccount", base_url))
319 .json(&json!({
320 "handle": handle,
321 "email": email,
322 "password": "password"
323 }))
324 .send()
325 .await
326 .expect("Failed to create account");
327 assert_eq!(res.status(), StatusCode::OK);
328 let body: Value = res.json().await.expect("Invalid JSON");
329 let access_jwt = body["accessJwt"].as_str().expect("No accessJwt");
330
331 let new_email = format!("pending_{}@example.com", handle);
332 let res = client
333 .post(format!("{}/xrpc/com.atproto.server.requestEmailUpdate", base_url))
334 .bearer_auth(access_jwt)
335 .json(&json!({"email": new_email}))
336 .send()
337 .await
338 .expect("Failed to request email update");
339 assert_eq!(res.status(), StatusCode::OK);
340
341 let res = client
342 .post(format!("{}/xrpc/com.atproto.server.updateEmail", base_url))
343 .bearer_auth(access_jwt)
344 .json(&json!({ "email": new_email }))
345 .send()
346 .await
347 .expect("Failed to attempt email update");
348
349 assert_eq!(res.status(), StatusCode::BAD_REQUEST);
350 let body: Value = res.json().await.expect("Invalid JSON");
351 assert_eq!(body["error"], "TokenRequired");
352}
353
354#[tokio::test]
355async fn test_update_email_with_valid_token() {
356 let client = common::client();
357 let base_url = common::base_url().await;
358 let pool = get_pool().await;
359
360 let handle = format!("emailup_valid_{}", uuid::Uuid::new_v4());
361 let email = format!("{}@example.com", handle);
362 let res = client
363 .post(format!("{}/xrpc/com.atproto.server.createAccount", base_url))
364 .json(&json!({
365 "handle": handle,
366 "email": email,
367 "password": "password"
368 }))
369 .send()
370 .await
371 .expect("Failed to create account");
372 assert_eq!(res.status(), StatusCode::OK);
373 let body: Value = res.json().await.expect("Invalid JSON");
374 let access_jwt = body["accessJwt"].as_str().expect("No accessJwt");
375
376 let new_email = format!("valid_{}@example.com", handle);
377 let res = client
378 .post(format!("{}/xrpc/com.atproto.server.requestEmailUpdate", base_url))
379 .bearer_auth(access_jwt)
380 .json(&json!({"email": new_email}))
381 .send()
382 .await
383 .expect("Failed to request email update");
384 assert_eq!(res.status(), StatusCode::OK);
385
386 let user = sqlx::query!(
387 "SELECT email_confirmation_code FROM users WHERE handle = $1",
388 handle
389 )
390 .fetch_one(&pool)
391 .await
392 .expect("User not found");
393 let code = user.email_confirmation_code.unwrap();
394
395 let res = client
396 .post(format!("{}/xrpc/com.atproto.server.updateEmail", base_url))
397 .bearer_auth(access_jwt)
398 .json(&json!({
399 "email": new_email,
400 "token": code
401 }))
402 .send()
403 .await
404 .expect("Failed to update email");
405
406 assert_eq!(res.status(), StatusCode::OK);
407
408 let user = sqlx::query!("SELECT email, email_pending_verification FROM users WHERE handle = $1", handle)
409 .fetch_one(&pool)
410 .await
411 .expect("User not found");
412 assert_eq!(user.email, new_email);
413 assert!(user.email_pending_verification.is_none());
414}
415
416#[tokio::test]
417async fn test_update_email_invalid_token() {
418 let client = common::client();
419 let base_url = common::base_url().await;
420
421 let handle = format!("emailup_badtok_{}", uuid::Uuid::new_v4());
422 let email = format!("{}@example.com", handle);
423 let res = client
424 .post(format!("{}/xrpc/com.atproto.server.createAccount", base_url))
425 .json(&json!({
426 "handle": handle,
427 "email": email,
428 "password": "password"
429 }))
430 .send()
431 .await
432 .expect("Failed to create account");
433 assert_eq!(res.status(), StatusCode::OK);
434 let body: Value = res.json().await.expect("Invalid JSON");
435 let access_jwt = body["accessJwt"].as_str().expect("No accessJwt");
436
437 let new_email = format!("badtok_{}@example.com", handle);
438 let res = client
439 .post(format!("{}/xrpc/com.atproto.server.requestEmailUpdate", base_url))
440 .bearer_auth(access_jwt)
441 .json(&json!({"email": new_email}))
442 .send()
443 .await
444 .expect("Failed to request email update");
445 assert_eq!(res.status(), StatusCode::OK);
446
447 let res = client
448 .post(format!("{}/xrpc/com.atproto.server.updateEmail", base_url))
449 .bearer_auth(access_jwt)
450 .json(&json!({
451 "email": new_email,
452 "token": "wrong-token-12345"
453 }))
454 .send()
455 .await
456 .expect("Failed to attempt email update");
457
458 assert_eq!(res.status(), StatusCode::BAD_REQUEST);
459 let body: Value = res.json().await.expect("Invalid JSON");
460 assert_eq!(body["error"], "InvalidToken");
461}
462
463#[tokio::test]
464async fn test_update_email_already_taken() {
465 let client = common::client();
466 let base_url = common::base_url().await;
467
468 let handle1 = format!("emailup_dup1_{}", uuid::Uuid::new_v4());
469 let email1 = format!("{}@example.com", handle1);
470 let res = client
471 .post(format!("{}/xrpc/com.atproto.server.createAccount", base_url))
472 .json(&json!({
473 "handle": handle1,
474 "email": email1,
475 "password": "password"
476 }))
477 .send()
478 .await
479 .expect("Failed to create account 1");
480 assert_eq!(res.status(), StatusCode::OK);
481
482 let handle2 = format!("emailup_dup2_{}", uuid::Uuid::new_v4());
483 let email2 = format!("{}@example.com", handle2);
484 let res = client
485 .post(format!("{}/xrpc/com.atproto.server.createAccount", base_url))
486 .json(&json!({
487 "handle": handle2,
488 "email": email2,
489 "password": "password"
490 }))
491 .send()
492 .await
493 .expect("Failed to create account 2");
494 assert_eq!(res.status(), StatusCode::OK);
495 let body: Value = res.json().await.expect("Invalid JSON");
496 let access_jwt2 = body["accessJwt"].as_str().expect("No accessJwt");
497
498 let res = client
499 .post(format!("{}/xrpc/com.atproto.server.updateEmail", base_url))
500 .bearer_auth(access_jwt2)
501 .json(&json!({ "email": email1 }))
502 .send()
503 .await
504 .expect("Failed to attempt email update");
505
506 assert_eq!(res.status(), StatusCode::BAD_REQUEST);
507 let body: Value = res.json().await.expect("Invalid JSON");
508 assert!(body["message"].as_str().unwrap().contains("already in use") || body["error"] == "InvalidRequest");
509}
510
511#[tokio::test]
512async fn test_update_email_no_auth() {
513 let client = common::client();
514 let base_url = common::base_url().await;
515
516 let res = client
517 .post(format!("{}/xrpc/com.atproto.server.updateEmail", base_url))
518 .json(&json!({ "email": "test@example.com" }))
519 .send()
520 .await
521 .expect("Failed to send request");
522
523 assert_eq!(res.status(), StatusCode::UNAUTHORIZED);
524 let body: Value = res.json().await.expect("Invalid JSON");
525 assert_eq!(body["error"], "AuthenticationRequired");
526}
527
528#[tokio::test]
529async fn test_update_email_invalid_format() {
530 let client = common::client();
531 let base_url = common::base_url().await;
532
533 let handle = format!("emailup_fmt_{}", uuid::Uuid::new_v4());
534 let email = format!("{}@example.com", handle);
535 let res = client
536 .post(format!("{}/xrpc/com.atproto.server.createAccount", base_url))
537 .json(&json!({
538 "handle": handle,
539 "email": email,
540 "password": "password"
541 }))
542 .send()
543 .await
544 .expect("Failed to create account");
545 assert_eq!(res.status(), StatusCode::OK);
546 let body: Value = res.json().await.expect("Invalid JSON");
547 let access_jwt = body["accessJwt"].as_str().expect("No accessJwt");
548
549 let res = client
550 .post(format!("{}/xrpc/com.atproto.server.updateEmail", base_url))
551 .bearer_auth(access_jwt)
552 .json(&json!({ "email": "not-an-email" }))
553 .send()
554 .await
555 .expect("Failed to send request");
556
557 assert_eq!(res.status(), StatusCode::BAD_REQUEST);
558 let body: Value = res.json().await.expect("Invalid JSON");
559 assert_eq!(body["error"], "InvalidRequest");
560}