use assert_cmd::Command; use std::fs; #[test] fn hook_blocks_read_of_botignore_match() { let tmp = tempfile::tempdir().unwrap(); fs::write(tmp.path().join(".botignore"), ".env\n").unwrap(); let target = tmp.path().join(".env"); fs::write(&target, "").unwrap(); let payload = serde_json::json!({ "tool_name": "Read", "tool_input": { "file_path": target.to_str().unwrap() } }) .to_string(); let out = Command::cargo_bin("fermata") .unwrap() .args(["hook", "--harness", "claude"]) .write_stdin(payload) .assert() .success() // hook always exits 0 .get_output() .stdout .clone(); let v: serde_json::Value = serde_json::from_slice(&out).unwrap(); assert_eq!(v["hookSpecificOutput"]["permissionDecision"], "deny"); assert!(v["hookSpecificOutput"]["permissionDecisionReason"] .as_str() .unwrap() .contains(".env")); } #[test] fn hook_allows_unrelated_read() { let tmp = tempfile::tempdir().unwrap(); fs::write(tmp.path().join(".botignore"), ".env\n").unwrap(); let target = tmp.path().join("src.rs"); fs::write(&target, "").unwrap(); let payload = serde_json::json!({ "tool_name": "Read", "tool_input": { "file_path": target.to_str().unwrap() } }) .to_string(); let out = Command::cargo_bin("fermata") .unwrap() .args(["hook", "--harness", "claude"]) .write_stdin(payload) .assert() .success() .get_output() .stdout .clone(); let v: serde_json::Value = serde_json::from_slice(&out).unwrap(); assert_eq!(v["hookSpecificOutput"]["permissionDecision"], "allow"); } #[test] fn hook_unknown_harness_errors() { Command::cargo_bin("fermata") .unwrap() .args(["hook", "--harness", "doesnotexist"]) .write_stdin("{}") .assert() .code(2); }