use super::*;
pub(crate) const INCIDENT_EXE: &str =
"/srv/projects/acme/target-7247/debug/deps/test_session_lifecycle-cd3ba8f03938239b";
fn group(cmd: &str) -> serde_json::Value {
serde_json::json!({
"matcher": "*",
"hooks": [{ "type": "command", "command": cmd, "timeout": 5 }]
})
}
pub(crate) fn incident_settings() -> serde_json::Value {
let hook = format!("{INCIDENT_EXE} hook");
serde_json::json!({
"outputStyle": "trusty-mpm",
"statusLine": {
"type": "command",
"command": format!("{INCIDENT_EXE} statusline"),
"padding": 0
},
"hooks": {
"PreToolUse": [
group(&hook),
group(&format!("{INCIDENT_EXE} hook --pm-guard")),
],
"PostToolUse": [group(&hook)],
"Stop": [group(&hook)],
"SubagentStop": [group(&hook)],
"SessionStart": [group(&hook)],
"SessionEnd": [group(&hook)],
"UserPromptSubmit": [group("trusty-memory prompt-context")],
}
})
}
#[test]
fn build_tree_hook_command_flags_the_incident_shape() {
assert!(is_build_tree_hook_command(&format!(
"{INCIDENT_EXE} hook --pm-guard"
)));
assert!(is_build_tree_hook_command(&format!("{INCIDENT_EXE} hook")));
}
#[test]
fn build_tree_hook_command_matches_every_tm_argv_shape() {
for tail in TM_HOOK_ARGV_TAILS {
assert!(
is_build_tree_hook_command(&format!("{INCIDENT_EXE}{tail}")),
"tail {tail:?} was not recognised"
);
}
}
#[test]
fn build_tree_hook_command_flags_other_build_layouts() {
for exe in [
"/repo/target/debug/deps/whatever-0123456789abcdef",
"/repo/target/release/some_bin",
"/repo/target-7224/debug/tm",
"/home/me/.claude/worktrees/agent-abc/target/debug/deps/x-0123456789abcdef",
] {
assert!(
is_build_tree_hook_command(&format!("{exe} hook")),
"{exe} was not recognised as a build-tree path"
);
}
}
#[test]
fn build_tree_hook_command_ignores_an_installed_binary() {
assert!(!is_build_tree_hook_command("/usr/local/bin/tm hook"));
assert!(!is_build_tree_hook_command(
"/opt/tm/bin/tm hook --pm-guard"
));
assert!(!is_build_tree_hook_command("tm hook"));
}
#[test]
fn build_tree_hook_command_ignores_a_foreign_argv_shape() {
for cmd in [
format!("{INCIDENT_EXE} --check"),
format!("{INCIDENT_EXE} lint --fix"),
format!("{INCIDENT_EXE} hookup"),
format!("{INCIDENT_EXE} hook --pm-guard --extra"),
] {
assert!(
!is_build_tree_hook_command(&cmd),
"{cmd} should not be claimed"
);
}
}
#[test]
fn build_tree_statusline_command_flags_a_build_tree_binary() {
assert!(is_build_tree_statusline_command(&format!(
"{INCIDENT_EXE} statusline"
)));
}
#[test]
fn build_tree_statusline_command_ignores_an_installed_binary() {
assert!(!is_build_tree_statusline_command(
"/usr/local/bin/tm statusline"
));
assert!(!is_build_tree_statusline_command("tm statusline"));
}
#[test]
fn repointed_hook_command_rewrites_every_argv_shape() {
let installed = std::path::Path::new("/usr/local/bin/tm");
for tail in TM_HOOK_ARGV_TAILS {
assert_eq!(
repointed_hook_command(&format!("{INCIDENT_EXE}{tail}"), installed).as_deref(),
Some(format!("/usr/local/bin/tm{tail}").as_str()),
"tail {tail:?} was not repointed"
);
}
}
#[test]
fn repointed_hook_command_declines_an_installed_binary() {
let installed = std::path::Path::new("/usr/local/bin/tm");
for cmd in [
"/usr/local/bin/tm hook",
"/usr/local/bin/tm hook --pm-guard",
"tm hook",
&format!("{INCIDENT_EXE} lint --fix"),
] {
assert!(
repointed_hook_command(cmd, installed).is_none(),
"{cmd} should not be repointed"
);
}
}
#[test]
fn repointed_statusline_command_rewrites_the_incident_shape() {
let installed = std::path::Path::new("/usr/local/bin/tm");
assert_eq!(
repointed_statusline_command(&format!("{INCIDENT_EXE} statusline"), installed).as_deref(),
Some("/usr/local/bin/tm statusline")
);
assert!(repointed_statusline_command(&format!("{INCIDENT_EXE} hook"), installed).is_none());
assert!(repointed_statusline_command("/usr/local/bin/tm statusline", installed).is_none());
}
#[test]
fn statusline_tail_is_not_a_hook_tail() {
assert!(!is_build_tree_hook_command(&format!(
"{INCIDENT_EXE} statusline"
)));
assert!(!TM_HOOK_ARGV_TAILS.contains(&STATUSLINE_ARGV_TAIL));
}
#[test]
fn repointed_hook_command_declines_a_foreign_build_tree_stem() {
let installed = std::path::Path::new("/usr/local/bin/tm");
for exe in [
"/repo/target/debug/mytool",
"/repo/target/release/some_bin",
"/repo/target-7224/debug/deps/mytool",
] {
let cmd = format!("{exe} hook");
assert!(
is_build_tree_hook_command(&cmd),
"{exe} is still named as a dead build artifact"
);
assert!(
repointed_hook_command(&cmd, installed).is_none(),
"{exe} must not be repointed"
);
assert!(
repointed_statusline_command(&format!("{exe} statusline"), installed).is_none(),
"{exe} statusline must not be repointed"
);
}
}
#[test]
fn repointed_hook_command_still_claims_what_tm_wrote() {
let installed = std::path::Path::new("/usr/local/bin/tm");
for exe in [
"/repo/target/debug/tm",
"/repo/target-7224/debug/trusty-mpm",
"/repo/target/debug/deps/trusty_mpm-1a2b3c4d5e6f7a8b",
INCIDENT_EXE,
] {
assert_eq!(
repointed_hook_command(&format!("{exe} hook"), installed).as_deref(),
Some("/usr/local/bin/tm hook"),
"{exe} must still be repaired"
);
}
}