server/cli/tests/open_cli_speaks_no_money.rs
//! **The open command line offers no money** (A28 M13): the half of Gunnar's guard that walks
//! the `clap` tree (`the_open_command_line_offers_no_money`), here over the built `holger`
//! binary's own `--help` (the CLI is a bin, so its `Cli` is not reachable from a test).
//! Every subcommand's help is asked for and judged by the one judge,
//! `server/lib/tests/money/judge.rs` (Gunnar's, verbatim). A `metered` build may speak of
//! money: it is the one paired with monetize, so this is scoped to the open build.
#[path = "../../lib/tests/money/judge.rs"]
mod judge;
use std::process::Command;
/// The subcommand names a `clap` help lists under `Commands:`.
fn subcommands(help: &str) -> Vec<String> {
let mut out = Vec::new();
let mut inside = false;
for line in help.lines() {
if line.trim_end() == "Commands:" {
inside = true;
continue;
}
if inside {
if line.trim().is_empty() || !line.starts_with(" ") {
if !line.trim().is_empty() {
inside = false;
}
continue;
}
if let Some(name) = line.split_whitespace().next() {
if name != "help" && !line.starts_with(" ") {
out.push(name.trim_end_matches(',').to_owned());
}
}
}
}
out
}
#[test]
fn the_help_reader_finds_the_subcommands() {
let help = "holger\n\nUsage: holger <COMMAND>\n\nCommands:\n start Start the server\n token Mint a token\n (continued)\n help Print this message\n\nOptions:\n -h, --help\n";
assert_eq!(subcommands(help), vec!["start", "token"]);
}
fn help_of(path: &[String]) -> String {
let out = Command::new(env!("CARGO_BIN_EXE_holger"))
.args(path)
.arg("--help")
.output()
.expect("the holger binary runs");
let mut text = String::from_utf8_lossy(&out.stdout).into_owned();
text.push_str(&String::from_utf8_lossy(&out.stderr));
text
}
#[cfg(not(feature = "metered"))]
#[test]
fn the_open_command_line_offers_no_money() {
let mut stack: Vec<Vec<String>> = vec![Vec::new()];
let mut walked = 0usize;
let mut leaks = Vec::new();
while let Some(path) = stack.pop() {
let help = help_of(&path);
walked += 1;
for (n, para) in help.split("\n\n").enumerate() {
let hits = judge::leaks_in(para);
if !hits.is_empty() {
leaks.push(format!(
"holger {} --help ¶{}: {hits:?} in {:?}",
path.join(" "),
n + 1,
para.trim()
));
}
}
if path.len() < 3 {
for sub in subcommands(&help) {
let mut next = path.clone();
next.push(sub);
stack.push(next);
}
}
}
assert!(
walked > 5,
"only {walked} help pages were read; the help layout moved"
);
assert!(
leaks.is_empty(),
"the OPEN holger command line offers money vocabulary to its operator ({} hits). holger \
takes no money; every word below belongs to the private monetize half, behind \
`metered`.\n {}",
leaks.len(),
leaks.join("\n ")
);
}