use help_probe::model::{ArgumentSpec, ArgumentType, OptionSpec, ProbeResult};
use help_probe::validation::{ValidationErrorType, validate_command};
fn create_test_result() -> ProbeResult {
ProbeResult {
command: "mytool".to_string(),
args: vec![],
exit_code: Some(0),
timed_out: false,
help_flag_detected: false,
usage_blocks: vec![],
options: vec![OptionSpec {
short_flags: vec!["-v".to_string()],
long_flags: vec!["--verbose".to_string()],
description: Some("Verbose output".to_string()),
option_type: help_probe::model::OptionType::Boolean,
required: false,
default_value: None,
takes_argument: false,
argument_name: None,
choices: vec![],
}],
subcommands: vec![],
arguments: vec![],
examples: vec![],
environment_variables: vec![],
validation_rules: vec![],
raw_stdout: String::new(),
raw_stderr: String::new(),
}
}
#[test]
fn validate_command_success() {
let result = create_test_result();
let validation = validate_command(&result, "mytool", &["--verbose".to_string()]);
assert!(validation.is_valid);
assert!(validation.errors.is_empty());
}
#[test]
fn validate_command_unknown_option() {
let result = create_test_result();
let validation = validate_command(&result, "mytool", &["--unknown".to_string()]);
assert!(!validation.is_valid);
assert!(
validation
.errors
.iter()
.any(|e| matches!(e.error_type, ValidationErrorType::UnknownOption))
);
}
#[test]
fn validate_command_missing_required_argument() {
let result = ProbeResult {
command: "mytool".to_string(),
args: vec![],
exit_code: Some(0),
timed_out: false,
help_flag_detected: false,
usage_blocks: vec![],
options: vec![],
subcommands: vec![],
arguments: vec![ArgumentSpec {
name: "FILE".to_string(),
description: Some("Input file".to_string()),
required: true,
variadic: false,
arg_type: Some(ArgumentType::Path),
placeholder: Some("<FILE>".to_string()),
}],
examples: vec![],
environment_variables: vec![],
validation_rules: vec![],
raw_stdout: String::new(),
raw_stderr: String::new(),
};
let validation = validate_command(&result, "mytool", &[]);
assert!(!validation.is_valid);
assert!(
validation
.errors
.iter()
.any(|e| matches!(e.error_type, ValidationErrorType::TooFewArguments))
);
}
#[test]
fn validate_command_option_missing_argument() {
let result = ProbeResult {
command: "mytool".to_string(),
args: vec![],
exit_code: Some(0),
timed_out: false,
help_flag_detected: false,
usage_blocks: vec![],
options: vec![OptionSpec {
short_flags: vec![],
long_flags: vec!["--file".to_string()],
description: Some("Input file".to_string()),
option_type: help_probe::model::OptionType::Path,
required: false,
default_value: None,
takes_argument: true,
argument_name: Some("FILE".to_string()),
choices: vec![],
}],
subcommands: vec![],
arguments: vec![],
examples: vec![],
environment_variables: vec![],
validation_rules: vec![],
raw_stdout: String::new(),
raw_stderr: String::new(),
};
let validation = validate_command(&result, "mytool", &["--file".to_string()]);
assert!(!validation.is_valid);
assert!(
validation
.errors
.iter()
.any(|e| matches!(e.error_type, ValidationErrorType::OptionMissingArgument))
);
}
#[test]
fn validate_command_required_option() {
let result = ProbeResult {
command: "mytool".to_string(),
args: vec![],
exit_code: Some(0),
timed_out: false,
help_flag_detected: false,
usage_blocks: vec![],
options: vec![OptionSpec {
short_flags: vec![],
long_flags: vec!["--required".to_string()],
description: Some("Required option (required)".to_string()),
option_type: help_probe::model::OptionType::Boolean,
required: true,
default_value: None,
takes_argument: false,
argument_name: None,
choices: vec![],
}],
subcommands: vec![],
arguments: vec![],
examples: vec![],
environment_variables: vec![],
validation_rules: vec![],
raw_stdout: String::new(),
raw_stderr: String::new(),
};
let validation = validate_command(&result, "mytool", &[]);
assert!(!validation.is_valid);
assert!(
validation
.errors
.iter()
.any(|e| matches!(e.error_type, ValidationErrorType::MissingRequiredOption))
);
}
#[test]
fn validate_command_too_many_arguments() {
let result = ProbeResult {
command: "mytool".to_string(),
args: vec![],
exit_code: Some(0),
timed_out: false,
help_flag_detected: false,
usage_blocks: vec![],
options: vec![],
subcommands: vec![],
arguments: vec![ArgumentSpec {
name: "FILE".to_string(),
description: Some("Input file".to_string()),
required: true,
variadic: false,
arg_type: Some(ArgumentType::Path),
placeholder: Some("<FILE>".to_string()),
}],
examples: vec![],
environment_variables: vec![],
validation_rules: vec![],
raw_stdout: String::new(),
raw_stderr: String::new(),
};
let validation = validate_command(
&result,
"mytool",
&["file1.txt".to_string(), "file2.txt".to_string()],
);
assert!(!validation.is_valid);
assert!(
validation
.errors
.iter()
.any(|e| matches!(e.error_type, ValidationErrorType::TooManyArguments))
);
}
#[test]
fn validate_command_invalid_argument_type() {
let result = ProbeResult {
command: "mytool".to_string(),
args: vec![],
exit_code: Some(0),
timed_out: false,
help_flag_detected: false,
usage_blocks: vec![],
options: vec![],
subcommands: vec![],
arguments: vec![ArgumentSpec {
name: "PORT".to_string(),
description: Some("Port number".to_string()),
required: true,
variadic: false,
arg_type: Some(ArgumentType::Number),
placeholder: Some("<PORT>".to_string()),
}],
examples: vec![],
environment_variables: vec![],
validation_rules: vec![],
raw_stdout: String::new(),
raw_stderr: String::new(),
};
let validation = validate_command(&result, "mytool", &["not-a-number".to_string()]);
assert!(
validation
.warnings
.iter()
.any(|e| matches!(e.error_type, ValidationErrorType::InvalidArgumentType))
);
}
#[test]
fn validate_command_option_unexpected_argument() {
let result = ProbeResult {
command: "mytool".to_string(),
args: vec![],
exit_code: Some(0),
timed_out: false,
help_flag_detected: false,
usage_blocks: vec![],
options: vec![OptionSpec {
short_flags: vec!["-v".to_string()],
long_flags: vec!["--verbose".to_string()],
description: Some("Verbose output".to_string()),
option_type: help_probe::model::OptionType::Boolean,
required: false,
default_value: None,
takes_argument: false,
argument_name: None,
choices: vec![],
}],
subcommands: vec![],
arguments: vec![],
examples: vec![],
environment_variables: vec![],
validation_rules: vec![],
raw_stdout: String::new(),
raw_stderr: String::new(),
};
let validation = validate_command(&result, "mytool", &["--verbose=yes".to_string()]);
assert!(
validation
.warnings
.iter()
.any(|e| matches!(e.error_type, ValidationErrorType::OptionUnexpectedArgument))
);
}
#[test]
fn validate_command_subcommand() {
use help_probe::model::SubcommandSpec;
let result = ProbeResult {
command: "mytool".to_string(),
args: vec![],
exit_code: Some(0),
timed_out: false,
help_flag_detected: false,
usage_blocks: vec![],
options: vec![],
subcommands: vec![SubcommandSpec {
name: "build".to_string(),
description: Some("Build the project".to_string()),
full_path: "build".to_string(),
parent: None,
options: Vec::new(),
arguments: Vec::new(),
subcommands: Vec::new(),
}],
arguments: vec![],
examples: vec![],
environment_variables: vec![],
validation_rules: vec![],
raw_stdout: String::new(),
raw_stderr: String::new(),
};
let validation = validate_command(&result, "mytool", &["build".to_string()]);
assert!(
validation
.errors
.iter()
.all(|e| !matches!(e.error_type, ValidationErrorType::UnknownSubcommand))
);
let validation = validate_command(&result, "mytool", &[]);
assert!(validation.is_valid);
}