zoe_app_primitives/group/
states.rs

1use serde::{Deserialize, Serialize};
2use std::collections::{BTreeMap, HashSet};
3use zoe_wire_protocol::{MessageId, VerifyingKey};
4
5use super::events::roles::GroupRole;
6use super::events::{GroupActivityEvent, GroupSettings};
7use crate::{IdentityInfo, IdentityRef, IdentityType, Metadata, Permission};
8
9#[cfg(feature = "frb-api")]
10use flutter_rust_bridge::frb;
11
12/// Advanced identity and membership management for distributed groups.
13///
14/// `GroupMembership` handles the complex identity scenarios that arise in distributed,
15/// encrypted group communication. It separates cryptographic identity (via
16/// [`zoe_wire_protocol::VerifyingKey`]) from display identity (names, aliases).
17///
18/// ## 🎭 Identity Architecture
19///
20/// The system operates on a two-layer identity model:
21///
22/// ### Layer 1: Cryptographic Identity (VerifyingKeys)
23/// - Each participant has one or more [`zoe_wire_protocol::VerifyingKey`]s
24/// - These keys are used for message signing and verification
25/// - Keys are the fundamental unit of authentication and authorization
26/// - A key represents a device, account, or cryptographic identity
27///
28/// ### Layer 2: Display Identity (Aliases and Names)
29/// - Each key can declare multiple [`crate::IdentityType`] variants:
30///   - **Main Identity**: The primary identity for a key (often a real name)
31///   - **Aliases**: Secondary identities for role-playing, privacy, or context
32/// - Each identity can have associated [`crate::IdentityInfo`] with display names
33/// - Identities are what users see and interact with in the UI
34///
35/// ## 🔄 Use Cases and Benefits
36///
37/// ### Privacy and Pseudonymity
38/// ```text
39/// VerifyingKey(Alice_Device_1) ──┬─→ Main: "Alice Johnson"
40///                                ├─→ Alias: "ProjectLead"
41///                                └─→ Alias: "AnonymousReviewer"
42/// ```
43///
44/// Alice can participate in the same group with different personas:
45/// - Official communications as "Alice Johnson"
46/// - Project management as "ProjectLead"
47/// - Anonymous feedback as "AnonymousReviewer"
48///
49/// ### Multi-Device Identity
50/// ```text
51/// Real Person: Bob ──┬─→ VerifyingKey(Bob_Phone) ─→ Main: "Bob Smith"
52///                    └─→ VerifyingKey(Bob_Laptop) ─→ Main: "Bob Smith"
53/// ```
54///
55/// Bob can use multiple devices with the same display identity.
56///
57/// ### Role-Based Communication
58/// ```text
59/// VerifyingKey(Company_Bot) ──┬─→ Alias: "HR Bot"
60///                             ├─→ Alias: "Security Alert System"  
61///                             └─→ Alias: "Meeting Scheduler"
62/// ```
63///
64/// Automated systems can present different faces for different functions.
65///
66/// ## 🔒 Security and Authorization
67///
68/// ### Key-Based Authorization
69/// - All permissions and role assignments are tied to [`IdentityRef`] variants
70/// - An [`IdentityRef::Key`] directly authorizes the key holder
71/// - An [`IdentityRef::Alias`] authorizes only if the key controls that alias
72/// - Use [`GroupMembership::is_authorized`] to check if a key can act as an identity
73///
74/// ### Self-Sovereign Identity Declaration
75/// - Only a key can declare identities for itself
76/// - Other participants cannot assign aliases to someone else's key
77/// - Identity information is cryptographically signed by the declaring key
78/// - Malicious identity claims are prevented by signature verification
79///
80/// ## 📊 Data Structure
81///
82/// ### Identity Storage
83/// - [`GroupMembership::identity_info`]: Maps `(VerifyingKey, IdentityType) → IdentityInfo`
84/// - Stores display names and metadata for each declared identity
85/// - Multiple identities per key are fully supported
86///
87/// ### Role Assignments  
88/// - [`GroupMembership::identity_roles`]: Maps `IdentityRef → GroupRole`
89/// - Roles can be assigned to specific identities, not just keys
90/// - Enables fine-grained permission control per identity
91///
92/// ## 🔧 Core Operations
93///
94/// ### Identity Discovery
95/// - [`GroupMembership::get_available_identities`]: Find all identities a key can use
96/// - [`GroupMembership::get_display_name`]: Get human-readable name for an identity
97/// - [`GroupMembership::has_identity_info`]: Check if identity has been declared
98///
99/// ### Role Management
100/// - [`GroupMembership::get_role`]: Get the role assigned to a specific identity
101/// - [`GroupMembership::get_effective_role`]: Get role when key acts as an alias
102/// - Roles default to [`super::events::roles::GroupRole::Member`] if not explicitly set
103///
104/// ## 💡 Usage Examples
105///
106/// ### Setting Up Multiple Identities
107/// ```rust
108/// use zoe_app_primitives::{GroupMembership, IdentityType, IdentityRef, IdentityInfo};
109/// use zoe_wire_protocol::KeyPair;
110/// use std::collections::HashMap;
111///
112/// let mut membership = GroupMembership::new();
113/// let alice_key = KeyPair::generate(&mut rand::rngs::OsRng).public_key();
114///
115/// // Alice declares her main identity
116/// let main_identity = IdentityInfo {
117///     display_name: "Alice Johnson".to_string(),
118///     metadata: vec![],
119/// };
120///
121/// // Alice declares an alias for anonymous feedback
122/// let anon_identity = IdentityInfo {
123///     display_name: "Anonymous Reviewer".to_string(),
124///     metadata: vec![],
125/// };
126///
127/// // In practice, these would be set via GroupManagementEvent::SetIdentity
128/// // Here we simulate the result of processing those events
129/// membership.identity_info.insert(
130///     IdentityRef::Key(alice_key.clone()),
131///     main_identity,
132/// );
133/// membership.identity_info.insert(
134///     IdentityRef::Alias { key: alice_key, alias: "anon".to_string() },
135///     anon_identity,
136/// );
137/// ```
138///
139/// ### Checking Authorization
140/// ```rust
141/// # use zoe_app_primitives::{GroupMembership, IdentityRef};
142/// # use zoe_wire_protocol::KeyPair;
143/// # let membership = GroupMembership::new();
144/// # let alice_key = KeyPair::generate(&mut rand::rngs::OsRng).public_key();
145///
146/// // Check if Alice can act as her main identity (always true)
147/// let main_ref = IdentityRef::Key(alice_key.clone());
148/// assert!(membership.is_authorized(&alice_key, &main_ref));
149///
150/// // Check if Alice can act as her anonymous alias
151/// let alias_ref = IdentityRef::Alias {
152///     key: alice_key.clone(),
153///     alias: "anon".to_string(),
154/// };
155/// assert!(membership.is_authorized(&alice_key, &alias_ref));
156///
157/// // Check if Alice can act as someone else's alias (false)
158/// let other_key = KeyPair::generate(&mut rand::rngs::OsRng).public_key();
159/// let other_alias = IdentityRef::Alias {
160///     key: other_key,
161///     alias: "not_alice".to_string(),
162/// };
163/// assert!(!membership.is_authorized(&alice_key, &other_alias));
164/// ```
165///
166/// ### Role-Based Access with Identities
167/// ```rust
168/// # use zoe_app_primitives::{GroupMembership, IdentityRef};
169/// # use zoe_app_primitives::events::roles::GroupRole;
170/// # use zoe_wire_protocol::KeyPair;
171/// # let mut membership = GroupMembership::new();
172/// # let alice_key = KeyPair::generate(&mut rand::rngs::OsRng).public_key();
173///
174/// // Assign admin role to Alice's main identity
175/// let main_ref = IdentityRef::Key(alice_key.clone());
176/// membership.identity_roles.insert(main_ref.clone(), GroupRole::Admin);
177///
178/// // Assign member role to Alice's anonymous alias
179/// let alias_ref = IdentityRef::Alias {
180///     key: alice_key,
181///     alias: "anon".to_string(),
182/// };
183/// membership.identity_roles.insert(alias_ref.clone(), GroupRole::Member);
184///
185/// // Check effective roles
186/// assert_eq!(
187///     membership.get_role(&main_ref),
188///     Some(GroupRole::Admin)
189/// );
190/// assert_eq!(
191///     membership.get_role(&alias_ref),
192///     Some(GroupRole::Member)
193/// );
194/// ```
195///
196/// ## 🌐 Integration with Group Events
197///
198/// Identity management integrates with the event system through:
199/// - [`super::events::GroupActivityEvent::SetIdentity`]: Declares new identities
200/// - [`super::events::GroupActivityEvent::AssignRole`]: Assigns roles to identities
201/// - Event processing updates the membership state automatically
202/// - All identity changes are part of the signed, encrypted event history
203///
204/// This ensures that identity management is:
205/// - **Auditable**: Full history of identity changes
206/// - **Consistent**: Same view across all group members  
207/// - **Secure**: Cryptographically signed and verified
208#[derive(Debug, Clone, Serialize, Deserialize)]
209pub struct GroupMembership {
210    /// Identity information for keys and their aliases: (key_bytes, identity_type) -> identity_info
211    /// Keys are ML-DSA verifying keys encoded as bytes for serialization compatibility
212    pub identity_info: BTreeMap<IdentityRef, IdentityInfo>,
213    /// Role assignments for identities (both keys and aliases)
214    pub identity_roles: BTreeMap<IdentityRef, GroupRole>,
215}
216
217impl GroupMembership {
218    /// Create a new empty membership state
219    pub fn new() -> Self {
220        Self {
221            identity_info: BTreeMap::new(),
222            identity_roles: BTreeMap::new(),
223        }
224    }
225
226    /// Check if a verifying key is authorized to act as a specific identity
227    pub fn is_authorized(&self, key: &VerifyingKey, identity_ref: &IdentityRef) -> bool {
228        // Check if this key controls the identity
229        // For now, we'll need to convert to bytes for comparison since IdentityRef expects Ed25519 keys
230        // This is a temporary compatibility layer
231        identity_ref.is_controlled_by(key)
232    }
233
234    /// Get all identities that a verifying key can act as
235    pub fn get_available_identities(&self, _key: &VerifyingKey) -> HashSet<IdentityRef> {
236        // For now, ML-DSA keys cannot act as Ed25519-based identities
237        // This will need to be updated when we fully transition to ML-DSA
238        // Return empty set as a temporary compatibility measure
239        HashSet::new()
240    }
241
242    /// Get the role for a specific identity
243    pub fn get_role(&self, identity_ref: &IdentityRef) -> Option<GroupRole> {
244        // Check for explicit role assignment first
245        if let Some(role) = self.identity_roles.get(identity_ref) {
246            return Some(role.clone());
247        }
248
249        // Fall back to default member role for any valid identity
250        Some(GroupRole::Member)
251    }
252
253    /// Get effective role when a key acts as a specific identity
254    pub fn get_effective_role(
255        &self,
256        _key: &VerifyingKey,
257        _acting_as_alias: &Option<String>,
258    ) -> Option<GroupRole> {
259        // For now, ML-DSA keys cannot act as Ed25519-based identities
260        // This will need to be updated when we fully transition to ML-DSA
261        // Return default member role as a temporary compatibility measure
262        Some(GroupRole::Member)
263    }
264
265    /// Get display name for an identity
266    pub fn get_display_name(&self, key: &VerifyingKey, identity_type: &IdentityType) -> String {
267        // For now, ML-DSA keys don't have identity info in the Ed25519-based system
268        // This will need to be updated when we fully transition to ML-DSA
269        // Fall back to default display
270        match identity_type {
271            IdentityType::Main => format!("ML-DSA Key:{key:?}"),
272            IdentityType::Alias { alias_id } => alias_id.clone(),
273        }
274    }
275
276    /// Check if an identity has been declared by a key
277    pub fn has_identity_info(&self, _key: &VerifyingKey, _identity_type: &IdentityType) -> bool {
278        // For now, ML-DSA keys don't have identity info in the Ed25519-based system
279        // This will need to be updated when we fully transition to ML-DSA
280        false
281    }
282}
283
284impl Default for GroupMembership {
285    fn default() -> Self {
286        Self::new()
287    }
288}
289
290/// Error types for group state operations
291#[derive(Debug, thiserror::Error)]
292pub enum GroupStateError {
293    #[error("Permission denied: {0}")]
294    PermissionDenied(String),
295
296    #[error("Member not found: {member} in group {group}")]
297    MemberNotFound { member: String, group: String },
298
299    #[error("State transition error: {0}")]
300    StateTransition(String),
301
302    #[error("Invalid operation: {0}")]
303    InvalidOperation(String),
304}
305
306/// Result type for group state operations
307pub type GroupStateResult<T> = Result<T, GroupStateError>;
308
309/// Runtime information about an active group member.
310///
311/// `GroupMember` tracks the runtime state of a participant in a group. This includes
312/// their role, activity timestamps, and member-specific metadata. It's distinct from
313/// the cryptographic identity and display identity managed by [`GroupMembership`].
314///
315/// ## 📊 Member State vs Identity State
316///
317/// - **GroupMember**: Runtime participation state (roles, activity, metadata)
318/// - **GroupMembership**: Identity management (aliases, display names, authorization)
319/// - **VerifyingKey**: Cryptographic identity (authentication, message signing)
320///
321/// These three layers work together to provide comprehensive member management:
322/// ```text
323/// VerifyingKey → GroupMember (runtime state) + GroupMembership (identity state)
324/// ```
325///
326/// ## 🔄 Lifecycle and State Transitions
327///
328/// 1. **Initial Creation**: When a key first participates, a `GroupMember` is created
329/// 2. **Activity Updates**: [`GroupMember::last_active`] updated with each message
330/// 3. **Role Changes**: [`GroupMember::role`] updated via role assignment events
331/// 4. **Metadata Updates**: [`GroupMember::metadata`] can store custom key-value data
332/// 5. **Departure**: `GroupMember` removed when user leaves (but could rejoin later)
333///
334/// ## 💡 Usage Examples
335///
336/// ### Tracking Member Activity
337/// ```rust
338/// use zoe_app_primitives::{GroupMember, IdentityRef, events::roles::GroupRole};
339/// use zoe_wire_protocol::KeyPair;
340/// use std::collections::BTreeMap;
341///
342/// let member_key = KeyPair::generate(&mut rand::rngs::OsRng).public_key();
343/// let join_time = 1234567890;
344///
345/// let mut member = GroupMember {
346///     key: IdentityRef::Key(member_key),
347///     role: GroupRole::Member,
348///     joined_at: join_time,
349///     last_active: join_time,
350///     metadata: vec![],
351/// };
352///
353/// // Update activity when they send a message
354/// member.last_active = join_time + 3600; // 1 hour later
355///
356/// // Check how long they've been inactive
357/// let current_time = join_time + 7200; // 2 hours later  
358/// let inactive_duration = current_time - member.last_active;
359/// assert_eq!(inactive_duration, 3600); // 1 hour inactive
360/// ```
361///
362/// ### Role-Based Member Management
363/// ```rust
364/// # use zoe_app_primitives::{GroupMember, IdentityRef, events::roles::GroupRole};
365/// # use zoe_wire_protocol::KeyPair;
366/// # use std::collections::BTreeMap;
367/// # let member_key = KeyPair::generate(&mut rand::rngs::OsRng).public_key();
368/// # let mut member = GroupMember {
369/// #     key: IdentityRef::Key(member_key), role: GroupRole::Member, joined_at: 0, last_active: 0,
370/// #     metadata: vec![],
371/// # };
372///
373/// // Promote member to moderator
374/// member.role = GroupRole::Moderator;
375///
376/// // Check permissions
377/// use zoe_app_primitives::Permission;
378/// assert!(member.role.has_permission(&Permission::AllMembers));
379/// assert!(member.role.has_permission(&Permission::ModeratorOrAbove));
380/// ```
381///
382/// ### Custom Member Metadata
383/// ```rust
384/// # use zoe_app_primitives::{GroupMember, IdentityRef, Metadata};
385/// # use zoe_wire_protocol::KeyPair;
386/// # let mut member = GroupMember {
387/// #     key: IdentityRef::Key(KeyPair::generate(&mut rand::rngs::OsRng).public_key()),
388/// #     role: zoe_app_primitives::events::roles::GroupRole::Member,
389/// #     joined_at: 0, last_active: 0, metadata: vec![],
390/// # };
391///
392/// // Store custom metadata about the member using structured types
393/// member.metadata.push(Metadata::Generic { key: "department".to_string(), value: "engineering".to_string() });
394/// member.metadata.push(Metadata::Generic { key: "team".to_string(), value: "backend".to_string() });
395/// member.metadata.push(Metadata::Generic { key: "timezone".to_string(), value: "UTC-8".to_string() });
396/// member.metadata.push(Metadata::Email("member@company.com".to_string()));
397///
398/// // Query metadata
399/// for meta in &member.metadata {
400///     match meta {
401///         Metadata::Generic { key, value } if key == "department" => {
402///             println!("Member is in {} department", value);
403///         }
404///         Metadata::Email(email) => {
405///             println!("Member email: {}", email);
406///         }
407///         _ => {}
408///     }
409/// }
410/// ```
411#[derive(Debug, Clone, Serialize, Deserialize)]
412pub struct GroupMember {
413    /// Member's public key encoded as bytes for serialization compatibility
414    pub key: IdentityRef,
415    /// Member's role in the group
416    pub role: GroupRole,
417    /// When they joined the group
418    pub joined_at: u64,
419    /// When they were last active
420    pub last_active: u64,
421    /// Member-specific metadata using structured types
422    pub metadata: Vec<Metadata>,
423}
424
425/// The complete runtime state of a distributed encrypted group.
426///
427/// `GroupState` represents the unified, authoritative state of a group at any point in time.
428/// It combines immutable group information (from [`super::events::GroupInfo`]) with runtime
429/// state such as active members, event history, and identity management.
430///
431/// ## 🏗️ Design Philosophy
432///
433/// This type unifies what were previously separate concerns:
434/// - **Static Group Information**: Name, settings, and structured metadata
435/// - **Dynamic Member State**: Active participants, roles, and activity tracking  
436/// - **Event History**: Audit trail and conflict resolution capability
437/// - **Identity Management**: Complex alias and display name handling
438///
439/// ## 🔄 Event-Sourced Architecture
440///
441/// Groups maintain state through event sourcing:
442/// ```text
443/// CreateGroup Event → Initial GroupState
444///        ↓
445/// Member Activity → Updated GroupState (new member added)
446///        ↓  
447/// Role Assignment → Updated GroupState (permissions changed)
448///        ↓
449/// Group Update → Updated GroupState (metadata modified)
450/// ```
451///
452/// Each event is applied via [`GroupState::apply_event`], ensuring consistency
453/// and providing an audit trail through [`GroupState::event_history`].
454///
455/// ## 🔐 Security and Access Control
456///
457/// ### Encryption-Based Membership
458/// - Anyone with the group's encryption key can participate
459/// - [`GroupState::members`] tracks known active participants, not access control
460/// - True access control is enforced by possession of the encryption key
461///
462/// ### Role-Based Permissions
463/// - Each member has a [`super::events::roles::GroupRole`] defining their capabilities
464/// - Permissions are checked via [`GroupState::check_permission`]
465/// - Role assignments are cryptographically signed and part of the event history
466///
467/// ### Identity Privacy
468/// - Members can use aliases within groups via [`GroupMembership`]
469/// - Display names can be set independently of cryptographic identities
470/// - Multiple aliases per [`zoe_wire_protocol::VerifyingKey`] are supported
471///
472/// ## 📊 Member Lifecycle
473///
474/// 1. **Discovery**: A user obtains the group encryption key through some secure channel
475/// 2. **Announcement**: User sends any [`super::events::GroupActivityEvent`] to announce participation
476/// 3. **Recognition**: Internal handling adds them to active member list
477/// 4. **Activity**: Member's [`GroupMember::last_active`] is updated with each message
478/// 5. **Departure**: [`super::events::GroupActivityEvent::LeaveGroup`] removes from active list
479///
480/// Note: Departure only removes from the active member tracking - the user still
481/// possesses the encryption key and could rejoin at any time.
482///
483/// ## 🏷️ Structured Metadata System
484///
485/// Metadata is stored as [`crate::Metadata`] variants rather than simple key-value pairs:
486/// - [`crate::Metadata::Description`]: Human-readable group description
487/// - [`crate::Metadata::Generic`]: Key-value pairs for backward compatibility
488/// - Future variants can add typed metadata (images, files, etc.)
489///
490/// Use [`GroupState::description()`] and [`GroupState::generic_metadata()`] for
491/// convenient access to common metadata patterns.
492///
493/// ## 🔗 Relationship to GroupInfo
494///
495/// [`super::events::GroupInfo`] is used for events (creation, updates) while
496/// `GroupState` represents the current runtime state:
497///
498/// ```text
499/// GroupInfo (in events) → GroupState (runtime) → GroupInfo (for updates)
500/// ```
501///
502/// Use [`GroupState::from_group_info`] and [`GroupState::to_group_info`] to
503/// convert between representations.
504///
505/// ## 💡 Usage Examples
506///
507/// ### Creating a Group State
508/// ```rust
509/// use zoe_app_primitives::{GroupState, GroupSettings, Metadata};
510/// use zoe_wire_protocol::KeyPair;
511/// use blake3::Hash;
512///
513/// let creator_key = KeyPair::generate(&mut rand::rngs::OsRng);
514/// let group_id = Hash::from([1u8; 32]);
515///
516/// let metadata = vec![
517///     Metadata::Description("Development team coordination".to_string()),
518///     Metadata::Generic { key: "department".to_string(), value: "engineering".to_string() },
519/// ];
520///
521/// let group_state = GroupState::new(
522///     group_id,
523///     "Dev Team".to_string(),
524///     GroupSettings::default(),
525///     metadata,
526///     creator_key.public_key(),
527///     1234567890,
528/// );
529///
530/// // Creator is automatically added as Owner
531/// assert_eq!(group_state.members.len(), 1);
532/// assert!(group_state.is_member(&creator_key.public_key()));
533/// ```
534///
535/// ### Processing Member Activity
536/// ```rust
537/// # use zoe_app_primitives::*;
538/// # use zoe_wire_protocol::KeyPair;
539/// # use blake3::Hash;
540/// # let mut group_state = GroupState::new(
541/// #     Hash::from([1u8; 32]), "Test".to_string(), GroupSettings::default(),
542/// #     vec![], KeyPair::generate(&mut rand::rngs::OsRng).public_key(), 1234567890
543/// # );
544///
545/// let new_member = KeyPair::generate(&mut rand::rngs::OsRng);
546/// let activity_event = GroupActivityEvent::Activity(());
547///
548/// // New member announces participation
549/// group_state.apply_event(
550///     &activity_event,
551///     Hash::from([2u8; 32]),
552///     new_member.public_key(),
553///     1234567891,
554/// ).unwrap();
555///
556/// // They're now tracked as an active member
557/// assert!(group_state.is_member(&new_member.public_key()));
558/// ```
559///
560/// ### Working with Metadata
561/// ```rust
562/// # use zoe_app_primitives::*;
563/// # use zoe_wire_protocol::KeyPair;
564/// # use blake3::Hash;
565/// # let group_state = GroupState::new(
566/// #     Hash::from([1u8; 32]), "Test".to_string(), GroupSettings::default(),
567/// #     vec![Metadata::Description("Test group".to_string())],
568/// #     KeyPair::generate(&mut rand::rngs::OsRng).public_key(), 1234567890
569/// # );
570///
571/// // Extract specific metadata types
572/// assert_eq!(group_state.description(), Some("Test group".to_string()));
573///
574/// // Get all generic metadata as a map
575/// let generic_meta = group_state.generic_metadata();
576/// ```
577#[derive(Debug, Clone, Serialize, Deserialize)]
578#[cfg_attr(feature = "frb-api", frb(opaque))]
579pub struct GroupState {
580    /// The group identifier - this is the Blake3 hash of the CreateGroup message
581    /// Also serves as the root event ID (used as channel tag)
582    pub group_id: MessageId,
583
584    /// Current group name
585    pub name: String,
586
587    /// Current group settings  
588    pub settings: GroupSettings,
589
590    /// Group metadata as structured types
591    pub metadata: Vec<Metadata>,
592
593    /// Runtime member state with roles and activity tracking
594    /// Keys are ML-DSA verifying keys encoded as bytes for serialization compatibility
595    pub members: BTreeMap<IdentityRef, GroupMember>,
596
597    /// Advanced identity management for aliases and display names
598    pub membership: GroupMembership,
599
600    /// Event history for this group (event ID -> event details)
601    pub event_history: Vec<MessageId>,
602
603    /// Last processed event timestamp (for ordering)
604    pub last_event_timestamp: u64,
605
606    /// State version (incremented on each event)
607    pub version: u64,
608}
609
610impl GroupState {
611    /// Create a new group state from a group creation event.
612    ///
613    /// This constructor sets up the initial state for a newly created group, including:
614    /// - Setting the creator as the first member with [`GroupRole::Owner`] role
615    /// - Initializing empty membership state for identity management
616    /// - Recording the group creation as the first event in history
617    /// - Setting initial timestamps and version number
618    ///
619    /// # Arguments
620    ///
621    /// * `group_id` - Blake3 hash of the group creation message (also serves as root event ID)
622    /// * `name` - Human-readable group name
623    /// * `settings` - Group configuration and permissions
624    /// * `metadata` - Structured metadata using [`crate::Metadata`] types
625    /// * `creator` - Public key of the group creator (becomes first Owner)
626    /// * `timestamp` - Unix timestamp of group creation
627    ///
628    /// # Returns
629    ///
630    /// A new `GroupState` with the creator as the sole member and owner.
631    ///
632    /// # Examples
633    ///
634    /// ```rust
635    /// use zoe_app_primitives::{GroupState, GroupSettings, Metadata, events::roles::GroupRole};
636    /// use zoe_wire_protocol::KeyPair;
637    /// use blake3::Hash;
638    ///
639    /// let creator_key = KeyPair::generate(&mut rand::rngs::OsRng);
640    /// let group_id = Hash::from([42u8; 32]);
641    ///
642    /// let metadata = vec![
643    ///     Metadata::Description("Team coordination space".to_string()),
644    ///     Metadata::Generic { key: "project".to_string(), value: "zoe-chat".to_string() },
645    /// ];
646    ///
647    /// let group_state = GroupState::new(
648    ///     group_id,
649    ///     "Engineering Team".to_string(),
650    ///     GroupSettings::default(),
651    ///     metadata,
652    ///     creator_key.public_key(),
653    ///     1640995200, // 2022-01-01 00:00:00 UTC
654    /// );
655    ///
656    /// // Verify initial state
657    /// assert_eq!(group_state.name, "Engineering Team");
658    /// assert_eq!(group_state.members.len(), 1);
659    /// assert_eq!(group_state.version, 1);
660    /// assert!(group_state.is_member(&creator_key.public_key()));
661    /// assert_eq!(
662    ///     group_state.member_role(&creator_key.public_key()),
663    ///     Some(&GroupRole::Owner)
664    /// );
665    /// ```
666    pub fn new(
667        group_id: MessageId,
668        name: String,
669        settings: GroupSettings,
670        metadata: Vec<Metadata>,
671        creator: VerifyingKey,
672        timestamp: u64,
673    ) -> Self {
674        let creator_ref = IdentityRef::Key(creator.clone());
675        let mut members = BTreeMap::new();
676        members.insert(
677            creator_ref.clone(),
678            GroupMember {
679                key: creator_ref.clone(),
680                role: GroupRole::Owner,
681                joined_at: timestamp,
682                last_active: timestamp,
683                metadata: vec![],
684            },
685        );
686
687        Self {
688            group_id,
689            name,
690            settings,
691            metadata,
692            members,
693            membership: GroupMembership::new(),
694            event_history: vec![group_id], // First event is the group creation
695            last_event_timestamp: timestamp,
696            version: 1,
697        }
698    }
699
700    /// Create a GroupState from existing GroupInfo (for compatibility)
701    pub fn from_group_info(
702        group_id: MessageId,
703        group_info: &super::events::GroupInfo,
704        creator: VerifyingKey,
705        timestamp: u64,
706    ) -> Self {
707        Self::new(
708            group_id,
709            group_info.name.clone(),
710            group_info.settings.clone(),
711            group_info.metadata.clone(),
712            creator,
713            timestamp,
714        )
715    }
716
717    /// Convert to GroupInfo for events (extracts the core group information)
718    pub fn to_group_info(&self, key_info: super::events::GroupKeyInfo) -> super::events::GroupInfo {
719        super::events::GroupInfo {
720            name: self.name.clone(),
721            settings: self.settings.clone(),
722            key_info,
723            metadata: self.metadata.clone(),
724        }
725    }
726
727    /// Apply an event to this group state, updating it according to event-sourced principles.
728    ///
729    /// This is the core method for updating group state. All state changes must go through
730    /// this method to ensure consistency, proper ordering, and audit trail maintenance.
731    /// Events are applied in chronological order to maintain deterministic state.
732    ///
733    /// # Event Processing
734    ///
735    /// The method handles several types of events:
736    /// - **Member Activity**: Any activity announces participation and updates last_active
737    /// - **Role Changes**: Updates member roles and permissions
738    /// - **Group Updates**: Modifies name, settings, and metadata  
739    /// - **Member Departure**: Removes members from active tracking
740    /// - **Identity Management**: Processes identity declarations and updates
741    ///
742    /// # Ordering and Consistency
743    ///
744    /// Events must be applied in timestamp order. The method will reject events with
745    /// timestamps older than the last processed event to maintain consistency across
746    /// all group participants.
747    ///
748    /// # Arguments
749    ///
750    /// * `event` - The group activity event to process
751    /// * `event_id` - Blake3 hash of the event message (for audit trail)
752    /// * `sender` - Public key of the event sender (for authorization)
753    /// * `timestamp` - Unix timestamp of the event (for ordering)
754    ///
755    /// # Returns
756    ///
757    /// `Ok(())` if the event was successfully applied, or [`GroupStateError`] if:
758    /// - Event timestamp is out of order
759    /// - Sender lacks required permissions
760    /// - Member is not found for role operations
761    /// - Other validation failures
762    ///
763    /// # Examples
764    ///
765    /// ```rust
766    /// use zoe_app_primitives::{GroupState, GroupActivityEvent, GroupSettings, Metadata};
767    /// use zoe_wire_protocol::KeyPair;
768    /// use blake3::Hash;
769    ///
770    /// let creator_key = KeyPair::generate(&mut rand::rngs::OsRng);
771    /// let new_member_key = KeyPair::generate(&mut rand::rngs::OsRng);
772    ///
773    /// let mut group_state = GroupState::new(
774    ///     Hash::from([1u8; 32]),
775    ///     "Test Group".to_string(),
776    ///     GroupSettings::default(),
777    ///     vec![],
778    ///     creator_key.public_key(),
779    ///     1000,
780    /// );
781    ///
782    /// // New member announces participation via activity
783    /// let activity_event = GroupActivityEvent::Activity(());
784    /// let event_id = Hash::from([2u8; 32]);
785    ///
786    /// group_state.apply_event(
787    ///     &activity_event,
788    ///     event_id,
789    ///     new_member_key.public_key(),
790    ///     1001, // Must be after creation timestamp
791    /// ).unwrap();
792    ///
793    /// // Member is now tracked in the group
794    /// assert!(group_state.is_member(&new_member_key.public_key()));
795    /// assert_eq!(group_state.members.len(), 2); // Creator + new member
796    /// assert_eq!(group_state.version, 2); // Version incremented
797    /// assert_eq!(group_state.event_history.len(), 2); // Event recorded
798    /// ```
799    ///
800    /// # State Transitions
801    ///
802    /// After each successful event application:
803    /// - [`GroupState::version`] is incremented
804    /// - [`GroupState::last_event_timestamp`] is updated
805    /// - [`GroupState::event_history`] includes the new event ID
806    /// - Specific state changes depend on the event type
807    pub fn apply_event<T>(
808        &mut self,
809        event: &GroupActivityEvent<T>,
810        event_id: MessageId,
811        sender: VerifyingKey,
812        timestamp: u64,
813    ) -> GroupStateResult<()> {
814        // Verify timestamp ordering (events should be processed in order)
815        if timestamp < self.last_event_timestamp {
816            return Err(GroupStateError::StateTransition(format!(
817                "Event timestamp {} is older than last processed timestamp {}",
818                timestamp, self.last_event_timestamp
819            )));
820        }
821
822        // Apply the specific event
823        match event {
824            GroupActivityEvent::LeaveGroup { message } => {
825                self.handle_leave_group(sender, message.clone(), timestamp)?;
826            }
827
828            GroupActivityEvent::UpdateGroup(group_info) => {
829                // Handle group updates
830                self.name = group_info.name.clone();
831                self.settings = group_info.settings.clone();
832                self.metadata = group_info.metadata.clone();
833            }
834
835            GroupActivityEvent::AssignRole { target, role } => {
836                self.handle_role_assignment(sender, target, role, timestamp)?;
837            }
838
839            GroupActivityEvent::SetIdentity(_) => {
840                // Handle identity setting - for now just ensure sender is a member
841                self.handle_member_announcement(sender, timestamp)?;
842            }
843
844            GroupActivityEvent::RemoveFromGroup { target: _ } => {
845                // For now, skip member removal for Ed25519-based identities when sender is ML-DSA
846                // This is a temporary compatibility limitation during the transition
847                // TODO: Implement proper key type conversion or dual-key support
848                // Note: Skipping member removal due to key type mismatch during ML-DSA transition
849            }
850
851            GroupActivityEvent::Unknown { discriminant, .. } => {
852                // Unknown management event - ignore for forward compatibility
853                // Future implementations could log this with: discriminant value {discriminant}
854                let _ = discriminant; // Acknowledge the discriminant without warning
855            }
856
857            GroupActivityEvent::Activity(_activity_data) => {
858                // Handle custom activity
859                self.handle_member_announcement(sender, timestamp)?;
860            }
861        }
862
863        // Update state metadata
864        self.event_history.push(event_id);
865        self.last_event_timestamp = timestamp;
866        self.version += 1;
867
868        Ok(())
869    }
870
871    /// Check if a member has permission to perform an action
872    pub fn check_permission(
873        &self,
874        member: &VerifyingKey,
875        required_permission: &Permission,
876    ) -> GroupStateResult<()> {
877        let member_ref = IdentityRef::Key(member.clone());
878        match self.members.get(&member_ref) {
879            Some(member_info) => {
880                if member_info.role.has_permission(required_permission) {
881                    Ok(())
882                } else {
883                    Err(GroupStateError::PermissionDenied(format!(
884                        "Member {:?} with role {:?} does not have required permission {:?}",
885                        member, member_info.role, required_permission
886                    )))
887                }
888            }
889            None => Err(GroupStateError::MemberNotFound {
890                member: format!("{member:?}"),
891                group: format!("{:?}", self.group_id),
892            }),
893        }
894    }
895
896    /// Handle a member announcing their participation in the group
897    /// In encrypted groups, anyone with the key can participate
898    fn handle_member_announcement(
899        &mut self,
900        sender: VerifyingKey,
901        timestamp: u64,
902    ) -> GroupStateResult<()> {
903        let sender_ref = IdentityRef::Key(sender.clone());
904        // Add or update member
905        if let Some(existing_member) = self.members.get_mut(&sender_ref) {
906            existing_member.last_active = timestamp;
907        } else {
908            // New member - anyone with the key can participate
909            self.members.insert(
910                sender_ref.clone(),
911                GroupMember {
912                    key: sender_ref.clone(),
913                    role: GroupRole::Member, // Default role for new key holders
914                    joined_at: timestamp,
915                    last_active: timestamp,
916                    metadata: vec![],
917                },
918            );
919        }
920
921        Ok(())
922    }
923
924    fn handle_leave_group(
925        &mut self,
926        sender: VerifyingKey,
927        _message: Option<String>,
928        _timestamp: u64,
929    ) -> GroupStateResult<()> {
930        let sender_ref = IdentityRef::Key(sender.clone());
931        // In encrypted groups, leaving is just an announcement - they still have the key
932        // This removes them from the active member list but doesn't revoke access
933        if !self.members.contains_key(&sender_ref) {
934            return Err(GroupStateError::MemberNotFound {
935                member: format!("{sender:?}"),
936                group: format!("{:?}", self.group_id),
937            });
938        }
939
940        // Remove from active members list
941        self.members.remove(&sender_ref);
942        Ok(())
943    }
944
945    /// Handle role assignment using IdentityRef
946    fn handle_role_assignment(
947        &mut self,
948        sender: VerifyingKey,
949        target: &IdentityRef,
950        role: &GroupRole,
951        _timestamp: u64,
952    ) -> GroupStateResult<()> {
953        // Check permission - sender must have permission to assign roles
954        self.check_permission(&sender, &self.settings.permissions.assign_roles)?;
955
956        // Check if target member exists
957        let member_info =
958            self.members
959                .get_mut(target)
960                .ok_or_else(|| GroupStateError::MemberNotFound {
961                    member: format!("{target:?}"),
962                    group: format!("{:?}", self.group_id),
963                })?;
964
965        // Update role
966        member_info.role = role.clone();
967        Ok(())
968    }
969
970    #[allow(dead_code)]
971    fn handle_update_member_role(
972        &mut self,
973        sender: VerifyingKey,
974        member: VerifyingKey,
975        role: GroupRole,
976    ) -> GroupStateResult<()> {
977        // Check permission
978        self.check_permission(&sender, &self.settings.permissions.assign_roles)?;
979
980        let member_ref = IdentityRef::Key(member.clone());
981        // Check if target member exists
982        let member_info =
983            self.members
984                .get_mut(&member_ref)
985                .ok_or_else(|| GroupStateError::MemberNotFound {
986                    member: format!("{member:?}"),
987                    group: format!("{:?}", self.group_id),
988                })?;
989
990        // Update role
991        member_info.role = role;
992        Ok(())
993    }
994
995    /// Get all active members
996    pub fn get_members(&self) -> &BTreeMap<IdentityRef, GroupMember> {
997        &self.members
998    }
999
1000    /// Check if a user is a member of this group
1001    pub fn is_member(&self, user: &VerifyingKey) -> bool {
1002        let user_ref = IdentityRef::Key(user.clone());
1003        self.members.contains_key(&user_ref)
1004    }
1005
1006    /// Get a member's role
1007    pub fn member_role(&self, user: &VerifyingKey) -> Option<&GroupRole> {
1008        let user_ref = IdentityRef::Key(user.clone());
1009        self.members.get(&user_ref).map(|m| &m.role)
1010    }
1011
1012    /// Extract the group description from structured metadata.
1013    ///
1014    /// This method searches through the structured [`crate::Metadata`] collection
1015    /// to find a [`crate::Metadata::Description`] variant and returns its value.
1016    /// This provides a convenient way to access the primary descriptive text
1017    /// for the group.
1018    ///
1019    /// # Returns
1020    ///
1021    /// `Some(description)` if a description metadata entry exists, `None` otherwise.
1022    ///
1023    /// # Examples
1024    ///
1025    /// ```rust
1026    /// use zoe_app_primitives::{GroupState, GroupSettings, Metadata};
1027    /// use zoe_wire_protocol::KeyPair;
1028    /// use blake3::Hash;
1029    ///
1030    /// let creator_key = KeyPair::generate(&mut rand::rngs::OsRng);
1031    ///
1032    /// // Group with description
1033    /// let metadata_with_desc = vec![
1034    ///     Metadata::Description("A team coordination space".to_string()),
1035    ///     Metadata::Generic { key: "category".to_string(), value: "work".to_string() },
1036    /// ];
1037    ///
1038    /// let group_state = GroupState::new(
1039    ///     Hash::from([1u8; 32]),
1040    ///     "Team Chat".to_string(),
1041    ///     GroupSettings::default(),
1042    ///     metadata_with_desc,
1043    ///     creator_key.public_key(),
1044    ///     1000,
1045    /// );
1046    ///
1047    /// assert_eq!(
1048    ///     group_state.description(),
1049    ///     Some("A team coordination space".to_string())
1050    /// );
1051    ///
1052    /// // Group without description
1053    /// let metadata_no_desc = vec![
1054    ///     Metadata::Generic { key: "category".to_string(), value: "work".to_string() },
1055    /// ];
1056    ///
1057    /// let group_state_no_desc = GroupState::new(
1058    ///     Hash::from([2u8; 32]),
1059    ///     "Another Group".to_string(),
1060    ///     GroupSettings::default(),
1061    ///     metadata_no_desc,
1062    ///     creator_key.public_key(),
1063    ///     1000,
1064    /// );
1065    ///
1066    /// assert_eq!(group_state_no_desc.description(), None);
1067    /// ```
1068    pub fn description(&self) -> Option<String> {
1069        self.metadata.iter().find_map(|m| match m {
1070            Metadata::Description(desc) => Some(desc.clone()),
1071            _ => None,
1072        })
1073    }
1074
1075    /// Extract generic key-value metadata as a BTreeMap for backward compatibility.
1076    ///
1077    /// This method filters the structured [`crate::Metadata`] collection to extract
1078    /// only the [`crate::Metadata::Generic`] variants and returns them as a
1079    /// [`std::collections::BTreeMap`]. This provides compatibility with code that
1080    /// expects simple key-value metadata storage.
1081    ///
1082    /// # Structured vs Generic Metadata
1083    ///
1084    /// The group system supports both structured metadata (typed variants like
1085    /// [`crate::Metadata::Description`]) and generic key-value pairs. This method
1086    /// extracts only the generic pairs, ignoring other metadata types.
1087    ///
1088    /// # Returns
1089    ///
1090    /// A [`std::collections::BTreeMap`] containing all generic metadata key-value pairs.
1091    /// The map will be empty if no generic metadata exists.
1092    ///
1093    /// # Examples
1094    ///
1095    /// ```rust
1096    /// use zoe_app_primitives::{GroupState, GroupSettings, Metadata};
1097    /// use zoe_wire_protocol::KeyPair;
1098    /// use blake3::Hash;
1099    ///
1100    /// let creator_key = KeyPair::generate(&mut rand::rngs::OsRng);
1101    ///
1102    /// let metadata = vec![
1103    ///     Metadata::Description("Team workspace".to_string()), // Not included in generic
1104    ///     Metadata::Generic { key: "department".to_string(), value: "engineering".to_string() },
1105    ///     Metadata::Generic { key: "project".to_string(), value: "zoe-chat".to_string() },
1106    ///     Metadata::Generic { key: "visibility".to_string(), value: "internal".to_string() },
1107    /// ];
1108    ///
1109    /// let group_state = GroupState::new(
1110    ///     Hash::from([1u8; 32]),
1111    ///     "Engineering Team".to_string(),
1112    ///     GroupSettings::default(),
1113    ///     metadata,
1114    ///     creator_key.public_key(),
1115    ///     1000,
1116    /// );
1117    ///
1118    /// let generic_meta = group_state.generic_metadata();
1119    ///
1120    /// // Only generic metadata is included (3 items, description excluded)
1121    /// assert_eq!(generic_meta.len(), 3);
1122    /// assert_eq!(generic_meta.get("department"), Some(&"engineering".to_string()));
1123    /// assert_eq!(generic_meta.get("project"), Some(&"zoe-chat".to_string()));
1124    /// assert_eq!(generic_meta.get("visibility"), Some(&"internal".to_string()));
1125    ///
1126    /// // Description is not in generic metadata
1127    /// assert!(!generic_meta.contains_key("description"));
1128    ///
1129    /// // But it's still accessible via the description() method
1130    /// assert_eq!(
1131    ///     group_state.description(),
1132    ///     Some("Team workspace".to_string())
1133    /// );
1134    /// ```
1135    ///
1136    /// # Use Cases
1137    ///
1138    /// This method is particularly useful for:
1139    /// - **Legacy Code Integration**: Existing code expecting simple key-value metadata
1140    /// - **Generic Queries**: Searching through all key-value pairs programmatically
1141    /// - **Serialization**: Converting to formats that don't support structured metadata
1142    /// - **Configuration**: Accessing arbitrary configuration key-value pairs
1143    pub fn generic_metadata(&self) -> BTreeMap<String, String> {
1144        let mut map = BTreeMap::new();
1145        for meta in &self.metadata {
1146            if let Metadata::Generic { key, value } = meta {
1147                map.insert(key.clone(), value.clone());
1148            }
1149        }
1150        map
1151    }
1152}
1153
1154#[cfg(test)]
1155mod tests {
1156    use super::*;
1157    use crate::group::events::roles::GroupRole;
1158    use crate::group::events::{GroupActivityEvent, GroupInfo, GroupKeyInfo, GroupSettings};
1159    use crate::{IdentityInfo, IdentityType, Metadata, Permission};
1160
1161    use rand::rngs::OsRng;
1162    use zoe_wire_protocol::{KeyPair, VerifyingKey};
1163
1164    // Helper functions for creating test data
1165    fn create_test_verifying_key() -> VerifyingKey {
1166        let keypair = KeyPair::generate(&mut OsRng);
1167        keypair.public_key()
1168    }
1169
1170    fn create_test_message_id(seed: u8) -> MessageId {
1171        MessageId::from_bytes([seed; 32])
1172    }
1173
1174    fn create_test_group_key_info() -> GroupKeyInfo {
1175        GroupKeyInfo::new_chacha20_poly1305(
1176            vec![1, 2, 3, 4],
1177            zoe_wire_protocol::crypto::KeyDerivationInfo {
1178                method: zoe_wire_protocol::crypto::KeyDerivationMethod::Bip39Argon2,
1179                salt: vec![1, 2, 3, 4, 5, 6, 7, 8],
1180                argon2_params: zoe_wire_protocol::crypto::Argon2Params::default(),
1181                context: "test-group-key".to_string(),
1182            },
1183        )
1184    }
1185
1186    fn create_test_group_info() -> GroupInfo {
1187        GroupInfo {
1188            name: "Test Group".to_string(),
1189            settings: GroupSettings::default(),
1190            key_info: create_test_group_key_info(),
1191            metadata: vec![
1192                Metadata::Description("Test group description".to_string()),
1193                Metadata::Generic {
1194                    key: "category".to_string(),
1195                    value: "test".to_string(),
1196                },
1197            ],
1198        }
1199    }
1200
1201    // GroupMembership Tests
1202    #[test]
1203    fn test_group_membership_new() {
1204        let membership = GroupMembership::new();
1205        assert!(membership.identity_info.is_empty());
1206        assert!(membership.identity_roles.is_empty());
1207    }
1208
1209    #[test]
1210    fn test_group_membership_default() {
1211        let membership = GroupMembership::default();
1212        assert!(membership.identity_info.is_empty());
1213        assert!(membership.identity_roles.is_empty());
1214    }
1215
1216    #[test]
1217    fn test_group_membership_is_authorized() {
1218        let membership = GroupMembership::new();
1219        let key = create_test_verifying_key();
1220
1221        // Test authorization for key identity
1222        let key_identity = IdentityRef::Key(key.clone());
1223        assert!(membership.is_authorized(&key, &key_identity));
1224
1225        // Test authorization for alias identity
1226        let alias_identity = IdentityRef::Alias {
1227            key: key.clone(),
1228            alias: "test_alias".to_string(),
1229        };
1230        assert!(membership.is_authorized(&key, &alias_identity));
1231
1232        // Test authorization failure for different key
1233        let other_key = create_test_verifying_key();
1234        let other_identity = IdentityRef::Key(other_key);
1235        assert!(!membership.is_authorized(&key, &other_identity));
1236    }
1237
1238    #[test]
1239    fn test_group_membership_get_role() {
1240        let mut membership = GroupMembership::new();
1241        let key = create_test_verifying_key();
1242        let identity = IdentityRef::Key(key);
1243
1244        // Test default role
1245        assert_eq!(membership.get_role(&identity), Some(GroupRole::Member));
1246
1247        // Test explicit role assignment
1248        membership
1249            .identity_roles
1250            .insert(identity.clone(), GroupRole::Admin);
1251        assert_eq!(membership.get_role(&identity), Some(GroupRole::Admin));
1252    }
1253
1254    #[test]
1255    fn test_group_membership_get_available_identities() {
1256        let membership = GroupMembership::new();
1257        let key = create_test_verifying_key();
1258
1259        // Currently returns empty set due to ML-DSA transition
1260        let identities = membership.get_available_identities(&key);
1261        assert!(identities.is_empty());
1262    }
1263
1264    #[test]
1265    fn test_group_membership_get_effective_role() {
1266        let membership = GroupMembership::new();
1267        let key = create_test_verifying_key();
1268
1269        // Currently returns default member role due to ML-DSA transition
1270        let role = membership.get_effective_role(&key, &None);
1271        assert_eq!(role, Some(GroupRole::Member));
1272
1273        let role_with_alias = membership.get_effective_role(&key, &Some("alias".to_string()));
1274        assert_eq!(role_with_alias, Some(GroupRole::Member));
1275    }
1276
1277    #[test]
1278    fn test_group_membership_get_display_name() {
1279        let membership = GroupMembership::new();
1280        let key = create_test_verifying_key();
1281
1282        // Test main identity display name
1283        let main_type = IdentityType::Main;
1284        let display_name = membership.get_display_name(&key, &main_type);
1285        assert!(display_name.starts_with("ML-DSA Key:"));
1286
1287        // Test alias identity display name
1288        let alias_type = IdentityType::Alias {
1289            alias_id: "test_alias".to_string(),
1290        };
1291        let alias_display_name = membership.get_display_name(&key, &alias_type);
1292        assert_eq!(alias_display_name, "test_alias");
1293    }
1294
1295    #[test]
1296    fn test_group_membership_has_identity_info() {
1297        let membership = GroupMembership::new();
1298        let key = create_test_verifying_key();
1299        let identity_type = IdentityType::Main;
1300
1301        // Currently returns false due to ML-DSA transition
1302        assert!(!membership.has_identity_info(&key, &identity_type));
1303    }
1304
1305    // GroupMember Tests
1306    #[test]
1307    fn test_group_member_creation() {
1308        let key = create_test_verifying_key();
1309        let identity_ref = IdentityRef::Key(key);
1310        let timestamp = 1234567890;
1311
1312        let member = GroupMember {
1313            key: identity_ref.clone(),
1314            role: GroupRole::Member,
1315            joined_at: timestamp,
1316            last_active: timestamp,
1317            metadata: vec![],
1318        };
1319
1320        assert_eq!(member.key, identity_ref);
1321        assert_eq!(member.role, GroupRole::Member);
1322        assert_eq!(member.joined_at, timestamp);
1323        assert_eq!(member.last_active, timestamp);
1324        assert!(member.metadata.is_empty());
1325    }
1326
1327    #[test]
1328    fn test_group_member_with_metadata() {
1329        let key = create_test_verifying_key();
1330        let identity_ref = IdentityRef::Key(key);
1331
1332        let metadata = vec![
1333            Metadata::Generic {
1334                key: "department".to_string(),
1335                value: "engineering".to_string(),
1336            },
1337            Metadata::Email("user@example.com".to_string()),
1338        ];
1339
1340        let member = GroupMember {
1341            key: identity_ref,
1342            role: GroupRole::Admin,
1343            joined_at: 1000,
1344            last_active: 2000,
1345            metadata: metadata.clone(),
1346        };
1347
1348        assert_eq!(member.metadata, metadata);
1349        assert_eq!(member.role, GroupRole::Admin);
1350    }
1351
1352    // GroupState Tests
1353    #[test]
1354    fn test_group_state_new() {
1355        let creator_key = create_test_verifying_key();
1356        let group_id = create_test_message_id(1);
1357        let timestamp = 1234567890;
1358
1359        let metadata = vec![
1360            Metadata::Description("Test group".to_string()),
1361            Metadata::Generic {
1362                key: "category".to_string(),
1363                value: "test".to_string(),
1364            },
1365        ];
1366
1367        let group_state = GroupState::new(
1368            group_id,
1369            "Test Group".to_string(),
1370            GroupSettings::default(),
1371            metadata.clone(),
1372            creator_key.clone(),
1373            timestamp,
1374        );
1375
1376        assert_eq!(group_state.group_id, group_id);
1377        assert_eq!(group_state.name, "Test Group");
1378        assert_eq!(group_state.metadata, metadata);
1379        assert_eq!(group_state.members.len(), 1);
1380        assert_eq!(group_state.version, 1);
1381        assert_eq!(group_state.last_event_timestamp, timestamp);
1382        assert_eq!(group_state.event_history.len(), 1);
1383        assert_eq!(group_state.event_history[0], group_id);
1384
1385        // Verify creator is added as owner
1386        assert!(group_state.is_member(&creator_key));
1387        assert_eq!(
1388            group_state.member_role(&creator_key),
1389            Some(&GroupRole::Owner)
1390        );
1391    }
1392
1393    #[test]
1394    fn test_group_state_from_group_info() {
1395        let creator_key = create_test_verifying_key();
1396        let group_id = create_test_message_id(2);
1397        let group_info = create_test_group_info();
1398        let timestamp = 1234567890;
1399
1400        let group_state =
1401            GroupState::from_group_info(group_id, &group_info, creator_key.clone(), timestamp);
1402
1403        assert_eq!(group_state.group_id, group_id);
1404        assert_eq!(group_state.name, group_info.name);
1405        assert_eq!(group_state.settings, group_info.settings);
1406        assert_eq!(group_state.metadata, group_info.metadata);
1407        assert!(group_state.is_member(&creator_key));
1408    }
1409
1410    #[test]
1411    fn test_group_state_to_group_info() {
1412        let creator_key = create_test_verifying_key();
1413        let group_id = create_test_message_id(3);
1414        let group_state = GroupState::new(
1415            group_id,
1416            "Test Group".to_string(),
1417            GroupSettings::default(),
1418            vec![],
1419            creator_key,
1420            1000,
1421        );
1422
1423        let key_info = create_test_group_key_info();
1424        let group_info = group_state.to_group_info(key_info.clone());
1425
1426        assert_eq!(group_info.name, group_state.name);
1427        assert_eq!(group_info.settings, group_state.settings);
1428        assert_eq!(group_info.key_info, key_info);
1429        assert_eq!(group_info.metadata, group_state.metadata);
1430    }
1431
1432    #[test]
1433    fn test_group_state_is_member() {
1434        let creator_key = create_test_verifying_key();
1435        let other_key = create_test_verifying_key();
1436        let group_state = GroupState::new(
1437            create_test_message_id(4),
1438            "Test".to_string(),
1439            GroupSettings::default(),
1440            vec![],
1441            creator_key.clone(),
1442            1000,
1443        );
1444
1445        assert!(group_state.is_member(&creator_key));
1446        assert!(!group_state.is_member(&other_key));
1447    }
1448
1449    #[test]
1450    fn test_group_state_member_role() {
1451        let creator_key = create_test_verifying_key();
1452        let group_state = GroupState::new(
1453            create_test_message_id(5),
1454            "Test".to_string(),
1455            GroupSettings::default(),
1456            vec![],
1457            creator_key.clone(),
1458            1000,
1459        );
1460
1461        assert_eq!(
1462            group_state.member_role(&creator_key),
1463            Some(&GroupRole::Owner)
1464        );
1465
1466        let non_member_key = create_test_verifying_key();
1467        assert_eq!(group_state.member_role(&non_member_key), None);
1468    }
1469
1470    #[test]
1471    fn test_group_state_get_members() {
1472        let creator_key = create_test_verifying_key();
1473        let group_state = GroupState::new(
1474            create_test_message_id(6),
1475            "Test".to_string(),
1476            GroupSettings::default(),
1477            vec![],
1478            creator_key.clone(),
1479            1000,
1480        );
1481
1482        let members = group_state.get_members();
1483        assert_eq!(members.len(), 1);
1484
1485        let creator_ref = IdentityRef::Key(creator_key);
1486        assert!(members.contains_key(&creator_ref));
1487    }
1488
1489    #[test]
1490    fn test_group_state_description() {
1491        let creator_key = create_test_verifying_key();
1492
1493        // Test with description
1494        let metadata_with_desc = vec![
1495            Metadata::Description("Test description".to_string()),
1496            Metadata::Generic {
1497                key: "other".to_string(),
1498                value: "value".to_string(),
1499            },
1500        ];
1501
1502        let group_state_with_desc = GroupState::new(
1503            create_test_message_id(7),
1504            "Test".to_string(),
1505            GroupSettings::default(),
1506            metadata_with_desc,
1507            creator_key.clone(),
1508            1000,
1509        );
1510
1511        assert_eq!(
1512            group_state_with_desc.description(),
1513            Some("Test description".to_string())
1514        );
1515
1516        // Test without description
1517        let metadata_no_desc = vec![Metadata::Generic {
1518            key: "other".to_string(),
1519            value: "value".to_string(),
1520        }];
1521
1522        let group_state_no_desc = GroupState::new(
1523            create_test_message_id(8),
1524            "Test".to_string(),
1525            GroupSettings::default(),
1526            metadata_no_desc,
1527            creator_key,
1528            1000,
1529        );
1530
1531        assert_eq!(group_state_no_desc.description(), None);
1532    }
1533
1534    #[test]
1535    fn test_group_state_generic_metadata() {
1536        let creator_key = create_test_verifying_key();
1537
1538        let metadata = vec![
1539            Metadata::Description("Not included".to_string()),
1540            Metadata::Generic {
1541                key: "category".to_string(),
1542                value: "test".to_string(),
1543            },
1544            Metadata::Generic {
1545                key: "priority".to_string(),
1546                value: "high".to_string(),
1547            },
1548        ];
1549
1550        let group_state = GroupState::new(
1551            create_test_message_id(9),
1552            "Test".to_string(),
1553            GroupSettings::default(),
1554            metadata,
1555            creator_key,
1556            1000,
1557        );
1558
1559        let generic_meta = group_state.generic_metadata();
1560        assert_eq!(generic_meta.len(), 2);
1561        assert_eq!(generic_meta.get("category"), Some(&"test".to_string()));
1562        assert_eq!(generic_meta.get("priority"), Some(&"high".to_string()));
1563        assert!(!generic_meta.contains_key("description"));
1564    }
1565
1566    // Event Processing Tests
1567    #[test]
1568    fn test_group_state_apply_activity_event() {
1569        let creator_key = create_test_verifying_key();
1570        let new_member_key = create_test_verifying_key();
1571        let mut group_state = GroupState::new(
1572            create_test_message_id(10),
1573            "Test".to_string(),
1574            GroupSettings::default(),
1575            vec![],
1576            creator_key,
1577            1000,
1578        );
1579
1580        let activity_event = GroupActivityEvent::Activity(());
1581        let event_id = create_test_message_id(11);
1582
1583        // New member announces participation
1584        let result =
1585            group_state.apply_event(&activity_event, event_id, new_member_key.clone(), 1001);
1586
1587        assert!(result.is_ok());
1588        assert!(group_state.is_member(&new_member_key));
1589        assert_eq!(group_state.members.len(), 2);
1590        assert_eq!(group_state.version, 2);
1591        assert_eq!(group_state.last_event_timestamp, 1001);
1592        assert_eq!(group_state.event_history.len(), 2);
1593        assert_eq!(group_state.event_history[1], event_id);
1594
1595        // Verify new member has default role
1596        assert_eq!(
1597            group_state.member_role(&new_member_key),
1598            Some(&GroupRole::Member)
1599        );
1600    }
1601
1602    #[test]
1603    fn test_group_state_apply_update_group_event() {
1604        let creator_key = create_test_verifying_key();
1605        let mut group_state = GroupState::new(
1606            create_test_message_id(12),
1607            "Original Name".to_string(),
1608            GroupSettings::default(),
1609            vec![],
1610            creator_key.clone(),
1611            1000,
1612        );
1613
1614        let new_group_info = GroupInfo {
1615            name: "Updated Name".to_string(),
1616            settings: GroupSettings::default(),
1617            key_info: create_test_group_key_info(),
1618            metadata: vec![Metadata::Description("Updated description".to_string())],
1619        };
1620
1621        let update_event: GroupActivityEvent<()> =
1622            GroupActivityEvent::UpdateGroup(new_group_info.clone());
1623        let event_id = create_test_message_id(13);
1624
1625        let result = group_state.apply_event(&update_event, event_id, creator_key.clone(), 1001);
1626
1627        assert!(result.is_ok());
1628        assert_eq!(group_state.name, "Updated Name");
1629        assert_eq!(group_state.settings, new_group_info.settings);
1630        assert_eq!(group_state.metadata, new_group_info.metadata);
1631        assert_eq!(group_state.version, 2);
1632    }
1633
1634    #[test]
1635    fn test_group_state_apply_leave_group_event() {
1636        let creator_key = create_test_verifying_key();
1637        let member_key = create_test_verifying_key();
1638        let mut group_state = GroupState::new(
1639            create_test_message_id(14),
1640            "Test".to_string(),
1641            GroupSettings::default(),
1642            vec![],
1643            creator_key,
1644            1000,
1645        );
1646
1647        // Add member first
1648        let activity_event = GroupActivityEvent::Activity(());
1649        group_state
1650            .apply_event(
1651                &activity_event,
1652                create_test_message_id(15),
1653                member_key.clone(),
1654                1001,
1655            )
1656            .unwrap();
1657
1658        assert!(group_state.is_member(&member_key));
1659        assert_eq!(group_state.members.len(), 2);
1660
1661        // Member leaves
1662        let leave_event: GroupActivityEvent<()> = GroupActivityEvent::LeaveGroup {
1663            message: Some("Goodbye".to_string()),
1664        };
1665        let result = group_state.apply_event(
1666            &leave_event,
1667            create_test_message_id(16),
1668            member_key.clone(),
1669            1002,
1670        );
1671
1672        assert!(result.is_ok());
1673        assert!(!group_state.is_member(&member_key));
1674        assert_eq!(group_state.members.len(), 1);
1675        assert_eq!(group_state.version, 3);
1676    }
1677
1678    #[test]
1679    fn test_group_state_apply_assign_role_event() {
1680        let creator_key = create_test_verifying_key();
1681        let member_key = create_test_verifying_key();
1682        let mut group_state = GroupState::new(
1683            create_test_message_id(17),
1684            "Test".to_string(),
1685            GroupSettings::default(),
1686            vec![],
1687            creator_key.clone(),
1688            1000,
1689        );
1690
1691        // Add member first
1692        let activity_event = GroupActivityEvent::Activity(());
1693        group_state
1694            .apply_event(
1695                &activity_event,
1696                create_test_message_id(18),
1697                member_key.clone(),
1698                1001,
1699            )
1700            .unwrap();
1701
1702        // Assign admin role
1703        let target_identity = IdentityRef::Key(member_key.clone());
1704        let assign_role_event: GroupActivityEvent<()> = GroupActivityEvent::AssignRole {
1705            target: target_identity,
1706            role: GroupRole::Admin,
1707        };
1708
1709        let result = group_state.apply_event(
1710            &assign_role_event,
1711            create_test_message_id(19),
1712            creator_key,
1713            1002,
1714        );
1715
1716        assert!(result.is_ok());
1717        assert_eq!(
1718            group_state.member_role(&member_key),
1719            Some(&GroupRole::Admin)
1720        );
1721        assert_eq!(group_state.version, 3);
1722    }
1723
1724    #[test]
1725    fn test_group_state_apply_event_timestamp_ordering() {
1726        let creator_key = create_test_verifying_key();
1727        let mut group_state = GroupState::new(
1728            create_test_message_id(20),
1729            "Test".to_string(),
1730            GroupSettings::default(),
1731            vec![],
1732            creator_key.clone(),
1733            1000,
1734        );
1735
1736        let activity_event = GroupActivityEvent::Activity(());
1737
1738        // Try to apply event with older timestamp
1739        let result = group_state.apply_event(
1740            &activity_event,
1741            create_test_message_id(21),
1742            creator_key,
1743            999, // Older than creation timestamp
1744        );
1745
1746        assert!(result.is_err());
1747        match result.unwrap_err() {
1748            GroupStateError::StateTransition(msg) => {
1749                assert!(msg.contains("older than last processed timestamp"));
1750            }
1751            _ => panic!("Expected StateTransition error"),
1752        }
1753    }
1754
1755    // Permission Tests
1756    #[test]
1757    fn test_group_state_check_permission_success() {
1758        let creator_key = create_test_verifying_key();
1759        let group_state = GroupState::new(
1760            create_test_message_id(22),
1761            "Test".to_string(),
1762            GroupSettings::default(),
1763            vec![],
1764            creator_key.clone(),
1765            1000,
1766        );
1767
1768        // Owner should have all permissions
1769        let result = group_state.check_permission(&creator_key, &Permission::OwnerOnly);
1770        assert!(result.is_ok());
1771
1772        let result = group_state.check_permission(&creator_key, &Permission::AdminOrAbove);
1773        assert!(result.is_ok());
1774
1775        let result = group_state.check_permission(&creator_key, &Permission::AllMembers);
1776        assert!(result.is_ok());
1777    }
1778
1779    #[test]
1780    fn test_group_state_check_permission_denied() {
1781        let creator_key = create_test_verifying_key();
1782        let member_key = create_test_verifying_key();
1783        let mut group_state = GroupState::new(
1784            create_test_message_id(23),
1785            "Test".to_string(),
1786            GroupSettings::default(),
1787            vec![],
1788            creator_key,
1789            1000,
1790        );
1791
1792        // Add member
1793        let activity_event = GroupActivityEvent::Activity(());
1794        group_state
1795            .apply_event(
1796                &activity_event,
1797                create_test_message_id(24),
1798                member_key.clone(),
1799                1001,
1800            )
1801            .unwrap();
1802
1803        // Member should not have owner-only permissions
1804        let result = group_state.check_permission(&member_key, &Permission::OwnerOnly);
1805        assert!(result.is_err());
1806        match result.unwrap_err() {
1807            GroupStateError::PermissionDenied(msg) => {
1808                assert!(msg.contains("does not have required permission"));
1809            }
1810            _ => panic!("Expected PermissionDenied error"),
1811        }
1812
1813        // But should have all-members permissions
1814        let result = group_state.check_permission(&member_key, &Permission::AllMembers);
1815        assert!(result.is_ok());
1816    }
1817
1818    #[test]
1819    fn test_group_state_check_permission_member_not_found() {
1820        let creator_key = create_test_verifying_key();
1821        let non_member_key = create_test_verifying_key();
1822        let group_state = GroupState::new(
1823            create_test_message_id(25),
1824            "Test".to_string(),
1825            GroupSettings::default(),
1826            vec![],
1827            creator_key,
1828            1000,
1829        );
1830
1831        let result = group_state.check_permission(&non_member_key, &Permission::AllMembers);
1832        assert!(result.is_err());
1833        match result.unwrap_err() {
1834            GroupStateError::MemberNotFound { .. } => {}
1835            _ => panic!("Expected MemberNotFound error"),
1836        }
1837    }
1838
1839    // Error Handling Tests
1840    #[test]
1841    fn test_group_state_error_display() {
1842        let error = GroupStateError::PermissionDenied("Test permission denied".to_string());
1843        assert_eq!(
1844            error.to_string(),
1845            "Permission denied: Test permission denied"
1846        );
1847
1848        let error = GroupStateError::MemberNotFound {
1849            member: "test_member".to_string(),
1850            group: "test_group".to_string(),
1851        };
1852        assert_eq!(
1853            error.to_string(),
1854            "Member not found: test_member in group test_group"
1855        );
1856
1857        let error = GroupStateError::StateTransition("Test transition error".to_string());
1858        assert_eq!(
1859            error.to_string(),
1860            "State transition error: Test transition error"
1861        );
1862
1863        let error = GroupStateError::InvalidOperation("Test invalid operation".to_string());
1864        assert_eq!(
1865            error.to_string(),
1866            "Invalid operation: Test invalid operation"
1867        );
1868    }
1869
1870    #[test]
1871    fn test_group_state_handle_leave_group_member_not_found() {
1872        let creator_key = create_test_verifying_key();
1873        let non_member_key = create_test_verifying_key();
1874        let mut group_state = GroupState::new(
1875            create_test_message_id(26),
1876            "Test".to_string(),
1877            GroupSettings::default(),
1878            vec![],
1879            creator_key,
1880            1000,
1881        );
1882
1883        let leave_event: GroupActivityEvent<()> = GroupActivityEvent::LeaveGroup { message: None };
1884        let result = group_state.apply_event(
1885            &leave_event,
1886            create_test_message_id(27),
1887            non_member_key,
1888            1001,
1889        );
1890
1891        assert!(result.is_err());
1892        match result.unwrap_err() {
1893            GroupStateError::MemberNotFound { .. } => {}
1894            _ => panic!("Expected MemberNotFound error"),
1895        }
1896    }
1897
1898    #[test]
1899    fn test_group_state_handle_role_assignment_member_not_found() {
1900        let creator_key = create_test_verifying_key();
1901        let non_member_key = create_test_verifying_key();
1902        let mut group_state = GroupState::new(
1903            create_test_message_id(28),
1904            "Test".to_string(),
1905            GroupSettings::default(),
1906            vec![],
1907            creator_key.clone(),
1908            1000,
1909        );
1910
1911        let target_identity = IdentityRef::Key(non_member_key);
1912        let assign_role_event: GroupActivityEvent<()> = GroupActivityEvent::AssignRole {
1913            target: target_identity,
1914            role: GroupRole::Admin,
1915        };
1916
1917        let result = group_state.apply_event(
1918            &assign_role_event,
1919            create_test_message_id(29),
1920            creator_key,
1921            1001,
1922        );
1923
1924        assert!(result.is_err());
1925        match result.unwrap_err() {
1926            GroupStateError::MemberNotFound { .. } => {}
1927            _ => panic!("Expected MemberNotFound error"),
1928        }
1929    }
1930
1931    #[test]
1932    fn test_group_state_handle_role_assignment_permission_denied() {
1933        let creator_key = create_test_verifying_key();
1934        let member_key = create_test_verifying_key();
1935        let target_key = create_test_verifying_key();
1936        let mut group_state = GroupState::new(
1937            create_test_message_id(30),
1938            "Test".to_string(),
1939            GroupSettings::default(),
1940            vec![],
1941            creator_key,
1942            1000,
1943        );
1944
1945        // Add both members
1946        let activity_event = GroupActivityEvent::Activity(());
1947        group_state
1948            .apply_event(
1949                &activity_event,
1950                create_test_message_id(31),
1951                member_key.clone(),
1952                1001,
1953            )
1954            .unwrap();
1955        group_state
1956            .apply_event(
1957                &activity_event,
1958                create_test_message_id(32),
1959                target_key.clone(),
1960                1002,
1961            )
1962            .unwrap();
1963
1964        // Regular member tries to assign role (should fail)
1965        let target_identity = IdentityRef::Key(target_key);
1966        let assign_role_event: GroupActivityEvent<()> = GroupActivityEvent::AssignRole {
1967            target: target_identity,
1968            role: GroupRole::Admin,
1969        };
1970
1971        let result = group_state.apply_event(
1972            &assign_role_event,
1973            create_test_message_id(33),
1974            member_key, // Regular member, not owner
1975            1003,
1976        );
1977
1978        assert!(result.is_err());
1979        match result.unwrap_err() {
1980            GroupStateError::PermissionDenied(_) => {}
1981            _ => panic!("Expected PermissionDenied error"),
1982        }
1983    }
1984
1985    // Serialization Tests
1986    #[test]
1987    fn test_postcard_serialization_group_membership() {
1988        let mut membership = GroupMembership::new();
1989        let key = create_test_verifying_key();
1990        let identity = IdentityRef::Key(key);
1991
1992        membership
1993            .identity_roles
1994            .insert(identity.clone(), GroupRole::Admin);
1995        membership.identity_info.insert(
1996            identity,
1997            IdentityInfo {
1998                display_name: "Test User".to_string(),
1999                metadata: vec![Metadata::Email("test@example.com".to_string())],
2000            },
2001        );
2002
2003        let serialized = postcard::to_stdvec(&membership).expect("Failed to serialize");
2004        let deserialized: GroupMembership =
2005            postcard::from_bytes(&serialized).expect("Failed to deserialize");
2006
2007        assert_eq!(
2008            membership.identity_roles.len(),
2009            deserialized.identity_roles.len()
2010        );
2011        assert_eq!(
2012            membership.identity_info.len(),
2013            deserialized.identity_info.len()
2014        );
2015    }
2016
2017    #[test]
2018    fn test_postcard_serialization_group_member() {
2019        let key = create_test_verifying_key();
2020        let identity_ref = IdentityRef::Key(key);
2021
2022        let member = GroupMember {
2023            key: identity_ref,
2024            role: GroupRole::Moderator,
2025            joined_at: 1234567890,
2026            last_active: 1234567900,
2027            metadata: vec![Metadata::Generic {
2028                key: "department".to_string(),
2029                value: "engineering".to_string(),
2030            }],
2031        };
2032
2033        let serialized = postcard::to_stdvec(&member).expect("Failed to serialize");
2034        let deserialized: GroupMember =
2035            postcard::from_bytes(&serialized).expect("Failed to deserialize");
2036
2037        assert_eq!(member.role, deserialized.role);
2038        assert_eq!(member.joined_at, deserialized.joined_at);
2039        assert_eq!(member.last_active, deserialized.last_active);
2040        assert_eq!(member.metadata, deserialized.metadata);
2041    }
2042
2043    #[test]
2044    fn test_postcard_serialization_group_state() {
2045        let creator_key = create_test_verifying_key();
2046        let group_state = GroupState::new(
2047            create_test_message_id(34),
2048            "Serialization Test Group".to_string(),
2049            GroupSettings::default(),
2050            vec![
2051                Metadata::Description("Test serialization".to_string()),
2052                Metadata::Generic {
2053                    key: "test".to_string(),
2054                    value: "value".to_string(),
2055                },
2056            ],
2057            creator_key,
2058            1234567890,
2059        );
2060
2061        let serialized = postcard::to_stdvec(&group_state).expect("Failed to serialize");
2062        let deserialized: GroupState =
2063            postcard::from_bytes(&serialized).expect("Failed to deserialize");
2064
2065        assert_eq!(group_state.group_id, deserialized.group_id);
2066        assert_eq!(group_state.name, deserialized.name);
2067        assert_eq!(group_state.settings, deserialized.settings);
2068        assert_eq!(group_state.metadata, deserialized.metadata);
2069        assert_eq!(group_state.members.len(), deserialized.members.len());
2070        assert_eq!(group_state.version, deserialized.version);
2071        assert_eq!(
2072            group_state.last_event_timestamp,
2073            deserialized.last_event_timestamp
2074        );
2075        assert_eq!(group_state.event_history, deserialized.event_history);
2076    }
2077
2078    // Integration Tests
2079    #[test]
2080    fn test_group_state_full_lifecycle() {
2081        let creator_key = create_test_verifying_key();
2082        let member1_key = create_test_verifying_key();
2083        let member2_key = create_test_verifying_key();
2084
2085        // Create group
2086        let mut group_state = GroupState::new(
2087            create_test_message_id(35),
2088            "Lifecycle Test Group".to_string(),
2089            GroupSettings::default(),
2090            vec![Metadata::Description("Full lifecycle test".to_string())],
2091            creator_key.clone(),
2092            1000,
2093        );
2094
2095        assert_eq!(group_state.members.len(), 1);
2096        assert_eq!(group_state.version, 1);
2097
2098        // Member 1 joins
2099        let activity_event = GroupActivityEvent::Activity(());
2100        group_state
2101            .apply_event(
2102                &activity_event,
2103                create_test_message_id(36),
2104                member1_key.clone(),
2105                1001,
2106            )
2107            .unwrap();
2108
2109        assert_eq!(group_state.members.len(), 2);
2110        assert_eq!(group_state.version, 2);
2111        assert!(group_state.is_member(&member1_key));
2112
2113        // Member 2 joins
2114        group_state
2115            .apply_event(
2116                &activity_event,
2117                create_test_message_id(37),
2118                member2_key.clone(),
2119                1002,
2120            )
2121            .unwrap();
2122
2123        assert_eq!(group_state.members.len(), 3);
2124        assert_eq!(group_state.version, 3);
2125
2126        // Creator promotes member1 to admin
2127        let promote_event: GroupActivityEvent<()> = GroupActivityEvent::AssignRole {
2128            target: IdentityRef::Key(member1_key.clone()),
2129            role: GroupRole::Admin,
2130        };
2131        group_state
2132            .apply_event(
2133                &promote_event,
2134                create_test_message_id(38),
2135                creator_key.clone(),
2136                1003,
2137            )
2138            .unwrap();
2139
2140        assert_eq!(
2141            group_state.member_role(&member1_key),
2142            Some(&GroupRole::Admin)
2143        );
2144        assert_eq!(group_state.version, 4);
2145
2146        // Creator (owner) promotes member2 to moderator (only owners can assign roles by default)
2147        let promote_event2: GroupActivityEvent<()> = GroupActivityEvent::AssignRole {
2148            target: IdentityRef::Key(member2_key.clone()),
2149            role: GroupRole::Moderator,
2150        };
2151        group_state
2152            .apply_event(
2153                &promote_event2,
2154                create_test_message_id(39),
2155                creator_key.clone(),
2156                1004,
2157            )
2158            .unwrap();
2159
2160        assert_eq!(
2161            group_state.member_role(&member2_key),
2162            Some(&GroupRole::Moderator)
2163        );
2164        assert_eq!(group_state.version, 5);
2165
2166        // Update group info
2167        let new_group_info = GroupInfo {
2168            name: "Updated Lifecycle Test Group".to_string(),
2169            settings: GroupSettings::default(),
2170            key_info: create_test_group_key_info(),
2171            metadata: vec![
2172                Metadata::Description("Updated description".to_string()),
2173                Metadata::Generic {
2174                    key: "status".to_string(),
2175                    value: "active".to_string(),
2176                },
2177            ],
2178        };
2179
2180        let update_event: GroupActivityEvent<()> =
2181            GroupActivityEvent::UpdateGroup(new_group_info.clone());
2182        group_state
2183            .apply_event(
2184                &update_event,
2185                create_test_message_id(40),
2186                creator_key.clone(),
2187                1005,
2188            )
2189            .unwrap();
2190
2191        assert_eq!(group_state.name, "Updated Lifecycle Test Group");
2192        assert_eq!(group_state.metadata, new_group_info.metadata);
2193        assert_eq!(group_state.version, 6);
2194
2195        // Member2 leaves
2196        let leave_event: GroupActivityEvent<()> = GroupActivityEvent::LeaveGroup {
2197            message: Some("Goodbye!".to_string()),
2198        };
2199        group_state
2200            .apply_event(
2201                &leave_event,
2202                create_test_message_id(41),
2203                member2_key.clone(),
2204                1006,
2205            )
2206            .unwrap();
2207
2208        assert!(!group_state.is_member(&member2_key));
2209        assert_eq!(group_state.members.len(), 2);
2210        assert_eq!(group_state.version, 7);
2211
2212        // Verify final state
2213        assert_eq!(group_state.event_history.len(), 7);
2214        assert_eq!(group_state.last_event_timestamp, 1006);
2215        assert!(group_state.is_member(&creator_key));
2216        assert!(group_state.is_member(&member1_key));
2217        assert!(!group_state.is_member(&member2_key));
2218    }
2219
2220    #[test]
2221    fn test_group_state_concurrent_member_activity() {
2222        let creator_key = create_test_verifying_key();
2223        let member_key = create_test_verifying_key();
2224        let mut group_state = GroupState::new(
2225            create_test_message_id(42),
2226            "Concurrent Test".to_string(),
2227            GroupSettings::default(),
2228            vec![],
2229            creator_key,
2230            1000,
2231        );
2232
2233        // Member joins
2234        let activity_event = GroupActivityEvent::Activity(());
2235        group_state
2236            .apply_event(
2237                &activity_event,
2238                create_test_message_id(43),
2239                member_key.clone(),
2240                1001,
2241            )
2242            .unwrap();
2243
2244        let initial_last_active = group_state
2245            .members
2246            .get(&IdentityRef::Key(member_key.clone()))
2247            .unwrap()
2248            .last_active;
2249        assert_eq!(initial_last_active, 1001);
2250
2251        // Member is active again (should update last_active)
2252        group_state
2253            .apply_event(
2254                &activity_event,
2255                create_test_message_id(44),
2256                member_key.clone(),
2257                1010,
2258            )
2259            .unwrap();
2260
2261        let updated_last_active = group_state
2262            .members
2263            .get(&IdentityRef::Key(member_key))
2264            .unwrap()
2265            .last_active;
2266        assert_eq!(updated_last_active, 1010);
2267        assert_eq!(group_state.members.len(), 2); // Should still be 2 members
2268    }
2269}