// Integration test for plugin hooks with HookManager use color_eyre::eyre::Result; use hooks::{HookEvent, HookManager, HookResult}; use tempfile::TempDir; #[tokio::test] async fn test_register_and_execute_plugin_hooks() -> Result<()> { // Create temporary directory to act as project root let temp_dir = TempDir::new()?; // Create hook manager let mut hook_mgr = HookManager::new(temp_dir.path().to_str().unwrap()); // Register a hook that matches Edit|Write tools hook_mgr.register_hook( "PreToolUse".to_string(), "echo 'Hook executed' && exit 0".to_string(), Some("Edit|Write".to_string()), Some(5000), ); // Test that the hook executes for Edit tool let event = HookEvent::PreToolUse { tool: "Edit".to_string(), args: serde_json::json!({"path": "/tmp/test.txt"}), }; let result = hook_mgr.execute(&event, Some(5000)).await?; assert_eq!(result, HookResult::Allow); // Test that the hook executes for Write tool let event = HookEvent::PreToolUse { tool: "Write".to_string(), args: serde_json::json!({"path": "/tmp/test.txt"}), }; let result = hook_mgr.execute(&event, Some(5000)).await?; assert_eq!(result, HookResult::Allow); // Test that the hook does NOT execute for Read tool (doesn't match pattern) let event = HookEvent::PreToolUse { tool: "Read".to_string(), args: serde_json::json!({"path": "/tmp/test.txt"}), }; let result = hook_mgr.execute(&event, Some(5000)).await?; assert_eq!(result, HookResult::Allow); Ok(()) } #[tokio::test] async fn test_deny_hook() -> Result<()> { // Create temporary directory to act as project root let temp_dir = TempDir::new()?; // Create hook manager let mut hook_mgr = HookManager::new(temp_dir.path().to_str().unwrap()); // Register a hook that denies Write operations hook_mgr.register_hook( "PreToolUse".to_string(), "exit 2".to_string(), // Exit code 2 means deny Some("Write".to_string()), Some(5000), ); // Test that the hook denies Write tool let event = HookEvent::PreToolUse { tool: "Write".to_string(), args: serde_json::json!({"path": "/tmp/test.txt"}), }; let result = hook_mgr.execute(&event, Some(5000)).await?; assert_eq!(result, HookResult::Deny); Ok(()) } #[tokio::test] async fn test_multiple_hooks_same_event() -> Result<()> { // Create temporary directory to act as project root let temp_dir = TempDir::new()?; // Create hook manager let mut hook_mgr = HookManager::new(temp_dir.path().to_str().unwrap()); // Register multiple hooks for the same event hook_mgr.register_hook( "PreToolUse".to_string(), "echo 'Hook 1' && exit 0".to_string(), Some("Edit".to_string()), Some(5000), ); hook_mgr.register_hook( "PreToolUse".to_string(), "echo 'Hook 2' && exit 0".to_string(), Some("Edit".to_string()), Some(5000), ); // Test that both hooks execute let event = HookEvent::PreToolUse { tool: "Edit".to_string(), args: serde_json::json!({"path": "/tmp/test.txt"}), }; let result = hook_mgr.execute(&event, Some(5000)).await?; assert_eq!(result, HookResult::Allow); Ok(()) } #[tokio::test] async fn test_hook_with_no_pattern_matches_all() -> Result<()> { // Create temporary directory to act as project root let temp_dir = TempDir::new()?; // Create hook manager let mut hook_mgr = HookManager::new(temp_dir.path().to_str().unwrap()); // Register a hook with no pattern (matches all tools) hook_mgr.register_hook( "PreToolUse".to_string(), "echo 'Hook for all tools' && exit 0".to_string(), None, // No pattern = match all Some(5000), ); // Test that the hook executes for any tool let event = HookEvent::PreToolUse { tool: "Read".to_string(), args: serde_json::json!({"path": "/tmp/test.txt"}), }; let result = hook_mgr.execute(&event, Some(5000)).await?; assert_eq!(result, HookResult::Allow); let event = HookEvent::PreToolUse { tool: "Write".to_string(), args: serde_json::json!({"path": "/tmp/test.txt"}), }; let result = hook_mgr.execute(&event, Some(5000)).await?; assert_eq!(result, HookResult::Allow); let event = HookEvent::PreToolUse { tool: "Bash".to_string(), args: serde_json::json!({"command": "ls"}), }; let result = hook_mgr.execute(&event, Some(5000)).await?; assert_eq!(result, HookResult::Allow); Ok(()) }