use super::*;
#[test]
fn test_tidy_check_detects_missing_newline() {
let dir = TempDir::new().unwrap();
let file = dir.path().join("test.txt");
fs::write(&file, "no trailing newline").unwrap();
Command::cargo_bin("patchloom")
.unwrap()
.arg("tidy")
.arg("check")
.arg(&file)
.assert()
.code(2);
}
#[test]
fn test_tidy_fix_apply() {
let dir = TempDir::new().unwrap();
let file = dir.path().join("test.txt");
fs::write(&file, "no trailing newline").unwrap();
Command::cargo_bin("patchloom")
.unwrap()
.arg("tidy")
.arg("fix")
.arg(&file)
.arg("--ensure-final-newline")
.arg("--apply")
.assert()
.code(0);
let content = fs::read(&file).unwrap();
assert!(
content.ends_with(b"\n"),
"file should end with a newline after fix"
);
}
#[test]
fn test_tidy_check_json_output() {
let dir = TempDir::new().unwrap();
let file = dir.path().join("test.txt");
fs::write(&file, "trailing spaces \nno final newline").unwrap();
let output = Command::cargo_bin("patchloom")
.unwrap()
.arg("tidy")
.arg("check")
.arg(&file)
.arg("--json")
.output()
.unwrap();
assert_eq!(output.status.code(), Some(2));
let json: serde_json::Value = serde_json::from_slice(&output.stdout).unwrap();
assert_eq!(json["ok"], false);
assert!(json["issue_count"].as_u64().unwrap() >= 2);
assert!(json["issues"].is_array());
assert_eq!(
json["error_kind"], "changes_detected",
"tidy check with issues must set error_kind: {json}"
);
assert_eq!(json["status"], "changes_detected");
}
#[test]
fn test_tidy_check_jsonl_output() {
let dir = TempDir::new().unwrap();
let a = dir.path().join("a.txt");
let b = dir.path().join("b.txt");
fs::write(&a, "trailing \n").unwrap();
fs::write(&b, "no final newline").unwrap();
let output = Command::cargo_bin("patchloom")
.unwrap()
.arg("tidy")
.arg("check")
.arg(&a)
.arg(&b)
.arg("--jsonl")
.output()
.unwrap();
assert_eq!(output.status.code(), Some(2));
let stdout = String::from_utf8_lossy(&output.stdout);
let lines: Vec<serde_json::Value> = stdout
.lines()
.filter(|l| !l.is_empty())
.map(|l| serde_json::from_str(l).expect("each line should be valid JSON"))
.collect();
assert!(
lines.len() >= 2,
"should have at least one JSONL line per issue"
);
for line in &lines {
assert!(line["path"].is_string());
assert!(line["issue"].is_string());
}
}
#[test]
fn test_tidy_check_exits_0_when_clean() {
let dir = TempDir::new().unwrap();
let file = dir.path().join("test.txt");
fs::write(&file, "clean file\n").unwrap();
Command::cargo_bin("patchloom")
.unwrap()
.arg("tidy")
.arg("check")
.arg(&file)
.assert()
.code(0);
}
#[test]
fn test_tidy_fix_check_exits_2_no_write() {
let dir = TempDir::new().unwrap();
let file = dir.path().join("test.txt");
fs::write(&file, "trailing whitespace ").unwrap();
Command::cargo_bin("patchloom")
.unwrap()
.arg("tidy")
.arg("fix")
.arg(&file)
.arg("--ensure-final-newline")
.arg("--check")
.assert()
.code(2);
let content = fs::read_to_string(&file).unwrap();
assert_eq!(
content, "trailing whitespace ",
"file should be unchanged in --check mode"
);
}
#[test]
fn test_tidy_fix_json_output() {
let dir = TempDir::new().unwrap();
let file = dir.path().join("test.txt");
fs::write(&file, "trailing \nno final newline").unwrap();
let output = Command::cargo_bin("patchloom")
.unwrap()
.args([
"tidy",
"fix",
".",
"--ensure-final-newline",
"--trim-trailing-whitespace",
"--json",
])
.current_dir(dir.path())
.output()
.unwrap();
assert_eq!(
output.status.code(),
Some(2),
"expected exit 2 (changes detected)"
);
let json: serde_json::Value =
serde_json::from_slice(&output.stdout).expect("stdout should be valid JSON");
assert_eq!(json["ok"], true);
assert!(json["files_changed"].as_u64().unwrap() >= 1);
assert!(!json["files"].as_array().unwrap().is_empty());
assert!(
json["diff"].is_string(),
"diff field should be present in default mode"
);
}
#[test]
fn test_tidy_fix_jsonl_output() {
let dir = TempDir::new().unwrap();
let file = dir.path().join("test.txt");
fs::write(&file, "trailing \n").unwrap();
let output = Command::cargo_bin("patchloom")
.unwrap()
.args(["tidy", "fix", ".", "--trim-trailing-whitespace", "--jsonl"])
.current_dir(dir.path())
.output()
.unwrap();
assert_eq!(output.status.code(), Some(2));
let stdout = String::from_utf8_lossy(&output.stdout);
let lines: Vec<&str> = stdout.trim().lines().collect();
assert!(!lines.is_empty(), "expected at least one JSONL line");
for line in &lines {
let v: serde_json::Value =
serde_json::from_str(line).expect("each JSONL line should be valid JSON");
assert!(v["path"].is_string());
}
}
#[test]
fn test_tidy_fix_json_check_mode() {
let dir = TempDir::new().unwrap();
let file = dir.path().join("dirty.txt");
fs::write(&file, "no newline").unwrap();
let output = Command::cargo_bin("patchloom")
.unwrap()
.args([
"tidy",
"fix",
".",
"--ensure-final-newline",
"--check",
"--json",
])
.current_dir(dir.path())
.output()
.unwrap();
assert_eq!(output.status.code(), Some(2));
let json: serde_json::Value =
serde_json::from_slice(&output.stdout).expect("stdout should be valid JSON");
assert_eq!(json["ok"], true);
assert!(json["files_changed"].as_u64().unwrap() >= 1);
assert!(
json["diff"].is_null(),
"diff should be absent in --check mode"
);
}
#[test]
fn test_tidy_fix_json_no_changes() {
let dir = TempDir::new().unwrap();
let file = dir.path().join("clean.txt");
fs::write(&file, "already clean\n").unwrap();
let output = Command::cargo_bin("patchloom")
.unwrap()
.args(["tidy", "fix", ".", "--ensure-final-newline", "--json"])
.current_dir(dir.path())
.output()
.unwrap();
assert!(output.status.success());
let json: serde_json::Value =
serde_json::from_slice(&output.stdout).expect("stdout should be valid JSON");
assert_eq!(json["ok"], true);
assert_eq!(json["files_changed"], 0);
assert!(json["files"].as_array().unwrap().is_empty());
}
#[test]
fn test_tidy_skips_binary_files() {
let dir = TempDir::new().unwrap();
let text_file = dir.path().join("text.txt");
fs::write(&text_file, "hello \n").unwrap();
let bin_file = dir.path().join("data.bin");
let mut bin_content = b"hello ".to_vec();
bin_content.push(0);
fs::write(&bin_file, &bin_content).unwrap();
Command::cargo_bin("patchloom")
.unwrap()
.arg("tidy")
.arg("fix")
.arg(dir.path().to_str().unwrap())
.arg("--trim-trailing-whitespace")
.arg("--apply")
.assert()
.code(0);
let text_content = fs::read_to_string(&text_file).unwrap();
assert_eq!(text_content, "hello\n");
let bin_after = fs::read(&bin_file).unwrap();
assert_eq!(bin_after, bin_content, "binary file should not be modified");
}
#[test]
fn test_tidy_check_sole_unreadable_is_invalid_input() {
let dir = TempDir::new().unwrap();
let file = dir.path().join("locked.txt");
fs::write(&file, "trail \n").unwrap();
#[cfg(unix)]
{
use std::os::unix::fs::PermissionsExt;
fs::set_permissions(&file, fs::Permissions::from_mode(0o000)).unwrap();
if fs::read_to_string(&file).is_ok() {
fs::set_permissions(&file, fs::Permissions::from_mode(0o644)).unwrap();
return;
}
let out = Command::cargo_bin("patchloom")
.unwrap()
.args(["--json", "--cwd"])
.arg(dir.path())
.args(["tidy", "check", "locked.txt"])
.output()
.unwrap();
let _ = fs::set_permissions(&file, fs::Permissions::from_mode(0o644));
assert_eq!(
out.status.code(),
Some(1),
"stdout={} stderr={}",
String::from_utf8_lossy(&out.stdout),
String::from_utf8_lossy(&out.stderr)
);
let v: serde_json::Value = serde_json::from_slice(&out.stdout).unwrap();
assert_eq!(v["error_kind"], "invalid_input", "{v}");
assert_ne!(v["ok"], true, "must not report vacuous clean: {v}");
}
}
#[test]
fn test_tidy_check_files_from_sole_binary_is_binary_error_kind() {
let dir = TempDir::new().unwrap();
fs::write(dir.path().join("data.bin"), b"x\x00y").unwrap();
fs::write(dir.path().join("list.txt"), "data.bin\n").unwrap();
let out = Command::cargo_bin("patchloom")
.unwrap()
.args(["--json", "--cwd"])
.arg(dir.path())
.args(["--files-from", "list.txt", "tidy", "check"])
.output()
.unwrap();
assert_eq!(
out.status.code(),
Some(1),
"stdout={} stderr={}",
String::from_utf8_lossy(&out.stdout),
String::from_utf8_lossy(&out.stderr)
);
let v: serde_json::Value = serde_json::from_slice(&out.stdout).unwrap();
assert_eq!(v["error_kind"], "binary", "{v}");
assert!(v["error"].as_str().unwrap_or("").contains("binary"), "{v}");
}
#[test]
fn test_tidy_check_files_from_multi_invalid_utf8_refused() {
let dir = TempDir::new().unwrap();
fs::write(dir.path().join("ok.txt"), "hello\n").unwrap();
fs::write(dir.path().join("bad.txt"), b"hello \xff\n").unwrap();
fs::write(dir.path().join("list.txt"), "ok.txt\nbad.txt\n").unwrap();
let out = Command::cargo_bin("patchloom")
.unwrap()
.args(["--json", "--cwd"])
.arg(dir.path())
.args(["--files-from", "list.txt", "tidy", "check"])
.output()
.unwrap();
assert_eq!(
out.status.code(),
Some(0),
"stdout={} stderr={}",
String::from_utf8_lossy(&out.stdout),
String::from_utf8_lossy(&out.stderr)
);
let v: serde_json::Value = serde_json::from_slice(&out.stdout).unwrap();
let refused = v["refused"]
.as_array()
.expect("refused[] for non-text files-from co-path");
assert!(
refused.iter().any(|r| {
r["path"].as_str() == Some("bad.txt") && r["reason"].as_str() == Some("invalid_utf8")
}),
"{v}"
);
}
#[test]
fn test_tidy_skips_invalid_utf8_files() {
let dir = TempDir::new().unwrap();
let text_file = dir.path().join("text.txt");
fs::write(&text_file, "hello \n").unwrap();
let invalid_file = dir.path().join("invalid.txt");
let invalid_content = b"hello\xff ".to_vec();
fs::write(&invalid_file, &invalid_content).unwrap();
let output = Command::cargo_bin("patchloom")
.unwrap()
.arg("tidy")
.arg("fix")
.arg(dir.path().to_str().unwrap())
.arg("--trim-trailing-whitespace")
.arg("--ensure-final-newline")
.arg("--apply")
.output()
.unwrap();
assert!(output.status.success());
let text_content = fs::read_to_string(&text_file).unwrap();
assert_eq!(text_content, "hello\n");
let invalid_after = fs::read(&invalid_file).unwrap();
assert_eq!(
invalid_after, invalid_content,
"invalid UTF-8 file should not be modified"
);
let stderr = String::from_utf8_lossy(&output.stderr);
assert!(
stderr.contains("invalid.txt (invalid UTF-8)"),
"expected tidy invalid UTF-8 warning, got: {stderr}"
);
}
#[cfg(unix)]
#[test]
fn test_tidy_apply_readonly_dir_fails_gracefully() {
use std::os::unix::fs::PermissionsExt;
let dir = TempDir::new().unwrap();
let sub = dir.path().join("locked");
fs::create_dir(&sub).unwrap();
let file = sub.join("target.txt");
fs::write(&file, "trailing spaces \n").unwrap();
if !readonly_dir_blocks_writes(&sub) {
return;
}
let output = Command::cargo_bin("patchloom")
.unwrap()
.arg("tidy")
.arg("fix")
.arg(&file)
.arg("--trim-trailing-whitespace")
.arg("--apply")
.output()
.unwrap();
assert_ne!(
output.status.code(),
Some(0),
"write in readonly dir should fail"
);
let content = fs::read_to_string(&file).unwrap();
assert_eq!(content, "trailing spaces \n", "file should be unchanged");
fs::set_permissions(&sub, fs::Permissions::from_mode(0o755)).unwrap();
}
#[test]
fn test_tidy_fix_dedent_auto_apply() {
let dir = TempDir::new().unwrap();
let file = dir.path().join("indented.txt");
fs::write(&file, " line1\n line2\n line3\n").unwrap();
Command::cargo_bin("patchloom")
.unwrap()
.args(["tidy", "fix", ".", "--dedent", "auto", "--apply"])
.current_dir(dir.path())
.assert()
.code(0);
let content = fs::read_to_string(&file).unwrap();
assert_eq!(content, "line1\n line2\nline3\n");
}
#[test]
fn test_tidy_fix_dedent_numeric_apply() {
let dir = TempDir::new().unwrap();
let file = dir.path().join("indented.txt");
fs::write(&file, " line1\n line2\n").unwrap();
Command::cargo_bin("patchloom")
.unwrap()
.args(["tidy", "fix", ".", "--dedent", "4", "--apply"])
.current_dir(dir.path())
.assert()
.code(0);
let content = fs::read_to_string(&file).unwrap();
assert_eq!(content, " line1\nline2\n");
}
#[test]
fn test_tidy_fix_indent_numeric_apply() {
let dir = TempDir::new().unwrap();
let file = dir.path().join("flat.txt");
fs::write(&file, "line1\nline2\n").unwrap();
Command::cargo_bin("patchloom")
.unwrap()
.args(["tidy", "fix", ".", "--indent", "4", "--apply"])
.current_dir(dir.path())
.assert()
.code(0);
let content = fs::read_to_string(&file).unwrap();
assert_eq!(content, " line1\n line2\n");
}
#[test]
fn test_tidy_fix_indent_tab_apply() {
let dir = TempDir::new().unwrap();
let file = dir.path().join("flat.txt");
fs::write(&file, "line1\nline2\n").unwrap();
Command::cargo_bin("patchloom")
.unwrap()
.args(["tidy", "fix", ".", "--indent", "tab", "--apply"])
.current_dir(dir.path())
.assert()
.code(0);
let content = fs::read_to_string(&file).unwrap();
assert_eq!(content, "\tline1\n\tline2\n");
}
#[test]
fn test_tidy_fix_dedent_with_line_range() {
let dir = TempDir::new().unwrap();
let file = dir.path().join("ranged.txt");
fs::write(&file, " line1\n line2\n line3\n line4\n").unwrap();
Command::cargo_bin("patchloom")
.unwrap()
.args([
"tidy", "fix", ".", "--dedent", "4", "--lines", "2:3", "--apply",
])
.current_dir(dir.path())
.assert()
.code(0);
let content = fs::read_to_string(&file).unwrap();
assert_eq!(content, " line1\nline2\nline3\n line4\n");
}
#[test]
fn test_tidy_fix_dedent_with_line_range_dash_separator() {
let dir = TempDir::new().unwrap();
let file = dir.path().join("ranged_dash.txt");
fs::write(&file, " line1\n line2\n line3\n line4\n").unwrap();
Command::cargo_bin("patchloom")
.unwrap()
.args([
"tidy", "fix", ".", "--dedent", "4", "--lines", "2-3", "--apply",
])
.current_dir(dir.path())
.assert()
.code(0);
let content = fs::read_to_string(&file).unwrap();
assert_eq!(content, " line1\nline2\nline3\n line4\n");
}
#[test]
fn test_tidy_fix_dedent_and_indent_rejects() {
let dir = TempDir::new().unwrap();
let file = dir.path().join("test.txt");
fs::write(&file, " line1\n").unwrap();
Command::cargo_bin("patchloom")
.unwrap()
.args([
"tidy", "fix", ".", "--dedent", "auto", "--indent", "4", "--apply",
])
.current_dir(dir.path())
.assert()
.code(1);
let output = Command::cargo_bin("patchloom")
.unwrap()
.args([
"--json", "tidy", "fix", ".", "--dedent", "auto", "--indent", "4", "--apply",
])
.current_dir(dir.path())
.output()
.unwrap();
assert_eq!(output.status.code(), Some(1));
let parsed: serde_json::Value = serde_json::from_slice(&output.stdout).unwrap();
assert_eq!(parsed["ok"], false);
assert_eq!(
parsed["error_kind"], "invalid_input",
"tidy dual flags should set error_kind: {parsed}"
);
}
#[test]
fn test_tidy_fix_dedent_dry_run_no_modify() {
let dir = TempDir::new().unwrap();
let file = dir.path().join("indented.txt");
let original = " line1\n line2\n";
fs::write(&file, original).unwrap();
Command::cargo_bin("patchloom")
.unwrap()
.args(["tidy", "fix", ".", "--dedent", "auto"])
.current_dir(dir.path())
.assert()
.code(2);
let content = fs::read_to_string(&file).unwrap();
assert_eq!(content, original, "file should not be modified in dry-run");
}
#[test]
fn test_tidy_fix_contain_rejects_parent_escape() {
let dir = TempDir::new().unwrap();
let escape_name = format!(
"patchloom-tidy-escape-{}.txt",
std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_millis()
);
let outside = dir.path().parent().unwrap().join(&escape_name);
fs::write(&outside, "no final newline").unwrap();
Command::cargo_bin("patchloom")
.unwrap()
.args(["--cwd"])
.arg(dir.path())
.args([
"--contain",
"tidy",
"fix",
&format!("../{escape_name}"),
"--ensure-final-newline",
"--apply",
])
.assert()
.code(1)
.stderr(
predicate::str::contains("escapes")
.or(predicate::str::contains("rejected"))
.or(predicate::str::contains("workspace guard")),
);
assert_eq!(
fs::read_to_string(&outside).unwrap(),
"no final newline",
"escaped file must not be tidied under --contain"
);
let _ = fs::remove_file(&outside);
}
#[test]
fn test_tidy_check_missing_path_is_not_found() {
let dir = TempDir::new().unwrap();
let missing = dir.path().join("nope.txt");
let output = Command::cargo_bin("patchloom")
.unwrap()
.args(["--json", "tidy", "check"])
.arg(&missing)
.output()
.unwrap();
assert_eq!(output.status.code(), Some(1));
let parsed: serde_json::Value = serde_json::from_slice(&output.stdout).unwrap();
assert_eq!(parsed["ok"], false);
assert_eq!(parsed["error_kind"], "not_found");
}
#[test]
fn test_tidy_fix_missing_path_is_not_found() {
let dir = TempDir::new().unwrap();
let missing = dir.path().join("nope.txt");
let output = Command::cargo_bin("patchloom")
.unwrap()
.args(["--json", "tidy", "fix"])
.arg(&missing)
.output()
.unwrap();
assert_eq!(output.status.code(), Some(1));
let parsed: serde_json::Value = serde_json::from_slice(&output.stdout).unwrap();
assert_eq!(parsed["ok"], false);
assert_eq!(parsed["error_kind"], "not_found");
}
#[test]
fn test_tidy_fix_format_failure_json_error_kind() {
let dir = TempDir::new().unwrap();
let file = dir.path().join("f.txt");
fs::write(&file, "x ").unwrap();
let output = Command::cargo_bin("patchloom")
.unwrap()
.args([
"--json",
"tidy",
"fix",
"f.txt",
"--trim-trailing-whitespace",
"--ensure-final-newline",
"--apply",
"--format",
"false",
"--cwd",
])
.arg(dir.path())
.output()
.unwrap();
assert_eq!(output.status.code(), Some(1));
let parsed: serde_json::Value = serde_json::from_slice(&output.stdout).unwrap();
assert_eq!(parsed["ok"], false);
assert_eq!(
parsed["error_kind"], "format_failed",
"tidy fix --format failure should set error_kind: {parsed}"
);
assert_eq!(fs::read_to_string(&file).unwrap(), "x\n");
}
#[test]
fn test_tidy_fix_files_from_missing_reports_skipped() {
let dir = TempDir::new().unwrap();
let existing = dir.path().join("a.txt");
fs::write(&existing, "x ").unwrap();
let list = dir.path().join("list.txt");
fs::write(&list, "a.txt\nmissing.txt\n").unwrap();
let output = Command::cargo_bin("patchloom")
.unwrap()
.args(["--json", "--cwd"])
.arg(dir.path())
.args(["--files-from", "list.txt", "tidy", "fix", "--apply"])
.output()
.unwrap();
assert_eq!(
output.status.code(),
Some(0),
"stderr={}",
String::from_utf8_lossy(&output.stderr)
);
let parsed: serde_json::Value = serde_json::from_slice(&output.stdout).unwrap();
assert_eq!(parsed["ok"], true, "{parsed}");
assert_eq!(parsed["files_changed"], 1, "{parsed}");
let skipped = parsed["skipped"]
.as_array()
.expect("tidy fix must report files-from missing in skipped");
assert_eq!(skipped.len(), 1, "{parsed}");
assert_eq!(skipped[0], "missing.txt");
}
#[test]
fn test_tidy_check_skips_git_directory() {
let dir = TempDir::new().unwrap();
fs::create_dir_all(dir.path().join(".git/objects/ab")).unwrap();
fs::write(
dir.path().join(".git/objects/ab/cdef"),
[0xffu8, 0xfe, 0x00],
)
.unwrap();
fs::write(dir.path().join("tw.txt"), "x \n").unwrap();
fs::write(dir.path().join(".env"), "A=1 \n").unwrap();
let output = Command::cargo_bin("patchloom")
.unwrap()
.args(["--json", "--cwd"])
.arg(dir.path())
.args(["tidy", "check", "."])
.output()
.unwrap();
let stderr = String::from_utf8_lossy(&output.stderr);
assert!(
!stderr.contains(".git"),
"tidy must not touch .git: {stderr}"
);
assert!(
!stderr.contains("invalid UTF-8"),
"no invalid-UTF-8 noise from .git objects: {stderr}"
);
let parsed: serde_json::Value = serde_json::from_slice(&output.stdout).unwrap();
assert!(
parsed["issue_count"].as_u64().unwrap_or(0) >= 1,
"expected issues in tw.txt/.env: {parsed}"
);
let issues = parsed["issues"].as_array().unwrap();
assert!(
!issues
.iter()
.any(|i| i["path"].as_str().unwrap_or("").contains(".git")),
"issues must not list .git paths: {parsed}"
);
}
#[test]
fn test_tidy_check_empty_files_from_json_is_invalid_input() {
let dir = TempDir::new().unwrap();
fs::write(dir.path().join("messy.txt"), "trail \n").unwrap();
fs::write(dir.path().join("empty.txt"), "").unwrap();
let out = Command::cargo_bin("patchloom")
.unwrap()
.args(["--json", "--cwd"])
.arg(dir.path())
.args(["--files-from", "empty.txt", "tidy", "check"])
.output()
.unwrap();
assert_eq!(
out.status.code(),
Some(1),
"stderr={}",
String::from_utf8_lossy(&out.stderr)
);
let v: serde_json::Value = serde_json::from_slice(&out.stdout).unwrap();
assert_eq!(v["ok"], false, "{v}");
assert_eq!(v["error_kind"], "invalid_input", "{v}");
assert!(
v["error"]
.as_str()
.unwrap_or("")
.contains("empty --files-from"),
"must blame empty list: {v}"
);
}
#[test]
fn test_tidy_fix_empty_files_from_json_is_invalid_input() {
let dir = TempDir::new().unwrap();
fs::write(dir.path().join("messy.txt"), "trail \n").unwrap();
fs::write(dir.path().join("empty.txt"), "").unwrap();
let out = Command::cargo_bin("patchloom")
.unwrap()
.args(["--json", "--cwd"])
.arg(dir.path())
.args([
"--files-from",
"empty.txt",
"tidy",
"fix",
"--apply",
"--trim-trailing-whitespace",
])
.output()
.unwrap();
assert_eq!(
out.status.code(),
Some(1),
"stderr={}",
String::from_utf8_lossy(&out.stderr)
);
let v: serde_json::Value = serde_json::from_slice(&out.stdout).unwrap();
assert_eq!(v["error_kind"], "invalid_input", "{v}");
assert_eq!(
fs::read_to_string(dir.path().join("messy.txt")).unwrap(),
"trail \n"
);
}
#[test]
fn test_tidy_check_dir_all_unreadable_not_clean() {
let dir = TempDir::new().unwrap();
let a = dir.path().join("a.txt");
fs::write(&a, "trail \n").unwrap();
#[cfg(unix)]
{
use std::os::unix::fs::PermissionsExt;
fs::set_permissions(&a, fs::Permissions::from_mode(0o000)).unwrap();
if fs::read_to_string(&a).is_ok() {
fs::set_permissions(&a, fs::Permissions::from_mode(0o644)).unwrap();
return;
}
let out = Command::cargo_bin("patchloom")
.unwrap()
.args(["--json", "--cwd"])
.arg(dir.path())
.args(["tidy", "check", "."])
.output()
.unwrap();
let _ = fs::set_permissions(&a, fs::Permissions::from_mode(0o644));
assert_eq!(
out.status.code(),
Some(1),
"stdout={} stderr={}",
String::from_utf8_lossy(&out.stdout),
String::from_utf8_lossy(&out.stderr)
);
let v: serde_json::Value = serde_json::from_slice(&out.stdout).unwrap();
assert_eq!(v["error_kind"], "invalid_input", "{v}");
assert_ne!(v["ok"], true, "must not vacuous clean: {v}");
}
}