cargo-allow 0.1.1

Source-tree exception ledger and policy scanner for Rust repositories.
use std::path::Path;

use crate::*;

#[cfg(test)]
mod tests {
    use super::*;
    use clap::CommandFactory;
    use clap::Parser;

    #[test]
    fn normalized_args_accepts_cargo_subcommand_prefix() {
        let normalized = normalized_args(argv(vec!["cargo-allow", "allow", "audit"]));
        let expected = argv(vec!["cargo-allow", "audit"]);
        assert_eq!(normalized, expected);
    }

    #[test]
    fn clap_exposes_package_version() {
        let mut command = CargoAllowCli::command();

        assert_eq!(
            command.get_version(),
            Some(env!("CARGO_PKG_VERSION")),
            "root CLI should expose the package version"
        );
        assert!(
            command.render_help().to_string().contains("-V, --version"),
            "root help should include the standard version flag"
        );
    }

    #[test]
    fn clap_parses_markdown_alias() {
        let parsed =
            CargoAllowCli::try_parse_from(argv(vec!["cargo-allow", "check", "--format", "md"]))
                .unwrap_or_else(|err| std::panic::panic_any(format!("CLI should parse: {err}")));

        assert!(matches!(
            parsed.command,
            Some(CargoAllowCommand::Check(check::CheckArgs {
                format: OutputFormat::Markdown,
                ..
            }))
        ));
    }

    #[test]
    fn clap_requires_diff_base() {
        let parsed = CargoAllowCli::try_parse_from(argv(vec!["cargo-allow", "diff"]));

        assert!(parsed.is_err());
    }

    #[test]
    fn clap_parses_source_tree_root_for_check() {
        let parsed = CargoAllowCli::try_parse_from(argv(vec![
            "cargo-allow",
            "check",
            "--root",
            "fixtures/source-snapshot",
        ]))
        .unwrap_or_else(|err| std::panic::panic_any(format!("CLI should parse --root: {err}")));

        assert!(matches!(
            parsed.command,
            Some(CargoAllowCommand::Check(check::CheckArgs {
                root: RootArgs { root: Some(root) },
                ..
            })) if root == Path::new("fixtures/source-snapshot")
        ));
    }

    #[test]
    fn clap_leaves_check_mode_unset_when_not_provided() {
        let parsed = CargoAllowCli::try_parse_from(argv(vec!["cargo-allow", "check"]))
            .unwrap_or_else(|err| std::panic::panic_any(format!("CLI should parse check: {err}")));

        assert!(matches!(
            parsed.command,
            Some(CargoAllowCommand::Check(check::CheckArgs {
                mode: None,
                ..
            }))
        ));
    }

    #[test]
    fn clap_parses_explicit_check_mode() {
        let parsed =
            CargoAllowCli::try_parse_from(argv(vec!["cargo-allow", "check", "--mode", "strict"]))
                .unwrap_or_else(|err| {
                    std::panic::panic_any(format!("CLI should parse --mode strict: {err}"))
                });

        assert!(matches!(
            parsed.command,
            Some(CargoAllowCommand::Check(check::CheckArgs {
                mode: Some(mode),
                ..
            })) if mode == "strict"
        ));
    }

    #[test]
    fn check_help_describes_policy_default_as_source_tree_gate_mode() {
        let mut command = CargoAllowCli::command();
        let help = command.render_help().to_string();

        assert!(help.contains("Source exception ledger for source trees"));

        let Some(check) = command.find_subcommand_mut("check") else {
            std::panic::panic_any("check subcommand should exist");
        };
        let help = check.render_help().to_string();

        assert!(help.contains("policy-configured source-tree gate mode"));
        assert!(!help.contains("workspace.default_mode"));
    }

    fn argv(items: Vec<&str>) -> Vec<String> {
        items.into_iter().map(String::from).collect()
    }
}