use super::{TEST_COUNTER, fixture_dir, run_command, temp_dir, temp_file};
use core::sync::atomic::Ordering;
use std::fs;
#[test]
fn fix_doc_comment_dry_run_reports_change() {
let before = fixture_dir().join("table_doc_comment_before.rs");
let output = run_command(&["--include", "tables", "--dry-run"], &before);
assert!(
!output.status.success(),
"fix --dry-run must fail for proposed changes: {}",
String::from_utf8_lossy(&output.stderr)
);
assert!(
output.stdout.is_empty(),
"dry-run must not print reconstructed source to stdout"
);
let stderr = String::from_utf8_lossy(&output.stderr);
assert!(
stderr.contains("success[FIX]"),
"dry-run must report a fix change line on stderr: {stderr}"
);
}
#[test]
fn fix_fence_in_place_matches_after() {
let expected = fs::read_to_string(fixture_dir().join("fence_md_after.md")).unwrap();
let tmp = temp_file("md");
fs::write(
&tmp,
fs::read_to_string(fixture_dir().join("fence_md_before.md")).unwrap(),
)
.unwrap();
let output = run_command(&["--include", "fences"], &tmp);
assert!(
output.status.success(),
"fence fix in-place should succeed: {}",
String::from_utf8_lossy(&output.stderr)
);
let actual = fs::read_to_string(&tmp).unwrap();
let _ = fs::remove_file(&tmp);
assert_eq!(actual, expected, "fence fix must match fence_md_after.md");
}
#[test]
fn fix_idempotent_on_after_fixtures() {
for name in ["table_md_after.md", "table_doc_comment_after.rs"] {
let path = fixture_dir().join(name);
let output = run_command(&["--include", "tables", "--dry-run"], &path);
assert!(
output.status.success(),
"fix --dry-run on {name} should succeed"
);
assert!(
output.stdout.is_empty(),
"{name} dry-run must not print source to stdout"
);
assert!(
output.stderr.is_empty(),
"{name} is already tidy: dry-run must emit zero change records"
);
}
{
let path = fixture_dir().join("fence_md_after.md");
let output = run_command(&["--include", "fences", "--dry-run"], &path);
assert!(
output.status.success(),
"fix --dry-run on fence_md_after.md should succeed"
);
assert!(
output.stdout.is_empty(),
"fence_md_after.md dry-run must not print source to stdout"
);
assert!(
output.stderr.is_empty(),
"fence_md_after.md is already tidy: dry-run must emit zero change records"
);
}
}
#[test]
fn fix_in_place_reports_same_records_and_writes() {
let before = fixture_dir().join("multi_md_before.md");
let dry_run = run_command(&["--include", "tables", "--dry-run"], &before);
let dry_stderr = String::from_utf8_lossy(&dry_run.stderr);
let tmp = temp_file("md");
fs::write(&tmp, fs::read_to_string(&before).unwrap()).unwrap();
let output = run_command(&["--include", "tables"], &tmp);
assert!(
output.status.success(),
"fix in-place should succeed: {}",
String::from_utf8_lossy(&output.stderr)
);
let actual = fs::read_to_string(&tmp).unwrap();
let _ = fs::remove_file(&tmp);
let stderr = String::from_utf8_lossy(&output.stderr);
assert_eq!(
stderr.matches("success[FIX]").count(),
1,
"in-place run reports the same record: {stderr}"
);
assert!(
stderr.contains("tables were aligned"),
"in-place change line matches dry-run: {stderr}"
);
assert_eq!(
stderr.matches("success[FIX]").count(),
dry_stderr.matches("success[FIX]").count(),
"in-place reports the same change lines as its dry-run twin"
);
assert_ne!(
actual,
fs::read_to_string(&before).unwrap(),
"in-place run must write the fixed file"
);
}
#[test]
fn fix_in_place_write() {
let expected = fs::read_to_string(fixture_dir().join("table_md_after.md")).unwrap();
let tmp = temp_file("md");
fs::write(
&tmp,
fs::read_to_string(fixture_dir().join("table_md_before.md")).unwrap(),
)
.unwrap();
let output = run_command(&["--include", "tables"], &tmp);
assert!(
output.status.success(),
"fix in-place should succeed: {}",
String::from_utf8_lossy(&output.stderr)
);
let actual = fs::read_to_string(&tmp).unwrap();
let _ = fs::remove_file(&tmp);
assert_eq!(actual, expected, "in-place file must match _after fixture");
}
#[test]
fn fix_md_dry_run_reports_change() {
let before = fixture_dir().join("table_md_before.md");
let output = run_command(&["--include", "tables", "--dry-run"], &before);
assert!(
!output.status.success(),
"fix --dry-run must fail for proposed changes: {}",
String::from_utf8_lossy(&output.stderr)
);
assert!(
output.stdout.is_empty(),
"dry-run must not print reconstructed source to stdout"
);
let stderr = String::from_utf8_lossy(&output.stderr);
assert!(
stderr.contains("success[FIX]"),
"dry-run must report a fix change line on stderr: {stderr}"
);
}
#[test]
fn fix_multi_entity_dry_run_reports_one_record_per_file() {
let before = fixture_dir().join("multi_md_before.md");
let output = run_command(&["--include", "tables", "--dry-run"], &before);
assert!(
!output.status.success(),
"fix --dry-run must fail for proposed changes: {}",
String::from_utf8_lossy(&output.stderr)
);
assert!(
output.stdout.is_empty(),
"dry-run must not print reconstructed source to stdout"
);
let stderr = String::from_utf8_lossy(&output.stderr);
assert_eq!(
stderr.matches("success[FIX]").count(),
1,
"one record for the whole file: {stderr}"
);
assert!(
stderr.contains("tables were aligned"),
"record covers both tables with no line: {stderr}"
);
}
#[test]
fn fix_nonexistent_path_fails() {
let nonexistent = std::env::temp_dir().join(format!(
"rust-llm-tidy-fix-missing-{}-{}.md",
std::process::id(),
TEST_COUNTER.fetch_add(1, Ordering::Relaxed)
));
let output = run_command(&["--include", "tables"], &nonexistent);
assert!(
!output.status.success(),
"non-existent path should exit non-zero"
);
}
#[test]
fn fix_recursive_directory_collects_md_and_rs() {
let dir = temp_dir();
let sub = dir.join("sub");
fs::create_dir_all(&sub).unwrap();
fs::write(
dir.join("readme.md"),
fs::read_to_string(fixture_dir().join("table_md_before.md")).unwrap(),
)
.unwrap();
fs::write(
sub.join("code.rs"),
fs::read_to_string(fixture_dir().join("table_doc_comment_before.rs")).unwrap(),
)
.unwrap();
let output = run_command(&["--include", "tables"], &dir);
assert!(
output.status.success(),
"fix directory should succeed: {}",
String::from_utf8_lossy(&output.stderr)
);
let md_expected = fs::read_to_string(fixture_dir().join("table_md_after.md")).unwrap();
let rs_expected = fs::read_to_string(fixture_dir().join("table_doc_comment_after.rs")).unwrap();
assert_eq!(
fs::read_to_string(dir.join("readme.md")).unwrap(),
md_expected,
".md file should be fixed"
);
assert_eq!(
fs::read_to_string(sub.join("code.rs")).unwrap(),
rs_expected,
".rs file should be fixed"
);
let _ = fs::remove_dir_all(&dir);
}