use std::process::Command;
const REGISTERED_COMMANDS: &[&str] = &[
"run", "serve", "chat", "code",
"inspect", "debug", "validate", "lint", "explain", "tensors",
"trace", "diff", "hex", "tree", "flow",
"export", "import", "convert", "compile", "merge", "quantize", "rosetta",
"pull", "list", "rm", "publish",
"finetune", "prune", "distill", "train", "tokenize", "tune",
"bench", "eval", "check", "qa", "qualify", "canary", "compare-hf", "parity",
"gpu", "profile", "ptx", "ptx-map", "cbtop",
"data", "pipeline",
"tui", "monitor", "runs", "experiment",
"showcase", "probar", "diagnose",
"oracle", "encrypt", "decrypt",
];
fn apr_binary() -> Command {
let mut cmd = Command::new(env!("CARGO_BIN_EXE_apr"));
cmd.env("NO_COLOR", "1");
cmd
}
fn get_help_commands() -> Vec<String> {
let output = apr_binary()
.arg("--help")
.output()
.expect("failed to run apr --help");
let stdout = String::from_utf8_lossy(&output.stdout);
let mut commands = Vec::new();
let mut in_commands = false;
for line in stdout.lines() {
if line.starts_with("Commands:") {
in_commands = true;
continue;
}
if in_commands {
if line.starts_with("Options:") || line.is_empty() && commands.len() > 5 {
break;
}
let trimmed = line.trim();
if let Some(cmd_name) = trimmed.split_whitespace().next() {
if !cmd_name.is_empty()
&& cmd_name.chars().next().map_or(false, |c| c.is_ascii_lowercase())
&& !cmd_name.contains('(')
&& !cmd_name.contains(')')
{
commands.push(cmd_name.to_string());
}
}
}
}
commands
}
#[test]
fn test_all_commands_respond_to_help() {
let mut failures = Vec::new();
for cmd in REGISTERED_COMMANDS {
let output = apr_binary()
.args([cmd, "--help"])
.output()
.unwrap_or_else(|e| panic!("failed to run apr {} --help: {}", cmd, e));
if !output.status.success() {
failures.push(format!(
"apr {} --help exited with {:?}",
cmd,
output.status.code()
));
}
}
assert!(
failures.is_empty(),
"FALSIFY-CLI-003: Commands that failed --help:\n{}",
failures.join("\n")
);
}
#[test]
fn test_all_contract_commands_exist() {
let help_commands = get_help_commands();
let mut missing = Vec::new();
for cmd in REGISTERED_COMMANDS {
if !help_commands.iter().any(|h| h == cmd) {
missing.push(*cmd);
}
}
assert!(
missing.is_empty(),
"FALSIFY-CLI-001: Commands in contract but missing from `apr --help`: {:?}",
missing
);
}
#[test]
fn test_no_unregistered_commands() {
let help_commands = get_help_commands();
let registered: std::collections::HashSet<&str> = REGISTERED_COMMANDS.iter().copied().collect();
let mut unregistered = Vec::new();
for cmd in &help_commands {
if !registered.contains(cmd.as_str()) {
if cmd != "help" {
unregistered.push(cmd.clone());
}
}
}
assert!(
unregistered.is_empty(),
"FALSIFY-CLI-002: Commands in `apr --help` but not in contract: {:?}\n\
Add them to contracts/apr-cli-commands-v1.yaml AND this test's REGISTERED_COMMANDS.",
unregistered
);
}
#[test]
fn test_command_count_matches() {
let help_commands = get_help_commands();
let help_count = help_commands.iter().filter(|c| c.as_str() != "help").count();
let contract_count = REGISTERED_COMMANDS.len();
assert_eq!(
help_count, contract_count,
"FALSIFY-CLI-005: Command count mismatch.\n\
`apr --help` has {} commands, contract has {}.\n\
Help commands: {:?}",
help_count, contract_count, help_commands
);
}
#[test]
fn test_version_flag() {
let output = apr_binary()
.arg("--version")
.output()
.expect("failed to run apr --version");
assert!(output.status.success(), "apr --version should exit 0");
let stdout = String::from_utf8_lossy(&output.stdout);
assert!(
stdout.contains("apr"),
"apr --version should contain 'apr': got {:?}",
stdout
);
}
#[test]
fn test_no_args_exits_usage_error() {
let output = apr_binary()
.output()
.expect("failed to run apr with no args");
let code = output.status.code().unwrap_or(-1);
assert_eq!(
code, 2,
"apr with no args should exit 2 (usage error), got {}",
code
);
}