aboutsummaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
authorOgulcan Celik <ogulcancelik@gmail.com>2026-04-27 17:48:08 +0300
committerOgulcan Celik <ogulcancelik@gmail.com>2026-04-27 17:48:08 +0300
commitfb4e5a73ff73c7024e03190897b2028597bf74a4 (patch)
tree21479ef9ab9b1ce51c753310a27a186456af0317 /tests
parent6144aa218efcc41d4ccb546d73443de65606ce0f (diff)
fix: ignore claude subagent hook reports
fixes #58
Diffstat (limited to 'tests')
-rw-r--r--tests/cli_wrapper.rs86
1 files changed, 85 insertions, 1 deletions
diff --git a/tests/cli_wrapper.rs b/tests/cli_wrapper.rs
index 3f81b371..f3b02b9b 100644
--- a/tests/cli_wrapper.rs
+++ b/tests/cli_wrapper.rs
@@ -6,7 +6,7 @@ use std::fs;
use std::io::{BufRead, BufReader, Write};
use std::os::unix::net::{UnixListener, UnixStream};
use std::path::{Path, PathBuf};
-use std::process::Command;
+use std::process::{Command, Stdio};
use std::thread;
use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH};
@@ -286,6 +286,90 @@ fn send_request(socket_path: &Path, json: &str) -> serde_json::Value {
serde_json::from_str(&line).unwrap()
}
+fn run_claude_hook(action: &str, hook_input: &str) -> Option<serde_json::Value> {
+ let base = unique_test_dir();
+ fs::create_dir_all(&base).unwrap();
+ let socket_path = base.join("herdr.sock");
+ let listener = UnixListener::bind(&socket_path).unwrap();
+
+ let server = thread::spawn(move || {
+ listener.set_nonblocking(true).unwrap();
+ let deadline = Instant::now() + Duration::from_millis(700);
+ while Instant::now() < deadline {
+ match listener.accept() {
+ Ok((mut stream, _)) => {
+ let mut line = String::new();
+ let mut reader = BufReader::new(stream.try_clone().unwrap());
+ reader.read_line(&mut line).unwrap();
+ stream
+ .write_all(br#"{"id":"test","result":{"type":"ok"}}"#)
+ .unwrap();
+ stream.write_all(b"\n").unwrap();
+ stream.flush().unwrap();
+ return Some(line);
+ }
+ Err(err) if err.kind() == std::io::ErrorKind::WouldBlock => {
+ thread::sleep(Duration::from_millis(10));
+ }
+ Err(err) => panic!("accept failed: {err}"),
+ }
+ }
+ None
+ });
+
+ let hook_path = Path::new(env!("CARGO_MANIFEST_DIR"))
+ .join("src/integration/assets/claude/herdr-agent-state.sh");
+ let mut child = Command::new("bash")
+ .arg(hook_path)
+ .arg(action)
+ .env("HERDR_ENV", "1")
+ .env("HERDR_SOCKET_PATH", &socket_path)
+ .env("HERDR_PANE_ID", "p_test")
+ .stdin(Stdio::piped())
+ .stdout(Stdio::piped())
+ .stderr(Stdio::piped())
+ .spawn()
+ .unwrap();
+ let mut stdin = child.stdin.take().unwrap();
+ stdin.write_all(hook_input.as_bytes()).unwrap();
+ drop(stdin);
+
+ let output = child.wait_with_output().unwrap();
+ assert!(
+ output.status.success(),
+ "hook failed: status={:?} stderr={} stdout={}",
+ output.status.code(),
+ String::from_utf8_lossy(&output.stderr),
+ String::from_utf8_lossy(&output.stdout)
+ );
+
+ let request = server.join().unwrap();
+ cleanup_test_base(&base);
+ request.map(|line| serde_json::from_str(&line).unwrap())
+}
+
+#[test]
+fn claude_hook_suppresses_subagent_reports() {
+ let subagent_input = r#"{"hook_event_name":"PermissionRequest","agent_id":"agent-abc123","agent_type":"Explore"}"#;
+
+ assert!(run_claude_hook("working", subagent_input).is_none());
+ assert!(run_claude_hook("blocked", subagent_input).is_none());
+ assert!(run_claude_hook("idle", subagent_input).is_none());
+ assert!(run_claude_hook("release", subagent_input).is_none());
+}
+
+#[test]
+fn claude_hook_keeps_parent_agent_type_only_blocked() {
+ let request = run_claude_hook(
+ "blocked",
+ r#"{"hook_event_name":"PermissionRequest","agent_type":"Explore"}"#,
+ )
+ .expect("parent blocked should still report blocked");
+
+ assert_eq!(request["method"], "pane.report_agent");
+ assert_eq!(request["params"]["state"], "blocked");
+}
+
#[test]
fn pane_run_sends_one_send_input_request_with_enter_key() {
let base = unique_test_dir();