#![allow(deprecated)]
#![allow(clippy::unwrap_used)] #![allow(clippy::expect_used)]
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")
}
fn create_messy_bashrc(dir: &TempDir) -> std::path::PathBuf {
let bashrc_path = dir.path().join(".bashrc");
let content = r#"# Messy .bashrc with duplicates
export PATH="/usr/local/bin:$PATH"
export PATH="/opt/homebrew/bin:$PATH"
export PATH="/usr/local/bin:$PATH"
export EDITOR=vim
alias ll='ls -la'
"#;
fs::write(&bashrc_path, content).expect("Failed to write fixture");
bashrc_path
}
#[test]
fn test_config_analyze_basic() {
let temp_dir = TempDir::new().unwrap();
let bashrc = create_messy_bashrc(&temp_dir);
bashrs_cmd()
.arg("config")
.arg("analyze")
.arg(&bashrc)
.assert()
.success()
.stdout(predicate::str::contains("Analysis:"))
.stdout(predicate::str::contains("CONFIG-001")); }
#[test]
fn test_config_analyze_shows_issues_count() {
let temp_dir = TempDir::new().unwrap();
let bashrc = create_messy_bashrc(&temp_dir);
bashrs_cmd()
.arg("config")
.arg("analyze")
.arg(&bashrc)
.assert()
.success()
.stdout(predicate::str::contains("Issues Found:")); }
#[test]
fn test_config_analyze_shows_path_entries() {
let temp_dir = TempDir::new().unwrap();
let bashrc = create_messy_bashrc(&temp_dir);
bashrs_cmd()
.arg("config")
.arg("analyze")
.arg(&bashrc)
.assert()
.success()
.stdout(predicate::str::contains("/usr/local/bin"))
.stdout(predicate::str::contains("/opt/homebrew/bin"));
}
#[test]
fn test_config_analyze_nonexistent_file() {
bashrs_cmd()
.arg("config")
.arg("analyze")
.arg("/nonexistent/.bashrc")
.assert()
.failure()
.stderr(predicate::str::contains("error").or(predicate::str::contains("No such file")));
}
#[test]
fn test_config_lint_detects_duplicates() {
let temp_dir = TempDir::new().unwrap();
let bashrc = create_messy_bashrc(&temp_dir);
bashrs_cmd()
.arg("config")
.arg("lint")
.arg(&bashrc)
.assert()
.code(1) .stdout(predicate::str::contains("CONFIG-001"))
.stdout(predicate::str::contains("Duplicate PATH entry"));
}
#[test]
fn test_config_lint_clean_file_exits_zero() {
let temp_dir = TempDir::new().unwrap();
let bashrc = temp_dir.path().join(".bashrc");
fs::write(
&bashrc,
r#"export PATH="/usr/local/bin:$PATH"
export EDITOR=vim
"#,
)
.unwrap();
bashrs_cmd()
.arg("config")
.arg("lint")
.arg(&bashrc)
.assert()
.success() .stdout(predicate::str::contains("No issues found"));
}
#[test]
fn test_config_lint_json_format() {
let temp_dir = TempDir::new().unwrap();
let bashrc = create_messy_bashrc(&temp_dir);
bashrs_cmd()
.arg("config")
.arg("lint")
.arg(&bashrc)
.arg("--format=json")
.assert()
.code(1)
.stdout(predicate::str::contains(r#""rule_id""#))
.stdout(predicate::str::contains("CONFIG-001"));
}
#[test]
fn test_config_purify_dry_run() {
let temp_dir = TempDir::new().unwrap();
let bashrc = create_messy_bashrc(&temp_dir);
bashrs_cmd()
.arg("config")
.arg("purify")
.arg(&bashrc)
.assert()
.success()
.stdout(predicate::str::contains("Preview of changes"))
.stdout(predicate::str::contains("CONFIG-001"));
let content = fs::read_to_string(&bashrc).unwrap();
assert!(content.contains("export PATH=\"/usr/local/bin:$PATH\""));
let path_count = content
.lines()
.filter(|l| l.contains("export PATH="))
.count();
assert_eq!(path_count, 3);
}
#[test]
fn test_config_purify_with_fix() {
let temp_dir = TempDir::new().unwrap();
let bashrc = create_messy_bashrc(&temp_dir);
let _backup_pattern = format!("{}.bak.*", bashrc.display());
bashrs_cmd()
.arg("config")
.arg("purify")
.arg(&bashrc)
.arg("--fix")
.assert()
.success()
.stdout(predicate::str::contains("Applying"))
.stdout(predicate::str::contains("fixes"))
.stdout(predicate::str::contains("Backup:"));
let content = fs::read_to_string(&bashrc).unwrap();
let path_count = content
.lines()
.filter(|l| l.contains("export PATH="))
.count();
assert_eq!(path_count, 2, "Should have only 2 unique PATH entries");
let backup_files: Vec<_> = fs::read_dir(temp_dir.path())
.unwrap()
.filter_map(|e| e.ok())
.filter(|e| e.file_name().to_str().unwrap().contains(".bak"))
.collect();
assert_eq!(backup_files.len(), 1, "Should create exactly one backup");
}
#[test]
fn test_config_purify_no_backup_flag() {
let temp_dir = TempDir::new().unwrap();
let bashrc = create_messy_bashrc(&temp_dir);
bashrs_cmd()
.arg("config")
.arg("purify")
.arg(&bashrc)
.arg("--fix")
.arg("--no-backup")
.assert()
.success();
let backup_files: Vec<_> = fs::read_dir(temp_dir.path())
.unwrap()
.filter_map(|e| e.ok())
.filter(|e| e.file_name().to_str().unwrap().contains(".bak"))
.collect();
assert_eq!(backup_files.len(), 0, "Should not create backup");
}
#[test]
fn test_config_purify_output_to_stdout() {
let temp_dir = TempDir::new().unwrap();
let bashrc = create_messy_bashrc(&temp_dir);
bashrs_cmd()
.arg("config")
.arg("purify")
.arg(&bashrc)
.arg("--output")
.arg("-")
.assert()
.success()
.stdout(predicate::str::contains("export PATH="));
let content = fs::read_to_string(&bashrc).unwrap();
let path_count = content
.lines()
.filter(|l| l.contains("export PATH="))
.count();
assert_eq!(path_count, 3, "Original file should be unchanged");
}
#[test]
fn test_config_purify_output_to_file() {
let temp_dir = TempDir::new().unwrap();
let bashrc = create_messy_bashrc(&temp_dir);
let output_file = temp_dir.path().join(".bashrc.purified");
bashrs_cmd()
.arg("config")
.arg("purify")
.arg(&bashrc)
.arg("--output")
.arg(&output_file)
.assert()
.success();
assert!(output_file.exists(), "Output file should be created");
let content = fs::read_to_string(&output_file).unwrap();
let path_count = content
.lines()
.filter(|l| l.contains("export PATH="))
.count();
assert_eq!(path_count, 2, "Output should have deduplicated PATH");
let original = fs::read_to_string(&bashrc).unwrap();
let original_count = original
.lines()
.filter(|l| l.contains("export PATH="))
.count();
assert_eq!(original_count, 3, "Original should be unchanged");
}
#[test]
fn test_config_with_real_fixture() {
let fixture_path = "tests/fixtures/configs/messy-bashrc.sh";
if !std::path::Path::new(fixture_path).exists() {
eprintln!("Skipping test - fixture not found: {}", fixture_path);
return;
}
bashrs_cmd()
.arg("config")
.arg("analyze")
.arg(fixture_path)
.assert()
.success()
.stdout(predicate::str::contains("CONFIG-001"))
.stdout(predicate::str::contains("Issues Found:"));
bashrs_cmd()
.arg("config")
.arg("lint")
.arg(fixture_path)
.assert()
.code(1) .stdout(predicate::str::contains("CONFIG-001"));
}
#[test]
fn test_config_missing_subcommand() {
bashrs_cmd()
.arg("config")
.assert()
.failure()
.stderr(predicate::str::contains("required").or(predicate::str::contains("COMMAND")));
}
#[test]
fn test_config_invalid_subcommand() {
bashrs_cmd()
.arg("config")
.arg("invalid")
.assert()
.failure()
.stderr(predicate::str::contains("unrecognized").or(predicate::str::contains("invalid")));
}
#[test]
fn test_config_help() {
bashrs_cmd()
.arg("config")
.arg("--help")
.assert()
.success()
.stdout(predicate::str::contains("analyze"))
.stdout(predicate::str::contains("lint"))
.stdout(predicate::str::contains("purify"));
}