crates/gunnar-build-thing/tests/the_door.rs
//! **The `gunnar-build-thing` binary's doors**, run as the operator runs them (lane
//! OSSF-SILVER-CODE, 2026-10-31): `--list`, `toml`, `emit`, a composing variant, an unknown
//! name and the usage. Every answer is read off the process: its exit status, its stdout
//! and the file it wrote. Building a variant (`cargo build`) is not exercised here; the
//! forge's release rows do that.
use std::process::{Command, Output};
fn door(args: &[&str]) -> Output {
Command::new(env!("CARGO_BIN_EXE_gunnar-build-thing"))
.args(args)
.output()
.expect("run gunnar-build-thing")
}
fn text(bytes: &[u8]) -> String {
String::from_utf8_lossy(bytes).into_owned()
}
/// No argument and `--help` print the usage and fail: a build tool asked to build
/// nothing has not succeeded.
#[test]
fn no_argument_and_help_print_the_usage_and_fail() {
for args in [&[][..], &["--help"][..], &["toml", "-h"][..]] {
let out = door(args);
assert!(!out.status.success(), "{args:?} succeeded");
assert!(
text(&out.stderr).contains("usage: gunnar-build <variant>"),
"{args:?}: {}",
text(&out.stderr)
);
}
}
/// `--list` names every declared build point once, with its cargo selector or the note
/// that it composes others.
#[test]
fn the_list_names_every_declared_build_point() {
let out = door(&["--list"]);
assert!(out.status.success());
let listed = text(&out.stdout);
for variant in gunnar_build_thing::VARIANTS {
let line = listed
.lines()
.find(|l| l.split_whitespace().next() == Some(variant.name))
.unwrap_or_else(|| panic!("{} is not listed:\n{listed}", variant.name));
match (variant.package, variant.bin) {
(Some(p), Some(b)) => assert!(line.contains(&format!("-p {p} --bin {b}")), "{line}"),
(Some(p), None) => assert!(line.contains(&format!("-p {p}")), "{line}"),
_ => assert!(line.contains("runs no cargo"), "{line}"),
}
}
assert_eq!(listed.lines().count(), gunnar_build_thing::VARIANTS.len());
}
/// `toml` prints the emitted declaration and `emit <root>` writes the same bytes to
/// `<root>/.nornir/build-variants.toml`; `emit` onto a root it cannot write fails by name.
#[test]
fn toml_and_emit_write_the_one_declaration() {
let printed = door(&["toml"]);
assert!(printed.status.success(), "{}", text(&printed.stderr));
let expected = gunnar_build_thing::emitted_toml().expect("the declaration emits");
assert_eq!(text(&printed.stdout), expected);
let root = std::env::temp_dir().join(format!("gunnar-build-emit-{}", std::process::id()));
let _ = std::fs::remove_dir_all(&root);
std::fs::create_dir_all(&root).unwrap();
let emitted = door(&["emit", root.to_str().unwrap()]);
assert!(emitted.status.success(), "{}", text(&emitted.stderr));
assert!(
text(&emitted.stdout).starts_with("emitted "),
"{}",
text(&emitted.stdout)
);
let written = std::fs::read_to_string(root.join(".nornir/build-variants.toml"))
.expect("the file emit wrote");
assert_eq!(written, expected);
// A root that is a file: `.nornir` cannot be made under it.
let file = root.join("a-file");
std::fs::write(&file, b"not a directory").unwrap();
let refused = door(&["emit", file.to_str().unwrap()]);
assert!(!refused.status.success(), "emit under a file succeeded");
assert!(
text(&refused.stderr).contains("gunnar-build-thing:"),
"{}",
text(&refused.stderr)
);
let _ = std::fs::remove_dir_all(&root);
}
/// An unknown name is refused before anything is built, and points at `--list`; a
/// variant that composes others runs no cargo and succeeds.
#[test]
fn an_unknown_name_is_refused_and_a_composing_variant_runs_no_cargo() {
let out = door(&["no-such-build-point"]);
assert!(!out.status.success());
let err = text(&out.stderr);
assert!(
err.contains("no build point named `no-such-build-point`") && err.contains("--list"),
"{err}"
);
let composing = gunnar_build_thing::VARIANTS
.iter()
.find(|v| v.package.is_none())
.expect("a variant that composes others");
let out = door(&[composing.name]);
assert!(out.status.success(), "{}", text(&out.stderr));
assert!(
text(&out.stderr).contains("runs no cargo; skipping"),
"{}",
text(&out.stderr)
);
}