assert!(result.is_ok());
let request = result.unwrap();
assert_eq!(request.method, Method::POST);
assert_eq!(request.path, "/api/v1/serve");
}
#[test]
fn test_cli_input_from_commands_generate() {
let params = vec![(
"project_name".to_string(),
Value::String("test".to_string()),
)];
let command = Commands::Generate {
category: "makefile".to_string(),
template: "rust/cli".to_string(),
params,
output: Some(PathBuf::from("output.txt")),
create_dirs: true,
};
let input = CliInput::from_commands(command);
assert_eq!(input.command_name, "generate");
}
#[test]
fn test_cli_input_from_commands_list() {
let command = Commands::List {
toolchain: Some("rust".to_string()),
category: Some("cli".to_string()),
format: OutputFormat::Json,
};
let input = CliInput::from_commands(command);
assert_eq!(input.command_name, "list");
assert_eq!(input.command_name, "list");
}
#[test]
fn test_cli_output_success() {
let output = CliOutput::Success {
content: "Success message".to_string(),
exit_code: 0,
};
assert_eq!(output.exit_code(), 0);
assert_eq!(output.content(), "Success message");
}
#[test]
fn test_cli_output_error() {
let output = CliOutput::Error {
message: "Error occurred".to_string(),
exit_code: 2,
};
assert_eq!(output.exit_code(), 2);
assert_eq!(output.content(), "Error occurred");
}
#[test]
fn test_cli_runner_new() {
let _ = CliRunner::new();
}
#[test]
fn test_cli_runner_default() {
let _ = CliRunner::default();
}
#[tokio::test]
async fn test_unsupported_command() {
let adapter = CliAdapter::new();
let diagnose_args = crate::cli::diagnose::DiagnoseArgs {
format: crate::cli::diagnose::DiagnosticFormat::Pretty,
only: vec![],
skip: vec![],
timeout: 60,
};
let command = Commands::Diagnose(diagnose_args);
let input = CliInput::from_commands(command);
let result = adapter.decode(input).await;
if let Err(error) = result {
assert!(matches!(
error,
ProtocolError::UnsupportedProtocol(_) | ProtocolError::InvalidFormat(_)
));
}
}
#[tokio::test]
async fn test_decode_analyze_deep_context() {
let adapter = CliAdapter::new();
let command = Commands::Analyze(AnalyzeCommands::DeepContext {
project_path: PathBuf::from("."),
output: Some(PathBuf::from("deep_context.json")),
format: DeepContextOutputFormat::Json,
full: false,
include: vec![],
exclude: vec![],
period_days: 30,
dag_type: DeepContextDagType::CallGraph,
max_depth: None,
include_patterns: vec![],
exclude_patterns: vec![],
cache_strategy: DeepContextCacheStrategy::Normal,
parallel: None,
verbose: false,
top_files: 10,
});
let input = CliInput::from_commands(command);
let result = adapter.decode(input).await;
assert!(result.is_ok());
let request = result.unwrap();
assert_eq!(request.method, Method::POST);
assert_eq!(request.path, "/api/v1/analyze/deep-context");
}
#[tokio::test]
async fn test_decode_analyze_tdg() {
let adapter = CliAdapter::new();
let command = Commands::Analyze(AnalyzeCommands::Tdg {
path: PathBuf::from("."),
threshold: 1.5,
top_files: 10,
format: crate::cli::TdgOutputFormat::Json,
include_components: false,
output: None,
critical_only: false,
verbose: false,
ml: false,
});
let input = CliInput::from_commands(command);
let result = adapter.decode(input).await;
assert!(result.is_ok());
let request = result.unwrap();
assert_eq!(request.method, Method::POST);
assert_eq!(request.path, "/api/v1/analyze/tdg");
}
#[tokio::test]
async fn test_encode_success_response() {
let adapter = CliAdapter::new();
let response = UnifiedResponse {
status: StatusCode::OK,
headers: HeaderMap::new(),
body: Body::from(json!({"status": "success"}).to_string()),
trace_id: Uuid::new_v4(),
};
let result = adapter.encode(response).await;
assert!(result.is_ok());
let cli_output = result.unwrap();
match cli_output {
CliOutput::Success { content, .. } => {
assert!(content.contains("success"));
}
_ => panic!("Expected Success output"),
}
}
#[tokio::test]
async fn test_encode_error_response() {
let adapter = CliAdapter::new();
let response = UnifiedResponse {
status: StatusCode::BAD_REQUEST,
headers: HeaderMap::new(),
body: Body::from("Bad Request"),
trace_id: Uuid::new_v4(),
};
let result = adapter.encode(response).await;
assert!(result.is_ok());
let cli_output = result.unwrap();
match cli_output {
CliOutput::Error { message, exit_code } => {
assert!(message.contains("Bad Request"));
assert_eq!(exit_code, 1);
}
_ => panic!("Expected Error output"),
}
}
#[tokio::test]
async fn test_dispatch_basic_analysis_churn() {
let command = AnalyzeCommands::Churn {
project_path: PathBuf::from("."),
days: 30,
format: ChurnOutputFormat::Json,
output: None,
top_files: 10,
include: vec![],
exclude: vec![],
};
let result = CliAdapter::dispatch_basic_analysis(&command);
assert!(result.is_ok());
let (method, path, _, _) = result.unwrap();
assert_eq!(method, Method::POST);
assert_eq!(path, "/api/v1/analyze/churn");
}
#[tokio::test]
async fn test_dispatch_basic_analysis_complexity() {
let command = AnalyzeCommands::Complexity {
path: PathBuf::from("."),
project_path: None,
file: None,
files: vec![],
toolchain: Some("rust".to_string()),
format: ComplexityOutputFormat::Json,
output: None,
max_cyclomatic: Some(10),
max_cognitive: Some(15),
include: vec![],
watch: false,
top_files: 0,
fail_on_violation: false,
timeout: 60,
ml: false,
};
let result = CliAdapter::dispatch_basic_analysis(&command);
assert!(result.is_ok());
let (method, path, _, _) = result.unwrap();
assert_eq!(method, Method::POST);
assert_eq!(path, "/api/v1/analyze/complexity");
}
#[tokio::test]
async fn test_dispatch_advanced_analysis_comprehensive() {
let command = AnalyzeCommands::Comprehensive {
project_path: PathBuf::from("."),
file: None,
files: vec![],
format: crate::cli::ComprehensiveOutputFormat::Json,
include_duplicates: true,
include_dead_code: true,
include_defects: true,
include_complexity: true,
include_tdg: false,
confidence_threshold: 0.8,
min_lines: 10,
include: None,
exclude: None,
output: None,
perf: false,
executive_summary: false,
top_files: 10,
};
let result = CliAdapter::dispatch_advanced_analysis(&command);
assert!(result.is_ok());
let (method, path, _, _) = result.unwrap();
assert_eq!(method, Method::POST);
assert_eq!(path, "/api/v1/analyze/comprehensive");
}
#[tokio::test]
async fn test_dispatch_structural_analysis_dag() {
let command = AnalyzeCommands::Dag {
dag_type: DagType::CallGraph,
project_path: PathBuf::from("."),
output: None,
max_depth: Some(5),
target_nodes: Some(100),
filter_external: true,
show_complexity: false,
include_duplicates: false,
include_dead_code: false,
enhanced: false,
};
let result = CliAdapter::dispatch_structural_analysis(&command);
assert!(result.is_ok());
let (method, path, _, _) = result.unwrap();
assert_eq!(method, Method::POST);
assert_eq!(path, "/api/v1/analyze/dag");
}
#[tokio::test]
async fn test_dispatch_specialized_analysis_makefile() {
let command = AnalyzeCommands::Makefile {
path: PathBuf::from("."),
rules: vec!["all".to_string()],
format: crate::cli::MakefileOutputFormat::Json,
fix: false,
gnu_version: "4.3".to_string(),
top_files: 5,
};
let result = CliAdapter::dispatch_specialized_analysis(&command);
assert!(result.is_ok());
let (method, path, _, _) = result.unwrap();
assert_eq!(method, Method::POST);
assert_eq!(path, "/api/v1/analyze/makefile");
}
#[test]
fn test_decode_analyze_complexity_with_migration_new_path() {
let result = CliAdapter::decode_analyze_complexity_with_migration(
&PathBuf::from("."),
&None, &None,
&[],
&Some("rust".to_string()),
&ComplexityOutputFormat::Json,
&None,
&Some(10),
&Some(15),
&[],
false,
0,
);
assert!(result.is_ok());
let (method, path, _, _) = result.unwrap();
assert_eq!(method, Method::POST);
assert_eq!(path, "/api/v1/analyze/complexity");
}
#[test]
fn test_decode_analyze_complexity_with_migration_deprecated_path() {
let result = CliAdapter::decode_analyze_complexity_with_migration(
&PathBuf::from("new_path"),
&Some(PathBuf::from("deprecated_path")), &None,
&[],
&Some("rust".to_string()),
&ComplexityOutputFormat::Json,
&None,
&Some(10),
&Some(15),
&[],
false,
0,
);
assert!(result.is_ok());
let (method, path, _, _) = result.unwrap();
assert_eq!(method, Method::POST);
assert_eq!(path, "/api/v1/analyze/complexity");
}
#[test]
fn test_dispatch_wrong_category_returns_error() {
let churn_command = AnalyzeCommands::Churn {
project_path: PathBuf::from("."),
days: 30,
format: ChurnOutputFormat::Json,
output: None,
top_files: 10,
include: vec![],
exclude: vec![],
};
let result = CliAdapter::dispatch_advanced_analysis(&churn_command);
assert!(result.is_err());
match result {
Err(ProtocolError::UnsupportedProtocol(msg)) => {
assert!(msg.contains("Command not supported in advanced analysis dispatch"));
}
_ => panic!("Expected UnsupportedProtocol error"),
}
}
}
mod property_tests {
use proptest::prelude::*;
proptest! {
#[test]
fn basic_property_stability(_input in ".*") {
prop_assert!(true);
}
#[test]
fn module_consistency_check(_x in 0u32..1000) {
prop_assert!(_x < 1001);
}
}
}
mod coverage_tests {
use super::*;
use crate::cli::{
BigOOutputFormat, DefectPredictionOutputFormat, DuplicateOutputFormat, DuplicateType,
GraphMetricType, GraphMetricsOutputFormat, IncrementalCoverageOutputFormat,
LintHotspotOutputFormat, MakefileOutputFormat, NameSimilarityOutputFormat,
ProofAnnotationOutputFormat, PropertyTypeFilter, ProvabilityOutputFormat, SearchScope,
SymbolTableOutputFormat, SymbolTypeFilter, TdgOutputFormat, VerificationMethodFilter,
};
use std::path::PathBuf;
#[test]
fn test_dead_code_format_all_variants() {
assert_eq!(
dead_code_format_to_string(&crate::cli::DeadCodeOutputFormat::Summary),
"summary"
);
assert_eq!(
dead_code_format_to_string(&crate::cli::DeadCodeOutputFormat::Json),
"json"
);
assert_eq!(
dead_code_format_to_string(&crate::cli::DeadCodeOutputFormat::Sarif),
"sarif"
);
assert_eq!(
dead_code_format_to_string(&crate::cli::DeadCodeOutputFormat::Markdown),
"markdown"
);
}
#[test]
fn test_satd_format_all_variants() {
assert_eq!(
satd_format_to_string(&crate::cli::SatdOutputFormat::Summary),