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}