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