diff --git a/crates/mind-body/tests/self_run.rs b/crates/mind-body/tests/self_run.rs index ddfc2f1..98f014f 100644 --- a/crates/mind-body/tests/self_run.rs +++ b/crates/mind-body/tests/self_run.rs @@ -46,6 +46,44 @@ fn the_run_cap_must_be_a_positive_number_of_seconds_and_the_runtime_directory_mu assert!(!message.contains("MIND_SELF_RUN_CAP_S") && message.contains("RUNTIME_DIRECTORY is not set"), "{message}"); } +/// The hook `claude` runs in the session: `mind-self-run inject --spool ` reads the hook's +/// JSON on stdin and prints what the spool holds as context, exit 0; with `--pre-compact` it +/// blocks the compaction with exit 2; without a spool it is a usage error. +#[test] +fn inject_prints_the_spooled_briefs_as_hook_context_and_blocks_a_compaction() { + use std::io::Write; + use std::process::Stdio; + let spool = std::env::temp_dir().join(format!("mind-self-run-inject-{}", std::process::id())); + std::fs::create_dir_all(&spool).unwrap(); + std::fs::write( + spool.join("00000000000000000007-b7.json"), + r#"{"id":"eureka-body:follow_up:b7","campaign":"eureka-body","repo":"GameCult/Huginn","item":"Wire the thing.","ordinal":7}"#, + ) + .unwrap(); + let inject = |extra: &[&str], input: &str| { + let mut child = Command::new(env!("CARGO_BIN_EXE_mind-self-run")) + .args(["inject", "--spool", spool.to_str().unwrap()]) + .args(extra) + .stdin(Stdio::piped()) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()) + .spawn() + .unwrap(); + child.stdin.take().unwrap().write_all(input.as_bytes()).unwrap(); + child.wait_with_output().unwrap() + }; + let blocked = inject(&["--pre-compact"], "{}"); + assert_eq!((blocked.status.code(), blocked.stdout.is_empty()), (Some(2), true)); + let printed = inject(&[], r#"{"hook_event_name":"PostToolBatch"}"#); + assert_eq!(printed.status.code(), Some(0)); + let text = String::from_utf8_lossy(&printed.stdout); + assert!(text.contains("hookSpecificOutput") && text.contains("eureka-body:follow_up:b7") && text.contains("Wire the thing."), "{text}"); + assert!(inject(&[], "{}").stdout.is_empty(), "claimed once"); + let bare = Command::new(env!("CARGO_BIN_EXE_mind-self-run")).arg("inject").stdin(Stdio::null()).output().unwrap(); + assert_eq!(bare.status.code(), Some(2)); + std::fs::remove_dir_all(&spool).unwrap(); +} + #[test] fn without_an_instance_or_with_a_stray_argument_it_exits_2_with_the_usage_and_echoes_nothing() { let canary = "canary-argument-3f9a"; @@ -321,7 +359,7 @@ mod lane { let body = format!( "read -r -u \"$CLAUDE_CODE_OAUTH_TOKEN_FILE_DESCRIPTOR\" cred echo journal-line >&2 -echo '{first}'\necho '{second}'\nread -t 5 start\nread -t 5 flush\necho \"$start\" >> {record}\necho \"$flush\" >> {record}\necho \"$SELF_RUN_ENV_CANARY\" >> {record}\necho \"$cred\" >> {record}\necho '{{\"type\":\"result\"}}'\nwhile read more; do :; done\nexit 0", +echo '{first}'\necho '{second}'\nread -t 5 start\nread -t 5 flush\necho \"$start\" >> {record}\necho \"$flush\" >> {record}\necho \"$SELF_RUN_ENV_CANARY\" >> {record}\necho \"$cred\" >> {record}\necho '{{\"type\":\"result\"}}'\necho '{{\"type\":\"result\"}}'\nwhile read more; do :; done\nexit 0", first = request("msg_1", 100), second = request("msg_2", 299_999), record = bin.path().join("record").display(), @@ -333,7 +371,8 @@ echo '{first}'\necho '{second}'\nread -t 5 start\nread -t 5 flush\necho \"$start let stdout = String::from_utf8_lossy(&output.stdout).to_string(); assert_eq!(output.status.code(), Some(0), "{stdout}\n{}", String::from_utf8_lossy(&output.stderr)); - assert_eq!(stdout, "request 1 prefix 101\nrequest 2 prefix 300000\nended: flush-requested code 0 requests 2 prefix 300000\n"); + let printed: String = stdout.lines().filter(|line| !line.starts_with("decision ") && !line.starts_with("result")).map(|line| format!("{line}\n")).collect(); + assert_eq!(printed, "request 1 prefix 101\nrequest 2 prefix 300000\nended: flush-requested code 0 requests 2 prefix 300000\n"); assert!(!all_output(&output).contains(ENV_CANARY), "the unit environment is not echoed"); assert!(String::from_utf8_lossy(&output.stderr).contains("journal-line"), "the child's stderr is the unit's journal");