use std::str::FromStr; use cm_domain::{AgentId, MessageId, SessionId, SessionKey, SessionKeyError}; fn sample() -> SessionKey { SessionKey { agent_id: AgentId::from_str("018f9c1e-8f2a-7c3b-9d4e-5f6a7b8c9d0e").unwrap(), shard: 3, session_id: SessionId::from_str("018f9c1e-9a1b-7c2d-8e3f-4a5b6c7d8e9f").unwrap(), message_id: MessageId::from_str("018f9c1e-ab2c-7d3e-9f4a-5b6c7d8e9f0a").unwrap(), } } #[test] fn formats_to_spec_layout() { let key = sample(); assert_eq!( key.to_string(), "agent:018f9c1e-8f2a-7c3b-9d4e-5f6a7b8c9d0e-claw-3\ :session:018f9c1e-9a1b-7c2d-8e3f-4a5b6c7d8e9f\ :018f9c1e-ab2c-7d3e-9f4a-5b6c7d8e9f0a" ); } #[test] fn parses_its_own_output() { let key = sample(); let parsed = SessionKey::from_str(&key.to_string()).unwrap(); assert_eq!(parsed, key); } #[test] fn rejects_missing_agent_prefix() { let err = SessionKey::from_str( "claw:018f9c1e-8f2a-7c3b-9d4e-5f6a7b8c9d0e-claw-3\ :session:018f9c1e-9a1b-7c2d-8e3f-4a5b6c7d8e9f\ :018f9c1e-ab2c-7d3e-9f4a-5b6c7d8e9f0a", ) .unwrap_err(); assert!(matches!(err, SessionKeyError::Malformed(_))); } #[test] fn rejects_missing_session_segment() { let err = SessionKey::from_str( "agent:018f9c1e-8f2a-7c3b-9d4e-5f6a7b8c9d0e-claw-3\ :chat:018f9c1e-9a1b-7c2d-8e3f-4a5b6c7d8e9f\ :018f9c1e-ab2c-7d3e-9f4a-5b6c7d8e9f0a", ) .unwrap_err(); assert!(matches!(err, SessionKeyError::Malformed(_))); } #[test] fn rejects_non_numeric_shard() { let err = SessionKey::from_str( "agent:018f9c1e-8f2a-7c3b-9d4e-5f6a7b8c9d0e-claw-x\ :session:018f9c1e-9a1b-7c2d-8e3f-4a5b6c7d8e9f\ :018f9c1e-ab2c-7d3e-9f4a-5b6c7d8e9f0a", ) .unwrap_err(); assert!(matches!(err, SessionKeyError::InvalidShard(_))); } #[test] fn rejects_invalid_agent_uuid() { let err = SessionKey::from_str( "agent:not-a-uuid-claw-3\ :session:018f9c1e-9a1b-7c2d-8e3f-4a5b6c7d8e9f\ :018f9c1e-ab2c-7d3e-9f4a-5b6c7d8e9f0a", ) .unwrap_err(); assert!(matches!(err, SessionKeyError::InvalidId(_))); } #[test] fn rejects_trailing_garbage() { let mut s = sample().to_string(); s.push_str(":extra"); let err = SessionKey::from_str(&s).unwrap_err(); assert!(matches!(err, SessionKeyError::Malformed(_))); } #[test] fn serde_round_trips_as_string() { let key = sample(); let json = serde_json::to_string(&key).unwrap(); assert_eq!(json, format!("\"{key}\"")); let back: SessionKey = serde_json::from_str(&json).unwrap(); assert_eq!(back, key); } mod properties { use super::*; use proptest::prelude::*; fn arb_uuid() -> impl Strategy { any::().prop_map(uuid::Uuid::from_u128) } proptest! { #[test] fn round_trips_for_any_ids( agent in arb_uuid(), session in arb_uuid(), message in arb_uuid(), shard in any::(), ) { let key = SessionKey { agent_id: AgentId::from(agent), shard, session_id: SessionId::from(session), message_id: MessageId::from(message), }; let parsed = SessionKey::from_str(&key.to_string()).unwrap(); prop_assert_eq!(parsed, key); } } }