1use anyhow::Result;
24use chrono::{DateTime, Utc};
25use once_cell::sync::Lazy;
26use parking_lot::RwLock;
27use serde::{Deserialize, Serialize};
28use std::collections::HashMap;
29use std::fmt;
30use std::future::Future;
31use std::pin::Pin;
32use std::sync::Arc;
33
34#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
56#[serde(rename_all = "lowercase")]
57pub enum InternalHookEventType {
58 Agent,
62
63 Session,
67
68 Tool,
72
73 Command,
77
78 Gateway,
82}
83
84impl fmt::Display for InternalHookEventType {
85 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
86 let s = match self {
87 InternalHookEventType::Agent => "agent",
88 InternalHookEventType::Session => "session",
89 InternalHookEventType::Tool => "tool",
90 InternalHookEventType::Command => "command",
91 InternalHookEventType::Gateway => "gateway",
92 };
93 write!(f, "{}", s)
94 }
95}
96
97#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
126#[serde(rename_all = "lowercase")]
127pub enum InternalHookAction {
128 Start,
133
134 Stop,
138
139 Error,
143
144 Bootstrap,
148
149 Create,
154
155 Resume,
159
160 End,
164
165 Compact,
169
170 Before,
175
176 After,
180 New,
187
188 Reset,
192
193 Status,
197
198 Help,
202
203 Connect,
208
209 Disconnect,
213
214 Message,
218}
219
220impl fmt::Display for InternalHookAction {
221 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
222 let s = match self {
223 InternalHookAction::Start => "start",
225 InternalHookAction::Stop => "stop",
226 InternalHookAction::Error => "error",
227 InternalHookAction::Bootstrap => "bootstrap",
228 InternalHookAction::Create => "create",
230 InternalHookAction::Resume => "resume",
231 InternalHookAction::End => "end",
232 InternalHookAction::Compact => "compact",
233 InternalHookAction::Before => "before",
235 InternalHookAction::After => "after",
236 InternalHookAction::New => "new",
238 InternalHookAction::Reset => "reset",
239 InternalHookAction::Status => "status",
240 InternalHookAction::Help => "help",
241 InternalHookAction::Connect => "connect",
243 InternalHookAction::Disconnect => "disconnect",
244 InternalHookAction::Message => "message",
245 };
246 write!(f, "{}", s)
247 }
248}
249
250#[derive(Debug, Clone, Serialize, Deserialize)]
289pub struct InternalHookEvent {
290 pub event_type: InternalHookEventType,
294
295 pub action: InternalHookAction,
299
300 pub session_key: Option<String>,
305
306 pub context: serde_json::Value,
316
317 pub timestamp: DateTime<Utc>,
321
322 pub messages: Vec<String>,
327}
328
329impl InternalHookEvent {
330 pub fn new(
358 event_type: InternalHookEventType,
359 action: InternalHookAction,
360 session_key: Option<String>,
361 context: serde_json::Value,
362 ) -> Self {
363 Self {
364 event_type,
365 action,
366 session_key,
367 context,
368 timestamp: Utc::now(),
369 messages: Vec::new(),
370 }
371 }
372
373 pub fn event_key(&self) -> String {
397 format!("{}:{}", self.event_type, self.action)
398 }
399}
400
401pub type InternalHookHandlerFn = Arc<
426 dyn Fn(&mut InternalHookEvent) -> Pin<Box<dyn Future<Output = Result<()>> + Send>>
427 + Send
428 + Sync,
429>;
430
431pub struct InternalHookRegistry {
469 handlers: RwLock<HashMap<String, Vec<InternalHookHandlerFn>>>,
474}
475
476impl InternalHookRegistry {
477 pub fn new() -> Self {
492 Self {
493 handlers: RwLock::new(HashMap::new()),
494 }
495 }
496
497 pub fn register(&self, event_key: &str, handler: InternalHookHandlerFn) {
527 let mut handlers = self.handlers.write();
528 handlers
529 .entry(event_key.to_string())
530 .or_default()
531 .push(handler);
532 }
533
534 pub fn unregister(&self, event_key: &str, handler: &InternalHookHandlerFn) -> bool {
568 let mut handlers = self.handlers.write();
569 if let Some(handler_list) = handlers.get_mut(event_key) {
570 let original_len = handler_list.len();
571 handler_list.retain(|h| !Arc::ptr_eq(h, handler));
572 let removed = handler_list.len() < original_len;
573
574 if handler_list.is_empty() {
576 handlers.remove(event_key);
577 }
578
579 removed
580 } else {
581 false
582 }
583 }
584
585 pub fn clear(&self) {
607 let mut handlers = self.handlers.write();
608 handlers.clear();
609 }
610
611 pub fn get_registered_keys(&self) -> Vec<String> {
639 let handlers = self.handlers.read();
640 handlers.keys().cloned().collect()
641 }
642
643 pub fn get_handlers(&self, event_key: &str) -> Vec<InternalHookHandlerFn> {
656 let handlers = self.handlers.read();
657 handlers.get(event_key).cloned().unwrap_or_default()
658 }
659
660 pub async fn trigger(&self, event: &mut InternalHookEvent) -> Result<()> {
724 use std::time::Duration;
725 use tokio::time::timeout;
726 use tracing::{debug, error, warn};
727
728 const HANDLER_TIMEOUT: Duration = Duration::from_secs(30);
730
731 let type_key = event.event_type.to_string();
732 let action_key = event.event_key();
733
734 debug!(
735 event_type = %type_key,
736 action_key = %action_key,
737 "Triggering internal hook event"
738 );
739
740 let type_handlers = self.get_handlers(&type_key);
742 let action_handlers = self.get_handlers(&action_key);
744
745 if type_handlers.is_empty() && action_handlers.is_empty() {
747 debug!(
748 event_type = %type_key,
749 action_key = %action_key,
750 "No handlers registered for event, skipping"
751 );
752 return Ok(());
753 }
754
755 for (index, handler) in type_handlers.iter().enumerate() {
757 debug!(
758 event_type = %type_key,
759 handler_index = index,
760 "Calling type-level handler"
761 );
762
763 match timeout(HANDLER_TIMEOUT, handler(event)).await {
764 Ok(Ok(())) => {
765 debug!(
766 event_type = %type_key,
767 handler_index = index,
768 "Type-level handler completed successfully"
769 );
770 }
771 Ok(Err(e)) => {
772 error!(
773 event_type = %type_key,
774 handler_index = index,
775 error = %e,
776 "Type-level handler failed with error"
777 );
778 }
780 Err(_) => {
781 warn!(
782 event_type = %type_key,
783 handler_index = index,
784 timeout_secs = HANDLER_TIMEOUT.as_secs(),
785 "Type-level handler timed out"
786 );
787 }
789 }
790 }
791
792 for (index, handler) in action_handlers.iter().enumerate() {
794 debug!(
795 action_key = %action_key,
796 handler_index = index,
797 "Calling action-level handler"
798 );
799
800 match timeout(HANDLER_TIMEOUT, handler(event)).await {
801 Ok(Ok(())) => {
802 debug!(
803 action_key = %action_key,
804 handler_index = index,
805 "Action-level handler completed successfully"
806 );
807 }
808 Ok(Err(e)) => {
809 error!(
810 action_key = %action_key,
811 handler_index = index,
812 error = %e,
813 "Action-level handler failed with error"
814 );
815 }
817 Err(_) => {
818 warn!(
819 action_key = %action_key,
820 handler_index = index,
821 timeout_secs = HANDLER_TIMEOUT.as_secs(),
822 "Action-level handler timed out"
823 );
824 }
826 }
827 }
828
829 debug!(
830 event_type = %type_key,
831 action_key = %action_key,
832 type_handler_count = type_handlers.len(),
833 action_handler_count = action_handlers.len(),
834 "Internal hook event trigger completed"
835 );
836
837 Ok(())
838 }
839
840 pub fn is_empty(&self) -> bool {
847 let handlers = self.handlers.read();
848 handlers.is_empty()
849 }
850
851 pub fn handler_count(&self) -> usize {
857 let handlers = self.handlers.read();
858 handlers.values().map(|v| v.len()).sum()
859 }
860}
861
862impl Default for InternalHookRegistry {
863 fn default() -> Self {
864 Self::new()
865 }
866}
867
868impl fmt::Debug for InternalHookRegistry {
869 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
870 let handlers = self.handlers.read();
871 let keys: Vec<_> = handlers.keys().collect();
872 let counts: HashMap<_, _> = handlers.iter().map(|(k, v)| (k, v.len())).collect();
873 f.debug_struct("InternalHookRegistry")
874 .field("registered_keys", &keys)
875 .field("handler_counts", &counts)
876 .finish()
877 }
878}
879
880static GLOBAL_INTERNAL_REGISTRY: Lazy<InternalHookRegistry> = Lazy::new(InternalHookRegistry::new);
889
890pub fn global_internal_registry() -> &'static InternalHookRegistry {
927 &GLOBAL_INTERNAL_REGISTRY
928}
929
930pub fn create_internal_hook_event(
968 event_type: InternalHookEventType,
969 action: InternalHookAction,
970 session_key: Option<String>,
971 context: serde_json::Value,
972) -> InternalHookEvent {
973 InternalHookEvent::new(event_type, action, session_key, context)
974}
975
976pub async fn trigger_internal_hook(event: &mut InternalHookEvent) -> Result<()> {
1011 global_internal_registry().trigger(event).await
1012}
1013
1014pub fn register_internal_hook(event_key: &str, handler: InternalHookHandlerFn) {
1046 global_internal_registry().register(event_key, handler);
1047}
1048
1049pub fn unregister_internal_hook(event_key: &str, handler: &InternalHookHandlerFn) -> bool {
1084 global_internal_registry().unregister(event_key, handler)
1085}
1086
1087pub fn clear_internal_hooks() {
1118 global_internal_registry().clear();
1119}
1120
1121pub async fn trigger_agent_start(
1161 agent_id: &str,
1162 agent_type: &str,
1163 session_key: Option<String>,
1164) -> Result<InternalHookEvent> {
1165 let mut event = create_internal_hook_event(
1166 InternalHookEventType::Agent,
1167 InternalHookAction::Start,
1168 session_key,
1169 serde_json::json!({
1170 "agent_id": agent_id,
1171 "agent_type": agent_type
1172 }),
1173 );
1174
1175 trigger_internal_hook(&mut event).await?;
1176
1177 Ok(event)
1178}
1179
1180pub async fn trigger_agent_stop(
1213 agent_id: &str,
1214 agent_type: &str,
1215 session_key: Option<String>,
1216) -> Result<InternalHookEvent> {
1217 let mut event = create_internal_hook_event(
1218 InternalHookEventType::Agent,
1219 InternalHookAction::Stop,
1220 session_key,
1221 serde_json::json!({
1222 "agent_id": agent_id,
1223 "agent_type": agent_type
1224 }),
1225 );
1226
1227 trigger_internal_hook(&mut event).await?;
1228
1229 Ok(event)
1230}
1231
1232pub async fn trigger_agent_error(
1267 agent_id: &str,
1268 agent_type: &str,
1269 error: &str,
1270 session_key: Option<String>,
1271) -> Result<InternalHookEvent> {
1272 let mut event = create_internal_hook_event(
1273 InternalHookEventType::Agent,
1274 InternalHookAction::Error,
1275 session_key,
1276 serde_json::json!({
1277 "agent_id": agent_id,
1278 "agent_type": agent_type,
1279 "error": error
1280 }),
1281 );
1282
1283 trigger_internal_hook(&mut event).await?;
1284
1285 Ok(event)
1286}
1287
1288pub async fn trigger_agent_bootstrap(
1321 agent_id: &str,
1322 agent_type: &str,
1323 session_key: Option<String>,
1324) -> Result<InternalHookEvent> {
1325 let mut event = create_internal_hook_event(
1326 InternalHookEventType::Agent,
1327 InternalHookAction::Bootstrap,
1328 session_key,
1329 serde_json::json!({
1330 "agent_id": agent_id,
1331 "agent_type": agent_type
1332 }),
1333 );
1334
1335 trigger_internal_hook(&mut event).await?;
1336
1337 Ok(event)
1338}
1339
1340pub async fn trigger_session_create(
1378 session_id: &str,
1379 session_key: &str,
1380) -> Result<InternalHookEvent> {
1381 let mut event = create_internal_hook_event(
1382 InternalHookEventType::Session,
1383 InternalHookAction::Create,
1384 Some(session_key.to_string()),
1385 serde_json::json!({
1386 "session_id": session_id,
1387 "session_key": session_key,
1388 "source": "startup"
1389 }),
1390 );
1391
1392 trigger_internal_hook(&mut event).await?;
1393
1394 Ok(event)
1395}
1396
1397pub async fn trigger_session_resume(
1428 session_id: &str,
1429 session_key: &str,
1430) -> Result<InternalHookEvent> {
1431 let mut event = create_internal_hook_event(
1432 InternalHookEventType::Session,
1433 InternalHookAction::Resume,
1434 Some(session_key.to_string()),
1435 serde_json::json!({
1436 "session_id": session_id,
1437 "session_key": session_key,
1438 "source": "resume"
1439 }),
1440 );
1441
1442 trigger_internal_hook(&mut event).await?;
1443
1444 Ok(event)
1445}
1446
1447pub async fn trigger_session_end(
1480 session_id: &str,
1481 session_key: &str,
1482 reason: Option<&str>,
1483) -> Result<InternalHookEvent> {
1484 let mut event = create_internal_hook_event(
1485 InternalHookEventType::Session,
1486 InternalHookAction::End,
1487 Some(session_key.to_string()),
1488 serde_json::json!({
1489 "session_id": session_id,
1490 "session_key": session_key,
1491 "source": "clear",
1492 "reason": reason.unwrap_or("other")
1493 }),
1494 );
1495
1496 trigger_internal_hook(&mut event).await?;
1497
1498 Ok(event)
1499}
1500
1501pub async fn trigger_session_compact(
1532 session_id: &str,
1533 session_key: &str,
1534) -> Result<InternalHookEvent> {
1535 let mut event = create_internal_hook_event(
1536 InternalHookEventType::Session,
1537 InternalHookAction::Compact,
1538 Some(session_key.to_string()),
1539 serde_json::json!({
1540 "session_id": session_id,
1541 "session_key": session_key,
1542 "source": "compact"
1543 }),
1544 );
1545
1546 trigger_internal_hook(&mut event).await?;
1547
1548 Ok(event)
1549}
1550
1551pub async fn trigger_command_new(
1591 command_args: &[String],
1592 raw_input: &str,
1593 session_key: Option<String>,
1594) -> Result<InternalHookEvent> {
1595 let mut event = create_internal_hook_event(
1596 InternalHookEventType::Command,
1597 InternalHookAction::New,
1598 session_key,
1599 serde_json::json!({
1600 "command_name": "new",
1601 "command_args": command_args,
1602 "raw_input": raw_input
1603 }),
1604 );
1605
1606 trigger_internal_hook(&mut event).await?;
1607
1608 Ok(event)
1609}
1610
1611pub async fn trigger_command_reset(
1644 command_args: &[String],
1645 raw_input: &str,
1646 session_key: Option<String>,
1647) -> Result<InternalHookEvent> {
1648 let mut event = create_internal_hook_event(
1649 InternalHookEventType::Command,
1650 InternalHookAction::Reset,
1651 session_key,
1652 serde_json::json!({
1653 "command_name": "reset",
1654 "command_args": command_args,
1655 "raw_input": raw_input
1656 }),
1657 );
1658
1659 trigger_internal_hook(&mut event).await?;
1660
1661 Ok(event)
1662}
1663
1664pub async fn trigger_command_status(
1697 command_args: &[String],
1698 raw_input: &str,
1699 session_key: Option<String>,
1700) -> Result<InternalHookEvent> {
1701 let mut event = create_internal_hook_event(
1702 InternalHookEventType::Command,
1703 InternalHookAction::Status,
1704 session_key,
1705 serde_json::json!({
1706 "command_name": "status",
1707 "command_args": command_args,
1708 "raw_input": raw_input
1709 }),
1710 );
1711
1712 trigger_internal_hook(&mut event).await?;
1713
1714 Ok(event)
1715}
1716
1717pub async fn trigger_command_help(
1750 command_args: &[String],
1751 raw_input: &str,
1752 session_key: Option<String>,
1753) -> Result<InternalHookEvent> {
1754 let mut event = create_internal_hook_event(
1755 InternalHookEventType::Command,
1756 InternalHookAction::Help,
1757 session_key,
1758 serde_json::json!({
1759 "command_name": "help",
1760 "command_args": command_args,
1761 "raw_input": raw_input
1762 }),
1763 );
1764
1765 trigger_internal_hook(&mut event).await?;
1766
1767 Ok(event)
1768}
1769
1770#[cfg(test)]
1771mod tests {
1772 use super::*;
1773 use serial_test::serial;
1774
1775 #[test]
1776 fn test_internal_hook_event_type_display() {
1777 assert_eq!(InternalHookEventType::Agent.to_string(), "agent");
1778 assert_eq!(InternalHookEventType::Session.to_string(), "session");
1779 assert_eq!(InternalHookEventType::Tool.to_string(), "tool");
1780 assert_eq!(InternalHookEventType::Command.to_string(), "command");
1781 assert_eq!(InternalHookEventType::Gateway.to_string(), "gateway");
1782 }
1783
1784 #[test]
1785 fn test_internal_hook_event_type_serialize() {
1786 assert_eq!(
1787 serde_json::to_string(&InternalHookEventType::Agent).unwrap(),
1788 "\"agent\""
1789 );
1790 assert_eq!(
1791 serde_json::to_string(&InternalHookEventType::Session).unwrap(),
1792 "\"session\""
1793 );
1794 assert_eq!(
1795 serde_json::to_string(&InternalHookEventType::Tool).unwrap(),
1796 "\"tool\""
1797 );
1798 assert_eq!(
1799 serde_json::to_string(&InternalHookEventType::Command).unwrap(),
1800 "\"command\""
1801 );
1802 assert_eq!(
1803 serde_json::to_string(&InternalHookEventType::Gateway).unwrap(),
1804 "\"gateway\""
1805 );
1806 }
1807
1808 #[test]
1809 fn test_internal_hook_event_type_deserialize() {
1810 assert_eq!(
1811 serde_json::from_str::<InternalHookEventType>("\"agent\"").unwrap(),
1812 InternalHookEventType::Agent
1813 );
1814 assert_eq!(
1815 serde_json::from_str::<InternalHookEventType>("\"session\"").unwrap(),
1816 InternalHookEventType::Session
1817 );
1818 assert_eq!(
1819 serde_json::from_str::<InternalHookEventType>("\"tool\"").unwrap(),
1820 InternalHookEventType::Tool
1821 );
1822 assert_eq!(
1823 serde_json::from_str::<InternalHookEventType>("\"command\"").unwrap(),
1824 InternalHookEventType::Command
1825 );
1826 assert_eq!(
1827 serde_json::from_str::<InternalHookEventType>("\"gateway\"").unwrap(),
1828 InternalHookEventType::Gateway
1829 );
1830 }
1831
1832 #[test]
1833 fn test_internal_hook_event_type_roundtrip() {
1834 let types = [
1835 InternalHookEventType::Agent,
1836 InternalHookEventType::Session,
1837 InternalHookEventType::Tool,
1838 InternalHookEventType::Command,
1839 InternalHookEventType::Gateway,
1840 ];
1841
1842 for event_type in types {
1843 let json = serde_json::to_string(&event_type).unwrap();
1844 let deserialized: InternalHookEventType = serde_json::from_str(&json).unwrap();
1845 assert_eq!(event_type, deserialized);
1846 }
1847 }
1848
1849 #[test]
1850 fn test_internal_hook_event_type_clone_copy() {
1851 let original = InternalHookEventType::Agent;
1852 let copied1 = original; let copied2 = original; assert_eq!(original, copied1);
1856 assert_eq!(original, copied2);
1857 }
1858
1859 #[test]
1860 fn test_internal_hook_event_type_hash() {
1861 use std::collections::HashSet;
1862
1863 let mut set = HashSet::new();
1864 set.insert(InternalHookEventType::Agent);
1865 set.insert(InternalHookEventType::Session);
1866 set.insert(InternalHookEventType::Tool);
1867 set.insert(InternalHookEventType::Command);
1868 set.insert(InternalHookEventType::Gateway);
1869
1870 assert_eq!(set.len(), 5);
1871 assert!(set.contains(&InternalHookEventType::Agent));
1872 assert!(set.contains(&InternalHookEventType::Session));
1873 assert!(set.contains(&InternalHookEventType::Tool));
1874 assert!(set.contains(&InternalHookEventType::Command));
1875 assert!(set.contains(&InternalHookEventType::Gateway));
1876 }
1877
1878 #[test]
1879 fn test_internal_hook_event_type_eq() {
1880 assert_eq!(InternalHookEventType::Agent, InternalHookEventType::Agent);
1881 assert_ne!(InternalHookEventType::Agent, InternalHookEventType::Session);
1882 }
1883
1884 #[test]
1885 fn test_internal_hook_event_type_debug() {
1886 let debug_str = format!("{:?}", InternalHookEventType::Agent);
1887 assert_eq!(debug_str, "Agent");
1888 }
1889
1890 #[test]
1893 fn test_internal_hook_action_display() {
1894 assert_eq!(InternalHookAction::Start.to_string(), "start");
1896 assert_eq!(InternalHookAction::Stop.to_string(), "stop");
1897 assert_eq!(InternalHookAction::Error.to_string(), "error");
1898 assert_eq!(InternalHookAction::Bootstrap.to_string(), "bootstrap");
1899
1900 assert_eq!(InternalHookAction::Create.to_string(), "create");
1902 assert_eq!(InternalHookAction::Resume.to_string(), "resume");
1903 assert_eq!(InternalHookAction::End.to_string(), "end");
1904 assert_eq!(InternalHookAction::Compact.to_string(), "compact");
1905
1906 assert_eq!(InternalHookAction::Before.to_string(), "before");
1908 assert_eq!(InternalHookAction::After.to_string(), "after");
1909
1910 assert_eq!(InternalHookAction::New.to_string(), "new");
1912 assert_eq!(InternalHookAction::Reset.to_string(), "reset");
1913 assert_eq!(InternalHookAction::Status.to_string(), "status");
1914 assert_eq!(InternalHookAction::Help.to_string(), "help");
1915
1916 assert_eq!(InternalHookAction::Connect.to_string(), "connect");
1918 assert_eq!(InternalHookAction::Disconnect.to_string(), "disconnect");
1919 assert_eq!(InternalHookAction::Message.to_string(), "message");
1920 }
1921
1922 #[test]
1923 fn test_internal_hook_action_display_all_lowercase() {
1924 let actions = [
1926 InternalHookAction::Start,
1927 InternalHookAction::Stop,
1928 InternalHookAction::Error,
1929 InternalHookAction::Bootstrap,
1930 InternalHookAction::Create,
1931 InternalHookAction::Resume,
1932 InternalHookAction::End,
1933 InternalHookAction::Compact,
1934 InternalHookAction::Before,
1935 InternalHookAction::After,
1936 InternalHookAction::New,
1937 InternalHookAction::Reset,
1938 InternalHookAction::Status,
1939 InternalHookAction::Help,
1940 InternalHookAction::Connect,
1941 InternalHookAction::Disconnect,
1942 InternalHookAction::Message,
1943 ];
1944
1945 for action in actions {
1946 let s = action.to_string();
1947 assert!(
1948 s.chars().all(|c| c.is_lowercase() || !c.is_alphabetic()),
1949 "Action {:?} should have lowercase display, got: {}",
1950 action,
1951 s
1952 );
1953 }
1954 }
1955
1956 #[test]
1957 fn test_internal_hook_action_serialize() {
1958 assert_eq!(
1960 serde_json::to_string(&InternalHookAction::Start).unwrap(),
1961 "\"start\""
1962 );
1963 assert_eq!(
1964 serde_json::to_string(&InternalHookAction::Stop).unwrap(),
1965 "\"stop\""
1966 );
1967 assert_eq!(
1968 serde_json::to_string(&InternalHookAction::Error).unwrap(),
1969 "\"error\""
1970 );
1971 assert_eq!(
1972 serde_json::to_string(&InternalHookAction::Bootstrap).unwrap(),
1973 "\"bootstrap\""
1974 );
1975
1976 assert_eq!(
1978 serde_json::to_string(&InternalHookAction::Create).unwrap(),
1979 "\"create\""
1980 );
1981 assert_eq!(
1982 serde_json::to_string(&InternalHookAction::Resume).unwrap(),
1983 "\"resume\""
1984 );
1985 assert_eq!(
1986 serde_json::to_string(&InternalHookAction::End).unwrap(),
1987 "\"end\""
1988 );
1989 assert_eq!(
1990 serde_json::to_string(&InternalHookAction::Compact).unwrap(),
1991 "\"compact\""
1992 );
1993
1994 assert_eq!(
1996 serde_json::to_string(&InternalHookAction::Before).unwrap(),
1997 "\"before\""
1998 );
1999 assert_eq!(
2000 serde_json::to_string(&InternalHookAction::After).unwrap(),
2001 "\"after\""
2002 );
2003
2004 assert_eq!(
2006 serde_json::to_string(&InternalHookAction::New).unwrap(),
2007 "\"new\""
2008 );
2009 assert_eq!(
2010 serde_json::to_string(&InternalHookAction::Reset).unwrap(),
2011 "\"reset\""
2012 );
2013 assert_eq!(
2014 serde_json::to_string(&InternalHookAction::Status).unwrap(),
2015 "\"status\""
2016 );
2017 assert_eq!(
2018 serde_json::to_string(&InternalHookAction::Help).unwrap(),
2019 "\"help\""
2020 );
2021
2022 assert_eq!(
2024 serde_json::to_string(&InternalHookAction::Connect).unwrap(),
2025 "\"connect\""
2026 );
2027 assert_eq!(
2028 serde_json::to_string(&InternalHookAction::Disconnect).unwrap(),
2029 "\"disconnect\""
2030 );
2031 assert_eq!(
2032 serde_json::to_string(&InternalHookAction::Message).unwrap(),
2033 "\"message\""
2034 );
2035 }
2036
2037 #[test]
2038 fn test_internal_hook_action_deserialize() {
2039 assert_eq!(
2041 serde_json::from_str::<InternalHookAction>("\"start\"").unwrap(),
2042 InternalHookAction::Start
2043 );
2044 assert_eq!(
2045 serde_json::from_str::<InternalHookAction>("\"stop\"").unwrap(),
2046 InternalHookAction::Stop
2047 );
2048 assert_eq!(
2049 serde_json::from_str::<InternalHookAction>("\"error\"").unwrap(),
2050 InternalHookAction::Error
2051 );
2052 assert_eq!(
2053 serde_json::from_str::<InternalHookAction>("\"bootstrap\"").unwrap(),
2054 InternalHookAction::Bootstrap
2055 );
2056
2057 assert_eq!(
2059 serde_json::from_str::<InternalHookAction>("\"create\"").unwrap(),
2060 InternalHookAction::Create
2061 );
2062 assert_eq!(
2063 serde_json::from_str::<InternalHookAction>("\"resume\"").unwrap(),
2064 InternalHookAction::Resume
2065 );
2066 assert_eq!(
2067 serde_json::from_str::<InternalHookAction>("\"end\"").unwrap(),
2068 InternalHookAction::End
2069 );
2070 assert_eq!(
2071 serde_json::from_str::<InternalHookAction>("\"compact\"").unwrap(),
2072 InternalHookAction::Compact
2073 );
2074
2075 assert_eq!(
2077 serde_json::from_str::<InternalHookAction>("\"before\"").unwrap(),
2078 InternalHookAction::Before
2079 );
2080 assert_eq!(
2081 serde_json::from_str::<InternalHookAction>("\"after\"").unwrap(),
2082 InternalHookAction::After
2083 );
2084
2085 assert_eq!(
2087 serde_json::from_str::<InternalHookAction>("\"new\"").unwrap(),
2088 InternalHookAction::New
2089 );
2090 assert_eq!(
2091 serde_json::from_str::<InternalHookAction>("\"reset\"").unwrap(),
2092 InternalHookAction::Reset
2093 );
2094 assert_eq!(
2095 serde_json::from_str::<InternalHookAction>("\"status\"").unwrap(),
2096 InternalHookAction::Status
2097 );
2098 assert_eq!(
2099 serde_json::from_str::<InternalHookAction>("\"help\"").unwrap(),
2100 InternalHookAction::Help
2101 );
2102
2103 assert_eq!(
2105 serde_json::from_str::<InternalHookAction>("\"connect\"").unwrap(),
2106 InternalHookAction::Connect
2107 );
2108 assert_eq!(
2109 serde_json::from_str::<InternalHookAction>("\"disconnect\"").unwrap(),
2110 InternalHookAction::Disconnect
2111 );
2112 assert_eq!(
2113 serde_json::from_str::<InternalHookAction>("\"message\"").unwrap(),
2114 InternalHookAction::Message
2115 );
2116 }
2117
2118 #[test]
2119 fn test_internal_hook_action_roundtrip() {
2120 let actions = [
2121 InternalHookAction::Start,
2122 InternalHookAction::Stop,
2123 InternalHookAction::Error,
2124 InternalHookAction::Bootstrap,
2125 InternalHookAction::Create,
2126 InternalHookAction::Resume,
2127 InternalHookAction::End,
2128 InternalHookAction::Compact,
2129 InternalHookAction::Before,
2130 InternalHookAction::After,
2131 InternalHookAction::New,
2132 InternalHookAction::Reset,
2133 InternalHookAction::Status,
2134 InternalHookAction::Help,
2135 InternalHookAction::Connect,
2136 InternalHookAction::Disconnect,
2137 InternalHookAction::Message,
2138 ];
2139
2140 for action in actions {
2141 let json = serde_json::to_string(&action).unwrap();
2142 let deserialized: InternalHookAction = serde_json::from_str(&json).unwrap();
2143 assert_eq!(action, deserialized);
2144 }
2145 }
2146
2147 #[test]
2148 fn test_internal_hook_action_clone_copy() {
2149 let original = InternalHookAction::Start;
2150 let copied1 = original; let copied2 = original; assert_eq!(original, copied1);
2154 assert_eq!(original, copied2);
2155 }
2156
2157 #[test]
2158 fn test_internal_hook_action_hash() {
2159 use std::collections::HashSet;
2160
2161 let mut set = HashSet::new();
2162 set.insert(InternalHookAction::Start);
2163 set.insert(InternalHookAction::Stop);
2164 set.insert(InternalHookAction::Error);
2165 set.insert(InternalHookAction::Bootstrap);
2166 set.insert(InternalHookAction::Create);
2167 set.insert(InternalHookAction::Resume);
2168 set.insert(InternalHookAction::End);
2169 set.insert(InternalHookAction::Compact);
2170 set.insert(InternalHookAction::Before);
2171 set.insert(InternalHookAction::After);
2172 set.insert(InternalHookAction::New);
2173 set.insert(InternalHookAction::Reset);
2174 set.insert(InternalHookAction::Status);
2175 set.insert(InternalHookAction::Help);
2176 set.insert(InternalHookAction::Connect);
2177 set.insert(InternalHookAction::Disconnect);
2178 set.insert(InternalHookAction::Message);
2179
2180 assert_eq!(set.len(), 17);
2181 assert!(set.contains(&InternalHookAction::Start));
2182 assert!(set.contains(&InternalHookAction::Message));
2183 }
2184
2185 #[test]
2186 fn test_internal_hook_action_eq() {
2187 assert_eq!(InternalHookAction::Start, InternalHookAction::Start);
2188 assert_ne!(InternalHookAction::Start, InternalHookAction::Stop);
2189 assert_ne!(InternalHookAction::Create, InternalHookAction::Resume);
2190 }
2191
2192 #[test]
2193 fn test_internal_hook_action_debug() {
2194 assert_eq!(format!("{:?}", InternalHookAction::Start), "Start");
2195 assert_eq!(format!("{:?}", InternalHookAction::Bootstrap), "Bootstrap");
2196 assert_eq!(format!("{:?}", InternalHookAction::Create), "Create");
2197 assert_eq!(format!("{:?}", InternalHookAction::Connect), "Connect");
2198 }
2199
2200 #[test]
2201 fn test_internal_hook_action_count() {
2202 let actions = [
2210 InternalHookAction::Start,
2211 InternalHookAction::Stop,
2212 InternalHookAction::Error,
2213 InternalHookAction::Bootstrap,
2214 InternalHookAction::Create,
2215 InternalHookAction::Resume,
2216 InternalHookAction::End,
2217 InternalHookAction::Compact,
2218 InternalHookAction::Before,
2219 InternalHookAction::After,
2220 InternalHookAction::New,
2221 InternalHookAction::Reset,
2222 InternalHookAction::Status,
2223 InternalHookAction::Help,
2224 InternalHookAction::Connect,
2225 InternalHookAction::Disconnect,
2226 InternalHookAction::Message,
2227 ];
2228 assert_eq!(actions.len(), 17);
2229 }
2230
2231 #[test]
2234 fn test_internal_hook_event_new() {
2235 let event = InternalHookEvent::new(
2236 InternalHookEventType::Agent,
2237 InternalHookAction::Start,
2238 Some("test-session".to_string()),
2239 serde_json::json!({"agent_id": "agent-001"}),
2240 );
2241
2242 assert_eq!(event.event_type, InternalHookEventType::Agent);
2243 assert_eq!(event.action, InternalHookAction::Start);
2244 assert_eq!(event.session_key, Some("test-session".to_string()));
2245 assert_eq!(event.context["agent_id"], "agent-001");
2246 assert!(event.messages.is_empty());
2247 let now = chrono::Utc::now();
2249 assert!(event.timestamp <= now);
2250 assert!(now.signed_duration_since(event.timestamp).num_seconds() < 1);
2251 }
2252
2253 #[test]
2254 fn test_internal_hook_event_new_without_session_key() {
2255 let event = InternalHookEvent::new(
2256 InternalHookEventType::Session,
2257 InternalHookAction::Create,
2258 None,
2259 serde_json::json!({}),
2260 );
2261
2262 assert_eq!(event.event_type, InternalHookEventType::Session);
2263 assert_eq!(event.action, InternalHookAction::Create);
2264 assert_eq!(event.session_key, None);
2265 assert_eq!(event.context, serde_json::json!({}));
2266 assert!(event.messages.is_empty());
2267 }
2268
2269 #[test]
2270 fn test_internal_hook_event_key() {
2271 let event = InternalHookEvent::new(
2273 InternalHookEventType::Agent,
2274 InternalHookAction::Start,
2275 None,
2276 serde_json::json!({}),
2277 );
2278 assert_eq!(event.event_key(), "agent:start");
2279
2280 let event = InternalHookEvent::new(
2281 InternalHookEventType::Agent,
2282 InternalHookAction::Stop,
2283 None,
2284 serde_json::json!({}),
2285 );
2286 assert_eq!(event.event_key(), "agent:stop");
2287
2288 let event = InternalHookEvent::new(
2289 InternalHookEventType::Agent,
2290 InternalHookAction::Error,
2291 None,
2292 serde_json::json!({}),
2293 );
2294 assert_eq!(event.event_key(), "agent:error");
2295
2296 let event = InternalHookEvent::new(
2297 InternalHookEventType::Agent,
2298 InternalHookAction::Bootstrap,
2299 None,
2300 serde_json::json!({}),
2301 );
2302 assert_eq!(event.event_key(), "agent:bootstrap");
2303
2304 let event = InternalHookEvent::new(
2306 InternalHookEventType::Session,
2307 InternalHookAction::Create,
2308 None,
2309 serde_json::json!({}),
2310 );
2311 assert_eq!(event.event_key(), "session:create");
2312
2313 let event = InternalHookEvent::new(
2314 InternalHookEventType::Session,
2315 InternalHookAction::Resume,
2316 None,
2317 serde_json::json!({}),
2318 );
2319 assert_eq!(event.event_key(), "session:resume");
2320
2321 let event = InternalHookEvent::new(
2322 InternalHookEventType::Session,
2323 InternalHookAction::End,
2324 None,
2325 serde_json::json!({}),
2326 );
2327 assert_eq!(event.event_key(), "session:end");
2328
2329 let event = InternalHookEvent::new(
2330 InternalHookEventType::Session,
2331 InternalHookAction::Compact,
2332 None,
2333 serde_json::json!({}),
2334 );
2335 assert_eq!(event.event_key(), "session:compact");
2336
2337 let event = InternalHookEvent::new(
2339 InternalHookEventType::Tool,
2340 InternalHookAction::Before,
2341 None,
2342 serde_json::json!({}),
2343 );
2344 assert_eq!(event.event_key(), "tool:before");
2345
2346 let event = InternalHookEvent::new(
2347 InternalHookEventType::Tool,
2348 InternalHookAction::After,
2349 None,
2350 serde_json::json!({}),
2351 );
2352 assert_eq!(event.event_key(), "tool:after");
2353
2354 let event = InternalHookEvent::new(
2356 InternalHookEventType::Command,
2357 InternalHookAction::New,
2358 None,
2359 serde_json::json!({}),
2360 );
2361 assert_eq!(event.event_key(), "command:new");
2362
2363 let event = InternalHookEvent::new(
2364 InternalHookEventType::Command,
2365 InternalHookAction::Reset,
2366 None,
2367 serde_json::json!({}),
2368 );
2369 assert_eq!(event.event_key(), "command:reset");
2370
2371 let event = InternalHookEvent::new(
2372 InternalHookEventType::Command,
2373 InternalHookAction::Status,
2374 None,
2375 serde_json::json!({}),
2376 );
2377 assert_eq!(event.event_key(), "command:status");
2378
2379 let event = InternalHookEvent::new(
2380 InternalHookEventType::Command,
2381 InternalHookAction::Help,
2382 None,
2383 serde_json::json!({}),
2384 );
2385 assert_eq!(event.event_key(), "command:help");
2386
2387 let event = InternalHookEvent::new(
2389 InternalHookEventType::Gateway,
2390 InternalHookAction::Connect,
2391 None,
2392 serde_json::json!({}),
2393 );
2394 assert_eq!(event.event_key(), "gateway:connect");
2395
2396 let event = InternalHookEvent::new(
2397 InternalHookEventType::Gateway,
2398 InternalHookAction::Disconnect,
2399 None,
2400 serde_json::json!({}),
2401 );
2402 assert_eq!(event.event_key(), "gateway:disconnect");
2403
2404 let event = InternalHookEvent::new(
2405 InternalHookEventType::Gateway,
2406 InternalHookAction::Message,
2407 None,
2408 serde_json::json!({}),
2409 );
2410 assert_eq!(event.event_key(), "gateway:message");
2411 }
2412
2413 #[test]
2414 fn test_internal_hook_event_key_format() {
2415 let event = InternalHookEvent::new(
2417 InternalHookEventType::Agent,
2418 InternalHookAction::Start,
2419 None,
2420 serde_json::json!({}),
2421 );
2422 let key = event.event_key();
2423 assert!(key.contains(':'), "Event key should contain ':'");
2424 let parts: Vec<&str> = key.split(':').collect();
2425 assert_eq!(parts.len(), 2, "Event key should have exactly two parts");
2426 assert_eq!(parts[0], "agent");
2427 assert_eq!(parts[1], "start");
2428 }
2429
2430 #[test]
2431 fn test_internal_hook_event_serialize() {
2432 let event = InternalHookEvent::new(
2433 InternalHookEventType::Agent,
2434 InternalHookAction::Start,
2435 Some("session-123".to_string()),
2436 serde_json::json!({"agent_id": "agent-001", "agent_type": "coding"}),
2437 );
2438
2439 let json = serde_json::to_string(&event).unwrap();
2440 assert!(json.contains("\"event_type\":\"agent\""));
2441 assert!(json.contains("\"action\":\"start\""));
2442 assert!(json.contains("\"session_key\":\"session-123\""));
2443 assert!(json.contains("\"agent_id\":\"agent-001\""));
2444 assert!(json.contains("\"agent_type\":\"coding\""));
2445 assert!(json.contains("\"timestamp\""));
2446 assert!(json.contains("\"messages\":[]"));
2447 }
2448
2449 #[test]
2450 fn test_internal_hook_event_deserialize() {
2451 let json = r#"{
2452 "event_type": "session",
2453 "action": "create",
2454 "session_key": "test-session",
2455 "context": {"session_id": "sess-001"},
2456 "timestamp": "2024-01-15T10:30:00Z",
2457 "messages": ["Hello", "World"]
2458 }"#;
2459
2460 let event: InternalHookEvent = serde_json::from_str(json).unwrap();
2461 assert_eq!(event.event_type, InternalHookEventType::Session);
2462 assert_eq!(event.action, InternalHookAction::Create);
2463 assert_eq!(event.session_key, Some("test-session".to_string()));
2464 assert_eq!(event.context["session_id"], "sess-001");
2465 assert_eq!(event.messages, vec!["Hello", "World"]);
2466 }
2467
2468 #[test]
2469 fn test_internal_hook_event_roundtrip() {
2470 let original = InternalHookEvent::new(
2471 InternalHookEventType::Command,
2472 InternalHookAction::New,
2473 Some("user:session:456".to_string()),
2474 serde_json::json!({
2475 "command_name": "new",
2476 "command_args": ["--model", "gpt-4"],
2477 "raw_input": "/new --model gpt-4"
2478 }),
2479 );
2480
2481 let json = serde_json::to_string(&original).unwrap();
2482 let deserialized: InternalHookEvent = serde_json::from_str(&json).unwrap();
2483
2484 assert_eq!(original.event_type, deserialized.event_type);
2485 assert_eq!(original.action, deserialized.action);
2486 assert_eq!(original.session_key, deserialized.session_key);
2487 assert_eq!(original.context, deserialized.context);
2488 assert_eq!(original.timestamp, deserialized.timestamp);
2489 assert_eq!(original.messages, deserialized.messages);
2490 }
2491
2492 #[test]
2493 fn test_internal_hook_event_clone() {
2494 let original = InternalHookEvent::new(
2495 InternalHookEventType::Gateway,
2496 InternalHookAction::Connect,
2497 None,
2498 serde_json::json!({"connection_id": "conn-001"}),
2499 );
2500
2501 let cloned = original.clone();
2502
2503 assert_eq!(original.event_type, cloned.event_type);
2504 assert_eq!(original.action, cloned.action);
2505 assert_eq!(original.session_key, cloned.session_key);
2506 assert_eq!(original.context, cloned.context);
2507 assert_eq!(original.timestamp, cloned.timestamp);
2508 assert_eq!(original.messages, cloned.messages);
2509 }
2510
2511 #[test]
2512 fn test_internal_hook_event_debug() {
2513 let event = InternalHookEvent::new(
2514 InternalHookEventType::Tool,
2515 InternalHookAction::Before,
2516 None,
2517 serde_json::json!({}),
2518 );
2519
2520 let debug_str = format!("{:?}", event);
2521 assert!(debug_str.contains("InternalHookEvent"));
2522 assert!(debug_str.contains("Tool"));
2523 assert!(debug_str.contains("Before"));
2524 }
2525
2526 #[test]
2527 fn test_internal_hook_event_messages_mutable() {
2528 let mut event = InternalHookEvent::new(
2529 InternalHookEventType::Agent,
2530 InternalHookAction::Start,
2531 None,
2532 serde_json::json!({}),
2533 );
2534
2535 assert!(event.messages.is_empty());
2536
2537 event
2539 .messages
2540 .push("Agent started successfully".to_string());
2541 event.messages.push("Initialization complete".to_string());
2542
2543 assert_eq!(event.messages.len(), 2);
2544 assert_eq!(event.messages[0], "Agent started successfully");
2545 assert_eq!(event.messages[1], "Initialization complete");
2546 }
2547
2548 #[test]
2549 fn test_internal_hook_event_context_types() {
2550 let agent_event = InternalHookEvent::new(
2554 InternalHookEventType::Agent,
2555 InternalHookAction::Start,
2556 None,
2557 serde_json::json!({
2558 "agent_id": "agent-123",
2559 "agent_type": "coding",
2560 "workspace_dir": "/path/to/workspace"
2561 }),
2562 );
2563 assert_eq!(agent_event.context["agent_id"], "agent-123");
2564 assert_eq!(agent_event.context["agent_type"], "coding");
2565 assert_eq!(agent_event.context["workspace_dir"], "/path/to/workspace");
2566
2567 let session_event = InternalHookEvent::new(
2569 InternalHookEventType::Session,
2570 InternalHookAction::End,
2571 Some("session-key".to_string()),
2572 serde_json::json!({
2573 "session_id": "session-456",
2574 "session_key": "user:session:key",
2575 "reason": "logout"
2576 }),
2577 );
2578 assert_eq!(session_event.context["session_id"], "session-456");
2579 assert_eq!(session_event.context["reason"], "logout");
2580
2581 let command_event = InternalHookEvent::new(
2583 InternalHookEventType::Command,
2584 InternalHookAction::New,
2585 None,
2586 serde_json::json!({
2587 "command_name": "new",
2588 "command_args": ["--model", "gpt-4"],
2589 "raw_input": "/new --model gpt-4"
2590 }),
2591 );
2592 assert_eq!(command_event.context["command_name"], "new");
2593 assert!(command_event.context["command_args"].is_array());
2594
2595 let gateway_event = InternalHookEvent::new(
2597 InternalHookEventType::Gateway,
2598 InternalHookAction::Message,
2599 None,
2600 serde_json::json!({
2601 "connection_id": "conn-789",
2602 "channel": "websocket",
2603 "message": "Hello, World!"
2604 }),
2605 );
2606 assert_eq!(gateway_event.context["connection_id"], "conn-789");
2607 assert_eq!(gateway_event.context["channel"], "websocket");
2608 }
2609
2610 #[test]
2611 fn test_internal_hook_event_empty_context() {
2612 let event = InternalHookEvent::new(
2613 InternalHookEventType::Agent,
2614 InternalHookAction::Stop,
2615 None,
2616 serde_json::json!({}),
2617 );
2618
2619 assert_eq!(event.context, serde_json::json!({}));
2620 assert!(event.context.as_object().unwrap().is_empty());
2621 }
2622
2623 #[test]
2624 fn test_internal_hook_event_null_context() {
2625 let event = InternalHookEvent::new(
2626 InternalHookEventType::Agent,
2627 InternalHookAction::Stop,
2628 None,
2629 serde_json::Value::Null,
2630 );
2631
2632 assert!(event.context.is_null());
2633 }
2634
2635 #[test]
2638 fn test_internal_hook_registry_new() {
2639 let registry = InternalHookRegistry::new();
2640 assert!(registry.is_empty());
2641 assert_eq!(registry.handler_count(), 0);
2642 assert!(registry.get_registered_keys().is_empty());
2643 }
2644
2645 #[test]
2646 fn test_internal_hook_registry_default() {
2647 let registry = InternalHookRegistry::default();
2648 assert!(registry.is_empty());
2649 assert_eq!(registry.handler_count(), 0);
2650 }
2651
2652 #[test]
2653 fn test_internal_hook_registry_register() {
2654 let registry = InternalHookRegistry::new();
2655
2656 let handler: InternalHookHandlerFn = Arc::new(|_| Box::pin(async { Ok(()) }));
2657 registry.register("agent:start", handler);
2658
2659 assert!(!registry.is_empty());
2660 assert_eq!(registry.handler_count(), 1);
2661 assert!(registry
2662 .get_registered_keys()
2663 .contains(&"agent:start".to_string()));
2664 }
2665
2666 #[test]
2667 fn test_internal_hook_registry_register_multiple_handlers_same_key() {
2668 let registry = InternalHookRegistry::new();
2669
2670 let handler1: InternalHookHandlerFn = Arc::new(|_| Box::pin(async { Ok(()) }));
2671 let handler2: InternalHookHandlerFn = Arc::new(|_| Box::pin(async { Ok(()) }));
2672 let handler3: InternalHookHandlerFn = Arc::new(|_| Box::pin(async { Ok(()) }));
2673
2674 registry.register("agent:start", handler1);
2675 registry.register("agent:start", handler2);
2676 registry.register("agent:start", handler3);
2677
2678 assert_eq!(registry.handler_count(), 3);
2679 assert_eq!(registry.get_registered_keys().len(), 1);
2680 assert_eq!(registry.get_handlers("agent:start").len(), 3);
2681 }
2682
2683 #[test]
2684 fn test_internal_hook_registry_register_multiple_keys() {
2685 let registry = InternalHookRegistry::new();
2686
2687 let handler: InternalHookHandlerFn = Arc::new(|_| Box::pin(async { Ok(()) }));
2688
2689 registry.register("agent:start", handler.clone());
2690 registry.register("agent:stop", handler.clone());
2691 registry.register("session:create", handler);
2692
2693 assert_eq!(registry.handler_count(), 3);
2694 assert_eq!(registry.get_registered_keys().len(), 3);
2695 }
2696
2697 #[test]
2698 fn test_internal_hook_registry_register_type_level() {
2699 let registry = InternalHookRegistry::new();
2700
2701 let handler: InternalHookHandlerFn = Arc::new(|_| Box::pin(async { Ok(()) }));
2703 registry.register("agent", handler);
2704
2705 assert!(registry
2706 .get_registered_keys()
2707 .contains(&"agent".to_string()));
2708 assert_eq!(registry.get_handlers("agent").len(), 1);
2709 }
2710
2711 #[test]
2712 fn test_internal_hook_registry_unregister() {
2713 let registry = InternalHookRegistry::new();
2714
2715 let handler: InternalHookHandlerFn = Arc::new(|_| Box::pin(async { Ok(()) }));
2716 registry.register("agent:start", handler.clone());
2717
2718 assert_eq!(registry.handler_count(), 1);
2719
2720 let removed = registry.unregister("agent:start", &handler);
2721 assert!(removed);
2722 assert!(registry.is_empty());
2723 assert_eq!(registry.handler_count(), 0);
2724 }
2725
2726 #[test]
2727 fn test_internal_hook_registry_unregister_not_found() {
2728 let registry = InternalHookRegistry::new();
2729
2730 let handler1: InternalHookHandlerFn = Arc::new(|_| Box::pin(async { Ok(()) }));
2731 let handler2: InternalHookHandlerFn = Arc::new(|_| Box::pin(async { Ok(()) }));
2732
2733 registry.register("agent:start", handler1);
2734
2735 let removed = registry.unregister("agent:start", &handler2);
2737 assert!(!removed);
2738 assert_eq!(registry.handler_count(), 1);
2739 }
2740
2741 #[test]
2742 fn test_internal_hook_registry_unregister_wrong_key() {
2743 let registry = InternalHookRegistry::new();
2744
2745 let handler: InternalHookHandlerFn = Arc::new(|_| Box::pin(async { Ok(()) }));
2746 registry.register("agent:start", handler.clone());
2747
2748 let removed = registry.unregister("agent:stop", &handler);
2750 assert!(!removed);
2751 assert_eq!(registry.handler_count(), 1);
2752 }
2753
2754 #[test]
2755 fn test_internal_hook_registry_unregister_one_of_multiple() {
2756 let registry = InternalHookRegistry::new();
2757
2758 let handler1: InternalHookHandlerFn = Arc::new(|_| Box::pin(async { Ok(()) }));
2759 let handler2: InternalHookHandlerFn = Arc::new(|_| Box::pin(async { Ok(()) }));
2760
2761 registry.register("agent:start", handler1.clone());
2762 registry.register("agent:start", handler2);
2763
2764 assert_eq!(registry.handler_count(), 2);
2765
2766 let removed = registry.unregister("agent:start", &handler1);
2767 assert!(removed);
2768 assert_eq!(registry.handler_count(), 1);
2769 assert!(registry
2771 .get_registered_keys()
2772 .contains(&"agent:start".to_string()));
2773 }
2774
2775 #[test]
2776 fn test_internal_hook_registry_clear() {
2777 let registry = InternalHookRegistry::new();
2778
2779 let handler: InternalHookHandlerFn = Arc::new(|_| Box::pin(async { Ok(()) }));
2780
2781 registry.register("agent:start", handler.clone());
2782 registry.register("agent:stop", handler.clone());
2783 registry.register("session:create", handler);
2784
2785 assert_eq!(registry.handler_count(), 3);
2786
2787 registry.clear();
2788
2789 assert!(registry.is_empty());
2790 assert_eq!(registry.handler_count(), 0);
2791 assert!(registry.get_registered_keys().is_empty());
2792 }
2793
2794 #[test]
2795 fn test_internal_hook_registry_get_registered_keys() {
2796 let registry = InternalHookRegistry::new();
2797
2798 let handler: InternalHookHandlerFn = Arc::new(|_| Box::pin(async { Ok(()) }));
2799
2800 registry.register("agent:start", handler.clone());
2801 registry.register("agent:stop", handler.clone());
2802 registry.register("session:create", handler.clone());
2803 registry.register("command:new", handler);
2804
2805 let keys = registry.get_registered_keys();
2806 assert_eq!(keys.len(), 4);
2807 assert!(keys.contains(&"agent:start".to_string()));
2808 assert!(keys.contains(&"agent:stop".to_string()));
2809 assert!(keys.contains(&"session:create".to_string()));
2810 assert!(keys.contains(&"command:new".to_string()));
2811 }
2812
2813 #[test]
2814 fn test_internal_hook_registry_get_handlers() {
2815 let registry = InternalHookRegistry::new();
2816
2817 let handler1: InternalHookHandlerFn = Arc::new(|_| Box::pin(async { Ok(()) }));
2818 let handler2: InternalHookHandlerFn = Arc::new(|_| Box::pin(async { Ok(()) }));
2819
2820 registry.register("agent:start", handler1);
2821 registry.register("agent:start", handler2);
2822
2823 let handlers = registry.get_handlers("agent:start");
2824 assert_eq!(handlers.len(), 2);
2825
2826 let empty_handlers = registry.get_handlers("nonexistent");
2828 assert!(empty_handlers.is_empty());
2829 }
2830
2831 #[test]
2832 fn test_internal_hook_registry_is_empty() {
2833 let registry = InternalHookRegistry::new();
2834 assert!(registry.is_empty());
2835
2836 let handler: InternalHookHandlerFn = Arc::new(|_| Box::pin(async { Ok(()) }));
2837 registry.register("agent:start", handler.clone());
2838 assert!(!registry.is_empty());
2839
2840 registry.unregister("agent:start", &handler);
2841 assert!(registry.is_empty());
2842 }
2843
2844 #[test]
2845 fn test_internal_hook_registry_handler_count() {
2846 let registry = InternalHookRegistry::new();
2847 assert_eq!(registry.handler_count(), 0);
2848
2849 let handler: InternalHookHandlerFn = Arc::new(|_| Box::pin(async { Ok(()) }));
2850
2851 registry.register("agent:start", handler.clone());
2852 assert_eq!(registry.handler_count(), 1);
2853
2854 registry.register("agent:start", handler.clone());
2855 assert_eq!(registry.handler_count(), 2);
2856
2857 registry.register("agent:stop", handler);
2858 assert_eq!(registry.handler_count(), 3);
2859 }
2860
2861 #[test]
2862 fn test_internal_hook_registry_debug() {
2863 let registry = InternalHookRegistry::new();
2864
2865 let handler: InternalHookHandlerFn = Arc::new(|_| Box::pin(async { Ok(()) }));
2866 registry.register("agent:start", handler);
2867
2868 let debug_str = format!("{:?}", registry);
2869 assert!(debug_str.contains("InternalHookRegistry"));
2870 assert!(debug_str.contains("agent:start"));
2871 }
2872
2873 #[test]
2874 fn test_internal_hook_registry_register_same_handler_twice() {
2875 let registry = InternalHookRegistry::new();
2876
2877 let handler: InternalHookHandlerFn = Arc::new(|_| Box::pin(async { Ok(()) }));
2878
2879 registry.register("agent:start", handler.clone());
2881 registry.register("agent:start", handler);
2882
2883 assert_eq!(registry.handler_count(), 2);
2884 assert_eq!(registry.get_handlers("agent:start").len(), 2);
2885 }
2886
2887 #[test]
2888 fn test_internal_hook_registry_unregister_removes_empty_key() {
2889 let registry = InternalHookRegistry::new();
2890
2891 let handler: InternalHookHandlerFn = Arc::new(|_| Box::pin(async { Ok(()) }));
2892 registry.register("agent:start", handler.clone());
2893
2894 assert!(registry
2895 .get_registered_keys()
2896 .contains(&"agent:start".to_string()));
2897
2898 registry.unregister("agent:start", &handler);
2899
2900 assert!(!registry
2902 .get_registered_keys()
2903 .contains(&"agent:start".to_string()));
2904 }
2905
2906 #[test]
2909 fn test_global_internal_registry_returns_same_instance() {
2910 let registry1 = global_internal_registry();
2912 let registry2 = global_internal_registry();
2913
2914 assert!(std::ptr::eq(registry1, registry2));
2916 }
2917
2918 #[test]
2919 #[serial]
2920 fn test_global_internal_registry_is_functional() {
2921 let registry = global_internal_registry();
2922 registry.clear();
2923
2924 let unique_key = format!("test:global_registry_{}", uuid::Uuid::new_v4());
2926
2927 let handler: InternalHookHandlerFn = Arc::new(|_| Box::pin(async { Ok(()) }));
2928 registry.register(&unique_key, handler.clone());
2929
2930 assert!(registry.get_registered_keys().contains(&unique_key));
2932 assert_eq!(registry.get_handlers(&unique_key).len(), 1);
2933
2934 registry.unregister(&unique_key, &handler);
2936 }
2937
2938 #[test]
2939 fn test_global_internal_registry_type() {
2940 let registry: &'static InternalHookRegistry = global_internal_registry();
2942
2943 let _ = registry.get_registered_keys();
2945 let _ = registry.is_empty();
2946 let _ = registry.handler_count();
2947 }
2948
2949 #[tokio::test]
2952 async fn test_trigger_no_handlers() {
2953 let registry = InternalHookRegistry::new();
2954
2955 let mut event = InternalHookEvent::new(
2956 InternalHookEventType::Agent,
2957 InternalHookAction::Start,
2958 None,
2959 serde_json::json!({}),
2960 );
2961
2962 let result = registry.trigger(&mut event).await;
2964 assert!(result.is_ok());
2965 assert!(event.messages.is_empty());
2966 }
2967
2968 #[tokio::test]
2969 async fn test_trigger_action_level_handler() {
2970 let registry = InternalHookRegistry::new();
2971
2972 let called = Arc::new(std::sync::Mutex::new(false));
2974 let called_clone = called.clone();
2975
2976 let handler: InternalHookHandlerFn = Arc::new(move |event| {
2978 *called_clone.lock().unwrap() = true;
2979 event.messages.push("Action handler called".to_string());
2980 Box::pin(async move { Ok(()) })
2981 });
2982
2983 registry.register("agent:start", handler);
2984
2985 let mut event = InternalHookEvent::new(
2986 InternalHookEventType::Agent,
2987 InternalHookAction::Start,
2988 None,
2989 serde_json::json!({}),
2990 );
2991
2992 let result = registry.trigger(&mut event).await;
2993 assert!(result.is_ok());
2994 assert!(*called.lock().unwrap());
2995 assert_eq!(event.messages, vec!["Action handler called"]);
2996 }
2997
2998 #[tokio::test]
2999 async fn test_trigger_type_level_handler() {
3000 let registry = InternalHookRegistry::new();
3001
3002 let called = Arc::new(std::sync::Mutex::new(false));
3003 let called_clone = called.clone();
3004
3005 let handler: InternalHookHandlerFn = Arc::new(move |event| {
3006 *called_clone.lock().unwrap() = true;
3007 event.messages.push("Type handler called".to_string());
3008 Box::pin(async move { Ok(()) })
3009 });
3010
3011 registry.register("agent", handler);
3013
3014 let mut event = InternalHookEvent::new(
3015 InternalHookEventType::Agent,
3016 InternalHookAction::Start,
3017 None,
3018 serde_json::json!({}),
3019 );
3020
3021 let result = registry.trigger(&mut event).await;
3022 assert!(result.is_ok());
3023 assert!(*called.lock().unwrap());
3024 assert_eq!(event.messages, vec!["Type handler called"]);
3025 }
3026
3027 #[tokio::test]
3028 async fn test_trigger_type_before_action_handlers() {
3029 let registry = InternalHookRegistry::new();
3030
3031 let call_order = Arc::new(std::sync::Mutex::new(Vec::new()));
3033
3034 let call_order_clone = call_order.clone();
3036 let type_handler: InternalHookHandlerFn = Arc::new(move |event| {
3037 call_order_clone.lock().unwrap().push("type");
3038 event.messages.push("Type handler".to_string());
3039 Box::pin(async move { Ok(()) })
3040 });
3041
3042 let call_order_clone = call_order.clone();
3044 let action_handler: InternalHookHandlerFn = Arc::new(move |event| {
3045 call_order_clone.lock().unwrap().push("action");
3046 event.messages.push("Action handler".to_string());
3047 Box::pin(async move { Ok(()) })
3048 });
3049
3050 registry.register("agent:start", action_handler);
3052 registry.register("agent", type_handler);
3053
3054 let mut event = InternalHookEvent::new(
3055 InternalHookEventType::Agent,
3056 InternalHookAction::Start,
3057 None,
3058 serde_json::json!({}),
3059 );
3060
3061 let result = registry.trigger(&mut event).await;
3062 assert!(result.is_ok());
3063
3064 let order = call_order.lock().unwrap();
3066 assert_eq!(*order, vec!["type", "action"]);
3067 assert_eq!(event.messages, vec!["Type handler", "Action handler"]);
3068 }
3069
3070 #[tokio::test]
3071 async fn test_trigger_multiple_handlers_same_level() {
3072 let registry = InternalHookRegistry::new();
3073
3074 let call_order = Arc::new(std::sync::Mutex::new(Vec::new()));
3075
3076 for i in 1..=3 {
3078 let call_order_clone = call_order.clone();
3079 let handler: InternalHookHandlerFn = Arc::new(move |event| {
3080 call_order_clone.lock().unwrap().push(i);
3081 event.messages.push(format!("Handler {}", i));
3082 Box::pin(async move { Ok(()) })
3083 });
3084 registry.register("agent:start", handler);
3085 }
3086
3087 let mut event = InternalHookEvent::new(
3088 InternalHookEventType::Agent,
3089 InternalHookAction::Start,
3090 None,
3091 serde_json::json!({}),
3092 );
3093
3094 let result = registry.trigger(&mut event).await;
3095 assert!(result.is_ok());
3096
3097 let order = call_order.lock().unwrap();
3099 assert_eq!(*order, vec![1, 2, 3]);
3100 assert_eq!(event.messages, vec!["Handler 1", "Handler 2", "Handler 3"]);
3101 }
3102
3103 #[tokio::test]
3104 async fn test_trigger_handler_error_does_not_stop_others() {
3105 let registry = InternalHookRegistry::new();
3106
3107 let call_order = Arc::new(std::sync::Mutex::new(Vec::new()));
3108
3109 let call_order_clone = call_order.clone();
3111 let handler1: InternalHookHandlerFn = Arc::new(move |event| {
3112 call_order_clone.lock().unwrap().push(1);
3113 event.messages.push("Handler 1 OK".to_string());
3114 Box::pin(async move { Ok(()) })
3115 });
3116
3117 let call_order_clone = call_order.clone();
3119 let handler2: InternalHookHandlerFn = Arc::new(move |_event| {
3120 call_order_clone.lock().unwrap().push(2);
3121 Box::pin(async move { Err(anyhow::anyhow!("Handler 2 failed")) })
3122 });
3123
3124 let call_order_clone = call_order.clone();
3126 let handler3: InternalHookHandlerFn = Arc::new(move |event| {
3127 call_order_clone.lock().unwrap().push(3);
3128 event.messages.push("Handler 3 OK".to_string());
3129 Box::pin(async move { Ok(()) })
3130 });
3131
3132 registry.register("agent:start", handler1);
3133 registry.register("agent:start", handler2);
3134 registry.register("agent:start", handler3);
3135
3136 let mut event = InternalHookEvent::new(
3137 InternalHookEventType::Agent,
3138 InternalHookAction::Start,
3139 None,
3140 serde_json::json!({}),
3141 );
3142
3143 let result = registry.trigger(&mut event).await;
3145 assert!(result.is_ok());
3146
3147 let order = call_order.lock().unwrap();
3149 assert_eq!(*order, vec![1, 2, 3]);
3150
3151 assert_eq!(event.messages, vec!["Handler 1 OK", "Handler 3 OK"]);
3153 }
3154
3155 #[tokio::test]
3156 async fn test_trigger_type_handler_error_does_not_stop_action_handlers() {
3157 let registry = InternalHookRegistry::new();
3158
3159 let call_order: Arc<std::sync::Mutex<Vec<&str>>> =
3160 Arc::new(std::sync::Mutex::new(Vec::new()));
3161
3162 let call_order_clone = call_order.clone();
3164 let type_handler: InternalHookHandlerFn = Arc::new(move |_event| {
3165 call_order_clone.lock().unwrap().push("type_error");
3166 Box::pin(async move { Err(anyhow::anyhow!("Type handler failed")) })
3167 });
3168
3169 let call_order_clone = call_order.clone();
3171 let action_handler: InternalHookHandlerFn = Arc::new(move |event| {
3172 call_order_clone.lock().unwrap().push("action_ok");
3173 event.messages.push("Action handler OK".to_string());
3174 Box::pin(async move { Ok(()) })
3175 });
3176
3177 registry.register("agent", type_handler);
3178 registry.register("agent:start", action_handler);
3179
3180 let mut event = InternalHookEvent::new(
3181 InternalHookEventType::Agent,
3182 InternalHookAction::Start,
3183 None,
3184 serde_json::json!({}),
3185 );
3186
3187 let result = registry.trigger(&mut event).await;
3188 assert!(result.is_ok());
3189
3190 let order = call_order.lock().unwrap();
3192 assert_eq!(*order, vec!["type_error", "action_ok"]);
3193 assert_eq!(event.messages, vec!["Action handler OK"]);
3194 }
3195
3196 #[tokio::test]
3197 async fn test_trigger_different_event_types() {
3198 let registry = InternalHookRegistry::new();
3199
3200 let agent_called = Arc::new(std::sync::Mutex::new(false));
3201 let session_called = Arc::new(std::sync::Mutex::new(false));
3202
3203 let agent_called_clone = agent_called.clone();
3205 let agent_handler: InternalHookHandlerFn = Arc::new(move |_| {
3206 *agent_called_clone.lock().unwrap() = true;
3207 Box::pin(async move { Ok(()) })
3208 });
3209
3210 let session_called_clone = session_called.clone();
3212 let session_handler: InternalHookHandlerFn = Arc::new(move |_| {
3213 *session_called_clone.lock().unwrap() = true;
3214 Box::pin(async move { Ok(()) })
3215 });
3216
3217 registry.register("agent:start", agent_handler);
3218 registry.register("session:create", session_handler);
3219
3220 let mut agent_event = InternalHookEvent::new(
3222 InternalHookEventType::Agent,
3223 InternalHookAction::Start,
3224 None,
3225 serde_json::json!({}),
3226 );
3227 registry.trigger(&mut agent_event).await.unwrap();
3228
3229 assert!(*agent_called.lock().unwrap());
3231 assert!(!*session_called.lock().unwrap());
3232
3233 *agent_called.lock().unwrap() = false;
3235
3236 let mut session_event = InternalHookEvent::new(
3238 InternalHookEventType::Session,
3239 InternalHookAction::Create,
3240 None,
3241 serde_json::json!({}),
3242 );
3243 registry.trigger(&mut session_event).await.unwrap();
3244
3245 assert!(!*agent_called.lock().unwrap());
3247 assert!(*session_called.lock().unwrap());
3248 }
3249
3250 #[tokio::test]
3251 async fn test_trigger_handler_can_modify_event_messages() {
3252 let registry = InternalHookRegistry::new();
3253
3254 let handler: InternalHookHandlerFn = Arc::new(|event| {
3255 event.messages.push("Message 1".to_string());
3256 event.messages.push("Message 2".to_string());
3257 Box::pin(async move { Ok(()) })
3258 });
3259
3260 registry.register("agent:start", handler);
3261
3262 let mut event = InternalHookEvent::new(
3263 InternalHookEventType::Agent,
3264 InternalHookAction::Start,
3265 None,
3266 serde_json::json!({}),
3267 );
3268
3269 registry.trigger(&mut event).await.unwrap();
3270
3271 assert_eq!(event.messages.len(), 2);
3272 assert_eq!(event.messages[0], "Message 1");
3273 assert_eq!(event.messages[1], "Message 2");
3274 }
3275
3276 #[tokio::test]
3277 async fn test_trigger_handler_can_read_event_context() {
3278 let registry = InternalHookRegistry::new();
3279
3280 let captured_agent_id = Arc::new(std::sync::Mutex::new(String::new()));
3281 let captured_clone = captured_agent_id.clone();
3282
3283 let handler: InternalHookHandlerFn = Arc::new(move |event| {
3284 if let Some(agent_id) = event.context.get("agent_id").and_then(|v| v.as_str()) {
3286 *captured_clone.lock().unwrap() = agent_id.to_string();
3287 }
3288 Box::pin(async move { Ok(()) })
3289 });
3290
3291 registry.register("agent:start", handler);
3292
3293 let mut event = InternalHookEvent::new(
3294 InternalHookEventType::Agent,
3295 InternalHookAction::Start,
3296 None,
3297 serde_json::json!({"agent_id": "test-agent-123"}),
3298 );
3299
3300 registry.trigger(&mut event).await.unwrap();
3301
3302 assert_eq!(*captured_agent_id.lock().unwrap(), "test-agent-123");
3303 }
3304
3305 #[test]
3308 fn test_create_internal_hook_event() {
3309 let event = create_internal_hook_event(
3310 InternalHookEventType::Agent,
3311 InternalHookAction::Start,
3312 Some("session-123".to_string()),
3313 serde_json::json!({"agent_id": "agent-001"}),
3314 );
3315
3316 assert_eq!(event.event_type, InternalHookEventType::Agent);
3317 assert_eq!(event.action, InternalHookAction::Start);
3318 assert_eq!(event.session_key, Some("session-123".to_string()));
3319 assert_eq!(event.context["agent_id"], "agent-001");
3320 assert_eq!(event.event_key(), "agent:start");
3321 assert!(event.messages.is_empty());
3322 }
3323
3324 #[test]
3325 fn test_create_internal_hook_event_without_session_key() {
3326 let event = create_internal_hook_event(
3327 InternalHookEventType::Session,
3328 InternalHookAction::Create,
3329 None,
3330 serde_json::json!({}),
3331 );
3332
3333 assert_eq!(event.event_type, InternalHookEventType::Session);
3334 assert_eq!(event.action, InternalHookAction::Create);
3335 assert_eq!(event.session_key, None);
3336 assert_eq!(event.event_key(), "session:create");
3337 }
3338
3339 #[test]
3340 #[serial]
3341 fn test_register_internal_hook() {
3342 let unique_key = format!("test:register_{}", uuid::Uuid::new_v4());
3344
3345 let handler: InternalHookHandlerFn = Arc::new(|_| Box::pin(async { Ok(()) }));
3346 register_internal_hook(&unique_key, handler.clone());
3347
3348 let registry = global_internal_registry();
3350 assert!(registry.get_registered_keys().contains(&unique_key));
3351 assert_eq!(registry.get_handlers(&unique_key).len(), 1);
3352
3353 registry.unregister(&unique_key, &handler);
3355 }
3356
3357 #[test]
3358 #[serial]
3359 fn test_unregister_internal_hook() {
3360 let unique_key = format!("test:unregister_{}", uuid::Uuid::new_v4());
3361
3362 let handler: InternalHookHandlerFn = Arc::new(|_| Box::pin(async { Ok(()) }));
3363 register_internal_hook(&unique_key, handler.clone());
3364
3365 let registry = global_internal_registry();
3367 assert!(registry.get_registered_keys().contains(&unique_key));
3368
3369 let removed = unregister_internal_hook(&unique_key, &handler);
3371 assert!(removed);
3372
3373 assert!(!registry.get_registered_keys().contains(&unique_key));
3375 }
3376
3377 #[test]
3378 fn test_unregister_internal_hook_not_found() {
3379 let unique_key = format!("test:unregister_not_found_{}", uuid::Uuid::new_v4());
3380
3381 let handler: InternalHookHandlerFn = Arc::new(|_| Box::pin(async { Ok(()) }));
3382
3383 let removed = unregister_internal_hook(&unique_key, &handler);
3385 assert!(!removed);
3386 }
3387
3388 #[test]
3389 #[serial]
3390 fn test_clear_internal_hooks() {
3391 let registry = global_internal_registry();
3393 registry.clear();
3394
3395 let unique_key1 = format!("test:clear1_{}", uuid::Uuid::new_v4());
3397 let unique_key2 = format!("test:clear2_{}", uuid::Uuid::new_v4());
3398
3399 let handler: InternalHookHandlerFn = Arc::new(|_| Box::pin(async { Ok(()) }));
3400 register_internal_hook(&unique_key1, handler.clone());
3401 register_internal_hook(&unique_key2, handler.clone());
3402
3403 assert!(registry.get_registered_keys().contains(&unique_key1));
3404 assert!(registry.get_registered_keys().contains(&unique_key2));
3405
3406 clear_internal_hooks();
3408
3409 assert!(!registry.get_registered_keys().contains(&unique_key1));
3411 assert!(!registry.get_registered_keys().contains(&unique_key2));
3412 }
3413
3414 #[tokio::test]
3415 #[serial]
3416 async fn test_trigger_internal_hook_function() {
3417 let registry = global_internal_registry();
3419 registry.clear();
3420
3421 let unique_key = format!("test:trigger_{}", uuid::Uuid::new_v4());
3422
3423 let called = Arc::new(std::sync::Mutex::new(false));
3424 let called_clone = called.clone();
3425
3426 let handler: InternalHookHandlerFn = Arc::new(move |event| {
3427 *called_clone.lock().unwrap() = true;
3428 event.messages.push("Handler called".to_string());
3429 Box::pin(async move { Ok(()) })
3430 });
3431
3432 register_internal_hook(&unique_key, handler.clone());
3433
3434 let mut event = InternalHookEvent {
3436 event_type: InternalHookEventType::Agent,
3437 action: InternalHookAction::Start,
3438 session_key: None,
3439 context: serde_json::json!({}),
3440 timestamp: chrono::Utc::now(),
3441 messages: Vec::new(),
3442 };
3443
3444 unregister_internal_hook(&unique_key, &handler);
3447
3448 register_internal_hook("agent:start", handler.clone());
3450
3451 let result = trigger_internal_hook(&mut event).await;
3452 assert!(result.is_ok());
3453 assert!(*called.lock().unwrap());
3454 assert!(event.messages.contains(&"Handler called".to_string()));
3455
3456 unregister_internal_hook("agent:start", &handler);
3458 }
3459
3460 #[tokio::test]
3461 #[serial]
3462 async fn test_trigger_internal_hook_no_handlers() {
3463 let registry = global_internal_registry();
3465 registry.clear();
3466
3467 let mut event = create_internal_hook_event(
3469 InternalHookEventType::Gateway,
3470 InternalHookAction::Disconnect,
3471 None,
3472 serde_json::json!({}),
3473 );
3474
3475 let handlers = registry.get_handlers(&event.event_key());
3477 assert!(handlers.is_empty());
3478
3479 let result = trigger_internal_hook(&mut event).await;
3481 assert!(result.is_ok());
3482 assert!(event.messages.is_empty());
3483 }
3484
3485 #[tokio::test]
3488 #[serial]
3489 async fn test_trigger_agent_start() {
3490 let registry = global_internal_registry();
3492 registry.clear();
3493
3494 let called = Arc::new(std::sync::Mutex::new(false));
3496 let captured_context = Arc::new(std::sync::Mutex::new(serde_json::Value::Null));
3497 let called_clone = called.clone();
3498 let context_clone = captured_context.clone();
3499
3500 let handler: InternalHookHandlerFn = Arc::new(move |event| {
3501 *called_clone.lock().unwrap() = true;
3502 *context_clone.lock().unwrap() = event.context.clone();
3503 event
3504 .messages
3505 .push("Agent start handler called".to_string());
3506 Box::pin(async move { Ok(()) })
3507 });
3508
3509 registry.register("agent:start", handler.clone());
3510
3511 let event =
3513 trigger_agent_start("test-agent-001", "coding", Some("test-session".to_string()))
3514 .await
3515 .unwrap();
3516
3517 assert!(*called.lock().unwrap());
3519
3520 assert_eq!(event.event_type, InternalHookEventType::Agent);
3522 assert_eq!(event.action, InternalHookAction::Start);
3523 assert_eq!(event.session_key, Some("test-session".to_string()));
3524 assert_eq!(event.event_key(), "agent:start");
3525
3526 let context = captured_context.lock().unwrap();
3528 assert_eq!(context["agent_id"], "test-agent-001");
3529 assert_eq!(context["agent_type"], "coding");
3530
3531 assert!(event
3533 .messages
3534 .contains(&"Agent start handler called".to_string()));
3535
3536 registry.unregister("agent:start", &handler);
3538 }
3539
3540 #[tokio::test]
3541 #[serial]
3542 async fn test_trigger_agent_stop() {
3543 let registry = global_internal_registry();
3544 registry.clear();
3545
3546 let called = Arc::new(std::sync::Mutex::new(false));
3547 let captured_context = Arc::new(std::sync::Mutex::new(serde_json::Value::Null));
3548 let called_clone = called.clone();
3549 let context_clone = captured_context.clone();
3550
3551 let handler: InternalHookHandlerFn = Arc::new(move |event| {
3552 *called_clone.lock().unwrap() = true;
3553 *context_clone.lock().unwrap() = event.context.clone();
3554 event.messages.push("Agent stop handler called".to_string());
3555 Box::pin(async move { Ok(()) })
3556 });
3557
3558 registry.register("agent:stop", handler.clone());
3559
3560 let event = trigger_agent_stop("test-agent-002", "chat", None)
3562 .await
3563 .unwrap();
3564
3565 assert!(*called.lock().unwrap());
3567
3568 assert_eq!(event.event_type, InternalHookEventType::Agent);
3570 assert_eq!(event.action, InternalHookAction::Stop);
3571 assert_eq!(event.session_key, None);
3572 assert_eq!(event.event_key(), "agent:stop");
3573
3574 let context = captured_context.lock().unwrap();
3576 assert_eq!(context["agent_id"], "test-agent-002");
3577 assert_eq!(context["agent_type"], "chat");
3578
3579 assert!(event
3581 .messages
3582 .contains(&"Agent stop handler called".to_string()));
3583
3584 registry.unregister("agent:stop", &handler);
3586 }
3587
3588 #[tokio::test]
3589 #[serial]
3590 async fn test_trigger_agent_error() {
3591 let registry = global_internal_registry();
3592 registry.clear();
3593
3594 let called = Arc::new(std::sync::Mutex::new(false));
3595 let captured_context = Arc::new(std::sync::Mutex::new(serde_json::Value::Null));
3596 let called_clone = called.clone();
3597 let context_clone = captured_context.clone();
3598
3599 let handler: InternalHookHandlerFn = Arc::new(move |event| {
3600 *called_clone.lock().unwrap() = true;
3601 *context_clone.lock().unwrap() = event.context.clone();
3602 event
3603 .messages
3604 .push("Agent error handler called".to_string());
3605 Box::pin(async move { Ok(()) })
3606 });
3607
3608 registry.register("agent:error", handler.clone());
3609
3610 let event = trigger_agent_error(
3612 "test-agent-003",
3613 "coding",
3614 "Connection timeout",
3615 Some("error-session".to_string()),
3616 )
3617 .await
3618 .unwrap();
3619
3620 assert!(*called.lock().unwrap());
3622
3623 assert_eq!(event.event_type, InternalHookEventType::Agent);
3625 assert_eq!(event.action, InternalHookAction::Error);
3626 assert_eq!(event.session_key, Some("error-session".to_string()));
3627 assert_eq!(event.event_key(), "agent:error");
3628
3629 let context = captured_context.lock().unwrap();
3631 assert_eq!(context["agent_id"], "test-agent-003");
3632 assert_eq!(context["agent_type"], "coding");
3633 assert_eq!(context["error"], "Connection timeout");
3634
3635 assert!(event
3637 .messages
3638 .contains(&"Agent error handler called".to_string()));
3639
3640 registry.unregister("agent:error", &handler);
3642 }
3643
3644 #[tokio::test]
3645 #[serial]
3646 async fn test_trigger_agent_bootstrap() {
3647 let registry = global_internal_registry();
3648 registry.clear();
3649
3650 let called = Arc::new(std::sync::Mutex::new(false));
3651 let captured_context = Arc::new(std::sync::Mutex::new(serde_json::Value::Null));
3652 let called_clone = called.clone();
3653 let context_clone = captured_context.clone();
3654
3655 let handler: InternalHookHandlerFn = Arc::new(move |event| {
3656 *called_clone.lock().unwrap() = true;
3657 *context_clone.lock().unwrap() = event.context.clone();
3658 event
3659 .messages
3660 .push("Agent bootstrap handler called".to_string());
3661 Box::pin(async move { Ok(()) })
3662 });
3663
3664 registry.register("agent:bootstrap", handler.clone());
3665
3666 let event = trigger_agent_bootstrap(
3668 "test-agent-004",
3669 "assistant",
3670 Some("bootstrap-session".to_string()),
3671 )
3672 .await
3673 .unwrap();
3674
3675 assert!(*called.lock().unwrap());
3677
3678 assert_eq!(event.event_type, InternalHookEventType::Agent);
3680 assert_eq!(event.action, InternalHookAction::Bootstrap);
3681 assert_eq!(event.session_key, Some("bootstrap-session".to_string()));
3682 assert_eq!(event.event_key(), "agent:bootstrap");
3683
3684 let context = captured_context.lock().unwrap();
3686 assert_eq!(context["agent_id"], "test-agent-004");
3687 assert_eq!(context["agent_type"], "assistant");
3688
3689 assert!(event
3691 .messages
3692 .contains(&"Agent bootstrap handler called".to_string()));
3693
3694 registry.unregister("agent:bootstrap", &handler);
3696 }
3697
3698 #[tokio::test]
3699 #[serial]
3700 async fn test_trigger_agent_start_no_handlers() {
3701 let registry = global_internal_registry();
3703 registry.clear();
3704
3705 let event = trigger_agent_start("no-handler-agent", "test", None)
3707 .await
3708 .unwrap();
3709
3710 assert_eq!(event.event_type, InternalHookEventType::Agent);
3712 assert_eq!(event.action, InternalHookAction::Start);
3713 assert_eq!(event.context["agent_id"], "no-handler-agent");
3714 assert_eq!(event.context["agent_type"], "test");
3715 }
3717
3718 #[tokio::test]
3719 #[serial]
3720 async fn test_trigger_agent_events_with_type_level_handler() {
3721 let registry = global_internal_registry();
3722 registry.clear();
3723
3724 let call_count = Arc::new(std::sync::Mutex::new(0));
3726 let call_count_clone = call_count.clone();
3727
3728 let handler: InternalHookHandlerFn = Arc::new(move |event| {
3729 *call_count_clone.lock().unwrap() += 1;
3730 event.messages.push("Type-level handler called".to_string());
3731 Box::pin(async move { Ok(()) })
3732 });
3733
3734 registry.register("agent", handler.clone());
3735
3736 let _ = trigger_agent_start("agent-1", "coding", None)
3738 .await
3739 .unwrap();
3740 let _ = trigger_agent_stop("agent-1", "coding", None).await.unwrap();
3741 let _ = trigger_agent_error("agent-1", "coding", "error", None)
3742 .await
3743 .unwrap();
3744 let _ = trigger_agent_bootstrap("agent-1", "coding", None)
3745 .await
3746 .unwrap();
3747
3748 assert_eq!(*call_count.lock().unwrap(), 4);
3750
3751 registry.unregister("agent", &handler);
3753 }
3754
3755 #[tokio::test]
3756 #[serial]
3757 async fn test_trigger_agent_error_context_contains_error_message() {
3758 let registry = global_internal_registry();
3760 registry.clear();
3761
3762 let event = trigger_agent_error(
3764 "error-agent",
3765 "coding",
3766 "This is a detailed error message with special chars: <>&\"'",
3767 None,
3768 )
3769 .await
3770 .unwrap();
3771
3772 assert_eq!(
3773 event.context["error"],
3774 "This is a detailed error message with special chars: <>&\"'"
3775 );
3776 }
3777
3778 #[tokio::test]
3779 #[serial]
3780 async fn test_trigger_agent_events_return_event_with_messages() {
3781 let registry = global_internal_registry();
3782 registry.clear();
3783
3784 let handler: InternalHookHandlerFn = Arc::new(|event| {
3786 event.messages.push("Message 1".to_string());
3787 event.messages.push("Message 2".to_string());
3788 event.messages.push("Message 3".to_string());
3789 Box::pin(async move { Ok(()) })
3790 });
3791
3792 registry.register("agent:start", handler.clone());
3793
3794 let event = trigger_agent_start("msg-agent", "coding", None)
3795 .await
3796 .unwrap();
3797
3798 assert!(event.messages.contains(&"Message 1".to_string()));
3800 assert!(event.messages.contains(&"Message 2".to_string()));
3801 assert!(event.messages.contains(&"Message 3".to_string()));
3802
3803 registry.unregister("agent:start", &handler);
3805 }
3806
3807 #[tokio::test]
3810 #[serial]
3811 async fn test_trigger_session_create() {
3812 let registry = global_internal_registry();
3813 registry.clear();
3814
3815 let handler: InternalHookHandlerFn = Arc::new(|event| {
3817 event
3818 .messages
3819 .push("session:create handler called".to_string());
3820 Box::pin(async move { Ok(()) })
3821 });
3822
3823 registry.register("session:create", handler.clone());
3824
3825 let event = trigger_session_create("test-session-001", "user:session:123")
3827 .await
3828 .unwrap();
3829
3830 assert_eq!(event.event_type, InternalHookEventType::Session);
3832 assert_eq!(event.action, InternalHookAction::Create);
3833 assert_eq!(event.event_key(), "session:create");
3834 assert_eq!(event.session_key, Some("user:session:123".to_string()));
3835
3836 assert_eq!(event.context["session_id"], "test-session-001");
3838 assert_eq!(event.context["session_key"], "user:session:123");
3839
3840 assert!(event
3842 .messages
3843 .contains(&"session:create handler called".to_string()));
3844
3845 registry.unregister("session:create", &handler);
3847 }
3848
3849 #[tokio::test]
3850 #[serial]
3851 async fn test_trigger_session_resume() {
3852 let registry = global_internal_registry();
3853 registry.clear();
3854
3855 let handler: InternalHookHandlerFn = Arc::new(|event| {
3857 event
3858 .messages
3859 .push("session:resume handler called".to_string());
3860 Box::pin(async move { Ok(()) })
3861 });
3862
3863 registry.register("session:resume", handler.clone());
3864
3865 let event = trigger_session_resume("test-session-002", "user:session:456")
3867 .await
3868 .unwrap();
3869
3870 assert_eq!(event.event_type, InternalHookEventType::Session);
3872 assert_eq!(event.action, InternalHookAction::Resume);
3873 assert_eq!(event.event_key(), "session:resume");
3874 assert_eq!(event.session_key, Some("user:session:456".to_string()));
3875
3876 assert_eq!(event.context["session_id"], "test-session-002");
3878 assert_eq!(event.context["session_key"], "user:session:456");
3879
3880 assert!(event
3882 .messages
3883 .contains(&"session:resume handler called".to_string()));
3884
3885 registry.unregister("session:resume", &handler);
3887 }
3888
3889 #[tokio::test]
3890 #[serial]
3891 async fn test_trigger_session_end() {
3892 let registry = global_internal_registry();
3893 registry.clear();
3894
3895 let handler: InternalHookHandlerFn = Arc::new(|event| {
3897 event
3898 .messages
3899 .push("session:end handler called".to_string());
3900 Box::pin(async move { Ok(()) })
3901 });
3902
3903 registry.register("session:end", handler.clone());
3904
3905 let event = trigger_session_end("test-session-003", "user:session:789", Some("logout"))
3907 .await
3908 .unwrap();
3909
3910 assert_eq!(event.event_type, InternalHookEventType::Session);
3912 assert_eq!(event.action, InternalHookAction::End);
3913 assert_eq!(event.event_key(), "session:end");
3914 assert_eq!(event.session_key, Some("user:session:789".to_string()));
3915
3916 assert_eq!(event.context["session_id"], "test-session-003");
3918 assert_eq!(event.context["session_key"], "user:session:789");
3919 assert_eq!(event.context["reason"], "logout");
3920
3921 assert!(event
3923 .messages
3924 .contains(&"session:end handler called".to_string()));
3925
3926 registry.unregister("session:end", &handler);
3928 }
3929
3930 #[tokio::test]
3931 #[serial]
3932 async fn test_trigger_session_end_without_reason() {
3933 let registry = global_internal_registry();
3935 registry.clear();
3936
3937 let event = trigger_session_end("test-session-004", "user:session:abc", None)
3939 .await
3940 .unwrap();
3941
3942 assert_eq!(event.context["reason"], "other");
3944 }
3945
3946 #[tokio::test]
3947 #[serial]
3948 async fn test_trigger_session_compact() {
3949 let registry = global_internal_registry();
3950 registry.clear();
3951
3952 let handler: InternalHookHandlerFn = Arc::new(|event| {
3954 event
3955 .messages
3956 .push("session:compact handler called".to_string());
3957 Box::pin(async move { Ok(()) })
3958 });
3959
3960 registry.register("session:compact", handler.clone());
3961
3962 let event = trigger_session_compact("test-session-005", "user:session:xyz")
3964 .await
3965 .unwrap();
3966
3967 assert_eq!(event.event_type, InternalHookEventType::Session);
3969 assert_eq!(event.action, InternalHookAction::Compact);
3970 assert_eq!(event.event_key(), "session:compact");
3971 assert_eq!(event.session_key, Some("user:session:xyz".to_string()));
3972
3973 assert_eq!(event.context["session_id"], "test-session-005");
3975 assert_eq!(event.context["session_key"], "user:session:xyz");
3976
3977 assert!(event
3979 .messages
3980 .contains(&"session:compact handler called".to_string()));
3981
3982 registry.unregister("session:compact", &handler);
3984 }
3985
3986 #[tokio::test]
3987 #[serial]
3988 async fn test_trigger_session_events_with_type_level_handler() {
3989 let registry = global_internal_registry();
3990 registry.clear();
3991
3992 let call_count = Arc::new(std::sync::Mutex::new(0));
3994 let call_count_clone = call_count.clone();
3995
3996 let handler: InternalHookHandlerFn = Arc::new(move |event| {
3997 event.messages.push("type_level_handler_called".to_string());
3999 *call_count_clone.lock().unwrap() += 1;
4000 Box::pin(async move { Ok(()) })
4001 });
4002
4003 registry.register("session", handler.clone());
4004
4005 let e1 = trigger_session_create("s1", "key1").await.unwrap();
4007 let e2 = trigger_session_resume("s2", "key2").await.unwrap();
4008 let e3 = trigger_session_end("s3", "key3", None).await.unwrap();
4009 let e4 = trigger_session_compact("s4", "key4").await.unwrap();
4010
4011 assert!(e1
4013 .messages
4014 .contains(&"type_level_handler_called".to_string()));
4015 assert!(e2
4016 .messages
4017 .contains(&"type_level_handler_called".to_string()));
4018 assert!(e3
4019 .messages
4020 .contains(&"type_level_handler_called".to_string()));
4021 assert!(e4
4022 .messages
4023 .contains(&"type_level_handler_called".to_string()));
4024
4025 assert!(*call_count.lock().unwrap() >= 4);
4027
4028 registry.unregister("session", &handler);
4030 }
4031}