Skip to main content

reinhardt_auth/
user_management.rs

1//! User Management
2//!
3//! Provides CRUD operations for user management.
4
5use crate::PasswordHasher;
6use crate::SimpleUser;
7use std::collections::HashMap;
8use std::sync::Arc;
9use tokio::sync::RwLock;
10use uuid::Uuid;
11
12/// User management error
13#[non_exhaustive]
14#[derive(Debug, Clone, PartialEq, Eq)]
15pub enum UserManagementError {
16	/// User not found
17	UserNotFound,
18	/// User already exists
19	UserAlreadyExists,
20	/// Invalid username
21	InvalidUsername,
22	/// Invalid email
23	InvalidEmail,
24	/// Invalid password
25	InvalidPassword,
26	/// Database error
27	DatabaseError(String),
28	/// Other error
29	Other(String),
30}
31
32impl std::fmt::Display for UserManagementError {
33	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
34		match self {
35			UserManagementError::UserNotFound => write!(f, "User not found"),
36			UserManagementError::UserAlreadyExists => write!(f, "User already exists"),
37			UserManagementError::InvalidUsername => write!(f, "Invalid username"),
38			UserManagementError::InvalidEmail => write!(f, "Invalid email"),
39			UserManagementError::InvalidPassword => write!(f, "Invalid password"),
40			UserManagementError::DatabaseError(msg) => write!(f, "Database error: {}", msg),
41			UserManagementError::Other(msg) => write!(f, "Error: {}", msg),
42		}
43	}
44}
45
46impl std::error::Error for UserManagementError {}
47
48/// User management result
49pub type UserManagementResult<T> = Result<T, UserManagementError>;
50
51/// User data for creation
52///
53/// # Examples
54///
55/// ```
56/// use reinhardt_auth::user_management::CreateUserData;
57///
58/// let user_data = CreateUserData {
59///     username: "alice".to_string(),
60///     email: "alice@example.com".to_string(),
61///     password: "password123".to_string(),
62///     is_active: true,
63///     is_admin: false,
64/// };
65///
66/// assert_eq!(user_data.username, "alice");
67/// assert_eq!(user_data.email, "alice@example.com");
68/// ```
69#[derive(Debug, Clone, PartialEq, Eq)]
70pub struct CreateUserData {
71	/// Login username for the new user.
72	pub username: String,
73	/// Email address for the new user.
74	pub email: String,
75	/// Plain-text password (will be hashed before storage).
76	pub password: String,
77	/// Whether the new user account should be active.
78	pub is_active: bool,
79	/// Whether the new user should have admin privileges.
80	pub is_admin: bool,
81}
82
83/// User data for update
84///
85/// # Examples
86///
87/// ```
88/// use reinhardt_auth::user_management::UpdateUserData;
89///
90/// let update_data = UpdateUserData {
91///     email: Some("newemail@example.com".to_string()),
92///     is_active: Some(false),
93///     is_admin: None,
94/// };
95///
96/// assert_eq!(update_data.email, Some("newemail@example.com".to_string()));
97/// assert_eq!(update_data.is_active, Some(false));
98/// ```
99#[derive(Debug, Clone, Default, PartialEq, Eq)]
100pub struct UpdateUserData {
101	/// New email address, if being updated.
102	pub email: Option<String>,
103	/// New active status, if being updated.
104	pub is_active: Option<bool>,
105	/// New admin status, if being updated.
106	pub is_admin: Option<bool>,
107}
108
109/// User manager
110///
111/// Provides CRUD operations for users.
112///
113/// # Examples
114///
115/// ```rust
116/// # use reinhardt_auth::PasswordHasher;
117/// # struct MockHasher;
118/// # impl PasswordHasher for MockHasher {
119/// #     fn hash(&self, p: &str) -> Result<String, reinhardt_core::exception::Error> {
120/// #         Ok(format!("hash:{p}"))
121/// #     }
122/// #     fn verify(&self, p: &str, h: &str) -> Result<bool, reinhardt_core::exception::Error> {
123/// #         Ok(h == format!("hash:{p}"))
124/// #     }
125/// # }
126/// use reinhardt_auth::user_management::{UserManager, CreateUserData};
127///
128/// #[tokio::main]
129/// async fn main() -> Result<(), Box<dyn std::error::Error>> {
130///     let hasher = MockHasher;
131///     let mut manager = UserManager::new(hasher);
132///
133///     // Create user
134///     let user_data = CreateUserData {
135///         username: "alice".to_string(),
136///         email: "alice@example.com".to_string(),
137///         password: "password123".to_string(),
138///         is_active: true,
139///         is_admin: false,
140///     };
141///
142///     let user = manager.create_user(user_data).await.unwrap();
143///     assert_eq!(user.username, "alice");
144///
145///     // Get user
146///     let retrieved = manager.get_user(&user.id.to_string()).await.unwrap();
147///     assert_eq!(retrieved.username, "alice");
148///
149///     // Delete user
150///     manager.delete_user(&user.id.to_string()).await.unwrap();
151///     assert!(manager.get_user(&user.id.to_string()).await.is_err());
152///     Ok(())
153/// }
154/// ```
155pub struct UserManager<H: PasswordHasher> {
156	users: Arc<RwLock<HashMap<Uuid, SimpleUser>>>,
157	username_index: Arc<RwLock<HashMap<String, Uuid>>>,
158	password_hashes: Arc<RwLock<HashMap<Uuid, String>>>,
159	hasher: H,
160}
161
162impl<H: PasswordHasher> UserManager<H> {
163	/// Create a new user manager
164	///
165	/// # Examples
166	///
167	/// ```rust
168	/// # use reinhardt_auth::PasswordHasher;
169	/// # struct MockHasher;
170	/// # impl PasswordHasher for MockHasher {
171	/// #     fn hash(&self, p: &str) -> Result<String, reinhardt_core::exception::Error> {
172	/// #         Ok(format!("hash:{p}"))
173	/// #     }
174	/// #     fn verify(&self, p: &str, h: &str) -> Result<bool, reinhardt_core::exception::Error> {
175	/// #         Ok(h == format!("hash:{p}"))
176	/// #     }
177	/// # }
178	/// use reinhardt_auth::user_management::UserManager;
179	///
180	/// let manager = UserManager::new(MockHasher);
181	/// ```
182	pub fn new(hasher: H) -> Self {
183		Self {
184			users: Arc::new(RwLock::new(HashMap::new())),
185			username_index: Arc::new(RwLock::new(HashMap::new())),
186			password_hashes: Arc::new(RwLock::new(HashMap::new())),
187			hasher,
188		}
189	}
190
191	/// Create a new user
192	///
193	/// # Examples
194	///
195	/// ```rust
196	/// # use reinhardt_auth::PasswordHasher;
197	/// # struct MockHasher;
198	/// # impl PasswordHasher for MockHasher {
199	/// #     fn hash(&self, p: &str) -> Result<String, reinhardt_core::exception::Error> {
200	/// #         Ok(format!("hash:{p}"))
201	/// #     }
202	/// #     fn verify(&self, p: &str, h: &str) -> Result<bool, reinhardt_core::exception::Error> {
203	/// #         Ok(h == format!("hash:{p}"))
204	/// #     }
205	/// # }
206	/// use reinhardt_auth::user_management::{UserManager, CreateUserData};
207	///
208	/// #[tokio::main]
209	/// async fn main() {
210	///     let mut manager = UserManager::new(MockHasher);
211	///
212	///     let user_data = CreateUserData {
213	///         username: "bob".to_string(),
214	///         email: "bob@example.com".to_string(),
215	///         password: "securepass".to_string(),
216	///         is_active: true,
217	///         is_admin: false,
218	///     };
219	///
220	///     let user = manager.create_user(user_data).await.unwrap();
221	///     assert_eq!(user.username, "bob");
222	/// }
223	/// ```
224	pub async fn create_user(&mut self, data: CreateUserData) -> UserManagementResult<SimpleUser> {
225		// Validate username
226		if data.username.is_empty() || data.username.len() < 3 {
227			return Err(UserManagementError::InvalidUsername);
228		}
229
230		// Validate email
231		// JWT/MFA authentication may not provide email; skip validation for empty emails
232		if !data.email.is_empty() && (!data.email.contains('@') || !data.email.contains('.')) {
233			return Err(UserManagementError::InvalidEmail);
234		}
235
236		// Validate password
237		if data.password.len() < 8 {
238			return Err(UserManagementError::InvalidPassword);
239		}
240
241		// Check if username already exists
242		let username_index = self.username_index.read().await;
243		if username_index.contains_key(&data.username) {
244			return Err(UserManagementError::UserAlreadyExists);
245		}
246		drop(username_index);
247
248		// Hash password
249		let password_hash = self
250			.hasher
251			.hash(&data.password)
252			.map_err(|e| UserManagementError::Other(e.to_string()))?;
253
254		// Create user
255		let user = SimpleUser {
256			id: Uuid::new_v4(),
257			username: data.username.clone(),
258			email: data.email,
259			is_active: data.is_active,
260			is_admin: data.is_admin,
261			is_staff: false,
262			is_superuser: false,
263		};
264
265		// Store user
266		let mut users = self.users.write().await;
267		let mut username_index = self.username_index.write().await;
268		let mut password_hashes = self.password_hashes.write().await;
269
270		users.insert(user.id, user.clone());
271		username_index.insert(data.username, user.id);
272		password_hashes.insert(user.id, password_hash);
273
274		Ok(user)
275	}
276
277	/// Get user by ID
278	///
279	/// # Examples
280	///
281	/// ```rust
282	/// # use reinhardt_auth::PasswordHasher;
283	/// # struct MockHasher;
284	/// # impl PasswordHasher for MockHasher {
285	/// #     fn hash(&self, p: &str) -> Result<String, reinhardt_core::exception::Error> {
286	/// #         Ok(format!("hash:{p}"))
287	/// #     }
288	/// #     fn verify(&self, p: &str, h: &str) -> Result<bool, reinhardt_core::exception::Error> {
289	/// #         Ok(h == format!("hash:{p}"))
290	/// #     }
291	/// # }
292	/// use reinhardt_auth::user_management::{UserManager, CreateUserData};
293	///
294	/// #[tokio::main]
295	/// async fn main() {
296	///     let mut manager = UserManager::new(MockHasher);
297	///
298	///     let user_data = CreateUserData {
299	///         username: "charlie".to_string(),
300	///         email: "charlie@example.com".to_string(),
301	///         password: "password123".to_string(),
302	///         is_active: true,
303	///         is_admin: false,
304	///     };
305	///
306	///     let user = manager.create_user(user_data).await.unwrap();
307	///     let retrieved = manager.get_user(&user.id.to_string()).await.unwrap();
308	///     assert_eq!(retrieved.username, "charlie");
309	/// }
310	/// ```
311	pub async fn get_user(&self, user_id: &str) -> UserManagementResult<SimpleUser> {
312		let uuid = Uuid::parse_str(user_id)
313			.map_err(|_| UserManagementError::Other("Invalid UUID".to_string()))?;
314
315		let users = self.users.read().await;
316		users
317			.get(&uuid)
318			.cloned()
319			.ok_or(UserManagementError::UserNotFound)
320	}
321
322	/// Get user by username
323	///
324	/// # Examples
325	///
326	/// ```rust
327	/// # use reinhardt_auth::PasswordHasher;
328	/// # struct MockHasher;
329	/// # impl PasswordHasher for MockHasher {
330	/// #     fn hash(&self, p: &str) -> Result<String, reinhardt_core::exception::Error> {
331	/// #         Ok(format!("hash:{p}"))
332	/// #     }
333	/// #     fn verify(&self, p: &str, h: &str) -> Result<bool, reinhardt_core::exception::Error> {
334	/// #         Ok(h == format!("hash:{p}"))
335	/// #     }
336	/// # }
337	/// use reinhardt_auth::user_management::{UserManager, CreateUserData};
338	///
339	/// #[tokio::main]
340	/// async fn main() {
341	///     let mut manager = UserManager::new(MockHasher);
342	///
343	///     let user_data = CreateUserData {
344	///         username: "diana".to_string(),
345	///         email: "diana@example.com".to_string(),
346	///         password: "password123".to_string(),
347	///         is_active: true,
348	///         is_admin: false,
349	///     };
350	///
351	///     manager.create_user(user_data).await.unwrap();
352	///     let retrieved = manager.get_user_by_username("diana").await.unwrap();
353	///     assert_eq!(retrieved.username, "diana");
354	/// }
355	/// ```
356	pub async fn get_user_by_username(&self, username: &str) -> UserManagementResult<SimpleUser> {
357		let username_index = self.username_index.read().await;
358		let user_id = username_index
359			.get(username)
360			.ok_or(UserManagementError::UserNotFound)?;
361
362		let users = self.users.read().await;
363		users
364			.get(user_id)
365			.cloned()
366			.ok_or(UserManagementError::UserNotFound)
367	}
368
369	/// Update user
370	///
371	/// # Examples
372	///
373	/// ```rust
374	/// # use reinhardt_auth::PasswordHasher;
375	/// # struct MockHasher;
376	/// # impl PasswordHasher for MockHasher {
377	/// #     fn hash(&self, p: &str) -> Result<String, reinhardt_core::exception::Error> {
378	/// #         Ok(format!("hash:{p}"))
379	/// #     }
380	/// #     fn verify(&self, p: &str, h: &str) -> Result<bool, reinhardt_core::exception::Error> {
381	/// #         Ok(h == format!("hash:{p}"))
382	/// #     }
383	/// # }
384	/// use reinhardt_auth::user_management::{UserManager, CreateUserData, UpdateUserData};
385	///
386	/// #[tokio::main]
387	/// async fn main() {
388	///     let mut manager = UserManager::new(MockHasher);
389	///
390	///     let user_data = CreateUserData {
391	///         username: "eve".to_string(),
392	///         email: "eve@example.com".to_string(),
393	///         password: "password123".to_string(),
394	///         is_active: true,
395	///         is_admin: false,
396	///     };
397	///
398	///     let user = manager.create_user(user_data).await.unwrap();
399	///
400	///     let update_data = UpdateUserData {
401	///         email: Some("newemail@example.com".to_string()),
402	///         is_active: Some(false),
403	///         is_admin: None,
404	///     };
405	///
406	///     let updated = manager.update_user(&user.id.to_string(), update_data).await.unwrap();
407	///     assert_eq!(updated.email, "newemail@example.com");
408	///     assert!(!updated.is_active);
409	/// }
410	/// ```
411	pub async fn update_user(
412		&mut self,
413		user_id: &str,
414		data: UpdateUserData,
415	) -> UserManagementResult<SimpleUser> {
416		let uuid = Uuid::parse_str(user_id)
417			.map_err(|_| UserManagementError::Other("Invalid UUID".to_string()))?;
418
419		let mut users = self.users.write().await;
420		let user = users
421			.get_mut(&uuid)
422			.ok_or(UserManagementError::UserNotFound)?;
423
424		if let Some(email) = data.email {
425			if !email.contains('@') || !email.contains('.') {
426				return Err(UserManagementError::InvalidEmail);
427			}
428			user.email = email;
429		}
430
431		if let Some(is_active) = data.is_active {
432			user.is_active = is_active;
433		}
434
435		if let Some(is_admin) = data.is_admin {
436			user.is_admin = is_admin;
437		}
438
439		Ok(user.clone())
440	}
441
442	/// Delete user
443	///
444	/// # Examples
445	///
446	/// ```rust
447	/// # use reinhardt_auth::PasswordHasher;
448	/// # struct MockHasher;
449	/// # impl PasswordHasher for MockHasher {
450	/// #     fn hash(&self, p: &str) -> Result<String, reinhardt_core::exception::Error> {
451	/// #         Ok(format!("hash:{p}"))
452	/// #     }
453	/// #     fn verify(&self, p: &str, h: &str) -> Result<bool, reinhardt_core::exception::Error> {
454	/// #         Ok(h == format!("hash:{p}"))
455	/// #     }
456	/// # }
457	/// use reinhardt_auth::user_management::{UserManager, CreateUserData};
458	///
459	/// #[tokio::main]
460	/// async fn main() {
461	///     let mut manager = UserManager::new(MockHasher);
462	///
463	///     let user_data = CreateUserData {
464	///         username: "frank".to_string(),
465	///         email: "frank@example.com".to_string(),
466	///         password: "password123".to_string(),
467	///         is_active: true,
468	///         is_admin: false,
469	///     };
470	///
471	///     let user = manager.create_user(user_data).await.unwrap();
472	///     manager.delete_user(&user.id.to_string()).await.unwrap();
473	///     assert!(manager.get_user(&user.id.to_string()).await.is_err());
474	/// }
475	/// ```
476	pub async fn delete_user(&mut self, user_id: &str) -> UserManagementResult<()> {
477		let uuid = Uuid::parse_str(user_id)
478			.map_err(|_| UserManagementError::Other("Invalid UUID".to_string()))?;
479
480		let mut users = self.users.write().await;
481		let user = users
482			.get(&uuid)
483			.ok_or(UserManagementError::UserNotFound)?
484			.clone();
485
486		let mut username_index = self.username_index.write().await;
487		let mut password_hashes = self.password_hashes.write().await;
488
489		users.remove(&uuid);
490		username_index.remove(&user.username);
491		password_hashes.remove(&uuid);
492
493		Ok(())
494	}
495
496	/// List all users
497	///
498	/// # Examples
499	///
500	/// ```rust
501	/// # use reinhardt_auth::PasswordHasher;
502	/// # struct MockHasher;
503	/// # impl PasswordHasher for MockHasher {
504	/// #     fn hash(&self, p: &str) -> Result<String, reinhardt_core::exception::Error> {
505	/// #         Ok(format!("hash:{p}"))
506	/// #     }
507	/// #     fn verify(&self, p: &str, h: &str) -> Result<bool, reinhardt_core::exception::Error> {
508	/// #         Ok(h == format!("hash:{p}"))
509	/// #     }
510	/// # }
511	/// use reinhardt_auth::user_management::{UserManager, CreateUserData};
512	///
513	/// #[tokio::main]
514	/// async fn main() {
515	///     let mut manager = UserManager::new(MockHasher);
516	///
517	///     let user_data1 = CreateUserData {
518	///         username: "grace".to_string(),
519	///         email: "grace@example.com".to_string(),
520	///         password: "password123".to_string(),
521	///         is_active: true,
522	///         is_admin: false,
523	///     };
524	///
525	///     let user_data2 = CreateUserData {
526	///         username: "henry".to_string(),
527	///         email: "henry@example.com".to_string(),
528	///         password: "password123".to_string(),
529	///         is_active: true,
530	///         is_admin: false,
531	///     };
532	///
533	///     manager.create_user(user_data1).await.unwrap();
534	///     manager.create_user(user_data2).await.unwrap();
535	///
536	///     let users = manager.list_users().await;
537	///     assert_eq!(users.len(), 2);
538	/// }
539	/// ```
540	pub async fn list_users(&self) -> Vec<SimpleUser> {
541		let users = self.users.read().await;
542		users.values().cloned().collect()
543	}
544
545	/// Verify user password
546	///
547	/// # Examples
548	///
549	/// ```rust
550	/// # use reinhardt_auth::PasswordHasher;
551	/// # struct MockHasher;
552	/// # impl PasswordHasher for MockHasher {
553	/// #     fn hash(&self, p: &str) -> Result<String, reinhardt_core::exception::Error> {
554	/// #         Ok(format!("hash:{p}"))
555	/// #     }
556	/// #     fn verify(&self, p: &str, h: &str) -> Result<bool, reinhardt_core::exception::Error> {
557	/// #         Ok(h == format!("hash:{p}"))
558	/// #     }
559	/// # }
560	/// use reinhardt_auth::user_management::{UserManager, CreateUserData};
561	///
562	/// #[tokio::main]
563	/// async fn main() {
564	///     let mut manager = UserManager::new(MockHasher);
565	///
566	///     let user_data = CreateUserData {
567	///         username: "iris".to_string(),
568	///         email: "iris@example.com".to_string(),
569	///         password: "mypassword".to_string(),
570	///         is_active: true,
571	///         is_admin: false,
572	///     };
573	///
574	///     let user = manager.create_user(user_data).await.unwrap();
575	///     assert!(manager.verify_password(&user.id.to_string(), "mypassword").await.unwrap());
576	///     assert!(!manager.verify_password(&user.id.to_string(), "wrongpassword").await.unwrap());
577	/// }
578	/// ```
579	pub async fn verify_password(
580		&self,
581		user_id: &str,
582		password: &str,
583	) -> UserManagementResult<bool> {
584		let uuid = Uuid::parse_str(user_id)
585			.map_err(|_| UserManagementError::Other("Invalid UUID".to_string()))?;
586
587		let password_hashes = self.password_hashes.read().await;
588		let hash = password_hashes
589			.get(&uuid)
590			.ok_or(UserManagementError::UserNotFound)?;
591
592		self.hasher
593			.verify(password, hash)
594			.map_err(|e| UserManagementError::Other(e.to_string()))
595	}
596}
597
598#[cfg(all(test, feature = "argon2-hasher"))]
599mod tests {
600	use super::*;
601	use crate::Argon2Hasher;
602
603	#[tokio::test]
604	async fn test_create_user() {
605		let hasher = Argon2Hasher::new();
606		let mut manager = UserManager::new(hasher);
607
608		let user_data = CreateUserData {
609			username: "alice".to_string(),
610			email: "alice@example.com".to_string(),
611			password: "password123".to_string(),
612			is_active: true,
613			is_admin: false,
614		};
615
616		let user = manager.create_user(user_data).await.unwrap();
617		assert_eq!(user.username, "alice");
618		assert_eq!(user.email, "alice@example.com");
619		assert!(user.is_active);
620		assert!(!user.is_admin);
621	}
622
623	#[tokio::test]
624	async fn test_create_user_duplicate_username() {
625		let hasher = Argon2Hasher::new();
626		let mut manager = UserManager::new(hasher);
627
628		let user_data1 = CreateUserData {
629			username: "bob".to_string(),
630			email: "bob@example.com".to_string(),
631			password: "password123".to_string(),
632			is_active: true,
633			is_admin: false,
634		};
635
636		let user_data2 = CreateUserData {
637			username: "bob".to_string(),
638			email: "bob2@example.com".to_string(),
639			password: "password456".to_string(),
640			is_active: true,
641			is_admin: false,
642		};
643
644		manager.create_user(user_data1).await.unwrap();
645		let result = manager.create_user(user_data2).await;
646		assert!(result.is_err());
647	}
648
649	#[tokio::test]
650	async fn test_get_user() {
651		let hasher = Argon2Hasher::new();
652		let mut manager = UserManager::new(hasher);
653
654		let user_data = CreateUserData {
655			username: "charlie".to_string(),
656			email: "charlie@example.com".to_string(),
657			password: "password123".to_string(),
658			is_active: true,
659			is_admin: false,
660		};
661
662		let user = manager.create_user(user_data).await.unwrap();
663		let retrieved = manager.get_user(&user.id.to_string()).await.unwrap();
664		assert_eq!(retrieved.username, "charlie");
665	}
666
667	#[tokio::test]
668	async fn test_get_user_by_username() {
669		let hasher = Argon2Hasher::new();
670		let mut manager = UserManager::new(hasher);
671
672		let user_data = CreateUserData {
673			username: "diana".to_string(),
674			email: "diana@example.com".to_string(),
675			password: "password123".to_string(),
676			is_active: true,
677			is_admin: false,
678		};
679
680		manager.create_user(user_data).await.unwrap();
681		let retrieved = manager.get_user_by_username("diana").await.unwrap();
682		assert_eq!(retrieved.username, "diana");
683	}
684
685	#[tokio::test]
686	async fn test_update_user() {
687		let hasher = Argon2Hasher::new();
688		let mut manager = UserManager::new(hasher);
689
690		let user_data = CreateUserData {
691			username: "eve".to_string(),
692			email: "eve@example.com".to_string(),
693			password: "password123".to_string(),
694			is_active: true,
695			is_admin: false,
696		};
697
698		let user = manager.create_user(user_data).await.unwrap();
699
700		let update_data = UpdateUserData {
701			email: Some("newemail@example.com".to_string()),
702			is_active: Some(false),
703			is_admin: Some(true),
704		};
705
706		let updated = manager
707			.update_user(&user.id.to_string(), update_data)
708			.await
709			.unwrap();
710		assert_eq!(updated.email, "newemail@example.com");
711		assert!(!updated.is_active);
712		assert!(updated.is_admin);
713	}
714
715	#[tokio::test]
716	async fn test_delete_user() {
717		let hasher = Argon2Hasher::new();
718		let mut manager = UserManager::new(hasher);
719
720		let user_data = CreateUserData {
721			username: "frank".to_string(),
722			email: "frank@example.com".to_string(),
723			password: "password123".to_string(),
724			is_active: true,
725			is_admin: false,
726		};
727
728		let user = manager.create_user(user_data).await.unwrap();
729		manager.delete_user(&user.id.to_string()).await.unwrap();
730		let result = manager.get_user(&user.id.to_string()).await;
731		assert!(result.is_err());
732	}
733
734	#[tokio::test]
735	async fn test_list_users() {
736		let hasher = Argon2Hasher::new();
737		let mut manager = UserManager::new(hasher);
738
739		let user_data1 = CreateUserData {
740			username: "grace".to_string(),
741			email: "grace@example.com".to_string(),
742			password: "password123".to_string(),
743			is_active: true,
744			is_admin: false,
745		};
746
747		let user_data2 = CreateUserData {
748			username: "henry".to_string(),
749			email: "henry@example.com".to_string(),
750			password: "password123".to_string(),
751			is_active: true,
752			is_admin: false,
753		};
754
755		manager.create_user(user_data1).await.unwrap();
756		manager.create_user(user_data2).await.unwrap();
757
758		let users = manager.list_users().await;
759		assert_eq!(users.len(), 2);
760	}
761
762	#[tokio::test]
763	async fn test_verify_password() {
764		let hasher = Argon2Hasher::new();
765		let mut manager = UserManager::new(hasher);
766
767		let user_data = CreateUserData {
768			username: "iris".to_string(),
769			email: "iris@example.com".to_string(),
770			password: "mypassword".to_string(),
771			is_active: true,
772			is_admin: false,
773		};
774
775		let user = manager.create_user(user_data).await.unwrap();
776		assert!(
777			manager
778				.verify_password(&user.id.to_string(), "mypassword")
779				.await
780				.unwrap()
781		);
782		assert!(
783			!manager
784				.verify_password(&user.id.to_string(), "wrongpassword")
785				.await
786				.unwrap()
787		);
788	}
789
790	#[rstest::rstest]
791	#[tokio::test]
792	async fn test_create_user_with_email_missing_at_sign_returns_invalid_email() {
793		// Arrange
794		let hasher = Argon2Hasher::new();
795		let mut manager = UserManager::new(hasher);
796
797		let user_data = CreateUserData {
798			username: "testuser".to_string(),
799			email: "invalidemail.com".to_string(),
800			password: "password123".to_string(),
801			is_active: true,
802			is_admin: false,
803		};
804
805		// Act
806		let result = manager.create_user(user_data).await;
807
808		// Assert
809		assert_eq!(result.unwrap_err(), UserManagementError::InvalidEmail);
810	}
811
812	#[rstest::rstest]
813	#[tokio::test]
814	async fn test_create_user_with_email_missing_dot_returns_invalid_email() {
815		// Arrange
816		let hasher = Argon2Hasher::new();
817		let mut manager = UserManager::new(hasher);
818
819		let user_data = CreateUserData {
820			username: "testuser".to_string(),
821			email: "invalid@emailcom".to_string(),
822			password: "password123".to_string(),
823			is_active: true,
824			is_admin: false,
825		};
826
827		// Act
828		let result = manager.create_user(user_data).await;
829
830		// Assert
831		assert_eq!(result.unwrap_err(), UserManagementError::InvalidEmail);
832	}
833
834	#[rstest::rstest]
835	#[tokio::test]
836	async fn test_get_user_nonexistent_returns_user_not_found() {
837		// Arrange
838		let hasher = Argon2Hasher::new();
839		let manager = UserManager::new(hasher);
840		let nonexistent_id = Uuid::new_v4().to_string();
841
842		// Act
843		let result = manager.get_user(&nonexistent_id).await;
844
845		// Assert
846		assert_eq!(result.unwrap_err(), UserManagementError::UserNotFound);
847	}
848
849	#[rstest::rstest]
850	#[tokio::test]
851	async fn test_create_user_with_empty_email_succeeds() {
852		// Arrange
853		let hasher = Argon2Hasher::new();
854		let mut manager = UserManager::new(hasher);
855
856		let user_data = CreateUserData {
857			username: "jwt_user".to_string(),
858			email: String::new(),
859			password: "password123".to_string(),
860			is_active: true,
861			is_admin: false,
862		};
863
864		// Act
865		let result = manager.create_user(user_data).await;
866
867		// Assert
868		let user = result.unwrap();
869		assert_eq!(user.username, "jwt_user");
870		assert_eq!(user.email, "");
871		assert!(user.is_active);
872	}
873}