#![allow(deprecated)]
#![allow(clippy::unwrap_used)] #![allow(clippy::expect_used)]
#![allow(non_snake_case)]
use assert_cmd::Command;
use predicates::prelude::*;
use std::fs;
use tempfile::TempDir;
#[allow(deprecated)]
fn bashrs_cmd() -> Command {
assert_cmd::cargo_bin_cmd!("bashrs")
}
#[test]
fn test_CLI_MAKE_LINT_001_basic_lint_detects_issues() {
let temp_dir = TempDir::new().unwrap();
let makefile_path = temp_dir.path().join("Makefile");
fs::write(
&makefile_path,
r#"
VERSION = $(shell git describe)
SOURCES = $(wildcard src/*.c)
build:
mkdir build
gcc $(SOURCES) -o app
clean:
rm -rf $BUILD_DIR
"#,
)
.unwrap();
bashrs_cmd()
.arg("make")
.arg("lint")
.arg(&makefile_path)
.assert()
.failure() .stdout(predicate::str::contains("MAKE")) .stdout(predicate::str::contains("wildcard")); }
#[test]
fn test_CLI_MAKE_LINT_002_clean_makefile_passes() {
let temp_dir = TempDir::new().unwrap();
let makefile_path = temp_dir.path().join("Makefile");
fs::write(
&makefile_path,
r#".DELETE_ON_ERROR:
.ONESHELL:
VERSION := 1.0.0
.PHONY: build clean
build:
mkdir -p build
gcc -o app
clean:
rm -rf build
"#,
)
.unwrap();
let output = bashrs_cmd()
.arg("make")
.arg("lint")
.arg(&makefile_path)
.output()
.unwrap();
assert!(
output.status.code().unwrap() < 2,
"Clean Makefile should not have errors (exit code should be 0 or 1, got {})",
output.status.code().unwrap()
);
let stdout = String::from_utf8_lossy(&output.stdout);
assert!(
!stdout.to_lowercase().contains("[error]"),
"Clean Makefile should not have errors in output"
);
}
#[test]
fn test_CLI_MAKE_LINT_003_fix_flag_applies_fixes() {
let temp_dir = TempDir::new().unwrap();
let makefile_path = temp_dir.path().join("Makefile");
fs::write(
&makefile_path,
r#"VERSION = $(shell git describe)
SOURCES = $(wildcard src/*.c)
"#,
)
.unwrap();
bashrs_cmd()
.arg("make")
.arg("lint")
.arg(&makefile_path)
.arg("--fix")
.assert()
.success();
let backup_path = makefile_path.with_extension("bak");
assert!(backup_path.exists(), "Backup file should be created");
let fixed_content = fs::read_to_string(&makefile_path).unwrap();
assert!(fixed_content.contains(":="), "Should use := assignment");
assert!(fixed_content.contains("sort"), "Should add sort() wrapper");
}
#[test]
fn test_CLI_MAKE_LINT_004_output_flag_writes_to_file() {
let temp_dir = TempDir::new().unwrap();
let makefile_path = temp_dir.path().join("Makefile");
let output_path = temp_dir.path().join("Makefile.fixed");
fs::write(
&makefile_path,
r#"VERSION = $(shell git describe)
"#,
)
.unwrap();
let result = bashrs_cmd()
.arg("make")
.arg("lint")
.arg(&makefile_path)
.arg("--fix")
.arg("-o")
.arg(&output_path)
.output()
.unwrap();
if result.status.success() || result.status.code() == Some(0) {
assert!(
output_path.exists(),
"Output file should be created on success"
);
let original_content = fs::read_to_string(&makefile_path).unwrap();
assert!(
original_content.contains("VERSION = $(shell"),
"Original should be unchanged"
);
let fixed_content = fs::read_to_string(&output_path).unwrap();
assert!(!fixed_content.is_empty(), "Output file should not be empty");
} else {
let original_content = fs::read_to_string(&makefile_path).unwrap();
assert!(
original_content.contains("VERSION = $(shell"),
"Original should be unchanged"
);
}
}
#[test]
fn test_CLI_MAKE_LINT_005_rules_filter_specific_rules() {
let temp_dir = TempDir::new().unwrap();
let makefile_path = temp_dir.path().join("Makefile");
fs::write(
&makefile_path,
r#"
VERSION = $(shell git describe)
SOURCES = $(wildcard src/*.c)
build:
mkdir build
"#,
)
.unwrap();
let output = bashrs_cmd()
.arg("make")
.arg("lint")
.arg(&makefile_path)
.arg("--rules")
.arg("MAKE001")
.output()
.unwrap();
let stdout = String::from_utf8_lossy(&output.stdout);
assert!(stdout.contains("MAKE001"), "Should show MAKE001 issues");
}
#[test]
fn test_CLI_MAKE_LINT_006_format_json() {
let temp_dir = TempDir::new().unwrap();
let makefile_path = temp_dir.path().join("Makefile");
fs::write(
&makefile_path,
r#"VERSION = $(shell git describe)
"#,
)
.unwrap();
let output = bashrs_cmd()
.arg("make")
.arg("lint")
.arg(&makefile_path)
.arg("--format")
.arg("json")
.output()
.unwrap();
let stdout = String::from_utf8_lossy(&output.stdout);
let json_start = stdout
.lines()
.position(|line| line.trim().starts_with('{') || line.trim().starts_with('['))
.expect("Should find JSON output");
let json_lines: Vec<&str> = stdout.lines().skip(json_start).collect();
let json_output = json_lines.join("\n");
assert!(
json_output.trim().starts_with('{') || json_output.trim().starts_with('['),
"JSON output should start with {{ or [, got: {}",
json_output
);
assert!(stdout.contains("\"code\""), "JSON should have code field");
assert!(
stdout.contains("\"message\""),
"JSON should have message field"
);
}
#[test]
fn test_CLI_MAKE_LINT_007_format_sarif() {
let temp_dir = TempDir::new().unwrap();
let makefile_path = temp_dir.path().join("Makefile");
fs::write(
&makefile_path,
r#"
VERSION = $(shell git describe)
SOURCES = $(wildcard src/*.c)
build:
mkdir build
"#,
)
.unwrap();
bashrs_cmd()
.arg("make")
.arg("lint")
.arg(&makefile_path)
.arg("--format")
.arg("sarif")
.assert()
.failure() .stdout(predicate::str::contains("\"version\"")) .stdout(predicate::str::contains("\"results\"")); }
#[test]
fn test_issue_001_makefile_fix_replaces_not_appends() {
let temp_dir = TempDir::new().unwrap();
let makefile_path = temp_dir.path().join("Makefile");
let original_content = r#"# Test Makefile with intentional issues
VERSION = $(shell git describe)
SOURCES = $(wildcard src/*.c)
build:
mkdir build
gcc $(SOURCES) -o app
clean:
rm -rf $BUILD_DIR
"#;
fs::write(&makefile_path, original_content).unwrap();
bashrs_cmd()
.arg("make")
.arg("lint")
.arg(&makefile_path)
.arg("--fix")
.assert()
.success();
let fixed_content = fs::read_to_string(&makefile_path).unwrap();
assert!(
fixed_content.contains("VERSION := $(shell git describe)"),
"VERSION should use := assignment (REPLACED, not appended)"
);
assert!(
!fixed_content.contains("VERSION VERSION"),
"VERSION should NOT be duplicated (Issue #1 bug)"
);
assert!(
!fixed_content.contains("$(shell git describe) $(shell git describe)"),
"Shell command should NOT be duplicated (Issue #1 bug)"
);
assert!(
fixed_content.contains("SOURCES = $(sort $(wildcard src/*.c))"),
"SOURCES should have sort wrapper (REPLACED correctly)"
);
assert!(
!fixed_content.contains("$(wildcard src/*.c)) src/*.c)"),
"SOURCES should NOT have malformed appended text (Issue #1 bug)"
);
assert!(
fixed_content.contains("mkdir -p build"),
"mkdir should become mkdir -p for idempotency"
);
assert!(
fixed_content.contains("\"$BUILD_DIR\"") || fixed_content.contains("\"${BUILD_DIR}\""),
"Unquoted variable $BUILD_DIR should be quoted (at least once, even if duplicated)"
);
println!(
"\n=== FIXED CONTENT ===\n{}\n=====================\n",
fixed_content
);
}