use clap::{CommandFactory, Parser};
use saya_cli::Cli;
const CLAP_INTERNAL_ARGS: &[&str] = &["help", "version"];
#[test]
fn every_subcommand_and_argument_has_help_text() {
let root = Cli::command();
let mut offenders: Vec<String> = Vec::new();
walk(&root, &mut offenders);
if !offenders.is_empty() {
panic!(
"blank --help entries (no description found):\n - {}\n\
Add a `///` doc comment (subcommands) or `///` on the arg field \
(arguments) so `--help` is never silent.",
offenders.join("\n - ")
);
}
}
fn walk(cmd: &clap::Command, offenders: &mut Vec<String>) {
let path = command_path(cmd);
if cmd
.get_about()
.map(|s| s.to_string())
.filter(|s| !s.trim().is_empty())
.is_none()
{
offenders.push(format!("subcommand `{path}` has no description"));
}
for arg in cmd.get_arguments() {
let id = arg.get_id().as_str().to_string();
if CLAP_INTERNAL_ARGS.contains(&id.as_str()) {
continue;
}
let has_help = arg
.get_help()
.map(|s| s.to_string())
.filter(|s| !s.trim().is_empty())
.is_some();
if !has_help {
let kind = if arg.is_positional() {
"positional"
} else {
"flag"
};
offenders.push(format!("`{path}` {kind} `{id}` has no description"));
}
}
for sub in cmd.get_subcommands() {
walk(sub, offenders);
}
}
fn command_path(cmd: &clap::Command) -> String {
cmd.get_name().to_string()
}
#[test]
fn config_show_no_longer_accepts_resolved_or_redacted() {
for flag in ["--resolved", "--redacted"] {
let parsed = Cli::try_parse_from(["saya", "config", "show", flag]);
assert!(
parsed.is_err(),
"`config show {flag}` must not parse after S15: {parsed:?}"
);
}
}
#[test]
fn config_show_help_no_longer_advertises_resolved_or_redacted() {
let mut cmd = Cli::command();
let show_help = cmd
.find_subcommand_mut("config")
.expect("`config` subcommand exists")
.find_subcommand_mut("show")
.expect("`config show` subcommand exists")
.render_help()
.to_string();
for flag in ["--resolved", "--redacted"] {
assert!(
!show_help.contains(flag),
"`config show --help` must not advertise {flag} after S15"
);
}
}