use anyhow::Result;
#[test]
fn test_all_required_global_flags_defined() {
let cli = xchecker::cli::build_cli();
let global_args: Vec<_> = cli.get_arguments().collect();
let global_arg_names: Vec<_> = global_args
.iter()
.filter_map(|arg| arg.get_long())
.collect();
let required_flags = vec![
"stdout-cap-bytes",
"stderr-cap-bytes",
"packet-max-bytes",
"packet-max-lines",
"phase-timeout",
"lock-ttl-seconds",
"ignore-secret-pattern",
"extra-secret-pattern",
"debug-packet",
"allow-links",
"runner-mode",
"runner-distro",
"verbose",
"config",
"model",
"max-turns",
"output-format",
"claude-path",
];
for flag in required_flags {
assert!(
global_arg_names.contains(&flag),
"Required global flag --{flag} is not defined in CLI"
);
}
}
#[test]
fn test_numeric_flags_have_documented_defaults() {
let mut cli = xchecker::cli::build_cli();
let help_text = cli.render_help().to_string();
let numeric_flags_with_defaults = vec![
("packet-max-bytes", "65536"),
("packet-max-lines", "1200"),
("phase-timeout", "600"),
("max-turns", "6"),
];
for (flag, _default) in numeric_flags_with_defaults {
assert!(
help_text.contains(&format!("--{flag}")),
"Flag --{flag} not found in help text"
);
}
}
#[test]
fn test_runner_mode_flag_values() {
use clap::Parser;
let valid_modes = vec!["auto", "native", "wsl"];
for mode in valid_modes {
let args = vec!["xchecker", "--runner-mode", mode, "status", "test-spec"];
let result = xchecker::cli::Cli::try_parse_from(args);
assert!(result.is_ok(), "Failed to parse valid runner-mode: {mode}");
}
}
#[test]
fn test_runner_distro_flag() {
use clap::Parser;
let args = vec![
"xchecker",
"--runner-distro",
"Ubuntu-22.04",
"status",
"test-spec",
];
let cli = xchecker::cli::Cli::try_parse_from(args).unwrap();
assert_eq!(cli.runner_distro, Some("Ubuntu-22.04".to_string()));
}
#[test]
fn test_llm_fallback_provider_flag() {
use clap::Parser;
let args = vec![
"xchecker",
"--llm-fallback-provider",
"openrouter",
"status",
"test-spec",
];
let cli = xchecker::cli::Cli::try_parse_from(args).unwrap();
assert_eq!(cli.llm_fallback_provider, Some("openrouter".to_string()));
}
#[test]
fn test_prompt_template_flag() {
use clap::Parser;
let args = vec![
"xchecker",
"--prompt-template",
"claude-optimized",
"status",
"test-spec",
];
let cli = xchecker::cli::Cli::try_parse_from(args).unwrap();
assert_eq!(cli.prompt_template, Some("claude-optimized".to_string()));
}
#[test]
fn test_llm_claude_binary_flag() {
use clap::Parser;
let args = vec![
"xchecker",
"--llm-claude-binary",
"/opt/claude/bin/claude",
"status",
"test-spec",
];
let cli = xchecker::cli::Cli::try_parse_from(args).unwrap();
assert_eq!(
cli.llm_claude_binary,
Some("/opt/claude/bin/claude".to_string())
);
}
#[test]
fn test_llm_claude_binary_flows_to_config() -> Result<()> {
use xchecker::config::{CliArgs, Config, ConfigSource};
let cli_args = CliArgs {
llm_claude_binary: Some("/custom/path/claude".to_string()),
..Default::default()
};
let config = Config::discover(&cli_args)?;
assert_eq!(
config.llm.claude.as_ref().and_then(|c| c.binary.as_deref()),
Some("/custom/path/claude")
);
assert_eq!(
config.source_attribution.get("llm_claude_binary"),
Some(&ConfigSource::Cli)
);
Ok(())
}
#[test]
fn test_llm_gemini_binary_flag() {
use clap::Parser;
let args = vec![
"xchecker",
"--llm-gemini-binary",
"/opt/gemini/bin/gemini",
"status",
"test-spec",
];
let cli = xchecker::cli::Cli::try_parse_from(args).unwrap();
assert_eq!(
cli.llm_gemini_binary,
Some("/opt/gemini/bin/gemini".to_string())
);
}
#[test]
fn test_llm_gemini_binary_flows_to_config() -> Result<()> {
use xchecker::config::{CliArgs, Config, ConfigSource};
let cli_args = CliArgs {
llm_gemini_binary: Some("/custom/path/gemini".to_string()),
..Default::default()
};
let config = Config::discover(&cli_args)?;
assert_eq!(
config.llm.gemini.as_ref().and_then(|g| g.binary.as_deref()),
Some("/custom/path/gemini")
);
assert_eq!(
config.source_attribution.get("llm_gemini_binary"),
Some(&ConfigSource::Cli)
);
Ok(())
}
#[test]
fn test_llm_gemini_default_model_flag() {
use clap::Parser;
let args = vec![
"xchecker",
"--llm-gemini-default-model",
"gemini-2.0-pro",
"status",
"test-spec",
];
let cli = xchecker::cli::Cli::try_parse_from(args).unwrap();
assert_eq!(
cli.llm_gemini_default_model,
Some("gemini-2.0-pro".to_string())
);
}
#[test]
fn test_phase_timeout_flag() {
use clap::Parser;
let args = vec!["xchecker", "--phase-timeout", "300", "status", "test-spec"];
let cli = xchecker::cli::Cli::try_parse_from(args).unwrap();
assert_eq!(cli.phase_timeout, Some(300));
}
#[test]
fn test_ignore_secret_pattern_flag() {
use clap::Parser;
let args = vec![
"xchecker",
"--ignore-secret-pattern",
"pattern1",
"--ignore-secret-pattern",
"pattern2",
"status",
"test-spec",
];
let cli = xchecker::cli::Cli::try_parse_from(args).unwrap();
assert_eq!(cli.ignore_secret_pattern.len(), 2);
assert!(cli.ignore_secret_pattern.contains(&"pattern1".to_string()));
assert!(cli.ignore_secret_pattern.contains(&"pattern2".to_string()));
}
#[test]
fn test_extra_secret_pattern_flag() {
use clap::Parser;
let args = vec![
"xchecker",
"--extra-secret-pattern",
"custom.*secret",
"--extra-secret-pattern",
"api.*key",
"status",
"test-spec",
];
let cli = xchecker::cli::Cli::try_parse_from(args).unwrap();
assert_eq!(cli.extra_secret_pattern.len(), 2);
assert!(
cli.extra_secret_pattern
.contains(&"custom.*secret".to_string())
);
assert!(cli.extra_secret_pattern.contains(&"api.*key".to_string()));
}
#[test]
fn test_packet_max_bytes_flag() {
use clap::Parser;
let args = vec![
"xchecker",
"--packet-max-bytes",
"32768",
"status",
"test-spec",
];
let cli = xchecker::cli::Cli::try_parse_from(args).unwrap();
assert_eq!(cli.packet_max_bytes, Some(32768));
}
#[test]
fn test_packet_max_lines_flag() {
use clap::Parser;
let args = vec![
"xchecker",
"--packet-max-lines",
"600",
"status",
"test-spec",
];
let cli = xchecker::cli::Cli::try_parse_from(args).unwrap();
assert_eq!(cli.packet_max_lines, Some(600));
}
#[test]
fn test_verbose_flag() {
use clap::Parser;
let args = vec!["xchecker", "--verbose", "status", "test-spec"];
let cli = xchecker::cli::Cli::try_parse_from(args).unwrap();
assert!(cli.verbose);
}
#[test]
fn test_force_flag_on_spec() {
use clap::Parser;
let args = vec!["xchecker", "spec", "test-spec", "--force"];
let cli = xchecker::cli::Cli::try_parse_from(args).unwrap();
match cli.command {
xchecker::cli::Commands::Spec { force, .. } => {
assert!(force);
}
_ => panic!("Expected Spec command"),
}
}
#[test]
fn test_apply_fixups_flag() {
use clap::Parser;
let args = vec!["xchecker", "spec", "test-spec", "--apply-fixups"];
let cli = xchecker::cli::Cli::try_parse_from(args).unwrap();
match cli.command {
xchecker::cli::Commands::Spec { apply_fixups, .. } => {
assert!(apply_fixups);
}
_ => panic!("Expected Spec command"),
}
}
#[test]
fn test_strict_lock_flag() {
use clap::Parser;
let args = vec!["xchecker", "spec", "test-spec", "--strict-lock"];
let cli = xchecker::cli::Cli::try_parse_from(args).unwrap();
match cli.command {
xchecker::cli::Commands::Spec { strict_lock, .. } => {
assert!(strict_lock);
}
_ => panic!("Expected Spec command"),
}
}
#[test]
fn test_cli_flags_override_config() -> Result<()> {
use xchecker::config::{CliArgs, Config};
let cli_args = CliArgs {
config_path: None,
model: Some("opus".to_string()),
max_turns: Some(10),
packet_max_bytes: Some(100000),
packet_max_lines: Some(2000),
output_format: Some("text".to_string()),
verbose: Some(true),
runner_mode: Some("native".to_string()),
runner_distro: Some("Ubuntu".to_string()),
claude_path: Some("/usr/bin/claude".to_string()),
allow: vec![],
deny: vec![],
dangerously_skip_permissions: false,
ignore_secret_pattern: vec!["test.*".to_string()],
extra_secret_pattern: vec!["custom.*".to_string()],
phase_timeout: Some(900),
stdout_cap_bytes: Some(4194304),
stderr_cap_bytes: Some(524288),
lock_ttl_seconds: Some(1800),
debug_packet: false,
allow_links: false,
strict_validation: None,
llm_provider: None,
llm_claude_binary: None,
llm_gemini_binary: None,
llm_gemini_default_model: None,
llm_fallback_provider: None,
prompt_template: None,
execution_strategy: None,
};
let config = Config::discover(&cli_args)?;
assert_eq!(config.defaults.model, Some("opus".to_string()));
assert_eq!(config.defaults.max_turns, Some(10));
assert_eq!(config.defaults.packet_max_bytes, Some(100000));
assert_eq!(config.defaults.packet_max_lines, Some(2000));
assert_eq!(config.defaults.output_format, Some("text".to_string()));
assert_eq!(config.defaults.verbose, Some(true));
assert_eq!(config.runner.mode, Some("native".to_string()));
assert_eq!(config.runner.distro, Some("Ubuntu".to_string()));
assert_eq!(
config.runner.claude_path,
Some("/usr/bin/claude".to_string())
);
assert_eq!(config.defaults.phase_timeout, Some(900));
use xchecker::config::ConfigSource;
assert_eq!(
config.source_attribution.get("model"),
Some(&ConfigSource::Cli)
);
assert_eq!(
config.source_attribution.get("max_turns"),
Some(&ConfigSource::Cli)
);
assert_eq!(
config.source_attribution.get("verbose"),
Some(&ConfigSource::Cli)
);
Ok(())
}
#[test]
fn test_stdout_cap_bytes_flag() {
use clap::Parser;
let args = vec![
"xchecker",
"--stdout-cap-bytes",
"4194304",
"status",
"test-spec",
];
let cli = xchecker::cli::Cli::try_parse_from(args).unwrap();
assert_eq!(cli.stdout_cap_bytes, Some(4194304));
}
#[test]
fn test_stderr_cap_bytes_flag() {
use clap::Parser;
let args = vec![
"xchecker",
"--stderr-cap-bytes",
"524288",
"status",
"test-spec",
];
let cli = xchecker::cli::Cli::try_parse_from(args).unwrap();
assert_eq!(cli.stderr_cap_bytes, Some(524288));
}
#[test]
fn test_lock_ttl_seconds_flag() {
use clap::Parser;
let args = vec![
"xchecker",
"--lock-ttl-seconds",
"1800",
"status",
"test-spec",
];
let cli = xchecker::cli::Cli::try_parse_from(args).unwrap();
assert_eq!(cli.lock_ttl_seconds, Some(1800));
}
#[test]
fn test_debug_packet_flag() {
use clap::Parser;
let args = vec!["xchecker", "--debug-packet", "status", "test-spec"];
let cli = xchecker::cli::Cli::try_parse_from(args).unwrap();
assert!(cli.debug_packet);
}
#[test]
fn test_allow_links_flag() {
use clap::Parser;
let args = vec!["xchecker", "--allow-links", "status", "test-spec"];
let cli = xchecker::cli::Cli::try_parse_from(args).unwrap();
assert!(cli.allow_links);
}
#[test]
fn test_new_flags_in_config_system() -> Result<()> {
use xchecker::config::{CliArgs, Config};
let cli_args = CliArgs {
config_path: None,
model: None,
max_turns: None,
packet_max_bytes: None,
packet_max_lines: None,
output_format: None,
verbose: None,
runner_mode: None,
runner_distro: None,
claude_path: None,
allow: vec![],
deny: vec![],
dangerously_skip_permissions: false,
ignore_secret_pattern: vec![],
extra_secret_pattern: vec![],
phase_timeout: None,
stdout_cap_bytes: Some(4194304),
stderr_cap_bytes: Some(524288),
lock_ttl_seconds: Some(1800),
debug_packet: true,
allow_links: true,
strict_validation: Some(true),
llm_provider: None,
llm_claude_binary: None,
llm_gemini_binary: None,
llm_gemini_default_model: None,
llm_fallback_provider: None,
prompt_template: None,
execution_strategy: None,
};
let config = Config::discover(&cli_args)?;
assert_eq!(config.defaults.stdout_cap_bytes, Some(4194304));
assert_eq!(config.defaults.stderr_cap_bytes, Some(524288));
assert_eq!(config.defaults.lock_ttl_seconds, Some(1800));
assert_eq!(config.defaults.debug_packet, Some(true));
assert_eq!(config.defaults.allow_links, Some(true));
use xchecker::config::ConfigSource;
assert_eq!(
config.source_attribution.get("stdout_cap_bytes"),
Some(&ConfigSource::Cli)
);
assert_eq!(
config.source_attribution.get("stderr_cap_bytes"),
Some(&ConfigSource::Cli)
);
assert_eq!(
config.source_attribution.get("lock_ttl_seconds"),
Some(&ConfigSource::Cli)
);
assert_eq!(
config.source_attribution.get("debug_packet"),
Some(&ConfigSource::Cli)
);
assert_eq!(
config.source_attribution.get("allow_links"),
Some(&ConfigSource::Cli)
);
Ok(())
}
#[test]
fn test_new_flags_defaults() -> Result<()> {
use xchecker::config::{CliArgs, Config};
let cli_args = CliArgs::default();
let config = Config::discover(&cli_args)?;
assert_eq!(config.defaults.stdout_cap_bytes, Some(2097152)); assert_eq!(config.defaults.stderr_cap_bytes, Some(262144)); assert_eq!(config.defaults.lock_ttl_seconds, Some(900)); assert_eq!(config.defaults.debug_packet, Some(false));
assert_eq!(config.defaults.allow_links, Some(false));
Ok(())
}
#[test]
fn test_stdout_cap_bytes_validation() {
use xchecker::config::{CliArgs, Config};
let cli_args = CliArgs {
stdout_cap_bytes: Some(512), ..Default::default()
};
let result = Config::discover(&cli_args);
assert!(result.is_err());
assert!(result.unwrap_err().to_string().contains("stdout_cap_bytes"));
let cli_args = CliArgs {
stdout_cap_bytes: Some(200_000_000), ..Default::default()
};
let result = Config::discover(&cli_args);
assert!(result.is_err());
assert!(result.unwrap_err().to_string().contains("stdout_cap_bytes"));
}
#[test]
fn test_stderr_cap_bytes_validation() {
use xchecker::config::{CliArgs, Config};
let cli_args = CliArgs {
stderr_cap_bytes: Some(512), ..Default::default()
};
let result = Config::discover(&cli_args);
assert!(result.is_err());
assert!(result.unwrap_err().to_string().contains("stderr_cap_bytes"));
let cli_args = CliArgs {
stderr_cap_bytes: Some(20_000_000), ..Default::default()
};
let result = Config::discover(&cli_args);
assert!(result.is_err());
assert!(result.unwrap_err().to_string().contains("stderr_cap_bytes"));
}
#[test]
fn test_lock_ttl_seconds_validation() {
use xchecker::config::{CliArgs, Config};
let cli_args = CliArgs {
lock_ttl_seconds: Some(30), ..Default::default()
};
let result = Config::discover(&cli_args);
assert!(result.is_err());
assert!(result.unwrap_err().to_string().contains("lock_ttl_seconds"));
let cli_args = CliArgs {
lock_ttl_seconds: Some(100_000), ..Default::default()
};
let result = Config::discover(&cli_args);
assert!(result.is_err());
assert!(result.unwrap_err().to_string().contains("lock_ttl_seconds"));
}
#[test]
fn test_cli_run_error_path_smoke() {
use xchecker::ExitCode;
use clap::Parser;
let result = xchecker::cli::Cli::try_parse_from(["xchecker"]);
assert!(
result.is_err(),
"Missing subcommand should result in parse error"
);
let result = xchecker::cli::Cli::try_parse_from(["xchecker", "invalid-command"]);
assert!(
result.is_err(),
"Invalid subcommand should result in parse error"
);
let result = xchecker::cli::Cli::try_parse_from([
"xchecker",
"--phase-timeout",
"not-a-number",
"status",
"test-spec",
]);
assert!(
result.is_err(),
"Invalid flag value should result in parse error"
);
assert_eq!(ExitCode::SUCCESS.as_i32(), 0);
assert_eq!(ExitCode::CLI_ARGS.as_i32(), 2);
assert_eq!(ExitCode::INTERNAL.as_i32(), 1);
}