use clap::{ArgAction, Command};
use brontes::{Config, Error};
fn build_cli_with_mcp() -> Command {
Command::new("hostc")
.version("0.0.1")
.subcommand(Command::new("noop"))
.subcommand(brontes::command(None))
}
fn run_async<F: std::future::Future>(fut: F) -> F::Output {
let rt = tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()
.expect("rt");
rt.block_on(fut)
}
#[test]
fn handle_rejects_explicit_empty_command_name_with_config_error() {
let cli = build_cli_with_mcp();
let matches = cli
.clone()
.try_get_matches_from(["hostc", "mcp", "tools"])
.expect("parses");
let sub = matches.subcommand_matches("mcp").expect("mcp matches");
let cfg = Config::default().command_name("");
let result = run_async(brontes::handle(sub, &cli, Some(&cfg)));
let err = result.expect_err("must reject empty command_name");
let msg = err.to_string();
assert!(msg.contains("command_name must not be empty"), "got: {msg}");
assert!(matches!(err, Error::Config(_)));
}
#[test]
fn handle_rejects_when_mcp_subtree_not_mounted() {
let cli_with_mcp = build_cli_with_mcp();
let matches = cli_with_mcp
.try_get_matches_from(["hostc", "mcp", "tools"])
.expect("parses");
let sub = matches.subcommand_matches("mcp").expect("mcp matches");
let cli_missing_mount = Command::new("hostc")
.version("0.0.1")
.subcommand(Command::new("noop"));
let result = run_async(brontes::handle(sub, &cli_missing_mount, None));
let err = result.expect_err("must reject missing mount");
let msg = err.to_string();
assert!(
msg.contains("did you forget to mount brontes::command"),
"got: {msg}"
);
}
#[test]
fn handle_rejects_sibling_collision_with_user_owned_mcp_subcommand() {
let cli = Command::new("hostc")
.version("0.0.1")
.subcommand(Command::new("mcp").subcommand(Command::new("install")));
let matches = cli
.clone()
.try_get_matches_from(["hostc", "mcp", "install"])
.expect("parses");
let sub = matches.subcommand_matches("mcp").expect("mcp matches");
let result = run_async(brontes::handle(sub, &cli, None));
let err = result.expect_err("must reject sibling collision");
let msg = err.to_string();
assert!(
msg.contains("not minted by brontes") && msg.contains("sibling collision"),
"got: {msg}"
);
}
#[test]
fn handle_rejects_internal_marker_leaf_without_leaking_marker_name() {
let cli = build_cli_with_mcp();
let matches = cli
.clone()
.try_get_matches_from(["hostc", "mcp", "__brontes_internal_marker"])
.expect("hidden subcommand still parses");
let sub = matches.subcommand_matches("mcp").expect("mcp matches");
let result = run_async(brontes::handle(sub, &cli, None));
let err = result.expect_err("internal marker must not be runnable");
let msg = err.to_string();
assert!(
msg.contains("internal marker") && !msg.contains("__brontes_internal_marker"),
"marker name must not leak; got: {msg}"
);
}
#[test]
fn handle_rejects_no_leaf_selected_with_friendly_error() {
let cli = Command::new("hostc")
.version("0.0.1")
.subcommand(
Command::new("mcp")
.subcommand(Command::new("__brontes_internal_marker").hide(true))
.subcommand_required(false)
.arg_required_else_help(false)
.arg(
clap::Arg::new("noise")
.long("noise")
.action(ArgAction::SetTrue),
),
);
let matches = cli
.clone()
.try_get_matches_from(["hostc", "mcp"])
.expect("parses without leaf");
let sub = matches.subcommand_matches("mcp").expect("mcp matches");
let result = run_async(brontes::handle(sub, &cli, None));
let err = result.expect_err("no leaf must error");
let msg = err.to_string();
assert!(msg.contains("no mcp subcommand selected"), "got: {msg}");
}
#[test]
fn handle_rejects_unknown_leaf_with_friendly_error() {
let cli = Command::new("hostc").version("0.0.1").subcommand(
Command::new("mcp")
.subcommand(Command::new("__brontes_internal_marker").hide(true))
.subcommand(Command::new("totallymade"))
.subcommand_required(false)
.arg_required_else_help(false),
);
let matches = cli
.clone()
.try_get_matches_from(["hostc", "mcp", "totallymade"])
.expect("parses");
let sub = matches.subcommand_matches("mcp").expect("mcp matches");
let result = run_async(brontes::handle(sub, &cli, None));
let err = result.expect_err("unknown leaf must error");
let msg = err.to_string();
assert!(
msg.contains("unknown mcp subcommand") && msg.contains("totallymade"),
"got: {msg}"
);
}