this repo has no description
1mod common;
2use reqwest::StatusCode;
3use serde_json::{Value, json};
4use sqlx::PgPool;
5
6async fn get_pool() -> PgPool {
7 let conn_str = common::get_db_connection_string().await;
8 sqlx::postgres::PgPoolOptions::new()
9 .max_connections(5)
10 .connect(&conn_str)
11 .await
12 .expect("Failed to connect to test database")
13}
14
15async fn get_email_update_token(pool: &PgPool, did: &str) -> String {
16 let body_text: String = sqlx::query_scalar!(
17 "SELECT body FROM comms_queue WHERE user_id = (SELECT id FROM users WHERE did = $1) AND comms_type = 'email_update' ORDER BY created_at DESC LIMIT 1",
18 did
19 )
20 .fetch_one(pool)
21 .await
22 .expect("Verification not found");
23
24 body_text
25 .lines()
26 .skip_while(|line| !line.contains("verification code"))
27 .nth(1)
28 .map(|line| line.trim().to_string())
29 .filter(|line| !line.is_empty() && line.contains('-'))
30 .unwrap_or_else(|| {
31 body_text
32 .lines()
33 .find(|line| line.trim().starts_with("MX") && line.contains('-'))
34 .map(|s| s.trim().to_string())
35 .unwrap_or_default()
36 })
37}
38
39async fn create_verified_account(
40 client: &reqwest::Client,
41 base_url: &str,
42 handle: &str,
43 email: &str,
44) -> (String, String) {
45 let res = client
46 .post(format!(
47 "{}/xrpc/com.atproto.server.createAccount",
48 base_url
49 ))
50 .json(&json!({
51 "handle": handle,
52 "email": email,
53 "password": "Testpass123!"
54 }))
55 .send()
56 .await
57 .expect("Failed to create account");
58 assert_eq!(res.status(), StatusCode::OK);
59 let body: Value = res.json().await.expect("Invalid JSON");
60 let did = body["did"].as_str().expect("No did").to_string();
61 let jwt = common::verify_new_account(client, &did).await;
62 (jwt, did)
63}
64
65#[tokio::test]
66async fn test_email_update_flow_success() {
67 let client = common::client();
68 let base_url = common::base_url().await;
69 let pool = get_pool().await;
70 let handle = format!("emailup-{}", uuid::Uuid::new_v4());
71 let email = format!("{}@example.com", handle);
72 let (access_jwt, did) = create_verified_account(&client, &base_url, &handle, &email).await;
73 let new_email = format!("new_{}@example.com", handle);
74 let res = client
75 .post(format!(
76 "{}/xrpc/com.atproto.server.requestEmailUpdate",
77 base_url
78 ))
79 .bearer_auth(&access_jwt)
80 .json(&json!({"email": new_email}))
81 .send()
82 .await
83 .expect("Failed to request email update");
84 assert_eq!(res.status(), StatusCode::OK);
85 let body: Value = res.json().await.expect("Invalid JSON");
86 assert_eq!(body["tokenRequired"], true);
87
88 let code = get_email_update_token(&pool, &did).await;
89 let res = client
90 .post(format!("{}/xrpc/com.atproto.server.confirmEmail", base_url))
91 .bearer_auth(&access_jwt)
92 .json(&json!({
93 "email": new_email,
94 "token": code
95 }))
96 .send()
97 .await
98 .expect("Failed to confirm email");
99 assert_eq!(res.status(), StatusCode::OK);
100 let user = sqlx::query!("SELECT email FROM users WHERE did = $1", did)
101 .fetch_one(&pool)
102 .await
103 .expect("User not found");
104 assert_eq!(user.email, Some(new_email));
105}
106
107#[tokio::test]
108async fn test_request_email_update_taken_email() {
109 let client = common::client();
110 let base_url = common::base_url().await;
111 let handle1 = format!("emailup-taken1-{}", uuid::Uuid::new_v4());
112 let email1 = format!("{}@example.com", handle1);
113 let (_, _) = create_verified_account(&client, &base_url, &handle1, &email1).await;
114 let handle2 = format!("emailup-taken2-{}", uuid::Uuid::new_v4());
115 let email2 = format!("{}@example.com", handle2);
116 let (access_jwt2, _) = create_verified_account(&client, &base_url, &handle2, &email2).await;
117 let res = client
118 .post(format!(
119 "{}/xrpc/com.atproto.server.requestEmailUpdate",
120 base_url
121 ))
122 .bearer_auth(&access_jwt2)
123 .json(&json!({"email": email1}))
124 .send()
125 .await
126 .expect("Failed to request email update");
127 assert_eq!(res.status(), StatusCode::BAD_REQUEST);
128 let body: Value = res.json().await.expect("Invalid JSON");
129 assert_eq!(body["error"], "EmailTaken");
130}
131
132#[tokio::test]
133async fn test_confirm_email_invalid_token() {
134 let client = common::client();
135 let base_url = common::base_url().await;
136 let handle = format!("emailup-inv-{}", uuid::Uuid::new_v4());
137 let email = format!("{}@example.com", handle);
138 let (access_jwt, _) = create_verified_account(&client, &base_url, &handle, &email).await;
139 let new_email = format!("new_{}@example.com", handle);
140 let res = client
141 .post(format!(
142 "{}/xrpc/com.atproto.server.requestEmailUpdate",
143 base_url
144 ))
145 .bearer_auth(&access_jwt)
146 .json(&json!({"email": new_email}))
147 .send()
148 .await
149 .expect("Failed to request email update");
150 assert_eq!(res.status(), StatusCode::OK);
151 let res = client
152 .post(format!("{}/xrpc/com.atproto.server.confirmEmail", base_url))
153 .bearer_auth(&access_jwt)
154 .json(&json!({
155 "email": new_email,
156 "token": "wrong-token"
157 }))
158 .send()
159 .await
160 .expect("Failed to confirm email");
161 assert_eq!(res.status(), StatusCode::BAD_REQUEST);
162 let body: Value = res.json().await.expect("Invalid JSON");
163 assert_eq!(body["error"], "InvalidToken");
164}
165
166#[tokio::test]
167async fn test_confirm_email_wrong_email() {
168 let client = common::client();
169 let base_url = common::base_url().await;
170 let pool = get_pool().await;
171 let handle = format!("emailup-wrong-{}", uuid::Uuid::new_v4());
172 let email = format!("{}@example.com", handle);
173 let (access_jwt, did) = create_verified_account(&client, &base_url, &handle, &email).await;
174 let new_email = format!("new_{}@example.com", handle);
175 let res = client
176 .post(format!(
177 "{}/xrpc/com.atproto.server.requestEmailUpdate",
178 base_url
179 ))
180 .bearer_auth(&access_jwt)
181 .json(&json!({"email": new_email}))
182 .send()
183 .await
184 .expect("Failed to request email update");
185 assert_eq!(res.status(), StatusCode::OK);
186 let code = get_email_update_token(&pool, &did).await;
187 let res = client
188 .post(format!("{}/xrpc/com.atproto.server.confirmEmail", base_url))
189 .bearer_auth(&access_jwt)
190 .json(&json!({
191 "email": "another_random@example.com",
192 "token": code
193 }))
194 .send()
195 .await
196 .expect("Failed to confirm email");
197 assert_eq!(res.status(), StatusCode::BAD_REQUEST);
198 let body: Value = res.json().await.expect("Invalid JSON");
199 assert!(
200 body["message"].as_str().unwrap().contains("mismatch") || body["error"] == "InvalidToken"
201 );
202}
203
204#[tokio::test]
205async fn test_update_email_requires_token() {
206 let client = common::client();
207 let base_url = common::base_url().await;
208 let handle = format!("emailup-direct-{}", uuid::Uuid::new_v4());
209 let email = format!("{}@example.com", handle);
210 let (access_jwt, _) = create_verified_account(&client, &base_url, &handle, &email).await;
211 let new_email = format!("direct_{}@example.com", handle);
212 let res = client
213 .post(format!("{}/xrpc/com.atproto.server.updateEmail", base_url))
214 .bearer_auth(&access_jwt)
215 .json(&json!({ "email": new_email }))
216 .send()
217 .await
218 .expect("Failed to update email");
219 assert_eq!(res.status(), StatusCode::BAD_REQUEST);
220 let body: Value = res.json().await.expect("Invalid JSON");
221 assert_eq!(body["error"], "TokenRequired");
222}
223
224#[tokio::test]
225async fn test_update_email_same_email_noop() {
226 let client = common::client();
227 let base_url = common::base_url().await;
228 let handle = format!("emailup-same-{}", uuid::Uuid::new_v4());
229 let email = format!("{}@example.com", handle);
230 let (access_jwt, _) = create_verified_account(&client, &base_url, &handle, &email).await;
231 let res = client
232 .post(format!("{}/xrpc/com.atproto.server.updateEmail", base_url))
233 .bearer_auth(&access_jwt)
234 .json(&json!({ "email": email }))
235 .send()
236 .await
237 .expect("Failed to update email");
238 assert_eq!(
239 res.status(),
240 StatusCode::OK,
241 "Updating to same email should succeed as no-op"
242 );
243}
244
245#[tokio::test]
246async fn test_update_email_requires_token_after_pending() {
247 let client = common::client();
248 let base_url = common::base_url().await;
249 let handle = format!("emailup-token-{}", uuid::Uuid::new_v4());
250 let email = format!("{}@example.com", handle);
251 let (access_jwt, _) = create_verified_account(&client, &base_url, &handle, &email).await;
252 let new_email = format!("pending_{}@example.com", handle);
253 let res = client
254 .post(format!(
255 "{}/xrpc/com.atproto.server.requestEmailUpdate",
256 base_url
257 ))
258 .bearer_auth(&access_jwt)
259 .json(&json!({"email": new_email}))
260 .send()
261 .await
262 .expect("Failed to request email update");
263 assert_eq!(res.status(), StatusCode::OK);
264 let res = client
265 .post(format!("{}/xrpc/com.atproto.server.updateEmail", base_url))
266 .bearer_auth(&access_jwt)
267 .json(&json!({ "email": new_email }))
268 .send()
269 .await
270 .expect("Failed to attempt email update");
271 assert_eq!(res.status(), StatusCode::BAD_REQUEST);
272 let body: Value = res.json().await.expect("Invalid JSON");
273 assert_eq!(body["error"], "TokenRequired");
274}
275
276#[tokio::test]
277async fn test_update_email_with_valid_token() {
278 let client = common::client();
279 let base_url = common::base_url().await;
280 let pool = get_pool().await;
281 let handle = format!("emailup-valid-{}", uuid::Uuid::new_v4());
282 let email = format!("{}@example.com", handle);
283 let (access_jwt, did) = create_verified_account(&client, &base_url, &handle, &email).await;
284 let new_email = format!("valid_{}@example.com", handle);
285 let res = client
286 .post(format!(
287 "{}/xrpc/com.atproto.server.requestEmailUpdate",
288 base_url
289 ))
290 .bearer_auth(&access_jwt)
291 .json(&json!({"email": new_email}))
292 .send()
293 .await
294 .expect("Failed to request email update");
295 assert_eq!(res.status(), StatusCode::OK);
296 let code = get_email_update_token(&pool, &did).await;
297 let res = client
298 .post(format!("{}/xrpc/com.atproto.server.updateEmail", base_url))
299 .bearer_auth(&access_jwt)
300 .json(&json!({
301 "email": new_email,
302 "token": code
303 }))
304 .send()
305 .await
306 .expect("Failed to update email");
307 assert_eq!(res.status(), StatusCode::OK);
308 let user = sqlx::query!("SELECT email FROM users WHERE did = $1", did)
309 .fetch_one(&pool)
310 .await
311 .expect("User not found");
312 assert_eq!(user.email, Some(new_email));
313}
314
315#[tokio::test]
316async fn test_update_email_invalid_token() {
317 let client = common::client();
318 let base_url = common::base_url().await;
319 let handle = format!("emailup-badtok-{}", uuid::Uuid::new_v4());
320 let email = format!("{}@example.com", handle);
321 let (access_jwt, _) = create_verified_account(&client, &base_url, &handle, &email).await;
322 let new_email = format!("badtok_{}@example.com", handle);
323 let res = client
324 .post(format!(
325 "{}/xrpc/com.atproto.server.requestEmailUpdate",
326 base_url
327 ))
328 .bearer_auth(&access_jwt)
329 .json(&json!({"email": new_email}))
330 .send()
331 .await
332 .expect("Failed to request email update");
333 assert_eq!(res.status(), StatusCode::OK);
334 let res = client
335 .post(format!("{}/xrpc/com.atproto.server.updateEmail", base_url))
336 .bearer_auth(&access_jwt)
337 .json(&json!({
338 "email": new_email,
339 "token": "wrong-token-12345"
340 }))
341 .send()
342 .await
343 .expect("Failed to attempt email update");
344 assert_eq!(res.status(), StatusCode::BAD_REQUEST);
345 let body: Value = res.json().await.expect("Invalid JSON");
346 assert_eq!(body["error"], "InvalidToken");
347}
348
349#[tokio::test]
350async fn test_update_email_already_taken() {
351 let client = common::client();
352 let base_url = common::base_url().await;
353 let handle1 = format!("emailup-dup1-{}", uuid::Uuid::new_v4());
354 let email1 = format!("{}@example.com", handle1);
355 let (_, _) = create_verified_account(&client, &base_url, &handle1, &email1).await;
356 let handle2 = format!("emailup-dup2-{}", uuid::Uuid::new_v4());
357 let email2 = format!("{}@example.com", handle2);
358 let (access_jwt2, _) = create_verified_account(&client, &base_url, &handle2, &email2).await;
359 let res = client
360 .post(format!("{}/xrpc/com.atproto.server.updateEmail", base_url))
361 .bearer_auth(&access_jwt2)
362 .json(&json!({ "email": email1 }))
363 .send()
364 .await
365 .expect("Failed to attempt email update");
366 assert_eq!(res.status(), StatusCode::BAD_REQUEST);
367 let body: Value = res.json().await.expect("Invalid JSON");
368 assert!(
369 body["error"] == "TokenRequired"
370 || body["message"]
371 .as_str()
372 .unwrap_or("")
373 .contains("already in use")
374 || body["error"] == "InvalidRequest"
375 );
376}
377
378#[tokio::test]
379async fn test_update_email_no_auth() {
380 let client = common::client();
381 let base_url = common::base_url().await;
382 let res = client
383 .post(format!("{}/xrpc/com.atproto.server.updateEmail", base_url))
384 .json(&json!({ "email": "test@example.com" }))
385 .send()
386 .await
387 .expect("Failed to send request");
388 assert_eq!(res.status(), StatusCode::UNAUTHORIZED);
389 let body: Value = res.json().await.expect("Invalid JSON");
390 assert_eq!(body["error"], "AuthenticationRequired");
391}
392
393#[tokio::test]
394async fn test_update_email_invalid_format() {
395 let client = common::client();
396 let base_url = common::base_url().await;
397 let handle = format!("emailup-fmt-{}", uuid::Uuid::new_v4());
398 let email = format!("{}@example.com", handle);
399 let (access_jwt, _) = create_verified_account(&client, &base_url, &handle, &email).await;
400 let res = client
401 .post(format!("{}/xrpc/com.atproto.server.updateEmail", base_url))
402 .bearer_auth(&access_jwt)
403 .json(&json!({ "email": "not-an-email" }))
404 .send()
405 .await
406 .expect("Failed to send request");
407 assert_eq!(res.status(), StatusCode::BAD_REQUEST);
408 let body: Value = res.json().await.expect("Invalid JSON");
409 assert_eq!(body["error"], "InvalidEmail");
410}