use clap::{Parser, Subcommand, ValueEnum};
use clap_complete::Shell;
use std::path::PathBuf;
#[derive(Parser, Debug)]
#[command(
name = "cli-test",
version,
about = "Comprehensive CLI testing framework",
long_about = "Analyzes CLI tools, generates BATS test suites, and produces detailed security reports"
)]
pub struct Cli {
#[command(subcommand)]
pub command: Commands,
#[arg(short, long, global = true)]
pub verbose: bool,
}
#[derive(Subcommand, Debug)]
pub enum Commands {
#[command(about = "Analyze CLI tool structure and options")]
Analyze {
#[arg(value_name = "BINARY")]
binary: PathBuf,
#[arg(short, long, default_value = "cli-analysis.json")]
output: PathBuf,
#[arg(short, long, default_value = "3")]
depth: u8,
#[arg(long)]
parallel: bool,
},
#[command(about = "Generate test suites from analysis (BATS, assert_cmd, or snapbox)")]
Generate {
#[arg(value_name = "ANALYSIS")]
analysis: PathBuf,
#[arg(short, long, default_value = "test-output")]
output: PathBuf,
#[arg(short, long, default_value = "all")]
categories: String,
#[arg(short, long, default_value = "bats")]
format: TestFormat,
#[arg(long)]
include_intensive: bool,
},
#[command(about = "Execute BATS tests and generate reports")]
Run {
#[arg(value_name = "TEST_DIR")]
test_dir: PathBuf,
#[arg(short, long, default_value = "markdown")]
format: ReportFormat,
#[arg(short, long, default_value = "reports")]
output: PathBuf,
#[arg(short = 't', long, default_value = "300")]
timeout: u64,
#[arg(short = 's', long)]
skip: Option<String>,
},
#[command(about = "Validate analysis JSON file structure")]
Validate {
#[arg(value_name = "FILE")]
file: PathBuf,
},
#[command(about = "Generate shell completion scripts")]
Completion {
#[arg(value_name = "SHELL")]
shell: Shell,
},
}
#[derive(ValueEnum, Clone, Debug)]
pub enum ReportFormat {
Markdown,
Json,
Html,
Junit,
All,
}
impl ReportFormat {
pub fn extension(&self) -> &'static str {
match self {
Self::Markdown => "md",
Self::Json => "json",
Self::Html => "html",
Self::Junit => "xml",
Self::All => "all",
}
}
}
#[derive(ValueEnum, Clone, Debug)]
pub enum TestFormat {
Bats,
#[value(name = "assert_cmd")]
AssertCmd,
Snapbox,
}
impl TestFormat {
pub fn extension(&self) -> &'static str {
match self {
Self::Bats => "bats",
Self::AssertCmd => "rs",
Self::Snapbox => "rs",
}
}
pub fn as_str(&self) -> &'static str {
match self {
Self::Bats => "bats",
Self::AssertCmd => "assert_cmd",
Self::Snapbox => "snapbox",
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_report_format_extension() {
assert_eq!(ReportFormat::Markdown.extension(), "md");
assert_eq!(ReportFormat::Json.extension(), "json");
assert_eq!(ReportFormat::Html.extension(), "html");
assert_eq!(ReportFormat::Junit.extension(), "xml");
}
}