1use std::collections::HashMap;
8use serde_json::{Value as JsonValue, json};
9use uuid::Uuid;
10use chrono::{DateTime, Utc, Duration};
11use crate::{TestError, TestResult, factories::{User, UserFactory, Factory}};
12
13pub struct TestAuthProvider {
15 jwt_secret: String,
16 session_store: HashMap<String, TestSession>,
17}
18
19impl TestAuthProvider {
20 pub fn new() -> Self {
22 Self {
23 jwt_secret: "test_jwt_secret_key_for_testing_only".to_string(),
24 session_store: HashMap::new(),
25 }
26 }
27
28 pub fn with_jwt_secret(secret: impl Into<String>) -> Self {
30 Self {
31 jwt_secret: secret.into(),
32 session_store: HashMap::new(),
33 }
34 }
35
36 pub fn generate_jwt_token(&self, user: &User) -> TestResult<String> {
38 let claims = TestJwtClaims {
39 sub: user.id.to_string(),
40 name: user.name.clone(),
41 email: user.email.clone(),
42 roles: vec!["user".to_string()], permissions: vec![],
44 exp: (Utc::now() + Duration::hours(1)).timestamp() as usize,
45 iat: Utc::now().timestamp() as usize,
46 };
47
48 let token = format!(
51 "test_jwt_token_{}_{}",
52 user.id.to_string().replace('-', ""),
53 claims.exp
54 );
55
56 Ok(token)
57 }
58
59 pub fn generate_jwt_with_claims(&self, claims: TestJwtClaims) -> TestResult<String> {
61 let token = format!(
62 "test_jwt_token_{}_{}",
63 claims.sub.replace('-', ""),
64 claims.exp
65 );
66 Ok(token)
67 }
68
69 pub fn generate_admin_token(&self, user: &User) -> TestResult<String> {
71 let claims = TestJwtClaims {
72 sub: user.id.to_string(),
73 name: user.name.clone(),
74 email: user.email.clone(),
75 roles: vec!["admin".to_string()],
76 permissions: vec![
77 "users.create".to_string(),
78 "users.read".to_string(),
79 "users.update".to_string(),
80 "users.delete".to_string(),
81 ],
82 exp: (Utc::now() + Duration::hours(1)).timestamp() as usize,
83 iat: Utc::now().timestamp() as usize,
84 };
85
86 self.generate_jwt_with_claims(claims)
87 }
88
89 pub fn create_session(&mut self, user: &User) -> TestResult<String> {
91 let session_id = Uuid::new_v4().to_string();
92 let session = TestSession {
93 id: session_id.clone(),
94 user_id: user.id,
95 user_name: user.name.clone(),
96 user_email: user.email.clone(),
97 roles: vec!["user".to_string()],
98 permissions: vec![],
99 created_at: Utc::now(),
100 expires_at: Utc::now() + Duration::hours(2),
101 data: HashMap::new(),
102 };
103
104 self.session_store.insert(session_id.clone(), session);
105 Ok(session_id)
106 }
107
108 pub fn create_admin_session(&mut self, user: &User) -> TestResult<String> {
110 let session_id = Uuid::new_v4().to_string();
111 let session = TestSession {
112 id: session_id.clone(),
113 user_id: user.id,
114 user_name: user.name.clone(),
115 user_email: user.email.clone(),
116 roles: vec!["admin".to_string()],
117 permissions: vec![
118 "users.create".to_string(),
119 "users.read".to_string(),
120 "users.update".to_string(),
121 "users.delete".to_string(),
122 ],
123 created_at: Utc::now(),
124 expires_at: Utc::now() + Duration::hours(2),
125 data: HashMap::new(),
126 };
127
128 self.session_store.insert(session_id.clone(), session);
129 Ok(session_id)
130 }
131
132 pub fn get_session(&self, session_id: &str) -> Option<&TestSession> {
134 self.session_store.get(session_id)
135 }
136
137 pub fn validate_jwt_token(&self, token: &str) -> TestResult<TestJwtClaims> {
139 if !token.starts_with("test_jwt_token_") {
141 return Err(TestError::Authentication("Invalid token format".to_string()));
142 }
143
144 let parts: Vec<&str> = token.split('_').collect();
145 if parts.len() < 4 {
146 return Err(TestError::Authentication("Invalid token structure".to_string()));
147 }
148
149 let user_id = parts[3];
151 let exp_str = parts.get(4).copied().unwrap_or("0");
152 let exp = exp_str.parse::<usize>().unwrap_or(0);
153
154 if exp < Utc::now().timestamp() as usize {
155 return Err(TestError::Authentication("Token expired".to_string()));
156 }
157
158 Ok(TestJwtClaims {
159 sub: format!("{}-{}-{}-{}", &user_id[0..8], &user_id[8..12], &user_id[12..16], &user_id[16..20]),
160 name: "Test User".to_string(),
161 email: "test@example.com".to_string(),
162 roles: vec!["user".to_string()],
163 permissions: vec![],
164 exp,
165 iat: Utc::now().timestamp() as usize,
166 })
167 }
168}
169
170impl Default for TestAuthProvider {
171 fn default() -> Self {
172 Self::new()
173 }
174}
175
176#[derive(Debug, Clone)]
178pub struct TestJwtClaims {
179 pub sub: String, pub name: String, pub email: String, pub roles: Vec<String>, pub permissions: Vec<String>, pub exp: usize, pub iat: usize, }
187
188impl TestJwtClaims {
189 pub fn user(user_id: impl Into<String>) -> Self {
191 Self {
192 sub: user_id.into(),
193 name: "Test User".to_string(),
194 email: "test@example.com".to_string(),
195 roles: vec!["user".to_string()],
196 permissions: vec![],
197 exp: (Utc::now() + Duration::hours(1)).timestamp() as usize,
198 iat: Utc::now().timestamp() as usize,
199 }
200 }
201
202 pub fn admin(user_id: impl Into<String>) -> Self {
204 Self {
205 sub: user_id.into(),
206 name: "Admin User".to_string(),
207 email: "admin@example.com".to_string(),
208 roles: vec!["admin".to_string()],
209 permissions: vec![
210 "users.create".to_string(),
211 "users.read".to_string(),
212 "users.update".to_string(),
213 "users.delete".to_string(),
214 ],
215 exp: (Utc::now() + Duration::hours(1)).timestamp() as usize,
216 iat: Utc::now().timestamp() as usize,
217 }
218 }
219
220 pub fn with_role(mut self, role: impl Into<String>) -> Self {
222 self.roles.push(role.into());
223 self
224 }
225
226 pub fn with_roles(mut self, roles: Vec<String>) -> Self {
228 self.roles.extend(roles);
229 self
230 }
231
232 pub fn with_permission(mut self, permission: impl Into<String>) -> Self {
234 self.permissions.push(permission.into());
235 self
236 }
237
238 pub fn with_permissions(mut self, permissions: Vec<String>) -> Self {
240 self.permissions.extend(permissions);
241 self
242 }
243
244 pub fn expires_in(mut self, duration: Duration) -> Self {
246 self.exp = (Utc::now() + duration).timestamp() as usize;
247 self
248 }
249}
250
251#[derive(Debug, Clone)]
253pub struct TestSession {
254 pub id: String,
255 pub user_id: Uuid,
256 pub user_name: String,
257 pub user_email: String,
258 pub roles: Vec<String>,
259 pub permissions: Vec<String>,
260 pub created_at: DateTime<Utc>,
261 pub expires_at: DateTime<Utc>,
262 pub data: HashMap<String, JsonValue>,
263}
264
265impl TestSession {
266 pub fn is_expired(&self) -> bool {
268 Utc::now() > self.expires_at
269 }
270
271 pub fn has_role(&self, role: &str) -> bool {
273 self.roles.contains(&role.to_string())
274 }
275
276 pub fn has_permission(&self, permission: &str) -> bool {
278 self.permissions.contains(&permission.to_string())
279 }
280
281 pub fn set_data(&mut self, key: impl Into<String>, value: JsonValue) {
283 self.data.insert(key.into(), value);
284 }
285
286 pub fn get_data(&self, key: &str) -> Option<&JsonValue> {
288 self.data.get(key)
289 }
290}
291
292pub struct TestUserBuilder {
294 user: User,
295 roles: Vec<String>,
296 permissions: Vec<String>,
297}
298
299impl TestUserBuilder {
300 pub fn new() -> TestResult<Self> {
302 let user = UserFactory::new().build()?;
303 Ok(Self {
304 user,
305 roles: vec!["user".to_string()],
306 permissions: vec![],
307 })
308 }
309
310 pub fn admin() -> TestResult<Self> {
312 let user = UserFactory::new().build()?;
313 Ok(Self {
314 user,
315 roles: vec!["admin".to_string()],
316 permissions: vec![
317 "users.create".to_string(),
318 "users.read".to_string(),
319 "users.update".to_string(),
320 "users.delete".to_string(),
321 ],
322 })
323 }
324
325 pub fn with_name(mut self, name: impl Into<String>) -> Self {
327 self.user.name = name.into();
328 self
329 }
330
331 pub fn with_email(mut self, email: impl Into<String>) -> Self {
333 self.user.email = email.into();
334 self
335 }
336
337 pub fn with_role(mut self, role: impl Into<String>) -> Self {
339 self.roles.push(role.into());
340 self
341 }
342
343 pub fn with_roles(mut self, roles: Vec<String>) -> Self {
345 self.roles.extend(roles);
346 self
347 }
348
349 pub fn with_permission(mut self, permission: impl Into<String>) -> Self {
351 self.permissions.push(permission.into());
352 self
353 }
354
355 pub fn with_permissions(mut self, permissions: Vec<String>) -> Self {
357 self.permissions.extend(permissions);
358 self
359 }
360
361 pub fn build(self) -> (User, Vec<String>, Vec<String>) {
363 (self.user, self.roles, self.permissions)
364 }
365
366 pub fn generate_jwt_token(self) -> TestResult<(User, String)> {
368 let auth_provider = TestAuthProvider::new();
369 let (user, roles, permissions) = self.build();
370
371 let claims = TestJwtClaims {
372 sub: user.id.to_string(),
373 name: user.name.clone(),
374 email: user.email.clone(),
375 roles,
376 permissions,
377 exp: (Utc::now() + Duration::hours(1)).timestamp() as usize,
378 iat: Utc::now().timestamp() as usize,
379 };
380
381 let token = auth_provider.generate_jwt_with_claims(claims)?;
382 Ok((user, token))
383 }
384}
385
386impl Default for TestUserBuilder {
387 fn default() -> Self {
388 Self::new().expect("Failed to create default test user")
389 }
390}
391
392pub struct AuthTestHelpers;
394
395impl AuthTestHelpers {
396 pub fn user_with_jwt() -> TestResult<(User, String)> {
398 TestUserBuilder::new()?.generate_jwt_token()
399 }
400
401 pub fn admin_with_jwt() -> TestResult<(User, String)> {
403 TestUserBuilder::admin()?.generate_jwt_token()
404 }
405
406 pub fn user_with_roles_and_jwt(roles: Vec<String>) -> TestResult<(User, String)> {
408 TestUserBuilder::new()?.with_roles(roles).generate_jwt_token()
409 }
410
411 pub fn user_with_permissions_and_jwt(permissions: Vec<String>) -> TestResult<(User, String)> {
413 TestUserBuilder::new()?.with_permissions(permissions).generate_jwt_token()
414 }
415
416 pub fn assert_token_has_roles(token: &str, expected_roles: &[String]) -> TestResult<()> {
418 let auth_provider = TestAuthProvider::new();
419 let claims = auth_provider.validate_jwt_token(token)?;
420
421 for role in expected_roles {
422 if !claims.roles.contains(role) {
423 return Err(TestError::Assertion {
424 message: format!("Token does not contain required role: {}", role),
425 });
426 }
427 }
428
429 Ok(())
430 }
431
432 pub fn assert_token_has_permissions(token: &str, expected_permissions: &[String]) -> TestResult<()> {
434 let auth_provider = TestAuthProvider::new();
435 let claims = auth_provider.validate_jwt_token(token)?;
436
437 for permission in expected_permissions {
438 if !claims.permissions.contains(permission) {
439 return Err(TestError::Assertion {
440 message: format!("Token does not contain required permission: {}", permission),
441 });
442 }
443 }
444
445 Ok(())
446 }
447}
448
449#[cfg(test)]
450mod tests {
451 use super::*;
452
453 #[test]
454 fn test_jwt_claims_creation() {
455 let claims = TestJwtClaims::user("user123");
456 assert_eq!(claims.sub, "user123");
457 assert!(claims.roles.contains(&"user".to_string()));
458 assert!(claims.exp > Utc::now().timestamp() as usize);
459
460 let admin_claims = TestJwtClaims::admin("admin123");
461 assert_eq!(admin_claims.sub, "admin123");
462 assert!(admin_claims.roles.contains(&"admin".to_string()));
463 assert!(!admin_claims.permissions.is_empty());
464 }
465
466 #[test]
467 fn test_jwt_claims_modification() {
468 let claims = TestJwtClaims::user("user123")
469 .with_role("moderator")
470 .with_permission("posts.delete");
471
472 assert!(claims.roles.contains(&"moderator".to_string()));
473 assert!(claims.permissions.contains(&"posts.delete".to_string()));
474 }
475
476 #[test]
477 fn test_test_auth_provider() -> TestResult<()> {
478 let provider = TestAuthProvider::new();
479 let user = UserFactory::new().build()?;
480
481 let token = provider.generate_jwt_token(&user)?;
482 assert!(token.starts_with("test_jwt_token_"));
483
484 let claims = provider.validate_jwt_token(&token)?;
485 assert!(!claims.sub.is_empty());
486
487 Ok(())
488 }
489
490 #[test]
491 fn test_session_functionality() {
492 let mut provider = TestAuthProvider::new();
493 let user = UserFactory::new().build().unwrap();
494
495 let session_id = provider.create_session(&user).unwrap();
496 let session = provider.get_session(&session_id).unwrap();
497
498 assert_eq!(session.user_id, user.id);
499 assert!(session.has_role("user"));
500 assert!(!session.is_expired());
501 }
502
503 #[test]
504 fn test_user_builder() -> TestResult<()> {
505 let builder = TestUserBuilder::new()?;
506 let (user, roles, permissions) = builder
507 .with_name("John Doe")
508 .with_role("moderator")
509 .with_permission("posts.create")
510 .build();
511
512 assert_eq!(user.name, "John Doe");
513 assert!(roles.contains(&"moderator".to_string()));
514 assert!(permissions.contains(&"posts.create".to_string()));
515
516 Ok(())
517 }
518
519 #[test]
520 fn test_auth_helpers() -> TestResult<()> {
521 let (_user, token) = AuthTestHelpers::user_with_jwt()?;
522 assert!(!token.is_empty());
523
524 let (_admin, admin_token) = AuthTestHelpers::admin_with_jwt()?;
525 assert!(!admin_token.is_empty());
526
527 Ok(())
528 }
529}