Skip to main content

reinhardt_auth/
group_management.rs

1//! Group Management
2//!
3//! Provides group management and permission assignment functionality.
4
5use std::collections::{HashMap, HashSet};
6use std::sync::{Arc, OnceLock};
7use tokio::sync::RwLock;
8use uuid::Uuid;
9
10// ---------------------------------------------------------------------------
11// Global GroupManager registry
12// ---------------------------------------------------------------------------
13
14static GLOBAL_GROUP_MANAGER: OnceLock<Arc<GroupManager>> = OnceLock::new();
15
16/// Register a global `GroupManager` instance.
17///
18/// Once registered, `PermissionsMixin::get_group_permissions()` will
19/// automatically resolve group permissions via this manager.
20///
21/// # Panics
22///
23/// Panics if called more than once (the global slot is write-once).
24pub fn register_group_manager(manager: Arc<GroupManager>) {
25	GLOBAL_GROUP_MANAGER
26		.set(manager)
27		.expect("GroupManager has already been registered");
28}
29
30/// Retrieve the global `GroupManager`, if registered.
31pub fn get_group_manager() -> Option<&'static Arc<GroupManager>> {
32	GLOBAL_GROUP_MANAGER.get()
33}
34
35/// Group management error
36#[non_exhaustive]
37#[derive(Debug, Clone, PartialEq, Eq)]
38pub enum GroupManagementError {
39	/// Group not found
40	GroupNotFound,
41	/// Group already exists
42	GroupAlreadyExists,
43	/// Invalid group name
44	InvalidGroupName,
45	/// User not found
46	UserNotFound,
47	/// Other error
48	Other(String),
49}
50
51impl std::fmt::Display for GroupManagementError {
52	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
53		match self {
54			GroupManagementError::GroupNotFound => write!(f, "Group not found"),
55			GroupManagementError::GroupAlreadyExists => write!(f, "Group already exists"),
56			GroupManagementError::InvalidGroupName => write!(f, "Invalid group name"),
57			GroupManagementError::UserNotFound => write!(f, "User not found"),
58			GroupManagementError::Other(msg) => write!(f, "Error: {}", msg),
59		}
60	}
61}
62
63impl std::error::Error for GroupManagementError {}
64
65/// Group management result
66pub type GroupManagementResult<T> = Result<T, GroupManagementError>;
67
68/// User group
69///
70/// When the `database` feature is enabled, this struct is also a database model
71/// mapped to the `auth_group` table.
72///
73/// # Examples
74///
75/// ```
76/// use reinhardt_auth::group_management::Group;
77/// use uuid::Uuid;
78///
79/// let group = Group {
80///     id: Uuid::new_v4(),
81///     name: "Editors".to_string(),
82///     description: Some("Content editors".to_string()),
83/// };
84///
85/// assert_eq!(group.name, "Editors");
86/// ```
87#[cfg_attr(
88	feature = "database",
89	reinhardt_core::macros::model(app_label = "auth", table_name = "auth_group")
90)]
91#[derive(Debug, Clone, PartialEq, Eq)]
92#[cfg_attr(feature = "database", derive(serde::Serialize, serde::Deserialize))]
93pub struct Group {
94	/// Unique identifier for the group.
95	#[cfg_attr(feature = "database", field(primary_key = true))]
96	pub id: Uuid,
97	/// Name of the group.
98	#[cfg_attr(feature = "database", field(max_length = 150, unique = true))]
99	pub name: String,
100	/// Optional description of the group's purpose.
101	#[cfg_attr(feature = "database", field(max_length = 500, null = true))]
102	pub description: Option<String>,
103}
104
105/// Group creation data
106///
107/// # Examples
108///
109/// ```
110/// use reinhardt_auth::group_management::CreateGroupData;
111///
112/// let data = CreateGroupData {
113///     name: "Admins".to_string(),
114///     description: Some("System administrators".to_string()),
115/// };
116///
117/// assert_eq!(data.name, "Admins");
118/// ```
119#[derive(Debug, Clone, PartialEq, Eq)]
120pub struct CreateGroupData {
121	/// Name for the new group.
122	pub name: String,
123	/// Optional description for the new group.
124	pub description: Option<String>,
125}
126
127/// Group manager
128///
129/// Manages groups and their permissions.
130///
131/// # Examples
132///
133/// ```
134/// use reinhardt_auth::group_management::{GroupManager, CreateGroupData};
135///
136/// #[tokio::main]
137/// async fn main() {
138///     let mut manager = GroupManager::new();
139///
140///     // Create group
141///     let group_data = CreateGroupData {
142///         name: "Editors".to_string(),
143///         description: Some("Content editors".to_string()),
144///     };
145///
146///     let group = manager.create_group(group_data).await.unwrap();
147///     assert_eq!(group.name, "Editors");
148///
149///     // Add permissions to group
150///     manager.add_group_permission(&group.id.to_string(), "blog.add_article").await.unwrap();
151///     manager.add_group_permission(&group.id.to_string(), "blog.change_article").await.unwrap();
152///
153///     // Add user to group
154///     manager.add_user_to_group("alice", &group.id.to_string()).await.unwrap();
155///
156///     // Check user permissions
157///     let perms = manager.get_user_permissions("alice").await.unwrap();
158///     assert!(perms.contains(&"blog.add_article".to_string()));
159/// }
160/// ```
161#[derive(Debug)]
162pub struct GroupManager {
163	groups: Arc<RwLock<HashMap<Uuid, Group>>>,
164	group_index: Arc<RwLock<HashMap<String, Uuid>>>,
165	group_permissions: Arc<RwLock<HashMap<Uuid, HashSet<String>>>>,
166	user_groups: Arc<RwLock<HashMap<String, HashSet<Uuid>>>>,
167}
168
169impl GroupManager {
170	/// Create a new group manager
171	///
172	/// # Examples
173	///
174	/// ```
175	/// use reinhardt_auth::group_management::GroupManager;
176	///
177	/// let manager = GroupManager::new();
178	/// ```
179	pub fn new() -> Self {
180		Self {
181			groups: Arc::new(RwLock::new(HashMap::new())),
182			group_index: Arc::new(RwLock::new(HashMap::new())),
183			group_permissions: Arc::new(RwLock::new(HashMap::new())),
184			user_groups: Arc::new(RwLock::new(HashMap::new())),
185		}
186	}
187
188	/// Create a new group
189	///
190	/// # Examples
191	///
192	/// ```
193	/// use reinhardt_auth::group_management::{GroupManager, CreateGroupData};
194	///
195	/// #[tokio::main]
196	/// async fn main() {
197	///     let mut manager = GroupManager::new();
198	///
199	///     let group_data = CreateGroupData {
200	///         name: "Moderators".to_string(),
201	///         description: Some("Content moderators".to_string()),
202	///     };
203	///
204	///     let group = manager.create_group(group_data).await.unwrap();
205	///     assert_eq!(group.name, "Moderators");
206	/// }
207	/// ```
208	pub async fn create_group(&mut self, data: CreateGroupData) -> GroupManagementResult<Group> {
209		// Validate group name
210		if data.name.is_empty() || data.name.len() < 2 {
211			return Err(GroupManagementError::InvalidGroupName);
212		}
213
214		// Check if group already exists
215		let group_index = self.group_index.read().await;
216		if group_index.contains_key(&data.name) {
217			return Err(GroupManagementError::GroupAlreadyExists);
218		}
219		drop(group_index);
220
221		// Create group
222		let group = Group {
223			id: Uuid::new_v4(),
224			name: data.name.clone(),
225			description: data.description,
226		};
227
228		// Store group
229		let mut groups = self.groups.write().await;
230		let mut group_index = self.group_index.write().await;
231
232		groups.insert(group.id, group.clone());
233		group_index.insert(data.name, group.id);
234
235		Ok(group)
236	}
237
238	/// Get group by ID
239	///
240	/// # Examples
241	///
242	/// ```
243	/// use reinhardt_auth::group_management::{GroupManager, CreateGroupData};
244	///
245	/// #[tokio::main]
246	/// async fn main() {
247	///     let mut manager = GroupManager::new();
248	///
249	///     let group_data = CreateGroupData {
250	///         name: "Viewers".to_string(),
251	///         description: None,
252	///     };
253	///
254	///     let group = manager.create_group(group_data).await.unwrap();
255	///     let retrieved = manager.get_group(&group.id.to_string()).await.unwrap();
256	///     assert_eq!(retrieved.name, "Viewers");
257	/// }
258	/// ```
259	pub async fn get_group(&self, group_id: &str) -> GroupManagementResult<Group> {
260		let uuid = Uuid::parse_str(group_id)
261			.map_err(|_| GroupManagementError::Other("Invalid UUID".to_string()))?;
262
263		let groups = self.groups.read().await;
264		groups
265			.get(&uuid)
266			.cloned()
267			.ok_or(GroupManagementError::GroupNotFound)
268	}
269
270	/// Get group by name
271	///
272	/// # Examples
273	///
274	/// ```
275	/// use reinhardt_auth::group_management::{GroupManager, CreateGroupData};
276	///
277	/// #[tokio::main]
278	/// async fn main() {
279	///     let mut manager = GroupManager::new();
280	///
281	///     let group_data = CreateGroupData {
282	///         name: "Contributors".to_string(),
283	///         description: None,
284	///     };
285	///
286	///     manager.create_group(group_data).await.unwrap();
287	///     let retrieved = manager.get_group_by_name("Contributors").await.unwrap();
288	///     assert_eq!(retrieved.name, "Contributors");
289	/// }
290	/// ```
291	pub async fn get_group_by_name(&self, name: &str) -> GroupManagementResult<Group> {
292		let group_index = self.group_index.read().await;
293		let group_id = group_index
294			.get(name)
295			.ok_or(GroupManagementError::GroupNotFound)?;
296
297		let groups = self.groups.read().await;
298		groups
299			.get(group_id)
300			.cloned()
301			.ok_or(GroupManagementError::GroupNotFound)
302	}
303
304	/// Delete group
305	///
306	/// # Examples
307	///
308	/// ```
309	/// use reinhardt_auth::group_management::{GroupManager, CreateGroupData};
310	///
311	/// #[tokio::main]
312	/// async fn main() {
313	///     let mut manager = GroupManager::new();
314	///
315	///     let group_data = CreateGroupData {
316	///         name: "TempGroup".to_string(),
317	///         description: None,
318	///     };
319	///
320	///     let group = manager.create_group(group_data).await.unwrap();
321	///     manager.delete_group(&group.id.to_string()).await.unwrap();
322	///     assert!(manager.get_group(&group.id.to_string()).await.is_err());
323	/// }
324	/// ```
325	pub async fn delete_group(&mut self, group_id: &str) -> GroupManagementResult<()> {
326		let uuid = Uuid::parse_str(group_id)
327			.map_err(|_| GroupManagementError::Other("Invalid UUID".to_string()))?;
328
329		let mut groups = self.groups.write().await;
330		let group = groups
331			.get(&uuid)
332			.ok_or(GroupManagementError::GroupNotFound)?
333			.clone();
334
335		let mut group_index = self.group_index.write().await;
336		let mut group_permissions = self.group_permissions.write().await;
337		let mut user_groups = self.user_groups.write().await;
338
339		groups.remove(&uuid);
340		group_index.remove(&group.name);
341		group_permissions.remove(&uuid);
342
343		// Remove group from all users
344		for user_group_set in user_groups.values_mut() {
345			user_group_set.remove(&uuid);
346		}
347
348		Ok(())
349	}
350
351	/// Add permission to group
352	///
353	/// # Examples
354	///
355	/// ```
356	/// use reinhardt_auth::group_management::{GroupManager, CreateGroupData};
357	///
358	/// #[tokio::main]
359	/// async fn main() {
360	///     let mut manager = GroupManager::new();
361	///
362	///     let group_data = CreateGroupData {
363	///         name: "Writers".to_string(),
364	///         description: None,
365	///     };
366	///
367	///     let group = manager.create_group(group_data).await.unwrap();
368	///     manager.add_group_permission(&group.id.to_string(), "blog.add_article").await.unwrap();
369	///
370	///     let perms = manager.get_group_permissions(&group.id.to_string()).await.unwrap();
371	///     assert!(perms.contains(&"blog.add_article".to_string()));
372	/// }
373	/// ```
374	pub async fn add_group_permission(
375		&mut self,
376		group_id: &str,
377		permission: &str,
378	) -> GroupManagementResult<()> {
379		let uuid = Uuid::parse_str(group_id)
380			.map_err(|_| GroupManagementError::Other("Invalid UUID".to_string()))?;
381
382		// Check if group exists
383		let groups = self.groups.read().await;
384		if !groups.contains_key(&uuid) {
385			return Err(GroupManagementError::GroupNotFound);
386		}
387		drop(groups);
388
389		let mut group_permissions = self.group_permissions.write().await;
390		group_permissions
391			.entry(uuid)
392			.or_default()
393			.insert(permission.to_string());
394
395		Ok(())
396	}
397
398	/// Remove permission from group
399	///
400	/// # Examples
401	///
402	/// ```
403	/// use reinhardt_auth::group_management::{GroupManager, CreateGroupData};
404	///
405	/// #[tokio::main]
406	/// async fn main() {
407	///     let mut manager = GroupManager::new();
408	///
409	///     let group_data = CreateGroupData {
410	///         name: "Reviewers".to_string(),
411	///         description: None,
412	///     };
413	///
414	///     let group = manager.create_group(group_data).await.unwrap();
415	///     manager.add_group_permission(&group.id.to_string(), "blog.view_article").await.unwrap();
416	///     manager.remove_group_permission(&group.id.to_string(), "blog.view_article").await.unwrap();
417	///
418	///     let perms = manager.get_group_permissions(&group.id.to_string()).await.unwrap();
419	///     assert!(!perms.contains(&"blog.view_article".to_string()));
420	/// }
421	/// ```
422	pub async fn remove_group_permission(
423		&mut self,
424		group_id: &str,
425		permission: &str,
426	) -> GroupManagementResult<()> {
427		let uuid = Uuid::parse_str(group_id)
428			.map_err(|_| GroupManagementError::Other("Invalid UUID".to_string()))?;
429
430		let mut group_permissions = self.group_permissions.write().await;
431		if let Some(perms) = group_permissions.get_mut(&uuid) {
432			perms.remove(permission);
433		}
434
435		Ok(())
436	}
437
438	/// Get group permissions
439	///
440	/// # Examples
441	///
442	/// ```
443	/// use reinhardt_auth::group_management::{GroupManager, CreateGroupData};
444	///
445	/// #[tokio::main]
446	/// async fn main() {
447	///     let mut manager = GroupManager::new();
448	///
449	///     let group_data = CreateGroupData {
450	///         name: "Publishers".to_string(),
451	///         description: None,
452	///     };
453	///
454	///     let group = manager.create_group(group_data).await.unwrap();
455	///     manager.add_group_permission(&group.id.to_string(), "blog.add_article").await.unwrap();
456	///     manager.add_group_permission(&group.id.to_string(), "blog.publish_article").await.unwrap();
457	///
458	///     let perms = manager.get_group_permissions(&group.id.to_string()).await.unwrap();
459	///     assert_eq!(perms.len(), 2);
460	/// }
461	/// ```
462	pub async fn get_group_permissions(
463		&self,
464		group_id: &str,
465	) -> GroupManagementResult<Vec<String>> {
466		let uuid = Uuid::parse_str(group_id)
467			.map_err(|_| GroupManagementError::Other("Invalid UUID".to_string()))?;
468
469		let group_permissions = self.group_permissions.read().await;
470		Ok(group_permissions
471			.get(&uuid)
472			.map(|perms| perms.iter().cloned().collect())
473			.unwrap_or_default())
474	}
475
476	/// Add user to group
477	///
478	/// # Examples
479	///
480	/// ```
481	/// use reinhardt_auth::group_management::{GroupManager, CreateGroupData};
482	///
483	/// #[tokio::main]
484	/// async fn main() {
485	///     let mut manager = GroupManager::new();
486	///
487	///     let group_data = CreateGroupData {
488	///         name: "Members".to_string(),
489	///         description: None,
490	///     };
491	///
492	///     let group = manager.create_group(group_data).await.unwrap();
493	///     manager.add_user_to_group("alice", &group.id.to_string()).await.unwrap();
494	///
495	///     let groups = manager.get_user_groups("alice").await.unwrap();
496	///     assert_eq!(groups.len(), 1);
497	/// }
498	/// ```
499	pub async fn add_user_to_group(
500		&mut self,
501		username: &str,
502		group_id: &str,
503	) -> GroupManagementResult<()> {
504		let uuid = Uuid::parse_str(group_id)
505			.map_err(|_| GroupManagementError::Other("Invalid UUID".to_string()))?;
506
507		// Check if group exists
508		let groups = self.groups.read().await;
509		if !groups.contains_key(&uuid) {
510			return Err(GroupManagementError::GroupNotFound);
511		}
512		drop(groups);
513
514		let mut user_groups = self.user_groups.write().await;
515		user_groups
516			.entry(username.to_string())
517			.or_default()
518			.insert(uuid);
519
520		Ok(())
521	}
522
523	/// Remove user from group
524	///
525	/// # Examples
526	///
527	/// ```
528	/// use reinhardt_auth::group_management::{GroupManager, CreateGroupData};
529	///
530	/// #[tokio::main]
531	/// async fn main() {
532	///     let mut manager = GroupManager::new();
533	///
534	///     let group_data = CreateGroupData {
535	///         name: "Staff".to_string(),
536	///         description: None,
537	///     };
538	///
539	///     let group = manager.create_group(group_data).await.unwrap();
540	///     manager.add_user_to_group("bob", &group.id.to_string()).await.unwrap();
541	///     manager.remove_user_from_group("bob", &group.id.to_string()).await.unwrap();
542	///
543	///     let groups = manager.get_user_groups("bob").await.unwrap();
544	///     assert_eq!(groups.len(), 0);
545	/// }
546	/// ```
547	pub async fn remove_user_from_group(
548		&mut self,
549		username: &str,
550		group_id: &str,
551	) -> GroupManagementResult<()> {
552		let uuid = Uuid::parse_str(group_id)
553			.map_err(|_| GroupManagementError::Other("Invalid UUID".to_string()))?;
554
555		let mut user_groups = self.user_groups.write().await;
556		if let Some(groups) = user_groups.get_mut(username) {
557			groups.remove(&uuid);
558		}
559
560		Ok(())
561	}
562
563	/// Get user groups
564	///
565	/// # Examples
566	///
567	/// ```
568	/// use reinhardt_auth::group_management::{GroupManager, CreateGroupData};
569	///
570	/// #[tokio::main]
571	/// async fn main() {
572	///     let mut manager = GroupManager::new();
573	///
574	///     let group_data1 = CreateGroupData {
575	///         name: "Team1".to_string(),
576	///         description: None,
577	///     };
578	///     let group_data2 = CreateGroupData {
579	///         name: "Team2".to_string(),
580	///         description: None,
581	///     };
582	///
583	///     let group1 = manager.create_group(group_data1).await.unwrap();
584	///     let group2 = manager.create_group(group_data2).await.unwrap();
585	///
586	///     manager.add_user_to_group("charlie", &group1.id.to_string()).await.unwrap();
587	///     manager.add_user_to_group("charlie", &group2.id.to_string()).await.unwrap();
588	///
589	///     let groups = manager.get_user_groups("charlie").await.unwrap();
590	///     assert_eq!(groups.len(), 2);
591	/// }
592	/// ```
593	pub async fn get_user_groups(&self, username: &str) -> GroupManagementResult<Vec<Group>> {
594		let user_groups = self.user_groups.read().await;
595		let group_ids = user_groups.get(username).cloned().unwrap_or_default();
596
597		let groups = self.groups.read().await;
598		Ok(group_ids
599			.iter()
600			.filter_map(|id| groups.get(id).cloned())
601			.collect())
602	}
603
604	/// Get user permissions from all groups
605	///
606	/// # Examples
607	///
608	/// ```
609	/// use reinhardt_auth::group_management::{GroupManager, CreateGroupData};
610	///
611	/// #[tokio::main]
612	/// async fn main() {
613	///     let mut manager = GroupManager::new();
614	///
615	///     let group_data = CreateGroupData {
616	///         name: "Developers".to_string(),
617	///         description: None,
618	///     };
619	///
620	///     let group = manager.create_group(group_data).await.unwrap();
621	///     manager.add_group_permission(&group.id.to_string(), "code.commit").await.unwrap();
622	///     manager.add_group_permission(&group.id.to_string(), "code.review").await.unwrap();
623	///     manager.add_user_to_group("diana", &group.id.to_string()).await.unwrap();
624	///
625	///     let perms = manager.get_user_permissions("diana").await.unwrap();
626	///     assert!(perms.contains(&"code.commit".to_string()));
627	///     assert!(perms.contains(&"code.review".to_string()));
628	/// }
629	/// ```
630	pub async fn get_user_permissions(&self, username: &str) -> GroupManagementResult<Vec<String>> {
631		let user_groups = self.user_groups.read().await;
632		let group_ids = user_groups.get(username).cloned().unwrap_or_default();
633
634		let group_permissions = self.group_permissions.read().await;
635		let mut all_permissions = HashSet::new();
636
637		for group_id in group_ids {
638			if let Some(perms) = group_permissions.get(&group_id) {
639				all_permissions.extend(perms.clone());
640			}
641		}
642
643		Ok(all_permissions.into_iter().collect())
644	}
645
646	/// List all groups
647	///
648	/// # Examples
649	///
650	/// ```
651	/// use reinhardt_auth::group_management::{GroupManager, CreateGroupData};
652	///
653	/// #[tokio::main]
654	/// async fn main() {
655	///     let mut manager = GroupManager::new();
656	///
657	///     let group_data1 = CreateGroupData {
658	///         name: "Group1".to_string(),
659	///         description: None,
660	///     };
661	///     let group_data2 = CreateGroupData {
662	///         name: "Group2".to_string(),
663	///         description: None,
664	///     };
665	///
666	///     manager.create_group(group_data1).await.unwrap();
667	///     manager.create_group(group_data2).await.unwrap();
668	///
669	///     let groups = manager.list_groups().await;
670	///     assert_eq!(groups.len(), 2);
671	/// }
672	/// ```
673	pub async fn list_groups(&self) -> Vec<Group> {
674		let groups = self.groups.read().await;
675		groups.values().cloned().collect()
676	}
677
678	/// Synchronously resolve permissions for the given group names.
679	///
680	/// Uses `try_read()` for non-blocking access to internal state.
681	/// Returns an empty set if the lock is held by a writer.
682	///
683	/// This method is designed for use in synchronous contexts such as
684	/// `PermissionsMixin::get_group_permissions()`.
685	pub fn get_permissions_for_groups_sync(
686		&self,
687		group_names: &[String],
688	) -> std::collections::HashSet<String> {
689		let index = match self.group_index.try_read() {
690			Ok(guard) => guard,
691			Err(_) => return std::collections::HashSet::new(),
692		};
693		let perms = match self.group_permissions.try_read() {
694			Ok(guard) => guard,
695			Err(_) => return std::collections::HashSet::new(),
696		};
697
698		let mut result = std::collections::HashSet::new();
699		for name in group_names {
700			if let Some(group_id) = index.get(name)
701				&& let Some(group_perms) = perms.get(group_id)
702			{
703				result.extend(group_perms.iter().cloned());
704			}
705		}
706		result
707	}
708}
709
710impl Default for GroupManager {
711	fn default() -> Self {
712		Self::new()
713	}
714}
715
716#[cfg(test)]
717mod tests {
718	use super::*;
719
720	#[tokio::test]
721	async fn test_create_group() {
722		let mut manager = GroupManager::new();
723
724		let group_data = CreateGroupData {
725			name: "Editors".to_string(),
726			description: Some("Content editors".to_string()),
727		};
728
729		let group = manager.create_group(group_data).await.unwrap();
730		assert_eq!(group.name, "Editors");
731		assert_eq!(group.description, Some("Content editors".to_string()));
732	}
733
734	#[tokio::test]
735	async fn test_get_group() {
736		let mut manager = GroupManager::new();
737
738		let group_data = CreateGroupData {
739			name: "Moderators".to_string(),
740			description: None,
741		};
742
743		let group = manager.create_group(group_data).await.unwrap();
744		let retrieved = manager.get_group(&group.id.to_string()).await.unwrap();
745		assert_eq!(retrieved.name, "Moderators");
746	}
747
748	#[tokio::test]
749	async fn test_get_group_by_name() {
750		let mut manager = GroupManager::new();
751
752		let group_data = CreateGroupData {
753			name: "Viewers".to_string(),
754			description: None,
755		};
756
757		manager.create_group(group_data).await.unwrap();
758		let retrieved = manager.get_group_by_name("Viewers").await.unwrap();
759		assert_eq!(retrieved.name, "Viewers");
760	}
761
762	#[tokio::test]
763	async fn test_delete_group() {
764		let mut manager = GroupManager::new();
765
766		let group_data = CreateGroupData {
767			name: "TempGroup".to_string(),
768			description: None,
769		};
770
771		let group = manager.create_group(group_data).await.unwrap();
772		manager.delete_group(&group.id.to_string()).await.unwrap();
773		let result = manager.get_group(&group.id.to_string()).await;
774		assert!(result.is_err());
775	}
776
777	#[tokio::test]
778	async fn test_group_permissions() {
779		let mut manager = GroupManager::new();
780
781		let group_data = CreateGroupData {
782			name: "Writers".to_string(),
783			description: None,
784		};
785
786		let group = manager.create_group(group_data).await.unwrap();
787		manager
788			.add_group_permission(&group.id.to_string(), "blog.add_article")
789			.await
790			.unwrap();
791		manager
792			.add_group_permission(&group.id.to_string(), "blog.change_article")
793			.await
794			.unwrap();
795
796		let perms = manager
797			.get_group_permissions(&group.id.to_string())
798			.await
799			.unwrap();
800		assert_eq!(perms.len(), 2);
801		assert!(perms.contains(&"blog.add_article".to_string()));
802		assert!(perms.contains(&"blog.change_article".to_string()));
803	}
804
805	#[tokio::test]
806	async fn test_user_groups() {
807		let mut manager = GroupManager::new();
808
809		let group_data1 = CreateGroupData {
810			name: "Team1".to_string(),
811			description: None,
812		};
813		let group_data2 = CreateGroupData {
814			name: "Team2".to_string(),
815			description: None,
816		};
817
818		let group1 = manager.create_group(group_data1).await.unwrap();
819		let group2 = manager.create_group(group_data2).await.unwrap();
820
821		manager
822			.add_user_to_group("alice", &group1.id.to_string())
823			.await
824			.unwrap();
825		manager
826			.add_user_to_group("alice", &group2.id.to_string())
827			.await
828			.unwrap();
829
830		let groups = manager.get_user_groups("alice").await.unwrap();
831		assert_eq!(groups.len(), 2);
832	}
833
834	#[tokio::test]
835	async fn test_user_permissions() {
836		let mut manager = GroupManager::new();
837
838		let group_data = CreateGroupData {
839			name: "Developers".to_string(),
840			description: None,
841		};
842
843		let group = manager.create_group(group_data).await.unwrap();
844		manager
845			.add_group_permission(&group.id.to_string(), "code.commit")
846			.await
847			.unwrap();
848		manager
849			.add_group_permission(&group.id.to_string(), "code.review")
850			.await
851			.unwrap();
852		manager
853			.add_user_to_group("bob", &group.id.to_string())
854			.await
855			.unwrap();
856
857		let perms = manager.get_user_permissions("bob").await.unwrap();
858		assert_eq!(perms.len(), 2);
859		assert!(perms.contains(&"code.commit".to_string()));
860		assert!(perms.contains(&"code.review".to_string()));
861	}
862
863	#[tokio::test]
864	async fn test_remove_user_from_group() {
865		let mut manager = GroupManager::new();
866
867		let group_data = CreateGroupData {
868			name: "Staff".to_string(),
869			description: None,
870		};
871
872		let group = manager.create_group(group_data).await.unwrap();
873		manager
874			.add_user_to_group("charlie", &group.id.to_string())
875			.await
876			.unwrap();
877		manager
878			.remove_user_from_group("charlie", &group.id.to_string())
879			.await
880			.unwrap();
881
882		let groups = manager.get_user_groups("charlie").await.unwrap();
883		assert_eq!(groups.len(), 0);
884	}
885
886	#[tokio::test]
887	async fn test_list_groups() {
888		let mut manager = GroupManager::new();
889
890		let group_data1 = CreateGroupData {
891			name: "Group1".to_string(),
892			description: None,
893		};
894		let group_data2 = CreateGroupData {
895			name: "Group2".to_string(),
896			description: None,
897		};
898
899		manager.create_group(group_data1).await.unwrap();
900		manager.create_group(group_data2).await.unwrap();
901
902		let groups = manager.list_groups().await;
903		assert_eq!(groups.len(), 2);
904	}
905}