use std::path::{Path, PathBuf};
use std::process::Command;
fn repository() -> PathBuf {
Path::new(env!("CARGO_MANIFEST_DIR"))
.join("../../..")
.canonicalize()
.expect("the repository root resolves")
}
struct Root {
at: PathBuf,
}
impl Root {
fn new(label: &str, resolving: bool) -> Root {
let at = std::env::temp_dir().join(format!(
"headwater-cli-sweep-{}-{label}",
std::process::id()
));
let _ = std::fs::remove_dir_all(&at);
std::fs::create_dir_all(&at).expect("the root is made");
let repository = repository();
copy(
&repository.join(headwater_resolve::package::PACKAGES),
&at.join(headwater_resolve::package::PACKAGES),
);
copy(
&repository.join("docs/taxonomies"),
&at.join("docs/taxonomies"),
);
without_doctrine(&at.join("docs/taxonomies"));
for name in ["taxonomy.yml", "overlay.yml"] {
let to = at.join(".headwater").join(name);
std::fs::create_dir_all(to.parent().expect("it has a parent"))
.expect("the declaration directory is there");
std::fs::copy(repository.join(".headwater").join(name), to)
.expect("the declaration copies");
}
let root = Root { at };
if resolving {
let resolved = root.run(&["taxonomy", "resolve"]);
assert_eq!(
resolved.code,
Some(0),
"the fixture resolves\n{}{}",
resolved.out,
resolved.err
);
}
root
}
fn decision(&self, seq: &str) {
let path = self.at.join(format!("docs/decisions/{seq}-a-choice.md"));
std::fs::create_dir_all(path.parent().expect("it has a parent"))
.expect("the shelf directory is there");
std::fs::write(
&path,
format!(
"---\n\
id: HW-DR-{seq}\n\
status: draft\n\
status_since: 2026-01-01\n\
summary: \"A choice, so that the slice of a plan is not empty.\"\n\
last_verified: 2026-01-01\n\
---\n\n\
# A choice\n\n\
## Context\n\n\
One sentence.\n\n\
## Decision\n\n\
One sentence.\n\n\
## Consequences\n\n\
One sentence.\n"
),
)
.expect("the decision writes");
}
fn returned(&self, name: &str, body: &str) -> PathBuf {
let path = self.at.join(name);
std::fs::write(&path, body).expect("the return file writes");
path
}
fn run(&self, arguments: &[&str]) -> Ran {
let output = Command::new(env!("CARGO_BIN_EXE_headwater"))
.args(arguments)
.arg("--root")
.arg(&self.at)
.output()
.expect("the binary runs");
Ran {
code: output.status.code(),
out: String::from_utf8_lossy(&output.stdout).into_owned(),
err: String::from_utf8_lossy(&output.stderr).into_owned(),
}
}
}
impl Drop for Root {
fn drop(&mut self) {
let _ = std::fs::remove_dir_all(&self.at);
}
}
#[derive(Debug)]
struct Ran {
code: Option<i32>,
out: String,
err: String,
}
fn without_doctrine(library: &Path) {
let index = library.join("README.md");
if index.is_file() {
std::fs::remove_file(&index).expect("the library index is removed");
}
for entry in std::fs::read_dir(library).expect("the copied library reads") {
let doctrine = entry.expect("the entry reads").path().join("doctrine.md");
if doctrine.is_file() {
std::fs::remove_file(&doctrine).expect("the doctrine page is removed");
}
}
}
fn copy(from: &Path, to: &Path) {
std::fs::create_dir_all(to).expect("the directory is there");
for entry in std::fs::read_dir(from).expect("the fixture directory reads") {
let entry = entry.expect("the entry reads");
let target = to.join(entry.file_name());
match entry.file_type().expect("the file type reads").is_dir() {
true => copy(&entry.path(), &target),
false => {
std::fs::copy(entry.path(), &target).expect("the fixture copies");
}
}
}
}
#[test]
fn the_briefing_exits_zero_over_a_slice_that_holds_nothing() {
let root = Root::new("plan", true);
root.decision("0001");
let whole = root.run(&["sweep", "plan"]);
assert_eq!(whole.code, Some(0), "{whole:?}");
assert!(
whole.out.contains("1 of the 1 classified documents"),
"the plan states its own extent:\n{}",
whole.out
);
let empty = root.run(&["sweep", "plan", "--under", "docs/nowhere"]);
assert_eq!(
empty.code,
Some(0),
"a slice that holds no document is a fact rather than an error\n{empty:?}"
);
let again = root.run(&["sweep", "plan"]);
assert_eq!(
again.out, whole.out,
"two runs over one tree write one plan"
);
}
#[test]
fn every_refusal_of_a_returned_file_exits_zero() {
let root = Root::new("refusals", true);
root.decision("0001");
let lock = std::fs::read_to_string(root.at.join(".headwater/taxonomy.lock"))
.expect("the resolve wrote a lock");
let digest = lock
.lines()
.find_map(|line| line.trim().strip_prefix("digest: "))
.expect("the lock states its digest")
.to_string();
let broken = root.returned(
"broken.yml",
"findings: [\n - class: undeclared_conflict\n",
);
let ran = root.run(&["sweep", "report", broken.to_str().expect("a path")]);
assert_eq!(ran.code, Some(0), "{ran:?}");
assert!(
ran.out.contains("did not parse as YAML"),
"the refusal reached the reader on standard output:\n{}",
ran.out
);
let moved = root.returned(
"moved.yml",
"taxonomy: sha256:not-the-lock\nslice: docs\nfindings: []\n",
);
let ran = root.run(&["sweep", "report", moved.to_str().expect("a path")]);
assert_eq!(ran.code, Some(0), "{ran:?}");
let stray = root.returned(
"stray.yml",
&format!(
"taxonomy: {digest}\n\
slice: docs\n\
findings:\n \
- class: undefined_concept\n \
documents:\n \
- docs/nowhere/absent.md\n \
evidence:\n \
- path: docs/nowhere/absent.md\n \
quote: a passage no document holds\n \
message: a term nothing defines\n"
),
);
let ran = root.run(&["sweep", "report", stray.to_str().expect("a path")]);
assert_eq!(ran.code, Some(0), "{ran:?}");
assert!(
ran.out.contains("is not a classified document"),
"the finding was refused and the run still succeeded:\n{}",
ran.out
);
let strict = root.run(&[
"sweep",
"report",
stray.to_str().expect("a path"),
"--strict",
]);
assert_eq!(strict.code, Some(1), "{strict:?}");
assert_eq!(
strict.out, "",
"a refused command line writes no report, so a sampler cannot be read as having run"
);
assert!(
strict.err.contains("--strict"),
"the refusal names the flag the caller wrote:\n{}",
strict.err
);
}
#[test]
fn a_path_that_does_not_read_and_a_format_that_names_nothing_exit_one() {
let root = Root::new("caller", true);
let absent = root.run(&["sweep", "report", "/nonexistent/return.yml"]);
assert_eq!(absent.code, Some(1), "{absent:?}");
assert!(
absent.err.contains("the sweep file at"),
"the message names the file:\n{}",
absent.err
);
let file = root.returned("empty.yml", "findings: []\n");
let target = root.run(&[
"sweep",
"report",
file.to_str().expect("a path"),
"--format",
"sarif",
]);
assert_eq!(target.code, Some(1), "{target:?}");
assert!(
target.err.contains("names no target"),
"the message names the two targets that exist:\n{}",
target.err
);
for words in [
vec!["sweep"],
vec!["sweep", "report"],
vec!["sweep", "audit"],
] {
let ran = root.run(&words);
assert_eq!(ran.code, Some(1), "{words:?} is refused\n{ran:?}");
}
}
#[test]
fn a_corpus_that_never_resolved_exits_one_out_of_either_half() {
let root = Root::new("no-lock", false);
let planned = root.run(&["sweep", "plan"]);
assert_eq!(
planned.code,
Some(1),
"a plan cannot be taken without a lock\n{planned:?}"
);
assert!(
planned.err.contains("taxonomy.lock"),
"the message names the file and the verb that writes it:\n{}",
planned.err
);
let file = root.returned("empty.yml", "findings: []\n");
let reported = root.run(&["sweep", "report", file.to_str().expect("a path")]);
assert_eq!(
reported.code,
Some(1),
"and neither can a report, over a file that read perfectly well\n{reported:?}"
);
assert!(
reported.err.contains("taxonomy.lock"),
"the file was not the problem, and the message says which one was:\n{}",
reported.err
);
}