mod cli_testing_tools;
use cli_testing_tools::*;
use predicates::prelude::*;
use std::fs;
use tempfile::TempDir;
#[test]
fn cli_no_subcommand_shows_info() {
decy_cmd()
.assert()
.success()
.stdout(predicate::str::contains("Decy: C-to-Rust Transpiler"))
.stdout(predicate::str::contains("decy --help"))
.stdout(predicate::str::contains("decy transpile"))
.stdout(predicate::str::contains("decy repl"));
}
#[test]
fn cli_help_flag() {
decy_cmd().arg("--help").assert().success().stdout(predicate::str::contains("Transpile"));
}
#[test]
fn cli_version_flag() {
decy_cmd().arg("--version").assert().success().stdout(predicate::str::contains("decy"));
}
#[test]
fn cli_transpile_output_to_file() {
let temp = TempDir::new().unwrap();
let input = create_temp_file(&temp, "input.c", VALID_C_CODE);
let output = temp.path().join("output.rs");
decy_cmd()
.arg("transpile")
.arg(&input)
.arg("-o")
.arg(&output)
.assert()
.success()
.stderr(predicate::str::contains("Transpiled"));
assert!(output.exists(), "Output file should be created");
let content = fs::read_to_string(&output).unwrap();
assert!(content.contains("fn main"), "Output should contain Rust code");
}
#[test]
fn cli_transpile_output_file_library_no_main() {
let temp = TempDir::new().unwrap();
let c_code = r#"
int add(int a, int b) {
return a + b;
}
"#;
let input = create_temp_file(&temp, "lib.c", c_code);
let output = temp.path().join("lib.rs");
decy_cmd()
.arg("transpile")
.arg(&input)
.arg("-o")
.arg(&output)
.assert()
.success()
.stderr(predicate::str::contains("No main function found"));
}
#[test]
fn cli_transpile_stdout_library_no_main() {
let temp = TempDir::new().unwrap();
let c_code = r#"
int multiply(int a, int b) {
return a * b;
}
"#;
let input = create_temp_file(&temp, "lib.c", c_code);
decy_cmd()
.arg("transpile")
.arg(&input)
.assert()
.success()
.stdout(predicate::str::contains("fn multiply"))
.stderr(predicate::str::contains("No main function found"));
}
#[test]
fn cli_transpile_with_trace() {
let temp = TempDir::new().unwrap();
let input = create_temp_file(&temp, "trace.c", VALID_C_CODE);
decy_cmd()
.arg("transpile")
.arg(&input)
.arg("--trace")
.assert()
.success()
.stdout(predicate::str::contains("fn main"))
.stderr(predicate::str::is_empty().not()); }
#[test]
fn cli_transpile_missing_file_error_message() {
decy_cmd()
.arg("transpile")
.arg("does_not_exist.c")
.assert()
.failure()
.stderr(predicate::str::contains("Failed to read input file"));
}
#[test]
fn cli_transpile_invalid_c_error_message() {
let temp = TempDir::new().unwrap();
let input = create_temp_file(&temp, "bad.c", "int main( { }");
decy_cmd()
.arg("transpile")
.arg(&input)
.assert()
.failure()
.stderr(predicate::str::is_empty().not());
}
#[test]
fn cli_audit_safe_code() {
let temp = TempDir::new().unwrap();
let rust_code = r#"
fn main() {
let x = 42;
println!("{}", x);
}
"#;
let input = create_temp_file(&temp, "safe.rs", rust_code);
decy_cmd()
.arg("audit")
.arg(&input)
.assert()
.success()
.stdout(predicate::str::contains("Unsafe Code Audit Report"))
.stdout(predicate::str::contains("No unsafe blocks found"));
}
#[test]
fn cli_audit_unsafe_code() {
let temp = TempDir::new().unwrap();
let rust_code = r#"
fn main() {
unsafe {
let x: *mut i32 = std::ptr::null_mut();
}
}
"#;
let input = create_temp_file(&temp, "unsafe.rs", rust_code);
decy_cmd()
.arg("audit")
.arg(&input)
.assert()
.success()
.stdout(predicate::str::contains("Unsafe Code Audit Report"))
.stdout(predicate::str::contains("Unsafe Blocks Found"));
}
#[test]
fn cli_audit_verbose_flag() {
let temp = TempDir::new().unwrap();
let rust_code = r#"
fn do_stuff() {
unsafe {
let raw: *const i32 = &42;
}
}
fn main() {
do_stuff();
}
"#;
let input = create_temp_file(&temp, "verbose.rs", rust_code);
decy_cmd().arg("audit").arg(&input).arg("--verbose").assert().success().stdout(
predicate::str::contains("Detailed Block Analysis").or(predicate::str::contains("Pattern")),
);
}
#[test]
fn cli_audit_missing_file() {
decy_cmd()
.arg("audit")
.arg("nonexistent.rs")
.assert()
.failure()
.stderr(predicate::str::contains("Failed to read input file"));
}
#[test]
fn cli_check_project_valid() {
let temp = TempDir::new().unwrap();
create_temp_file(&temp, "main.c", "int main() { return 0; }");
create_temp_file(&temp, "util.c", "int helper() { return 1; }");
decy_cmd()
.arg("check-project")
.arg(temp.path())
.assert()
.success()
.stdout(predicate::str::contains("Found"))
.stdout(predicate::str::contains("C files"))
.stdout(predicate::str::contains("No circular dependencies"))
.stdout(predicate::str::contains("Build order"));
}
#[test]
fn cli_check_project_empty() {
let temp = TempDir::new().unwrap();
decy_cmd()
.arg("check-project")
.arg(temp.path())
.assert()
.success()
.stdout(predicate::str::contains("No C files found"));
}
#[test]
fn cli_check_project_nonexistent_dir() {
decy_cmd()
.arg("check-project")
.arg("/tmp/nonexistent_decy_dir_12345")
.assert()
.failure()
.stderr(predicate::str::contains("not found"));
}
#[test]
fn cli_cache_stats_no_cache() {
let temp = TempDir::new().unwrap();
decy_cmd()
.arg("cache-stats")
.arg(temp.path())
.assert()
.success()
.stdout(predicate::str::contains("No cache found"));
}
#[test]
fn cli_cache_stats_nonexistent_dir() {
decy_cmd()
.arg("cache-stats")
.arg("/tmp/nonexistent_decy_dir_99999")
.assert()
.failure()
.stderr(predicate::str::contains("not found"));
}
#[test]
fn cli_transpile_project_basic() {
let temp = TempDir::new().unwrap();
let output_dir = temp.path().join("output");
create_temp_file(&temp, "hello.c", "int main() { return 0; }");
decy_cmd()
.arg("transpile-project")
.arg(temp.path())
.arg("-o")
.arg(&output_dir)
.assert()
.success()
.stdout(predicate::str::contains("Found"))
.stdout(predicate::str::contains("Transpiled"));
}
#[test]
fn cli_transpile_project_dry_run() {
let temp = TempDir::new().unwrap();
let output_dir = temp.path().join("output");
create_temp_file(&temp, "test.c", "int main() { return 0; }");
decy_cmd()
.arg("transpile-project")
.arg(temp.path())
.arg("-o")
.arg(&output_dir)
.arg("--dry-run")
.assert()
.success()
.stdout(predicate::str::contains("DRY RUN"))
.stdout(predicate::str::contains("Would transpile"));
}
#[test]
fn cli_transpile_project_verbose() {
let temp = TempDir::new().unwrap();
let output_dir = temp.path().join("output");
create_temp_file(&temp, "verbose.c", "int main() { return 0; }");
decy_cmd()
.arg("transpile-project")
.arg(temp.path())
.arg("-o")
.arg(&output_dir)
.arg("--verbose")
.assert()
.success()
.stdout(predicate::str::contains("Transpiled"));
}
#[test]
fn cli_transpile_project_quiet() {
let temp = TempDir::new().unwrap();
let output_dir = temp.path().join("output");
create_temp_file(&temp, "quiet.c", "int main() { return 0; }");
decy_cmd()
.arg("transpile-project")
.arg(temp.path())
.arg("-o")
.arg(&output_dir)
.arg("--quiet")
.assert()
.success();
}
#[test]
fn cli_transpile_project_stats() {
let temp = TempDir::new().unwrap();
let output_dir = temp.path().join("output");
create_temp_file(&temp, "stats.c", "int main() { return 0; }");
decy_cmd()
.arg("transpile-project")
.arg(temp.path())
.arg("-o")
.arg(&output_dir)
.arg("--stats")
.assert()
.success()
.stdout(predicate::str::contains("Statistics"));
}
#[test]
fn cli_transpile_project_no_cache() {
let temp = TempDir::new().unwrap();
let output_dir = temp.path().join("output");
create_temp_file(&temp, "nocache.c", "int main() { return 0; }");
decy_cmd()
.arg("transpile-project")
.arg(temp.path())
.arg("-o")
.arg(&output_dir)
.arg("--no-cache")
.assert()
.success();
}
#[test]
fn cli_transpile_project_empty() {
let temp = TempDir::new().unwrap();
let output_dir = temp.path().join("output");
decy_cmd()
.arg("transpile-project")
.arg(temp.path())
.arg("-o")
.arg(&output_dir)
.assert()
.success()
.stdout(predicate::str::contains("No C files found"));
}
#[test]
fn cli_transpile_project_nonexistent_dir() {
decy_cmd()
.arg("transpile-project")
.arg("/tmp/nonexistent_decy_project_dir")
.arg("-o")
.arg("/tmp/nonexistent_output")
.assert()
.failure()
.stderr(predicate::str::contains("not found"));
}
#[test]
fn cli_transpile_project_multiple_files() {
let temp = TempDir::new().unwrap();
let output_dir = temp.path().join("output");
create_temp_file(&temp, "a.c", "int a() { return 1; }");
create_temp_file(&temp, "b.c", "int b() { return 2; }");
create_temp_file(&temp, "main.c", "int main() { return 0; }");
decy_cmd()
.arg("transpile-project")
.arg(temp.path())
.arg("-o")
.arg(&output_dir)
.arg("--verbose")
.arg("--stats")
.assert()
.success()
.stdout(predicate::str::contains("Found 3 C files"))
.stdout(predicate::str::contains("Statistics"));
}
#[test]
fn cli_transpile_struct_code() {
let temp = TempDir::new().unwrap();
let c_code = r#"
struct Point {
int x;
int y;
};
int main() {
struct Point p;
p.x = 10;
return p.x;
}
"#;
let input = create_temp_file(&temp, "struct.c", c_code);
decy_cmd()
.arg("transpile")
.arg(&input)
.assert()
.success()
.stdout(predicate::str::contains("Point"));
}
#[test]
fn cli_transpile_enum_code() {
let temp = TempDir::new().unwrap();
let c_code = r#"
enum Color { RED, GREEN, BLUE };
int main() {
return RED;
}
"#;
let input = create_temp_file(&temp, "enum.c", c_code);
decy_cmd()
.arg("transpile")
.arg(&input)
.assert()
.success()
.stdout(predicate::str::contains("RED").or(predicate::str::contains("Color")));
}
#[test]
fn cli_transpile_global_variables() {
let temp = TempDir::new().unwrap();
let c_code = r#"
int counter = 0;
int main() {
counter = 42;
return counter;
}
"#;
let input = create_temp_file(&temp, "globals.c", c_code);
decy_cmd()
.arg("transpile")
.arg(&input)
.assert()
.success()
.stdout(predicate::str::contains("counter"));
}
#[test]
fn cli_transpile_multiple_functions() {
let temp = TempDir::new().unwrap();
let c_code = r#"
int square(int x) { return x * x; }
int cube(int x) { return x * x * x; }
int main() { return square(3) + cube(2); }
"#;
let input = create_temp_file(&temp, "multi.c", c_code);
decy_cmd()
.arg("transpile")
.arg(&input)
.assert()
.success()
.stdout(predicate::str::contains("fn square"))
.stdout(predicate::str::contains("fn cube"));
}
#[test]
fn cli_oracle_bootstrap_without_feature() {
let result = decy_cmd().arg("oracle").arg("bootstrap").assert();
let _ = result;
}
#[test]
fn cli_oracle_stats_without_feature() {
let result = decy_cmd().arg("oracle").arg("stats").assert();
let _ = result;
}
#[test]
fn cli_oracle_query_without_feature() {
let result = decy_cmd().arg("oracle").arg("query").arg("--error").arg("E0308").assert();
let _ = result;
}
#[test]
fn cli_audit_empty_file() {
let temp = TempDir::new().unwrap();
let input = create_temp_file(&temp, "empty.rs", "");
decy_cmd()
.arg("audit")
.arg(&input)
.assert()
.success()
.stdout(predicate::str::contains("Unsafe Code Audit Report"));
}
#[test]
fn cli_audit_multiple_unsafe_blocks() {
let temp = TempDir::new().unwrap();
let rust_code = r#"
fn a() {
unsafe {
let _ = 1;
}
}
fn b() {
unsafe {
let _ = 2;
}
}
fn main() {
a();
b();
}
"#;
let input = create_temp_file(&temp, "multi_unsafe.rs", rust_code);
decy_cmd()
.arg("audit")
.arg(&input)
.assert()
.success()
.stdout(predicate::str::contains("Unsafe Blocks Found"))
.stdout(predicate::str::contains("Summary by Confidence"));
}
#[test]
fn cli_audit_multiple_unsafe_verbose() {
let temp = TempDir::new().unwrap();
let rust_code = r#"
fn danger() {
unsafe {
let raw: *const i32 = &42;
let _val = *raw;
}
}
fn main() {
danger();
}
"#;
let input = create_temp_file(&temp, "verbose_unsafe.rs", rust_code);
decy_cmd()
.arg("audit")
.arg(&input)
.arg("--verbose")
.assert()
.success()
.stdout(predicate::str::contains("Unsafe Code Audit Report"));
}
#[test]
fn cli_transpile_with_pointers() {
let temp = TempDir::new().unwrap();
let input = create_temp_file(&temp, "pointers.c", C_WITH_POINTERS);
decy_cmd()
.arg("transpile")
.arg(&input)
.assert()
.success()
.stdout(predicate::str::contains("fn increment"))
.stdout(predicate::str::contains("fn main"));
}
#[test]
fn cli_transpile_with_loops() {
let temp = TempDir::new().unwrap();
let c_code = r#"
int sum_to(int n) {
int sum = 0;
int i;
for (i = 1; i <= n; i++) {
sum = sum + i;
}
return sum;
}
int main() {
return sum_to(10);
}
"#;
let input = create_temp_file(&temp, "loops.c", c_code);
decy_cmd()
.arg("transpile")
.arg(&input)
.assert()
.success()
.stdout(predicate::str::contains("fn sum_to"));
}
#[test]
fn cli_transpile_with_switch() {
let temp = TempDir::new().unwrap();
let c_code = r#"
int classify(int x) {
switch(x) {
case 0: return 0;
case 1: return 1;
default: return -1;
}
}
int main() {
return classify(1);
}
"#;
let input = create_temp_file(&temp, "switch.c", c_code);
decy_cmd()
.arg("transpile")
.arg(&input)
.assert()
.success()
.stdout(predicate::str::contains("fn classify"));
}
#[test]
fn cli_transpile_project_verbose_stats() {
let temp = TempDir::new().unwrap();
let output_dir = temp.path().join("output");
create_temp_file(&temp, "test1.c", "int main() { return 0; }");
create_temp_file(&temp, "test2.c", "int helper() { return 1; }");
decy_cmd()
.arg("transpile-project")
.arg(temp.path())
.arg("-o")
.arg(&output_dir)
.arg("--verbose")
.arg("--stats")
.assert()
.success()
.stdout(predicate::str::contains("Statistics"))
.stdout(predicate::str::contains("Files found"));
}
#[test]
fn cli_check_project_single_file() {
let temp = TempDir::new().unwrap();
create_temp_file(&temp, "only.c", "int main() { return 0; }");
decy_cmd()
.arg("check-project")
.arg(temp.path())
.assert()
.success()
.stdout(predicate::str::contains("Found 1 C files"))
.stdout(predicate::str::contains("Build order"));
}