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