use std::collections::BTreeSet;
use std::process::Command;
use x0x::api::ENDPOINTS;
fn tokenize_cli_name(cli: &str) -> Vec<Vec<&str>> {
cli.split(" / ")
.map(|variant| variant.split_whitespace().collect::<Vec<_>>())
.filter(|v| !v.is_empty())
.collect()
}
fn bin_path() -> &'static str {
env!("CARGO_BIN_EXE_x0x")
}
fn run_cli(args: &[&str]) -> Result<std::process::Output, String> {
Command::new(bin_path())
.args(args)
.output()
.map_err(|e| format!("failed to spawn {}: {e}", bin_path()))
}
fn probe_help(tokens: &[&str]) -> Result<(), String> {
let mut args: Vec<&str> = tokens.to_vec();
args.push("--help");
let output = run_cli(&args)?;
if output.status.success() {
return Ok(());
}
Err(format!(
"`x0x {}` failed (status {:?})\nstderr: {}",
args.join(" "),
output.status.code(),
String::from_utf8_lossy(&output.stderr).trim(),
))
}
#[test]
fn every_endpoint_is_reachable_from_cli() {
let mut failures = Vec::new();
let mut seen: BTreeSet<Vec<&str>> = BTreeSet::new();
for ep in ENDPOINTS {
for tokens in tokenize_cli_name(ep.cli_name) {
if !seen.insert(tokens.clone()) {
continue;
}
if let Err(msg) = probe_help(&tokens) {
failures.push(format!(
" {} {} (cli_name: \"{}\"): {}",
ep.method, ep.path, ep.cli_name, msg
));
}
}
}
assert!(
failures.is_empty(),
"\n\nCLI parity violations — endpoints in ENDPOINTS have no matching \
`x0x` subcommand ({} failures):\n{}\n\n\
Fix: add the subcommand to src/bin/x0x.rs or correct the cli_name \
in src/api/mod.rs::ENDPOINTS.\n",
failures.len(),
failures.join("\n")
);
}
#[test]
fn group_update_rejects_empty_patch_before_daemon_check() {
let output = run_cli(&["group", "update", "deadbeef"]).expect("spawn x0x group update");
assert!(!output.status.success(), "empty group update should fail");
let stderr = String::from_utf8_lossy(&output.stderr);
assert!(
stderr.contains("group update requires at least one of: --name, --description"),
"unexpected stderr: {stderr}"
);
}
#[test]
fn group_policy_rejects_empty_patch_before_daemon_check() {
let output = run_cli(&["group", "policy", "deadbeef"]).expect("spawn x0x group policy");
assert!(!output.status.success(), "empty group policy should fail");
let stderr = String::from_utf8_lossy(&output.stderr);
assert!(
stderr.contains(
"group policy requires at least one of: --preset, --discoverability, --admission, --confidentiality, --read-access, --write-access"
),
"unexpected stderr: {stderr}"
);
}