use assert_cmd::cargo::cargo_bin_cmd;
use predicates::prelude::*;
use tempfile::TempDir;
#[test]
fn test_exit_code_0_success() {
let temp_dir = TempDir::new().unwrap();
std::fs::write(
temp_dir.path().join("README.md"),
"# Project Title\n\nThis is a sample README file.\n",
)
.unwrap();
std::fs::write(
temp_dir.path().join("config.json"),
"{\n \"setting\": \"value\"\n}\n",
)
.unwrap();
std::fs::write(
temp_dir.path().join("script.py"),
"#!/usr/bin/env python3\nprint('Hello, World!')\n",
)
.unwrap();
let mut cmd = cargo_bin_cmd!("lineguard");
cmd.arg(temp_dir.path().join("README.md"))
.assert()
.success()
.code(0);
let mut cmd = cargo_bin_cmd!("lineguard");
cmd.arg(temp_dir.path()).assert().success().code(0);
let mut cmd = cargo_bin_cmd!("lineguard");
cmd.arg("--fix")
.arg(temp_dir.path())
.assert()
.success()
.code(0);
let mut cmd = cargo_bin_cmd!("lineguard");
cmd.arg("--extensions")
.arg("md,json")
.arg(temp_dir.path())
.assert()
.success()
.code(0);
}
#[test]
fn test_exit_code_1_issues_found() {
let temp_dir = TempDir::new().unwrap();
std::fs::write(
temp_dir.path().join("README.md"),
"# Project\n\nDescription without final newline",
)
.unwrap();
std::fs::write(
temp_dir.path().join("config.yaml"),
"database:\n host: localhost \n port: 5432\n",
)
.unwrap(); std::fs::write(
temp_dir.path().join("script.sh"),
"#!/bin/bash\necho 'Hello World'",
)
.unwrap();
let mut cmd = cargo_bin_cmd!("lineguard");
cmd.arg(temp_dir.path().join("README.md"))
.assert()
.failure()
.code(1);
let mut cmd = cargo_bin_cmd!("lineguard");
cmd.arg(temp_dir.path().join("config.yaml"))
.assert()
.failure()
.code(1);
let mut cmd = cargo_bin_cmd!("lineguard");
cmd.arg(temp_dir.path()).assert().failure().code(1);
std::fs::write(
temp_dir.path().join("good_file.txt"),
"This file is properly formatted.\n",
)
.unwrap();
let mut cmd = cargo_bin_cmd!("lineguard");
cmd.arg(temp_dir.path()).assert().failure().code(1);
let mut cmd = cargo_bin_cmd!("lineguard");
cmd.arg("--extensions")
.arg("md,yaml")
.arg(temp_dir.path())
.assert()
.failure()
.code(1);
}
#[test]
fn test_exit_code_2_invalid_arguments() {
let mut cmd = cargo_bin_cmd!("lineguard");
cmd.arg("--format")
.arg("invalid_format")
.assert()
.failure()
.code(2);
let mut cmd = cargo_bin_cmd!("lineguard");
cmd.arg("--invalid-option").assert().failure().code(2);
let mut cmd = cargo_bin_cmd!("lineguard");
cmd.arg("--format").assert().failure().code(2);
}
#[test]
fn test_exit_code_3_file_io_error() {
let temp_dir = TempDir::new().unwrap();
let mut cmd = cargo_bin_cmd!("lineguard");
cmd.arg("--quiet")
.arg(temp_dir.path().join("nonexistent_pattern_*.txt"))
.assert()
.success()
.code(0);
let mut cmd = cargo_bin_cmd!("lineguard");
cmd.current_dir(temp_dir.path())
.arg("--from")
.arg("HEAD~1")
.assert()
.failure()
.code(3)
.stderr(predicate::str::contains("Error"));
let mut cmd = cargo_bin_cmd!("lineguard");
cmd.current_dir(temp_dir.path())
.arg("--from")
.arg("nonexistent_commit_hash_12345")
.assert()
.failure()
.code(3)
.stderr(predicate::str::contains("Error"));
}
#[test]
fn test_exit_code_4_configuration_error() {
let temp_dir = TempDir::new().unwrap();
std::fs::write(temp_dir.path().join("test.txt"), "content\n").unwrap();
let config_path = temp_dir.path().join("invalid_syntax.toml");
std::fs::write(&config_path, "invalid toml syntax [[[").unwrap();
let mut cmd = cargo_bin_cmd!("lineguard");
cmd.arg("--config")
.arg(&config_path)
.arg(temp_dir.path())
.assert()
.failure()
.code(4)
.stderr(predicate::str::contains("Error loading configuration"));
let malformed_config = temp_dir.path().join("malformed.toml");
std::fs::write(
&malformed_config,
"[section\nmissing_closing_bracket = true",
)
.unwrap();
let mut cmd = cargo_bin_cmd!("lineguard");
cmd.arg("--config")
.arg(&malformed_config)
.arg(temp_dir.path())
.assert()
.failure()
.code(4)
.stderr(predicate::str::contains("Error loading configuration"));
let non_existent_config = temp_dir.path().join("does_not_exist.toml");
let mut cmd = cargo_bin_cmd!("lineguard");
cmd.arg("--config")
.arg(&non_existent_config)
.arg(temp_dir.path())
.assert()
.failure()
.code(4)
.stderr(predicate::str::contains("Error loading configuration"));
}
#[test]
fn test_exit_code_1_fix_mode_with_errors() {
let temp_dir = TempDir::new().unwrap();
let file_path = temp_dir.path().join("readonly.txt");
std::fs::write(&file_path, "content without newline").unwrap();
#[cfg(unix)]
{
use std::os::unix::fs::PermissionsExt;
let mut perms = std::fs::metadata(&file_path).unwrap().permissions();
perms.set_mode(0o444);
std::fs::set_permissions(&file_path, perms).unwrap();
}
#[cfg(windows)]
{
let mut perms = std::fs::metadata(&file_path).unwrap().permissions();
perms.set_readonly(true);
std::fs::set_permissions(&file_path, perms).unwrap();
}
let mut cmd = cargo_bin_cmd!("lineguard");
cmd.arg("--fix").arg(&file_path).assert().failure().code(1);
}
#[test]
#[cfg(unix)]
fn test_permission_errors_still_exit_0() {
use std::os::unix::fs::PermissionsExt;
let temp_dir = TempDir::new().unwrap();
let file_path = temp_dir.path().join("unreadable.txt");
std::fs::write(&file_path, "content\n").unwrap();
let mut perms = std::fs::metadata(&file_path).unwrap().permissions();
perms.set_mode(0o000);
std::fs::set_permissions(&file_path, perms).unwrap();
let mut cmd = cargo_bin_cmd!("lineguard");
cmd.arg(&file_path).assert().success().code(0);
let mut perms = std::fs::metadata(&file_path).unwrap().permissions();
perms.set_mode(0o644);
std::fs::set_permissions(&file_path, perms).unwrap();
}
#[test]
fn test_stdin_mode_exit_codes() {
use std::io::Write;
use std::process::{Command, Stdio};
let temp_dir = TempDir::new().unwrap();
let good_file1 = temp_dir.path().join("good1.txt");
let good_file2 = temp_dir.path().join("good2.txt");
std::fs::write(&good_file1, "content with newline\n").unwrap();
std::fs::write(&good_file2, "more content with newline\n").unwrap();
let mut child = Command::new(assert_cmd::cargo::cargo_bin!("lineguard"))
.arg("--stdin")
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.spawn()
.unwrap();
{
let mut stdin = child.stdin.take().unwrap();
let input = format!("{}\n{}\n", good_file1.display(), good_file2.display());
stdin.write_all(input.as_bytes()).unwrap();
}
let output = child.wait_with_output().unwrap();
assert_eq!(output.status.code(), Some(0));
let bad_file1 = temp_dir.path().join("bad1.txt");
let bad_file2 = temp_dir.path().join("bad2.txt");
std::fs::write(&bad_file1, "no newline").unwrap();
std::fs::write(&bad_file2, "trailing spaces \n").unwrap();
let mut child = Command::new(assert_cmd::cargo::cargo_bin!("lineguard"))
.arg("--stdin")
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.spawn()
.unwrap();
{
let mut stdin = child.stdin.take().unwrap();
let input = format!("{}\n{}\n", bad_file1.display(), bad_file2.display());
stdin.write_all(input.as_bytes()).unwrap();
}
let output = child.wait_with_output().unwrap();
assert_eq!(output.status.code(), Some(1));
let mut child = Command::new(assert_cmd::cargo::cargo_bin!("lineguard"))
.arg("--stdin")
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.spawn()
.unwrap();
{
let mut stdin = child.stdin.take().unwrap();
let input = format!("{}\n{}\n", good_file1.display(), bad_file1.display());
stdin.write_all(input.as_bytes()).unwrap();
}
let output = child.wait_with_output().unwrap();
assert_eq!(output.status.code(), Some(1));
}
#[test]
fn test_dry_run_exit_codes() {
let temp_dir = TempDir::new().unwrap();
let file_path = temp_dir.path().join("fix_me.txt");
std::fs::write(&file_path, "content ").unwrap();
let mut cmd = cargo_bin_cmd!("lineguard");
cmd.arg("--fix")
.arg("--dry-run")
.arg(&file_path)
.assert()
.success()
.code(0)
.stdout(predicate::str::contains("Would fix"));
}
#[test]
fn test_quiet_mode_exit_codes() {
let temp_dir = TempDir::new().unwrap();
std::fs::write(temp_dir.path().join("bad.txt"), "no newline").unwrap();
let mut cmd = cargo_bin_cmd!("lineguard");
cmd.arg("--quiet")
.arg(temp_dir.path())
.assert()
.failure()
.code(1);
}
#[test]
fn test_git_range_exit_codes() {
let temp_dir = TempDir::new().unwrap();
let mut cmd = cargo_bin_cmd!("lineguard");
cmd.current_dir(temp_dir.path())
.arg("--from")
.arg("HEAD~1")
.assert()
.failure()
.code(3)
.stderr(predicate::str::contains("Error"));
let mut cmd = cargo_bin_cmd!("lineguard");
cmd.current_dir(temp_dir.path())
.arg("--from")
.arg("invalid_commit_ref")
.assert()
.failure()
.code(3);
}
#[test]
fn test_multiple_error_conditions() {
let temp_dir = TempDir::new().unwrap();
let config_path = temp_dir.path().join("bad.toml");
std::fs::write(&config_path, "invalid [[[").unwrap();
std::fs::write(temp_dir.path().join("bad.txt"), "no newline").unwrap();
let mut cmd = cargo_bin_cmd!("lineguard");
cmd.arg("--config")
.arg(&config_path)
.arg(temp_dir.path())
.assert()
.failure()
.code(4);
}
#[test]
fn test_realistic_project_scenarios() {
let temp_dir = TempDir::new().unwrap();
let src_dir = temp_dir.path().join("src");
let docs_dir = temp_dir.path().join("docs");
std::fs::create_dir(&src_dir).unwrap();
std::fs::create_dir(&docs_dir).unwrap();
std::fs::write(
src_dir.join("main.rs"),
"fn main() {\n println!(\"Hello, world!\");\n}\n",
)
.unwrap();
std::fs::write(
temp_dir.path().join("Cargo.toml"),
"[package]\nname = \"test\"\nversion = \"0.1.0\"\n",
)
.unwrap();
std::fs::write(
docs_dir.join("README.md"),
"# Project\n\nDocumentation without newline",
)
.unwrap();
std::fs::write(
src_dir.join("lib.rs"),
"pub fn hello() { \n println!(\"Hello\");\n}",
)
.unwrap(); std::fs::write(
temp_dir.path().join("config.txt"),
"configuration without newline",
)
.unwrap();
let mut cmd = cargo_bin_cmd!("lineguard");
cmd.arg("--recursive")
.arg(temp_dir.path())
.assert()
.failure()
.code(1);
let mut cmd = cargo_bin_cmd!("lineguard");
cmd.arg("--extensions")
.arg("toml")
.arg(temp_dir.path())
.assert()
.success()
.code(0);
let mut cmd = cargo_bin_cmd!("lineguard");
cmd.arg(temp_dir.path().join("config.txt"))
.assert()
.failure()
.code(1);
}
#[test]
fn test_fix_mode_realistic_scenarios() {
let temp_dir = TempDir::new().unwrap();
std::fs::write(
temp_dir.path().join("fixable1.txt"),
"content without newline",
)
.unwrap();
std::fs::write(
temp_dir.path().join("fixable2.py"),
"#!/usr/bin/env python3\nprint('test') \n",
)
.unwrap();
let mut cmd = cargo_bin_cmd!("lineguard");
cmd.arg("--fix")
.arg(temp_dir.path())
.assert()
.success()
.code(0);
let content1 = std::fs::read_to_string(temp_dir.path().join("fixable1.txt")).unwrap();
assert_eq!(content1, "content without newline\n");
let content2 = std::fs::read_to_string(temp_dir.path().join("fixable2.py")).unwrap();
assert_eq!(content2, "#!/usr/bin/env python3\nprint('test')\n");
}
#[test]
fn test_extension_filtering_exit_codes() {
let temp_dir = TempDir::new().unwrap();
std::fs::write(temp_dir.path().join("good.txt"), "good content\n").unwrap();
std::fs::write(temp_dir.path().join("bad.txt"), "bad content").unwrap(); std::fs::write(temp_dir.path().join("good.md"), "# Good markdown\n").unwrap();
std::fs::write(temp_dir.path().join("bad.md"), "# Bad markdown").unwrap();
let mut cmd = cargo_bin_cmd!("lineguard");
cmd.arg("--extensions")
.arg("txt")
.arg(temp_dir.path().join("good.txt"))
.assert()
.success()
.code(0);
let mut cmd = cargo_bin_cmd!("lineguard");
cmd.arg("--extensions")
.arg("md")
.arg(temp_dir.path().join("bad.md"))
.assert()
.failure()
.code(1);
let mut cmd = cargo_bin_cmd!("lineguard");
cmd.arg("--extensions")
.arg("txt,md")
.arg(temp_dir.path())
.assert()
.failure()
.code(1);
}