xtask/tests/staging_guard.rs
//! Red-first proofs for [`xtask::staging_guard`] — the I6 guard, over the one
//! directory git is *configured* not to watch.
//!
//! # Why every fixture is built at run time
//!
//! `xtask` has **zero dependencies** by construction, so
//! there is no `tempfile` here; the [`Scratch`] guard below is the same shape
//! `xtask/tests/bench_guard.rs` uses, and it removes its tree on drop.
//!
//! # What has to be red before the green means anything
//!
//! The leak this guard exists for was **invisible to git by design**: the
//! staging tree's own `.gitignore` is `*`, so `git status` was clean while 4.4
//! MB of another repository's forge-test fixtures sat on the initramfs root of
//! every ISO this box built, from 2026-08-13 to 2026-08-27. A guard that read
//! git would have been green throughout. So the red case here is the real
//! shape — `control.redb`, `control.git/`, `repos/` — and there is a third
//! test for the guard's OWN blindness: a tree it cannot find must not read as
//! a clean one.
use std::path::{Path, PathBuf};
use xtask::staging_guard::{scan, ALLOWED, STAGING_TREE};
/// A directory that removes itself. `xtask` has no `tempfile`.
struct Scratch(PathBuf);
impl Drop for Scratch {
fn drop(&mut self) {
let _ = std::fs::remove_dir_all(&self.0);
}
}
/// A root holding a staging tree with the two declared files in it.
fn declared_tree(name: &str) -> Scratch {
let dir =
Scratch(std::env::temp_dir().join(format!("gunnar-staging-{name}-{}", std::process::id())));
let _ = std::fs::remove_dir_all(&dir.0);
let data = dir.0.join(STAGING_TREE).join("data");
std::fs::create_dir_all(&data).expect("mkdir data");
std::fs::write(data.join(".keep"), "/data ships EMPTY\n").expect("write .keep");
std::fs::write(data.join(".gitignore"), "*\n!.keep\n!.gitignore\n").expect("write .gitignore");
dir
}
fn names(root: &Path) -> Vec<String> {
scan(root).0.into_iter().map(|s| s.rel).collect()
}
/// The two files in [`ALLOWED`] are the two files the tree carries, and the
/// walk says how many entries it saw so that "clean" is distinguishable from
/// "found nothing".
#[test]
fn the_declared_tree_is_clean_and_the_walk_is_not_silent() {
let dir = declared_tree("clean");
let (strays, seen) = scan(&dir.0);
assert!(strays.is_empty(), "{strays:?}");
assert_eq!(seen, 3, "data/ and its two declared files");
assert_eq!(ALLOWED.len(), 2, "both of them are what ALLOWED names");
}
/// **RED-FIRST, the leak as measured.** dwarves' `gunnar_appliance_iso` forge
/// test — another repository entirely — bootstraps `control.redb`,
/// `control.git/` and `repos/` into this directory. Every one of them is
/// named, and the redb's size is in the report because "4 MB on the initramfs
/// root" is the sentence that makes somebody act.
#[test]
fn the_forge_tests_fixture_state_is_caught_by_name_and_carries_its_size() {
let dir = declared_tree("leak");
let data = dir.0.join(STAGING_TREE).join("data");
std::fs::write(data.join("control.redb"), vec![0u8; 4096]).expect("write redb");
std::fs::create_dir_all(data.join("control.git")).expect("mkdir control.git");
std::fs::write(
data.join("control.git").join("HEAD"),
"ref: refs/heads/main\n",
)
.expect("write HEAD");
std::fs::create_dir_all(data.join("repos").join("demo.git")).expect("mkdir repos");
std::fs::write(data.join("repos").join("demo.git").join("HEAD"), "x\n").expect("write demo");
let (strays, seen) = scan(&dir.0);
let found = names(&dir.0);
for want in [
"data/control.redb",
"data/control.git/HEAD",
"data/repos/demo.git/HEAD",
] {
assert!(
found.contains(&want.to_string()),
"{want} missing from {found:?}"
);
}
assert!(
seen >= 7,
"the walk descended into both directories: {seen}"
);
let redb = strays
.iter()
.find(|s| s.rel == "data/control.redb")
.expect("the redb is reported");
assert_eq!(redb.bytes, 4096, "the size is measured, not assumed");
assert!(!redb.is_dir);
let said = redb.to_string();
assert!(said.contains("initramfs ROOT"), "explains itself: {said}");
assert!(
said.contains("gitignore"),
"names why no diff shows it: {said}"
);
}
/// **The guard's own blindness, asserted rather than assumed.** A root with no
/// staging tree produces no strays — identical in shape to a clean verdict —
/// so `scan` returns the walked count and every caller refuses on zero. Without
/// this, renaming the directory would leave a guard that is green forever.
#[test]
fn a_tree_that_cannot_be_found_walks_zero_and_is_not_a_clean_verdict() {
let dir = Scratch(
std::env::temp_dir().join(format!("gunnar-staging-missing-{}", std::process::id())),
);
let _ = std::fs::remove_dir_all(&dir.0);
std::fs::create_dir_all(&dir.0).expect("mkdir root");
let (strays, seen) = scan(&dir.0);
assert!(strays.is_empty(), "nothing to report: {strays:?}");
assert_eq!(seen, 0, "and nothing walked, which is the caller's refusal");
}
/// An undeclared EMPTY directory is a stray too: on an initramfs root it is a
/// mount point nobody declared, and it is the shape a half-cleaned leak leaves.
#[test]
fn an_undeclared_empty_directory_is_a_stray() {
let dir = declared_tree("emptydir");
std::fs::create_dir_all(dir.0.join(STAGING_TREE).join("etc")).expect("mkdir etc");
let (strays, _) = scan(&dir.0);
assert_eq!(strays.len(), 1, "{strays:?}");
assert_eq!(strays[0].rel, "etc");
assert!(strays[0].is_dir);
}
/// **This checkout's own staging tree**, walked. The fixtures above prove the
/// scanner can go red; this is the assertion that keeps the leak closed here.
#[test]
fn this_repositorys_own_staging_tree_is_clean() {
let root = xtask::guard::workspace_root();
let (strays, seen) = scan(&root);
assert!(
seen > 0,
"walked nothing under {}/{STAGING_TREE} — the tree moved, and a guard that cannot \
find what it watches must not report clean",
root.display()
);
assert!(
strays.is_empty(),
"the staging tree carries what it does not declare:\n{}",
strays
.iter()
.map(ToString::to_string)
.collect::<Vec<_>>()
.join("\n")
);
}