use std::path::{Path, PathBuf};
use super::{process_holding, run_cwd_probe, scan_probe};
fn set(pid: &str, command: &str, cwd: &str) -> String {
format!("p{pid}\nc{command}\nn{cwd}\n")
}
fn self_set() -> String {
set(&std::process::id().to_string(), "test", "/")
}
#[test]
fn liveness_reports_a_process_standing_in_the_directory() {
let root = PathBuf::from("/r/.claude/worktrees/agent-x");
let listing = format!(
"{}{}",
set("100", "zsh", "/Users/bob"),
set("7762", "trusty-memory", "/r/.claude/worktrees/agent-x")
);
let reason = scan_probe(&listing, &root).expect("a process in the tree must refuse removal");
assert!(reason.contains("7762"), "must name the pid: {reason}");
assert!(
reason.contains("trusty-memory"),
"must name the command: {reason}"
);
}
#[test]
fn liveness_reports_a_process_nested_below_the_directory() {
let root = PathBuf::from("/r/.claude/worktrees/agent-x");
let listing = set(
"7762",
"cargo",
"/r/.claude/worktrees/agent-x/crates/trusty-mpm",
);
assert!(
scan_probe(&listing, &root).is_some(),
"a process one level down is still inside the tree"
);
}
#[test]
fn liveness_ignores_a_sibling_directory() {
let root = PathBuf::from("/r/.claude/worktrees/agent-x");
let listing = format!(
"{}{}{}",
self_set(),
set("100", "zsh", "/r/.claude/worktrees/agent-y"),
set("101", "zsh", "/r/.claude/worktrees/agent-x2")
);
assert_eq!(
scan_probe(&listing, &root),
None,
"neither sibling is inside the candidate"
);
}
#[test]
fn liveness_treats_a_listing_that_cannot_see_this_process_as_in_use() {
let root = PathBuf::from("/r/.claude/worktrees/agent-x");
let listing = set("100", "zsh", "/Users/bob");
let reason =
scan_probe(&listing, &root).expect("a listing blind to this process rules nothing out");
assert!(
reason.contains(&std::process::id().to_string()),
"the refusal must name the pid it could not find: {reason}"
);
}
#[test]
fn liveness_treats_a_missing_lsof_as_in_use() {
let err = run_cwd_probe("trusty-mpm-no-such-probe-binary")
.expect_err("an absent probe binary must not yield a listing");
assert!(
err.contains("could not run"),
"must say which step failed: {err}"
);
}
#[test]
fn liveness_treats_an_empty_probe_as_in_use() {
let root = PathBuf::from("/r/.claude/worktrees/agent-x");
let reason = scan_probe("", &root).expect("an empty listing observed nothing");
assert!(reason.contains("ADR-0045"), "{reason}");
}
#[test]
fn liveness_treats_an_unparsable_probe_as_in_use() {
let root = PathBuf::from("/r/.claude/worktrees/agent-x");
assert!(
scan_probe("COMMAND PID USER FD\nzsh 100 bob cwd\n", &root).is_some(),
"a human-readable table carries no `n` field — the probe answered in a \
shape this cannot read, which is not evidence of freedom"
);
}
#[test]
fn liveness_treats_an_uncanonicalizable_path_as_in_use() {
let reason = process_holding(Path::new("/nonexistent/trusty-mpm/agent-x"))
.expect("an unresolvable path must not read as free");
assert!(reason.contains("canonicalize"), "{reason}");
}
#[test]
fn liveness_clears_an_unused_directory_against_the_real_probe() {
let dir = tempfile::tempdir().expect("tempdir");
match process_holding(dir.path()) {
None => {}
Some(reason) => assert!(
reason.contains("could not run") || reason.contains("exited"),
"a fresh temp directory holds no process; the only acceptable \
refusal is an unavailable probe, got: {reason}"
),
}
}