use clap::{Parser, Subcommand};
#[derive(Parser)]
#[command(
name = "complior",
version,
about = "AI Act Compliance Scanner & Fixer",
long_about = "Complior scans your project for EU AI Act compliance, identifies gaps, and helps you fix them.\n\nRun without a subcommand to launch the interactive TUI."
)]
pub struct Cli {
#[command(subcommand)]
pub command: Option<Command>,
#[arg(long = "engine-url", global = true)]
pub engine_url: Option<String>,
#[arg(long, global = true)]
pub resume: bool,
#[arg(long, global = true)]
pub theme: Option<String>,
#[arg(long, short = 'y', global = true)]
pub yes: bool,
#[arg(long, global = true)]
pub no_color: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, clap::ValueEnum)]
pub enum FixSource {
Scan,
Eval,
All,
}
impl std::fmt::Display for FixSource {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Scan => write!(f, "scan"),
Self::Eval => write!(f, "eval"),
Self::All => write!(f, "all"),
}
}
}
#[derive(Subcommand)]
pub enum Command {
Scan {
#[arg(long)]
ci: bool,
#[arg(long)]
json: bool,
#[arg(long)]
sarif: bool,
#[arg(long)]
no_tui: bool,
#[arg(long, default_value = "50")]
threshold: u32,
#[arg(long)]
fail_on: Option<String>,
#[arg(long)]
diff: Option<String>,
#[arg(long)]
fail_on_regression: bool,
#[arg(long)]
comment: bool,
#[arg(long)]
deep: bool,
#[arg(long)]
llm: bool,
#[arg(long)]
cloud: bool,
#[arg(long, short = 'q')]
quiet: bool,
#[arg(long)]
agent: Option<String>,
path: Option<String>,
},
Fix {
#[arg(long)]
dry_run: bool,
#[arg(long)]
json: bool,
#[arg(long)]
ai: bool,
#[arg(long, default_value_t = FixSource::Scan, value_enum)]
source: FixSource,
#[arg(long)]
check_id: Option<String>,
path: Option<String>,
},
Version,
Doctor,
Report {
#[arg(long, default_value = "md")]
format: String,
#[arg(long, short)]
output: Option<String>,
path: Option<String>,
},
Init {
#[arg(long)]
force: bool,
path: Option<String>,
},
Update,
Daemon {
#[command(subcommand)]
action: Option<DaemonAction>,
#[arg(long)]
watch: bool,
},
Agent {
#[command(subcommand)]
action: AgentAction,
},
#[cfg(feature = "extras")]
Cert {
#[command(subcommand)]
action: CertAction,
},
#[cfg(feature = "extras")]
Chat {
message: String,
#[arg(long)]
json: bool,
#[arg(long)]
model: Option<String>,
},
#[cfg(feature = "extras")]
SupplyChain {
#[arg(long)]
json: bool,
#[arg(long)]
models: bool,
path: Option<String>,
},
#[cfg(feature = "extras")]
Cost {
#[arg(long, default_value = "150")]
hourly_rate: u32,
#[arg(long)]
agent: Option<String>,
#[arg(long)]
json: bool,
},
#[cfg(feature = "extras")]
Debt {
#[arg(long)]
json: bool,
#[arg(long)]
trend: bool,
},
#[cfg(feature = "extras")]
Simulate {
#[arg(long)]
fix: Vec<String>,
#[arg(long, value_name = "TYPE")]
add_doc: Vec<String>,
#[arg(long, value_name = "FIELD")]
complete_passport: Vec<String>,
#[arg(long)]
json: bool,
},
#[cfg(feature = "extras")]
Jurisdiction {
#[command(subcommand)]
action: JurisdictionAction,
},
#[cfg(feature = "extras")]
Proxy {
#[command(subcommand)]
action: ProxyAction,
},
#[cfg(feature = "extras")]
Doc {
#[command(subcommand)]
action: DocAction,
},
#[cfg(feature = "extras")]
Import {
#[command(subcommand)]
action: ImportAction,
},
#[cfg(feature = "extras")]
Redteam {
#[command(subcommand)]
action: RedteamAction,
},
#[cfg(feature = "extras")]
Tools {
#[command(subcommand)]
action: ToolsAction,
},
Eval {
target: Option<String>,
#[arg(long)]
det: bool,
#[arg(long)]
llm: bool,
#[arg(long)]
security: bool,
#[arg(long)]
full: bool,
#[arg(long)]
agent: Option<String>,
#[arg(long, value_delimiter = ',')]
categories: Vec<String>,
#[arg(long)]
json: bool,
#[arg(long)]
ci: bool,
#[arg(long, default_value = "60")]
threshold: u32,
#[arg(long)]
model: Option<String>,
#[arg(long)]
api_key: Option<String>,
#[arg(long)]
request_template: Option<String>,
#[arg(long)]
response_path: Option<String>,
#[arg(long)]
headers: Option<String>,
#[arg(long)]
last: bool,
#[arg(long)]
failures: bool,
#[arg(long)]
verbose: bool,
#[arg(long, short = 'j', default_value = "5")]
concurrency: u32,
#[arg(long)]
no_remediation: bool,
#[arg(long)]
remediation: bool,
#[arg(long)]
fix: bool,
#[arg(long)]
dry_run: bool,
path: Option<String>,
},
#[cfg(feature = "extras")]
Audit {
target: String,
#[arg(long)]
agent: Option<String>,
#[arg(long)]
json: bool,
path: Option<String>,
},
#[cfg(feature = "extras")]
Login,
#[cfg(feature = "extras")]
Logout,
#[cfg(feature = "extras")]
Sync {
#[arg(long)]
passport: bool,
#[arg(long)]
scan: bool,
#[arg(long)]
docs: bool,
#[arg(long)]
audit: bool,
#[arg(long)]
evidence: bool,
#[arg(long)]
registry: bool,
#[arg(long)]
no_sync: bool,
},
}
#[derive(Subcommand, Debug, Clone)]
pub enum AgentAction {
Rename {
old_name: String,
new_name: String,
#[arg(long)]
json: bool,
path: Option<String>,
},
Init {
#[arg(long)]
json: bool,
#[arg(long)]
force: bool,
path: Option<String>,
},
List {
#[arg(long)]
json: bool,
#[arg(long, short = 'v')]
verbose: bool,
path: Option<String>,
},
Show {
name: String,
#[arg(long)]
json: bool,
path: Option<String>,
},
Autonomy {
#[arg(long)]
json: bool,
path: Option<String>,
},
Validate {
name: Option<String>,
#[arg(long)]
json: bool,
#[arg(long)]
ci: bool,
#[arg(long)]
strict: bool,
#[arg(long)]
verbose: bool,
path: Option<String>,
},
Completeness {
name: String,
#[arg(long)]
json: bool,
path: Option<String>,
},
Fria {
name: String,
#[arg(long)]
json: bool,
#[arg(long)]
organization: Option<String>,
#[arg(long)]
impact: Option<String>,
#[arg(long)]
mitigation: Option<String>,
#[arg(long)]
approval: Option<String>,
path: Option<String>,
},
Notify {
name: String,
#[arg(long)]
json: bool,
#[arg(long)]
company_name: Option<String>,
#[arg(long)]
contact_name: Option<String>,
#[arg(long)]
contact_email: Option<String>,
#[arg(long)]
contact_phone: Option<String>,
#[arg(long)]
deployment_date: Option<String>,
#[arg(long)]
affected_roles: Option<String>,
#[arg(long)]
impact_description: Option<String>,
path: Option<String>,
},
Export {
name: String,
#[arg(long)]
format: String,
#[arg(long)]
json: bool,
path: Option<String>,
},
Registry {
#[arg(long)]
json: bool,
path: Option<String>,
},
Evidence {
#[arg(long)]
json: bool,
#[arg(long)]
verify: bool,
path: Option<String>,
},
Permissions {
#[arg(long)]
json: bool,
path: Option<String>,
},
Policy {
name: String,
#[arg(long)]
industry: String,
#[arg(long)]
json: bool,
#[arg(long)]
organization: Option<String>,
#[arg(long)]
approver: Option<String>,
path: Option<String>,
},
TestGen {
name: String,
#[arg(long)]
path: Option<String>,
#[arg(long)]
json: bool,
},
Diff {
name: String,
#[arg(long)]
path: Option<String>,
#[arg(long)]
json: bool,
},
Import {
#[arg(long)]
from: String,
file: String,
#[arg(long)]
json: bool,
#[arg(long)]
path: Option<String>,
},
AuditPackage {
#[arg(long, short)]
output: Option<String>,
#[arg(long)]
json: bool,
#[arg(long)]
path: Option<String>,
},
Audit {
#[arg(long)]
agent: Option<String>,
#[arg(long)]
since: Option<String>,
#[arg(long = "type")]
event_type: Option<String>,
#[arg(long, default_value = "50")]
limit: u32,
#[arg(long)]
json: bool,
path: Option<String>,
},
}
#[cfg(feature = "extras")]
#[derive(Subcommand, Debug, Clone)]
pub enum CertAction {
Readiness {
name: String,
#[arg(long)]
json: bool,
path: Option<String>,
},
Test {
name: String,
#[arg(long)]
adversarial: bool,
#[arg(long)]
categories: Option<String>,
#[arg(long)]
json: bool,
path: Option<String>,
},
}
#[derive(Subcommand, Debug, Clone)]
pub enum DaemonAction {
Start {
#[arg(long)]
watch: bool,
#[arg(long)]
port: Option<u16>,
},
Status,
Stop,
}
#[cfg(feature = "extras")]
#[derive(Subcommand, Debug, Clone)]
pub enum ProxyAction {
Start {
command: String,
#[arg(trailing_var_arg = true)]
args: Vec<String>,
},
Stop,
Status,
}
#[cfg(feature = "extras")]
#[derive(Subcommand, Debug, Clone)]
pub enum DocAction {
Generate {
name: String,
#[arg(long = "type")]
doc_type: Option<String>,
#[arg(long)]
all: bool,
#[arg(long)]
organization: Option<String>,
#[arg(long)]
json: bool,
path: Option<String>,
},
}
#[cfg(feature = "extras")]
#[derive(Subcommand, Debug, Clone)]
pub enum ImportAction {
Promptfoo {
#[arg(long)]
file: Option<String>,
#[arg(long)]
json: bool,
},
}
#[cfg(feature = "extras")]
#[derive(Subcommand, Debug, Clone)]
pub enum RedteamAction {
Run {
#[arg(long, default_value = "default")]
agent: String,
#[arg(long, value_delimiter = ',')]
categories: Vec<String>,
#[arg(long)]
max_probes: Option<u32>,
#[arg(long)]
json: bool,
},
Last {
#[arg(long)]
json: bool,
},
Target {
url: String,
#[arg(long)]
json: bool,
#[arg(long)]
ci: bool,
#[arg(long, default_value = "60")]
threshold: u32,
},
}
#[cfg(feature = "extras")]
#[derive(Subcommand, Debug, Clone)]
pub enum ToolsAction {
Status,
Update,
}
#[cfg(feature = "extras")]
#[derive(Subcommand, Debug, Clone)]
pub enum JurisdictionAction {
List {
#[arg(long)]
json: bool,
},
Show {
code: String,
#[arg(long)]
json: bool,
},
}
pub fn needs_engine(cli: &Cli) -> bool {
match &cli.command {
None => false,
Some(Command::Version | Command::Update | Command::Daemon { .. }) => false,
#[cfg(feature = "extras")]
Some(Command::Login | Command::Logout) => false,
_ => true,
}
}
pub fn explicit_project_path(cli: &Cli) -> Option<std::path::PathBuf> {
let raw = match &cli.command {
Some(Command::Scan { path, .. } | Command::Fix { path, .. } | Command::Init {
path, .. } | Command::Report { path, .. }) => path.as_deref(),
Some(Command::Eval { path, .. }) => path.as_deref(),
Some(Command::Agent { action }) => match action {
AgentAction::Init { path, .. }
| AgentAction::List { path, .. }
| AgentAction::Show { path, .. }
| AgentAction::Autonomy { path, .. }
| AgentAction::Validate { path, .. }
| AgentAction::Completeness { path, .. }
| AgentAction::Fria { path, .. }
| AgentAction::Notify { path, .. }
| AgentAction::Export { path, .. }
| AgentAction::Registry { path, .. }
| AgentAction::Evidence { path, .. }
| AgentAction::Permissions { path, .. }
| AgentAction::Policy { path, .. }
| AgentAction::TestGen { path, .. }
| AgentAction::Diff { path, .. }
| AgentAction::Import { path, .. }
| AgentAction::AuditPackage { path, .. }
| AgentAction::Audit { path, .. }
| AgentAction::Rename { path, .. } => path.as_deref(),
},
#[cfg(feature = "extras")]
Some(Command::Audit { path, .. } | Command::SupplyChain { path, .. }) => path.as_deref(),
#[cfg(feature = "extras")]
Some(Command::Cert { action }) => match action {
CertAction::Readiness { path, .. }
| CertAction::Test { path, .. } => path.as_deref(),
},
#[cfg(feature = "extras")]
Some(Command::Doc { action }) => match action {
DocAction::Generate { path, .. } => path.as_deref(),
},
_ => None,
};
raw.map(|p| {
let pb = std::path::PathBuf::from(p);
if pb.is_absolute() { pb } else { std::env::current_dir().unwrap_or_default().join(pb) }
})
}
pub const fn wants_pid_file(cli: &Cli) -> bool {
!matches!(&cli.command, Some(Command::Doctor))
}
pub fn is_headless(cli: &Cli) -> bool {
match &cli.command {
Some(
Command::Scan { .. }
| Command::Fix { .. }
| Command::Version
| Command::Doctor
| Command::Report { .. }
| Command::Init { .. }
| Command::Update
| Command::Daemon { .. }
| Command::Agent { .. }
| Command::Eval { .. },
) => true,
#[cfg(feature = "extras")]
Some(
Command::Cert { .. }
| Command::Chat { .. }
| Command::SupplyChain { .. }
| Command::Cost { .. }
| Command::Debt { .. }
| Command::Simulate { .. }
| Command::Doc { .. }
| Command::Jurisdiction { .. }
| Command::Proxy { .. }
| Command::Import { .. }
| Command::Redteam { .. }
| Command::Tools { .. }
| Command::Audit { .. }
| Command::Login
| Command::Logout
| Command::Sync { .. },
) => true,
None => false,
#[allow(unreachable_patterns)]
_ => true,
}
}
#[cfg(test)]
mod tests {
use super::*;
use clap::Parser;
#[test]
fn cli_parse_scan_json() {
let cli = Cli::parse_from(["complior", "scan", "--json"]);
assert!(matches!(cli.command, Some(Command::Scan { json: true, .. })));
}
#[test]
fn cli_parse_scan_ci() {
let cli = Cli::parse_from(["complior", "scan", "--ci", "--threshold", "80"]);
match cli.command {
Some(Command::Scan { ci, threshold, .. }) => {
assert!(ci);
assert_eq!(threshold, 80);
}
_ => panic!("Expected Scan command"),
}
}
#[test]
fn cli_parse_no_subcommand() {
let cli = Cli::parse_from(["complior"]);
assert!(cli.command.is_none());
assert!(!is_headless(&cli));
}
#[test]
fn cli_parse_version() {
let cli = Cli::parse_from(["complior", "version"]);
assert!(matches!(cli.command, Some(Command::Version)));
assert!(is_headless(&cli));
}
#[test]
fn cli_parse_global_flags() {
let cli = Cli::parse_from(["complior", "--engine-url", "http://localhost:4000", "--resume"]);
assert_eq!(cli.engine_url.as_deref(), Some("http://localhost:4000"));
assert!(cli.resume);
}
#[test]
fn cli_parse_yes_flag() {
let cli = Cli::parse_from(["complior", "--yes"]);
assert!(cli.yes);
assert!(!is_headless(&cli));
}
#[test]
fn cli_parse_y_shorthand() {
let cli = Cli::parse_from(["complior", "-y"]);
assert!(cli.yes);
}
#[test]
fn cli_parse_fix_dry_run() {
let cli = Cli::parse_from(["complior", "fix", "--dry-run"]);
match cli.command {
Some(Command::Fix { dry_run, json, ref check_id, .. }) => {
assert!(dry_run);
assert!(!json);
assert!(check_id.is_none());
}
_ => panic!("Expected Fix command"),
}
assert!(is_headless(&cli));
}
#[test]
fn cli_parse_fix_json() {
let cli = Cli::parse_from(["complior", "fix", "--dry-run", "--json"]);
match cli.command {
Some(Command::Fix { dry_run, json, .. }) => {
assert!(dry_run);
assert!(json);
}
_ => panic!("Expected Fix command"),
}
}
#[test]
fn cli_parse_fix_check_id() {
let cli = Cli::parse_from(["complior", "fix", "--check-id", "l1-fria"]);
match cli.command {
Some(Command::Fix { check_id, dry_run, .. }) => {
assert_eq!(check_id.as_deref(), Some("l1-fria"));
assert!(!dry_run);
}
_ => panic!("Expected Fix command"),
}
}
#[test]
fn cli_parse_daemon_bare() {
let cli = Cli::parse_from(["complior", "daemon"]);
match &cli.command {
Some(Command::Daemon { action, watch }) => {
assert!(action.is_none());
assert!(!watch);
}
_ => panic!("Expected Daemon command"),
}
assert!(is_headless(&cli));
}
#[test]
fn cli_parse_daemon_start_watch_port() {
let cli = Cli::parse_from(["complior", "daemon", "start", "--watch", "--port", "4000"]);
match cli.command {
Some(Command::Daemon { action: Some(DaemonAction::Start { watch, port }), .. }) => {
assert!(watch);
assert_eq!(port, Some(4000));
}
_ => panic!("Expected Daemon Start"),
}
}
#[test]
fn cli_parse_daemon_top_level_watch() {
let cli = Cli::parse_from(["complior", "daemon", "--watch"]);
match cli.command {
Some(Command::Daemon { action, watch }) => {
assert!(action.is_none());
assert!(watch);
}
_ => panic!("Expected Daemon with --watch"),
}
}
#[test]
fn cli_parse_daemon_status() {
let cli = Cli::parse_from(["complior", "daemon", "status"]);
assert!(matches!(
cli.command,
Some(Command::Daemon { action: Some(DaemonAction::Status), .. })
));
}
#[test]
fn cli_parse_daemon_stop() {
let cli = Cli::parse_from(["complior", "daemon", "stop"]);
assert!(matches!(
cli.command,
Some(Command::Daemon { action: Some(DaemonAction::Stop), .. })
));
}
#[test]
fn cli_parse_agent_init() {
let cli = Cli::parse_from(["complior", "agent", "init"]);
match &cli.command {
Some(Command::Agent { action: AgentAction::Init { json, path, .. } }) => {
assert!(!json);
assert!(path.is_none());
}
_ => panic!("Expected Agent Init command"),
}
assert!(is_headless(&cli));
}
#[test]
fn cli_parse_agent_init_json() {
let cli = Cli::parse_from(["complior", "agent", "init", "--json"]);
match &cli.command {
Some(Command::Agent { action: AgentAction::Init { json, .. } }) => {
assert!(*json);
}
_ => panic!("Expected Agent Init command"),
}
}
#[test]
fn cli_parse_agent_init_path() {
let cli = Cli::parse_from(["complior", "agent", "init", "/tmp/project"]);
match &cli.command {
Some(Command::Agent { action: AgentAction::Init { path, .. } }) => {
assert_eq!(path.as_deref(), Some("/tmp/project"));
}
_ => panic!("Expected Agent Init command"),
}
}
#[test]
fn cli_parse_agent_list() {
let cli = Cli::parse_from(["complior", "agent", "list"]);
assert!(matches!(
&cli.command,
Some(Command::Agent { action: AgentAction::List { json: false, verbose: false, path: None } })
));
assert!(is_headless(&cli));
}
#[test]
fn cli_parse_agent_list_verbose() {
let cli = Cli::parse_from(["complior", "agent", "list", "--verbose"]);
match &cli.command {
Some(Command::Agent { action: AgentAction::List { json, verbose, path } }) => {
assert!(!json);
assert!(*verbose);
assert!(path.is_none());
}
_ => panic!("Expected Agent List command"),
}
}
#[test]
fn cli_parse_agent_show() {
let cli = Cli::parse_from(["complior", "agent", "show", "my-bot"]);
match &cli.command {
Some(Command::Agent { action: AgentAction::Show { name, json, path } }) => {
assert_eq!(name, "my-bot");
assert!(!json);
assert!(path.is_none());
}
_ => panic!("Expected Agent Show command"),
}
assert!(is_headless(&cli));
}
#[test]
fn cli_parse_agent_show_json() {
let cli = Cli::parse_from(["complior", "agent", "show", "my-bot", "--json"]);
match &cli.command {
Some(Command::Agent { action: AgentAction::Show { name, json, .. } }) => {
assert_eq!(name, "my-bot");
assert!(*json);
}
_ => panic!("Expected Agent Show command"),
}
}
#[test]
fn cli_parse_agent_autonomy() {
let cli = Cli::parse_from(["complior", "agent", "autonomy"]);
match &cli.command {
Some(Command::Agent { action: AgentAction::Autonomy { json, path } }) => {
assert!(!json);
assert!(path.is_none());
}
_ => panic!("Expected Agent Autonomy command"),
}
assert!(is_headless(&cli));
}
#[test]
fn cli_parse_agent_autonomy_json() {
let cli = Cli::parse_from(["complior", "agent", "autonomy", "--json"]);
match &cli.command {
Some(Command::Agent { action: AgentAction::Autonomy { json, .. } }) => {
assert!(*json);
}
_ => panic!("Expected Agent Autonomy command"),
}
}
#[test]
fn cli_parse_agent_autonomy_path() {
let cli = Cli::parse_from(["complior", "agent", "autonomy", "/tmp/proj"]);
match &cli.command {
Some(Command::Agent { action: AgentAction::Autonomy { path, .. } }) => {
assert_eq!(path.as_deref(), Some("/tmp/proj"));
}
_ => panic!("Expected Agent Autonomy command"),
}
}
#[test]
fn cli_parse_agent_validate() {
let cli = Cli::parse_from(["complior", "agent", "validate"]);
match &cli.command {
Some(Command::Agent { action: AgentAction::Validate { name, json, ci, strict, verbose, path } }) => {
assert!(name.is_none());
assert!(!json);
assert!(!ci);
assert!(!strict);
assert!(!verbose);
assert!(path.is_none());
}
_ => panic!("Expected Agent Validate command"),
}
assert!(is_headless(&cli));
}
#[test]
fn cli_parse_agent_validate_name() {
let cli = Cli::parse_from(["complior", "agent", "validate", "my-bot"]);
match &cli.command {
Some(Command::Agent { action: AgentAction::Validate { name, .. } }) => {
assert_eq!(name.as_deref(), Some("my-bot"));
}
_ => panic!("Expected Agent Validate command"),
}
}
#[test]
fn cli_parse_agent_validate_ci_strict() {
let cli = Cli::parse_from(["complior", "agent", "validate", "--ci", "--strict"]);
match &cli.command {
Some(Command::Agent { action: AgentAction::Validate { ci, strict, .. } }) => {
assert!(*ci);
assert!(*strict);
}
_ => panic!("Expected Agent Validate command"),
}
}
#[test]
fn cli_parse_agent_validate_verbose() {
let cli = Cli::parse_from(["complior", "agent", "validate", "--verbose"]);
match &cli.command {
Some(Command::Agent { action: AgentAction::Validate { verbose, .. } }) => {
assert!(*verbose);
}
_ => panic!("Expected Agent Validate command"),
}
}
#[test]
fn cli_parse_agent_completeness() {
let cli = Cli::parse_from(["complior", "agent", "completeness", "my-bot"]);
match &cli.command {
Some(Command::Agent { action: AgentAction::Completeness { name, json, path } }) => {
assert_eq!(name, "my-bot");
assert!(!json);
assert!(path.is_none());
}
_ => panic!("Expected Agent Completeness command"),
}
assert!(is_headless(&cli));
}
#[test]
fn cli_parse_agent_completeness_json() {
let cli = Cli::parse_from(["complior", "agent", "completeness", "my-bot", "--json"]);
match &cli.command {
Some(Command::Agent { action: AgentAction::Completeness { name, json, .. } }) => {
assert_eq!(name, "my-bot");
assert!(*json);
}
_ => panic!("Expected Agent Completeness command"),
}
}
#[test]
fn cli_parse_agent_fria() {
let cli = Cli::parse_from(["complior", "agent", "fria", "my-bot"]);
match &cli.command {
Some(Command::Agent { action: AgentAction::Fria { name, json, organization, impact, mitigation, approval, path } }) => {
assert_eq!(name, "my-bot");
assert!(!json);
assert!(organization.is_none());
assert!(impact.is_none());
assert!(mitigation.is_none());
assert!(approval.is_none());
assert!(path.is_none());
}
_ => panic!("Expected Agent Fria command"),
}
assert!(is_headless(&cli));
}
#[test]
fn cli_parse_agent_fria_json() {
let cli = Cli::parse_from(["complior", "agent", "fria", "my-bot", "--json"]);
match &cli.command {
Some(Command::Agent { action: AgentAction::Fria { name, json, .. } }) => {
assert_eq!(name, "my-bot");
assert!(*json);
}
_ => panic!("Expected Agent Fria command"),
}
}
#[test]
fn cli_parse_agent_fria_organization() {
let cli = Cli::parse_from(["complior", "agent", "fria", "my-bot", "--organization", "Acme"]);
match &cli.command {
Some(Command::Agent { action: AgentAction::Fria { name, organization, .. } }) => {
assert_eq!(name, "my-bot");
assert_eq!(organization.as_deref(), Some("Acme"));
}
_ => panic!("Expected Agent Fria command"),
}
}
#[test]
fn cli_parse_agent_fria_manual_fields() {
let cli = Cli::parse_from([
"complior", "agent", "fria", "my-bot",
"--impact", "Credit scoring bias",
"--mitigation", "Quarterly audits",
"--approval", "Jane Doe, CTO",
]);
match &cli.command {
Some(Command::Agent { action: AgentAction::Fria { name, impact, mitigation, approval, .. } }) => {
assert_eq!(name, "my-bot");
assert_eq!(impact.as_deref(), Some("Credit scoring bias"));
assert_eq!(mitigation.as_deref(), Some("Quarterly audits"));
assert_eq!(approval.as_deref(), Some("Jane Doe, CTO"));
}
_ => panic!("Expected Agent Fria command"),
}
}
#[test]
fn cli_parse_agent_notify() {
let cli = Cli::parse_from(["complior", "agent", "notify", "my-bot"]);
match &cli.command {
Some(Command::Agent { action: AgentAction::Notify { name, json, company_name, contact_name, contact_email, contact_phone, deployment_date, affected_roles, impact_description, path } }) => {
assert_eq!(name, "my-bot");
assert!(!json);
assert!(company_name.is_none());
assert!(contact_name.is_none());
assert!(contact_email.is_none());
assert!(contact_phone.is_none());
assert!(deployment_date.is_none());
assert!(affected_roles.is_none());
assert!(impact_description.is_none());
assert!(path.is_none());
}
_ => panic!("Expected Agent Notify command"),
}
assert!(is_headless(&cli));
}
#[test]
fn cli_parse_agent_notify_json() {
let cli = Cli::parse_from(["complior", "agent", "notify", "my-bot", "--json"]);
match &cli.command {
Some(Command::Agent { action: AgentAction::Notify { name, json, .. } }) => {
assert_eq!(name, "my-bot");
assert!(*json);
}
_ => panic!("Expected Agent Notify command"),
}
}
#[test]
fn cli_parse_agent_notify_all_flags() {
let cli = Cli::parse_from([
"complior", "agent", "notify", "my-bot",
"--company-name", "Acme Corp",
"--contact-name", "Jane Doe",
"--contact-email", "jane@acme.com",
"--contact-phone", "+1-555-0100",
"--deployment-date", "2026-04-01",
"--affected-roles", "Customer Support",
"--impact-description", "Assists with ticket triage",
]);
match &cli.command {
Some(Command::Agent { action: AgentAction::Notify { name, company_name, contact_name, contact_email, contact_phone, deployment_date, affected_roles, impact_description, .. } }) => {
assert_eq!(name, "my-bot");
assert_eq!(company_name.as_deref(), Some("Acme Corp"));
assert_eq!(contact_name.as_deref(), Some("Jane Doe"));
assert_eq!(contact_email.as_deref(), Some("jane@acme.com"));
assert_eq!(contact_phone.as_deref(), Some("+1-555-0100"));
assert_eq!(deployment_date.as_deref(), Some("2026-04-01"));
assert_eq!(affected_roles.as_deref(), Some("Customer Support"));
assert_eq!(impact_description.as_deref(), Some("Assists with ticket triage"));
}
_ => panic!("Expected Agent Notify command"),
}
}
#[test]
fn cli_parse_agent_export() {
let cli = Cli::parse_from(["complior", "agent", "export", "my-bot", "--format", "a2a"]);
match &cli.command {
Some(Command::Agent { action: AgentAction::Export { name, format, json, path } }) => {
assert_eq!(name, "my-bot");
assert_eq!(format, "a2a");
assert!(!json);
assert!(path.is_none());
}
_ => panic!("Expected Agent Export command"),
}
assert!(is_headless(&cli));
}
#[test]
fn cli_parse_agent_export_json() {
let cli = Cli::parse_from(["complior", "agent", "export", "my-bot", "--format", "aiuc-1", "--json"]);
match &cli.command {
Some(Command::Agent { action: AgentAction::Export { name, format, json, .. } }) => {
assert_eq!(name, "my-bot");
assert_eq!(format, "aiuc-1");
assert!(*json);
}
_ => panic!("Expected Agent Export command"),
}
}
#[test]
fn cli_parse_agent_export_nist() {
let cli = Cli::parse_from(["complior", "agent", "export", "my-bot", "--format", "nist", "/tmp/project"]);
match &cli.command {
Some(Command::Agent { action: AgentAction::Export { name, format, path, .. } }) => {
assert_eq!(name, "my-bot");
assert_eq!(format, "nist");
assert_eq!(path.as_deref(), Some("/tmp/project"));
}
_ => panic!("Expected Agent Export command"),
}
}
#[test]
fn cli_parse_agent_registry() {
let cli = Cli::parse_from(["complior", "agent", "registry"]);
match &cli.command {
Some(Command::Agent { action: AgentAction::Registry { json, path } }) => {
assert!(!json);
assert!(path.is_none());
}
_ => panic!("Expected Agent Registry command"),
}
assert!(is_headless(&cli));
}
#[test]
fn cli_parse_agent_registry_json() {
let cli = Cli::parse_from(["complior", "agent", "registry", "--json"]);
match &cli.command {
Some(Command::Agent { action: AgentAction::Registry { json, .. } }) => {
assert!(*json);
}
_ => panic!("Expected Agent Registry command"),
}
}
#[test]
fn cli_parse_agent_registry_path() {
let cli = Cli::parse_from(["complior", "agent", "registry", "/tmp/proj"]);
match &cli.command {
Some(Command::Agent { action: AgentAction::Registry { path, .. } }) => {
assert_eq!(path.as_deref(), Some("/tmp/proj"));
}
_ => panic!("Expected Agent Registry command"),
}
}
#[test]
fn cli_parse_agent_evidence() {
let cli = Cli::parse_from(["complior", "agent", "evidence"]);
match &cli.command {
Some(Command::Agent { action: AgentAction::Evidence { json, verify, path } }) => {
assert!(!json);
assert!(!verify);
assert!(path.is_none());
}
_ => panic!("Expected Agent Evidence command"),
}
assert!(is_headless(&cli));
}
#[test]
fn cli_parse_agent_evidence_verify() {
let cli = Cli::parse_from(["complior", "agent", "evidence", "--verify"]);
match &cli.command {
Some(Command::Agent { action: AgentAction::Evidence { verify, .. } }) => {
assert!(*verify);
}
_ => panic!("Expected Agent Evidence command"),
}
}
#[test]
fn cli_parse_agent_evidence_json() {
let cli = Cli::parse_from(["complior", "agent", "evidence", "--json"]);
match &cli.command {
Some(Command::Agent { action: AgentAction::Evidence { json, .. } }) => {
assert!(*json);
}
_ => panic!("Expected Agent Evidence command"),
}
}
#[test]
fn cli_parse_agent_permissions() {
let cli = Cli::parse_from(["complior", "agent", "permissions"]);
match &cli.command {
Some(Command::Agent { action: AgentAction::Permissions { json, path } }) => {
assert!(!json);
assert!(path.is_none());
}
_ => panic!("Expected Agent Permissions command"),
}
assert!(is_headless(&cli));
}
#[test]
fn cli_parse_agent_permissions_json() {
let cli = Cli::parse_from(["complior", "agent", "permissions", "--json"]);
match &cli.command {
Some(Command::Agent { action: AgentAction::Permissions { json, .. } }) => {
assert!(*json);
}
_ => panic!("Expected Agent Permissions command"),
}
}
#[test]
fn cli_parse_agent_policy() {
let cli = Cli::parse_from(["complior", "agent", "policy", "my-bot", "--industry", "hr"]);
match &cli.command {
Some(Command::Agent { action: AgentAction::Policy { name, industry, json, organization, approver, path } }) => {
assert_eq!(name, "my-bot");
assert_eq!(industry, "hr");
assert!(!json);
assert!(organization.is_none());
assert!(approver.is_none());
assert!(path.is_none());
}
_ => panic!("Expected Agent Policy command"),
}
assert!(is_headless(&cli));
}
#[test]
fn cli_parse_agent_policy_all_flags() {
let cli = Cli::parse_from([
"complior", "agent", "policy", "my-bot",
"--industry", "finance",
"--json",
"--organization", "Acme Corp",
"--approver", "Jane Doe, CTO",
"/tmp/project",
]);
match &cli.command {
Some(Command::Agent { action: AgentAction::Policy { name, industry, json, organization, approver, path } }) => {
assert_eq!(name, "my-bot");
assert_eq!(industry, "finance");
assert!(*json);
assert_eq!(organization.as_deref(), Some("Acme Corp"));
assert_eq!(approver.as_deref(), Some("Jane Doe, CTO"));
assert_eq!(path.as_deref(), Some("/tmp/project"));
}
_ => panic!("Expected Agent Policy command"),
}
}
#[test]
fn cli_parse_agent_audit() {
let cli = Cli::parse_from(["complior", "agent", "audit"]);
match &cli.command {
Some(Command::Agent { action: AgentAction::Audit { agent, since, event_type, limit, json, path } }) => {
assert!(agent.is_none());
assert!(since.is_none());
assert!(event_type.is_none());
assert_eq!(*limit, 50);
assert!(!json);
assert!(path.is_none());
}
_ => panic!("Expected Agent Audit command"),
}
assert!(is_headless(&cli));
}
#[test]
fn cli_parse_agent_audit_with_filters() {
let cli = Cli::parse_from([
"complior", "agent", "audit",
"--agent", "my-bot",
"--since", "2026-01-01",
"--type", "scan.completed",
"--limit", "10",
]);
match &cli.command {
Some(Command::Agent { action: AgentAction::Audit { agent, since, event_type, limit, .. } }) => {
assert_eq!(agent.as_deref(), Some("my-bot"));
assert_eq!(since.as_deref(), Some("2026-01-01"));
assert_eq!(event_type.as_deref(), Some("scan.completed"));
assert_eq!(*limit, 10);
}
_ => panic!("Expected Agent Audit command"),
}
}
#[test]
fn cli_parse_agent_test_gen() {
let cli = Cli::parse_from(["complior", "agent", "test-gen", "my-bot"]);
match &cli.command {
Some(Command::Agent { action: AgentAction::TestGen { name, json, path } }) => {
assert_eq!(name, "my-bot");
assert!(!json);
assert!(path.is_none());
}
_ => panic!("Expected Agent TestGen command"),
}
assert!(is_headless(&cli));
}
#[test]
fn cli_parse_agent_test_gen_json() {
let cli = Cli::parse_from(["complior", "agent", "test-gen", "my-bot", "--json"]);
match &cli.command {
Some(Command::Agent { action: AgentAction::TestGen { name, json, .. } }) => {
assert_eq!(name, "my-bot");
assert!(*json);
}
_ => panic!("Expected Agent TestGen command"),
}
}
#[test]
fn cli_parse_agent_test_gen_path() {
let cli = Cli::parse_from(["complior", "agent", "test-gen", "my-bot", "--path", "/tmp/proj"]);
match &cli.command {
Some(Command::Agent { action: AgentAction::TestGen { name, path, .. } }) => {
assert_eq!(name, "my-bot");
assert_eq!(path.as_deref(), Some("/tmp/proj"));
}
_ => panic!("Expected Agent TestGen command"),
}
}
#[test]
fn cli_parse_agent_diff() {
let cli = Cli::parse_from(["complior", "agent", "diff", "my-bot"]);
match &cli.command {
Some(Command::Agent { action: AgentAction::Diff { name, json, path } }) => {
assert_eq!(name, "my-bot");
assert!(!json);
assert!(path.is_none());
}
_ => panic!("Expected Agent Diff command"),
}
assert!(is_headless(&cli));
}
#[test]
fn cli_parse_agent_diff_json() {
let cli = Cli::parse_from(["complior", "agent", "diff", "my-bot", "--json"]);
match &cli.command {
Some(Command::Agent { action: AgentAction::Diff { name, json, .. } }) => {
assert_eq!(name, "my-bot");
assert!(*json);
}
_ => panic!("Expected Agent Diff command"),
}
}
#[test]
fn cli_parse_agent_diff_path() {
let cli = Cli::parse_from(["complior", "agent", "diff", "my-bot", "--path", "/tmp/proj"]);
match &cli.command {
Some(Command::Agent { action: AgentAction::Diff { name, path, .. } }) => {
assert_eq!(name, "my-bot");
assert_eq!(path.as_deref(), Some("/tmp/proj"));
}
_ => panic!("Expected Agent Diff command"),
}
}
#[cfg(feature = "extras")]
#[test]
fn cli_parse_chat() {
let cli = Cli::parse_from(["complior", "chat", "What is Article 5?"]);
match &cli.command {
Some(Command::Chat { message, json, model }) => {
assert_eq!(message, "What is Article 5?");
assert!(!json);
assert!(model.is_none());
}
_ => panic!("Expected Chat command"),
}
assert!(is_headless(&cli));
}
#[cfg(feature = "extras")]
#[test]
fn cli_parse_chat_json() {
let cli = Cli::parse_from(["complior", "chat", "test", "--json"]);
match &cli.command {
Some(Command::Chat { message, json, .. }) => {
assert_eq!(message, "test");
assert!(*json);
}
_ => panic!("Expected Chat command"),
}
}
#[cfg(feature = "extras")]
#[test]
fn cli_parse_chat_model() {
let cli = Cli::parse_from(["complior", "chat", "test", "--model", "gpt-4o"]);
match &cli.command {
Some(Command::Chat { message, model, .. }) => {
assert_eq!(message, "test");
assert_eq!(model.as_deref(), Some("gpt-4o"));
}
_ => panic!("Expected Chat command"),
}
}
#[test]
fn cli_parse_scan_agent() {
let cli = Cli::parse_from(["complior", "scan", "--agent", "my-bot"]);
match &cli.command {
Some(Command::Scan { agent, .. }) => {
assert_eq!(agent.as_deref(), Some("my-bot"));
}
_ => panic!("Expected Scan command"),
}
}
#[test]
fn cli_parse_scan_diff() {
let cli = Cli::parse_from(["complior", "scan", "--diff", "main"]);
match &cli.command {
Some(Command::Scan { diff, fail_on_regression, comment, .. }) => {
assert_eq!(diff.as_deref(), Some("main"));
assert!(!fail_on_regression);
assert!(!comment);
}
_ => panic!("Expected Scan command"),
}
assert!(is_headless(&cli));
}
#[test]
fn cli_parse_scan_diff_full() {
let cli = Cli::parse_from([
"complior", "scan", "--diff", "develop",
"--fail-on-regression", "--comment", "--json",
]);
match &cli.command {
Some(Command::Scan { diff, fail_on_regression, comment, json, .. }) => {
assert_eq!(diff.as_deref(), Some("develop"));
assert!(*fail_on_regression);
assert!(*comment);
assert!(*json);
}
_ => panic!("Expected Scan command"),
}
}
#[cfg(feature = "extras")]
#[test]
fn cli_parse_cert_readiness() {
let cli = Cli::parse_from(["complior", "cert", "readiness", "my-bot"]);
match &cli.command {
Some(Command::Cert { action: CertAction::Readiness { name, json, path } }) => {
assert_eq!(name, "my-bot");
assert!(!json);
assert!(path.is_none());
}
_ => panic!("Expected Cert Readiness command"),
}
assert!(is_headless(&cli));
}
#[cfg(feature = "extras")]
#[test]
fn cli_parse_cert_readiness_json_path() {
let cli = Cli::parse_from(["complior", "cert", "readiness", "my-bot", "--json", "/tmp/project"]);
match &cli.command {
Some(Command::Cert { action: CertAction::Readiness { name, json, path } }) => {
assert_eq!(name, "my-bot");
assert!(*json);
assert_eq!(path.as_deref(), Some("/tmp/project"));
}
_ => panic!("Expected Cert Readiness command"),
}
}
#[cfg(feature = "extras")]
#[test]
fn cli_parse_sync_audit() {
let cli = Cli::parse_from(["complior", "sync", "--audit"]);
match &cli.command {
Some(Command::Sync { audit, evidence, registry, .. }) => {
assert!(*audit);
assert!(!evidence);
assert!(!registry);
}
_ => panic!("Expected Sync command"),
}
}
#[cfg(feature = "extras")]
#[test]
fn cli_parse_sync_all_new_flags() {
let cli = Cli::parse_from(["complior", "sync", "--audit", "--evidence", "--registry"]);
match &cli.command {
Some(Command::Sync { audit, evidence, registry, .. }) => {
assert!(*audit);
assert!(*evidence);
assert!(*registry);
}
_ => panic!("Expected Sync command"),
}
}
#[cfg(feature = "extras")]
#[test]
fn cli_parse_simulate_fix() {
let cli = Cli::parse_from(["complior", "simulate", "--fix", "l1-risk"]);
match &cli.command {
Some(Command::Simulate { fix, add_doc, complete_passport, json }) => {
assert_eq!(fix, &["l1-risk"]);
assert!(add_doc.is_empty());
assert!(complete_passport.is_empty());
assert!(!json);
}
_ => panic!("Expected Simulate command"),
}
assert!(is_headless(&cli));
}
#[cfg(feature = "extras")]
#[test]
fn cli_parse_simulate_add_doc() {
let cli = Cli::parse_from(["complior", "simulate", "--add-doc", "fria"]);
match &cli.command {
Some(Command::Simulate { fix, add_doc, .. }) => {
assert!(fix.is_empty());
assert_eq!(add_doc, &["fria"]);
}
_ => panic!("Expected Simulate command"),
}
}
#[cfg(feature = "extras")]
#[test]
fn cli_parse_simulate_complete_passport() {
let cli = Cli::parse_from(["complior", "simulate", "--complete-passport", "description"]);
match &cli.command {
Some(Command::Simulate { complete_passport, .. }) => {
assert_eq!(complete_passport, &["description"]);
}
_ => panic!("Expected Simulate command"),
}
}
#[cfg(feature = "extras")]
#[test]
fn cli_parse_simulate_multiple_actions() {
let cli = Cli::parse_from([
"complior", "simulate",
"--fix", "l1-risk",
"--fix", "l2-fria",
"--add-doc", "fria",
"--complete-passport", "description",
"--json",
]);
match &cli.command {
Some(Command::Simulate { fix, add_doc, complete_passport, json }) => {
assert_eq!(fix, &["l1-risk", "l2-fria"]);
assert_eq!(add_doc, &["fria"]);
assert_eq!(complete_passport, &["description"]);
assert!(*json);
}
_ => panic!("Expected Simulate command"),
}
assert!(is_headless(&cli));
}
#[cfg(feature = "extras")]
#[test]
fn cli_parse_simulate_json() {
let cli = Cli::parse_from(["complior", "simulate", "--fix", "l1-risk", "--json"]);
match &cli.command {
Some(Command::Simulate { json, .. }) => {
assert!(*json);
}
_ => panic!("Expected Simulate command"),
}
}
#[test]
fn cli_parse_agent_import() {
let cli = Cli::parse_from(["complior", "agent", "import", "--from", "a2a", "card.json"]);
match &cli.command {
Some(Command::Agent { action: AgentAction::Import { from, file, json, path } }) => {
assert_eq!(from, "a2a");
assert_eq!(file, "card.json");
assert!(!json);
assert!(path.is_none());
}
_ => panic!("Expected Agent Import command"),
}
assert!(is_headless(&cli));
}
#[test]
fn cli_parse_agent_import_json() {
let cli = Cli::parse_from(["complior", "agent", "import", "--from", "a2a", "card.json", "--json"]);
match &cli.command {
Some(Command::Agent { action: AgentAction::Import { from, file, json, .. } }) => {
assert_eq!(from, "a2a");
assert_eq!(file, "card.json");
assert!(*json);
}
_ => panic!("Expected Agent Import command"),
}
}
#[test]
fn cli_parse_agent_import_path() {
let cli = Cli::parse_from(["complior", "agent", "import", "--from", "a2a", "card.json", "--path", "/tmp/proj"]);
match &cli.command {
Some(Command::Agent { action: AgentAction::Import { from, file, path, .. } }) => {
assert_eq!(from, "a2a");
assert_eq!(file, "card.json");
assert_eq!(path.as_deref(), Some("/tmp/proj"));
}
_ => panic!("Expected Agent Import command"),
}
}
#[test]
fn cli_parse_agent_audit_package() {
let cli = Cli::parse_from(["complior", "agent", "audit-package"]);
match &cli.command {
Some(Command::Agent { action: AgentAction::AuditPackage { output, json, path } }) => {
assert!(output.is_none());
assert!(!json);
assert!(path.is_none());
}
_ => panic!("Expected Agent AuditPackage command"),
}
assert!(is_headless(&cli));
}
#[test]
fn cli_parse_agent_audit_package_output() {
let cli = Cli::parse_from(["complior", "agent", "audit-package", "--output", "audit.tar.gz"]);
match &cli.command {
Some(Command::Agent { action: AgentAction::AuditPackage { output, .. } }) => {
assert_eq!(output.as_deref(), Some("audit.tar.gz"));
}
_ => panic!("Expected Agent AuditPackage command"),
}
}
#[cfg(feature = "extras")]
#[test]
fn cli_parse_proxy_start() {
let cli = Cli::parse_from(["complior", "proxy", "start", "npx", "@modelcontextprotocol/server-filesystem"]);
match &cli.command {
Some(Command::Proxy { action: ProxyAction::Start { command, args } }) => {
assert_eq!(command, "npx");
assert_eq!(args, &["@modelcontextprotocol/server-filesystem"]);
}
_ => panic!("Expected Proxy Start command"),
}
assert!(is_headless(&cli));
}
#[cfg(feature = "extras")]
#[test]
fn cli_parse_proxy_stop() {
let cli = Cli::parse_from(["complior", "proxy", "stop"]);
assert!(matches!(
&cli.command,
Some(Command::Proxy { action: ProxyAction::Stop })
));
assert!(is_headless(&cli));
}
#[cfg(feature = "extras")]
#[test]
fn cli_parse_proxy_status() {
let cli = Cli::parse_from(["complior", "proxy", "status"]);
assert!(matches!(
&cli.command,
Some(Command::Proxy { action: ProxyAction::Status })
));
assert!(is_headless(&cli));
}
#[cfg(feature = "extras")]
#[test]
fn cli_parse_proxy_start_multiple_args() {
let cli = Cli::parse_from(["complior", "proxy", "start", "node", "server.js", "--port", "3000"]);
match &cli.command {
Some(Command::Proxy { action: ProxyAction::Start { command, args } }) => {
assert_eq!(command, "node");
assert_eq!(args, &["server.js", "--port", "3000"]);
}
_ => panic!("Expected Proxy Start command"),
}
}
#[cfg(feature = "extras")]
#[test]
fn cli_parse_doc_generate_type() {
let cli = Cli::parse_from(["complior", "doc", "generate", "my-bot", "--type", "ai-literacy"]);
match &cli.command {
Some(Command::Doc { action: DocAction::Generate { name, doc_type, all, organization, json, path } }) => {
assert_eq!(name, "my-bot");
assert_eq!(doc_type.as_deref(), Some("ai-literacy"));
assert!(!all);
assert!(organization.is_none());
assert!(!json);
assert!(path.is_none());
}
_ => panic!("Expected Doc Generate command"),
}
assert!(is_headless(&cli));
}
#[cfg(feature = "extras")]
#[test]
fn cli_parse_doc_generate_all() {
let cli = Cli::parse_from(["complior", "doc", "generate", "my-bot", "--all"]);
match &cli.command {
Some(Command::Doc { action: DocAction::Generate { name, all, doc_type, .. } }) => {
assert_eq!(name, "my-bot");
assert!(*all);
assert!(doc_type.is_none());
}
_ => panic!("Expected Doc Generate command"),
}
assert!(is_headless(&cli));
}
#[cfg(feature = "extras")]
#[test]
fn cli_parse_doc_generate_all_with_options() {
let cli = Cli::parse_from([
"complior", "doc", "generate", "my-bot",
"--all", "--organization", "Acme Corp", "--json", "/tmp/project",
]);
match &cli.command {
Some(Command::Doc { action: DocAction::Generate { name, all, organization, json, path, .. } }) => {
assert_eq!(name, "my-bot");
assert!(*all);
assert_eq!(organization.as_deref(), Some("Acme Corp"));
assert!(*json);
assert_eq!(path.as_deref(), Some("/tmp/project"));
}
_ => panic!("Expected Doc Generate command"),
}
}
#[test]
fn cli_parse_scan_deep() {
let cli = Cli::parse_from(["complior", "scan", "--deep"]);
match cli.command {
Some(Command::Scan { deep, llm, cloud, .. }) => {
assert!(deep);
assert!(!llm);
assert!(!cloud);
}
_ => panic!("Expected Scan command"),
}
}
#[test]
fn cli_parse_scan_llm() {
let cli = Cli::parse_from(["complior", "scan", "--llm"]);
match cli.command {
Some(Command::Scan { deep, llm, cloud, .. }) => {
assert!(!deep);
assert!(llm);
assert!(!cloud);
}
_ => panic!("Expected Scan command"),
}
}
#[test]
fn cli_parse_scan_deep_llm() {
let cli = Cli::parse_from(["complior", "scan", "--deep", "--llm"]);
match cli.command {
Some(Command::Scan { deep, llm, .. }) => {
assert!(deep);
assert!(llm);
}
_ => panic!("Expected Scan command"),
}
}
#[test]
fn cli_parse_scan_cloud() {
let cli = Cli::parse_from(["complior", "scan", "--cloud"]);
match cli.command {
Some(Command::Scan { cloud, .. }) => {
assert!(cloud);
}
_ => panic!("Expected Scan command"),
}
}
#[cfg(feature = "extras")]
#[test]
fn cli_parse_tools_status() {
let cli = Cli::parse_from(["complior", "tools", "status"]);
assert!(matches!(
cli.command,
Some(Command::Tools { action: ToolsAction::Status })
));
assert!(is_headless(&cli));
}
#[cfg(feature = "extras")]
#[test]
fn cli_parse_tools_update() {
let cli = Cli::parse_from(["complior", "tools", "update"]);
assert!(matches!(
cli.command,
Some(Command::Tools { action: ToolsAction::Update })
));
assert!(is_headless(&cli));
}
#[test]
fn cli_parse_eval_default() {
let cli = Cli::parse_from(["complior", "eval", "http://localhost:4000/api/chat"]);
match &cli.command {
Some(Command::Eval { target, det, llm, security, full, agent, categories, json, ci, threshold, model, api_key, request_template, response_path, headers, last, failures, verbose, concurrency, no_remediation, remediation, fix, dry_run, path }) => {
assert_eq!(target.as_deref(), Some("http://localhost:4000/api/chat"));
assert!(!det);
assert!(!llm);
assert!(!security);
assert!(!full);
assert!(agent.is_none());
assert!(categories.is_empty());
assert!(!json);
assert!(!ci);
assert_eq!(*threshold, 60);
assert!(model.is_none());
assert!(api_key.is_none());
assert!(request_template.is_none());
assert!(response_path.is_none());
assert!(headers.is_none());
assert!(!last);
assert!(!failures);
assert!(!verbose);
assert_eq!(*concurrency, 5);
assert!(!no_remediation);
assert!(!remediation);
assert!(!fix);
assert!(!dry_run);
assert!(path.is_none());
}
_ => panic!("Expected Eval command"),
}
assert!(is_headless(&cli));
}
#[test]
fn cli_parse_eval_llm_flag() {
let cli = Cli::parse_from(["complior", "eval", "http://localhost:4000", "--llm"]);
match &cli.command {
Some(Command::Eval { llm, security, full, .. }) => {
assert!(*llm);
assert!(!security);
assert!(!full);
}
_ => panic!("Expected Eval command"),
}
}
#[test]
fn cli_parse_eval_security_flag() {
let cli = Cli::parse_from(["complior", "eval", "http://localhost:4000", "--security"]);
match &cli.command {
Some(Command::Eval { llm, security, full, .. }) => {
assert!(!llm);
assert!(*security);
assert!(!full);
}
_ => panic!("Expected Eval command"),
}
}
#[test]
fn cli_parse_eval_full_flag() {
let cli = Cli::parse_from([
"complior", "eval", "http://localhost:4000",
"--full",
"--agent", "my-bot",
"--categories", "transparency,bias,prohibited",
"--json",
"--ci",
"--threshold", "80",
"--model", "gpt-4o",
"--api-key", "sk-test",
]);
match &cli.command {
Some(Command::Eval { target, llm, security, full, agent, categories, json, ci, threshold, model, api_key, request_template, response_path, headers, last, .. }) => {
assert_eq!(target.as_deref(), Some("http://localhost:4000"));
assert!(!llm);
assert!(!security);
assert!(*full);
assert_eq!(agent.as_deref(), Some("my-bot"));
assert_eq!(categories, &["transparency", "bias", "prohibited"]);
assert!(*json);
assert!(*ci);
assert_eq!(*threshold, 80);
assert_eq!(model.as_deref(), Some("gpt-4o"));
assert_eq!(api_key.as_deref(), Some("sk-test"));
assert!(request_template.is_none());
assert!(response_path.is_none());
assert!(headers.is_none());
assert!(!last);
}
_ => panic!("Expected Eval command"),
}
}
#[test]
fn cli_parse_eval_det_llm_combo() {
let cli = Cli::parse_from(["complior", "eval", "http://localhost:4000", "--det", "--llm"]);
match &cli.command {
Some(Command::Eval { det, llm, security, full, .. }) => {
assert!(*det);
assert!(*llm);
assert!(!security);
assert!(!full);
}
_ => panic!("Expected Eval command"),
}
}
#[test]
fn cli_parse_eval_llm_security_combo() {
let cli = Cli::parse_from(["complior", "eval", "http://localhost:4000", "--llm", "--security"]);
match &cli.command {
Some(Command::Eval { llm, security, full, .. }) => {
assert!(*llm);
assert!(*security);
assert!(!full);
}
_ => panic!("Expected Eval command"),
}
}
#[test]
fn cli_parse_eval_last() {
let cli = Cli::parse_from(["complior", "eval", "--last"]);
match &cli.command {
Some(Command::Eval { target, last, failures, .. }) => {
assert!(target.is_none());
assert!(*last);
assert!(!failures);
}
_ => panic!("Expected Eval command"),
}
assert!(is_headless(&cli));
}
#[test]
fn cli_parse_eval_last_failures() {
let cli = Cli::parse_from(["complior", "eval", "--last", "--failures"]);
match &cli.command {
Some(Command::Eval { last, failures, .. }) => {
assert!(*last);
assert!(*failures);
}
_ => panic!("Expected Eval command"),
}
}
#[test]
fn cli_parse_eval_verbose() {
let cli = Cli::parse_from(["complior", "eval", "http://localhost:4000", "--verbose"]);
match &cli.command {
Some(Command::Eval { verbose, .. }) => {
assert!(*verbose);
}
_ => panic!("Expected Eval command"),
}
}
#[test]
fn cli_parse_eval_concurrency() {
let cli = Cli::parse_from(["complior", "eval", "http://localhost:4000", "-j", "10"]);
match &cli.command {
Some(Command::Eval { concurrency, .. }) => {
assert_eq!(*concurrency, 10);
}
_ => panic!("Expected Eval command"),
}
}
#[test]
fn cli_parse_eval_concurrency_long() {
let cli = Cli::parse_from(["complior", "eval", "http://localhost:4000", "--concurrency", "1"]);
match &cli.command {
Some(Command::Eval { concurrency, .. }) => {
assert_eq!(*concurrency, 1);
}
_ => panic!("Expected Eval command"),
}
}
#[test]
fn cli_parse_eval_ci_mode() {
let cli = Cli::parse_from(["complior", "eval", "http://localhost:4000", "--ci", "--threshold", "75"]);
match &cli.command {
Some(Command::Eval { ci, threshold, .. }) => {
assert!(*ci);
assert_eq!(*threshold, 75);
}
_ => panic!("Expected Eval command"),
}
}
#[test]
fn cli_parse_eval_custom_adapter() {
let cli = Cli::parse_from([
"complior", "eval", "http://api.company.com/predict",
"--request-template", r#"{"prompt":"{{probe}}"}"#,
"--response-path", "result.text",
"--headers", r#"{"Authorization":"Bearer xxx"}"#,
]);
match &cli.command {
Some(Command::Eval { target, request_template, response_path, headers, .. }) => {
assert_eq!(target.as_deref(), Some("http://api.company.com/predict"));
assert_eq!(request_template.as_deref(), Some(r#"{"prompt":"{{probe}}"}"#));
assert_eq!(response_path.as_deref(), Some("result.text"));
assert_eq!(headers.as_deref(), Some(r#"{"Authorization":"Bearer xxx"}"#));
}
_ => panic!("Expected Eval command"),
}
}
#[cfg(feature = "extras")]
#[test]
fn cli_parse_redteam_target_alias() {
let cli = Cli::parse_from(["complior", "redteam", "target", "http://localhost:4000"]);
match &cli.command {
Some(Command::Redteam { action }) => {
match action {
RedteamAction::Target { url, json, ci, threshold } => {
assert_eq!(url, "http://localhost:4000");
assert!(!json);
assert!(!ci);
assert_eq!(*threshold, 60);
}
_ => panic!("Expected Target subcommand"),
}
}
_ => panic!("Expected Redteam command"),
}
}
#[cfg(feature = "extras")]
#[test]
fn cli_parse_audit_basic() {
let cli = Cli::parse_from(["complior", "audit", "http://localhost:4000/api/chat"]);
match &cli.command {
Some(Command::Audit { target, agent, json, path }) => {
assert_eq!(target, "http://localhost:4000/api/chat");
assert!(agent.is_none());
assert!(!json);
assert!(path.is_none());
}
_ => panic!("Expected Audit command"),
}
assert!(is_headless(&cli));
}
#[cfg(feature = "extras")]
#[test]
fn cli_parse_audit_full_flags() {
let cli = Cli::parse_from([
"complior", "audit", "http://localhost:4000",
"--agent", "my-bot",
"--json",
"/tmp/project",
]);
match &cli.command {
Some(Command::Audit { target, agent, json, path }) => {
assert_eq!(target, "http://localhost:4000");
assert_eq!(agent.as_deref(), Some("my-bot"));
assert!(*json);
assert_eq!(path.as_deref(), Some("/tmp/project"));
}
_ => panic!("Expected Audit command"),
}
}
#[test]
fn cli_headless_detection_core() {
let json_cli = Cli::parse_from(["complior", "scan", "--json"]);
assert!(is_headless(&json_cli));
let ci_cli = Cli::parse_from(["complior", "scan", "--ci"]);
assert!(is_headless(&ci_cli));
let sarif_cli = Cli::parse_from(["complior", "scan", "--sarif"]);
assert!(is_headless(&sarif_cli));
let no_tui_cli = Cli::parse_from(["complior", "scan", "--no-tui"]);
assert!(is_headless(&no_tui_cli));
let dry_run_cli = Cli::parse_from(["complior", "fix", "--dry-run"]);
assert!(is_headless(&dry_run_cli));
let bare_scan_cli = Cli::parse_from(["complior", "scan"]);
assert!(is_headless(&bare_scan_cli));
let tui_cli = Cli::parse_from(["complior"]);
assert!(!is_headless(&tui_cli));
let eval_cli = Cli::parse_from(["complior", "eval", "http://localhost:4000"]);
assert!(is_headless(&eval_cli));
}
#[cfg(feature = "extras")]
#[test]
fn cli_headless_detection_extras() {
let audit_cli = Cli::parse_from(["complior", "audit", "http://localhost:4000"]);
assert!(is_headless(&audit_cli));
}
}