use std::env;
use std::fs;
use std::process::Command;
fn setup_test_files() -> tempfile::TempDir {
let temp_dir = tempfile::tempdir().unwrap();
let base_path = temp_dir.path();
fs::write(
base_path.join("single_file.md"),
"# Test Heading!\nThis line has trailing spaces. \nNo newline at end",
)
.unwrap();
fs::write(
base_path.join("second_file.md"),
"# Another Heading\n\n## Missing space after ##heading\n\nSome content.",
)
.unwrap();
fs::write(
base_path.join("multi_issue.md"),
"# Heading 1\n# Heading 1 duplicate\nThis line has trailing spaces. \n\n\nMultiple blank lines above.\n```\nCode block with no language\n```\nNo newline at end",
)
.unwrap();
fs::write(
base_path.join("unfixable_issue.md"),
"# Unfixable Issue\n\nThis is a very long line that exceeds the default line length limit of 80 characters which cannot be automatically fixed by the linter.\n",
)
.unwrap();
fs::write(
base_path.join("mixed_issues.md"),
"# Mixed Issues\nThis line has trailing spaces. \n\nThis paragraph contains * spaced emphasis * that should be fixable.\nThis line should have a newline at the end but doesn't",
)
.unwrap();
temp_dir
}
#[test]
fn test_output_format_singular() {
let temp_dir = setup_test_files();
let base_path = temp_dir.path();
let output = Command::new(env!("CARGO_BIN_EXE_rumdl"))
.current_dir(base_path)
.args(["check", "single_file.md"])
.output()
.expect("Failed to execute command");
let stdout = String::from_utf8_lossy(&output.stdout);
println!("Single file output:\n{stdout}");
assert!(
stdout.contains("issues in 1 file"),
"Expected output to contain 'issues in 1 file', but got:\n{stdout}"
);
assert!(
!stdout.contains("issues in 1 files"),
"Output should not contain 'issues in 1 files'"
);
}
#[test]
fn test_output_format_plural() {
let temp_dir = setup_test_files();
let base_path = temp_dir.path();
let output = Command::new(env!("CARGO_BIN_EXE_rumdl"))
.current_dir(base_path)
.args(["check", "single_file.md", "second_file.md"])
.output()
.expect("Failed to execute command");
let stdout = String::from_utf8_lossy(&output.stdout);
println!("Multiple files output:\n{stdout}");
assert!(
stdout.contains("issues in 2 files"),
"Expected output to contain 'issues in 2 files', but got:\n{stdout}"
);
}
#[test]
fn test_output_format_fix_mode_singular() {
let temp_dir = setup_test_files();
let base_path = temp_dir.path();
let output = Command::new(env!("CARGO_BIN_EXE_rumdl"))
.current_dir(base_path)
.args(["check", "single_file.md", "--fix"])
.output()
.expect("Failed to execute command");
let stdout = String::from_utf8_lossy(&output.stdout);
println!("Fix mode single file output:\n{stdout}");
assert!(
stdout.contains("issues in 1 file"),
"Expected output to contain 'issues in 1 file', but got:\n{stdout}"
);
assert!(
!stdout.contains("issues in 1 files"),
"Output should not contain 'issues in 1 files'"
);
assert!(stdout.contains("Fixed:"), "Output should contain 'Fixed:' line");
assert!(
!stdout.contains("Issues:"),
"Output should not contain 'Issues:' line when in fix mode"
);
}
#[test]
fn test_output_format_fix_mode_plural() {
let temp_dir = setup_test_files();
let base_path = temp_dir.path();
let output = Command::new(env!("CARGO_BIN_EXE_rumdl"))
.current_dir(base_path)
.args(["check", "single_file.md", "second_file.md", "--fix"])
.output()
.expect("Failed to execute command");
let stdout = String::from_utf8_lossy(&output.stdout);
println!("Fix mode multiple files output:\n{stdout}");
assert!(
stdout.contains("issues in 2 files"),
"Expected output to contain 'issues in 2 files', but got:\n{stdout}"
);
assert!(stdout.contains("Fixed:"), "Output should contain 'Fixed:' line");
assert!(
!stdout.contains("Issues:"),
"Output should not contain 'Issues:' line when in fix mode"
);
}
#[test]
fn test_output_format_fix_mode_label() {
let temp_dir = setup_test_files();
let test_file = temp_dir.path().join("single_file.md");
let test_file_path = test_file.to_str().unwrap();
let output_normal = Command::new(env!("CARGO_BIN_EXE_rumdl"))
.args(["check", test_file_path])
.output()
.expect("Failed to execute command");
let stdout_normal = String::from_utf8_lossy(&output_normal.stdout);
assert!(
stdout_normal.contains("[*]"),
"Normal mode should show [*] for fixable issues"
);
assert!(
!stdout_normal.contains("[fixed]"),
"Normal mode should not show [fixed] labels"
);
let output_fix = Command::new(env!("CARGO_BIN_EXE_rumdl"))
.args(["check", test_file_path, "--fix"])
.output()
.expect("Failed to execute command");
let stdout_fix = String::from_utf8_lossy(&output_fix.stdout);
assert!(
stdout_fix.contains("[fixed]"),
"Fix mode should show [fixed] for fixed issues. stdout: {stdout_fix}"
);
assert!(!stdout_fix.contains("[*]"), "Fix mode should not show [*] labels");
}
#[test]
fn test_multi_issue_output_format() {
let temp_dir = setup_test_files();
let test_file = temp_dir.path().join("multi_issue.md");
let test_file_path = test_file.to_str().unwrap();
let output_normal = Command::new(env!("CARGO_BIN_EXE_rumdl"))
.args(["check", test_file_path])
.output()
.expect("Failed to execute command");
let stdout_normal = String::from_utf8_lossy(&output_normal.stdout);
println!("Multi-issue normal mode output:\n{stdout_normal}");
assert!(
stdout_normal.contains("[MD022]") && stdout_normal.contains("blank line"),
"Should detect heading blank line issue"
);
assert!(
stdout_normal.contains("[MD025]") && stdout_normal.contains("Multiple top-level headings"),
"Should detect duplicate level 1 heading issue"
);
assert!(
stdout_normal.contains("[MD012]") && stdout_normal.contains("Multiple consecutive blank lines"),
"Should detect multiple blank lines issue"
);
assert!(
stdout_normal.contains("[MD040]") && stdout_normal.contains("Code block (```) missing language"),
"Should detect code block language issue"
);
assert!(
stdout_normal.contains("[MD047]") && stdout_normal.contains("File should end with a single newline"),
"Should detect file ending newline issue"
);
let normal_mode_fixable_issues = stdout_normal.matches("[*]").count();
assert!(
normal_mode_fixable_issues >= 5,
"Expected at least 5 fixable issues marked with [*], found {normal_mode_fixable_issues}"
);
let output_fix = Command::new(env!("CARGO_BIN_EXE_rumdl"))
.args(["check", test_file_path, "--fix"])
.output()
.expect("Failed to execute command");
let stdout_fix = String::from_utf8_lossy(&output_fix.stdout);
println!("Multi-issue fix mode output:\n{stdout_fix}");
let fix_mode_fixed_issues = stdout_fix.matches("[fixed]").count();
assert!(
fix_mode_fixed_issues >= 5,
"Expected at least 5 issues marked as [fixed], found {fix_mode_fixed_issues}. stdout: {stdout_fix}"
);
assert!(!stdout_fix.contains("[*]"), "Fix mode should not have any [*] labels");
assert!(
stdout_fix.contains("Fixed:"),
"Fix mode summary should report fixes applied. stdout: {stdout_fix}"
);
}
#[test]
fn test_fixable_issues_labeling() {
let temp_dir = setup_test_files();
let test_file = temp_dir.path().join("fixable_issue.md");
fs::write(
&test_file,
"# Fixable Issue\n\nThis paragraph contains * spaced emphasis *.\n",
)
.unwrap();
let test_file_path = test_file.to_str().unwrap();
let output = Command::new(env!("CARGO_BIN_EXE_rumdl"))
.args(["check", test_file_path])
.output()
.expect("Failed to execute command");
let stdout = String::from_utf8_lossy(&output.stdout);
println!("Fixable issue output:\n{stdout}");
assert!(stdout.contains("[MD037]"), "Should detect spaces around emphasis issue");
if stdout.contains("[MD037]") {
let md037_line = stdout.lines().find(|line| line.contains("[MD037]")).unwrap_or("");
assert!(
md037_line.contains("[*]"),
"MD037 should have [*] label indicating it's fixable"
);
}
let fix_output = Command::new(env!("CARGO_BIN_EXE_rumdl"))
.args(["check", test_file_path, "--fix"])
.output()
.expect("Failed to execute command");
let fix_stdout = String::from_utf8_lossy(&fix_output.stdout);
println!("Fixable issue with --fix output:\n{fix_stdout}");
assert!(
fix_stdout.contains("[fixed]"),
"Fixed issue should show [fixed] label. stdout: {fix_stdout}"
);
assert!(fix_stdout.contains("Fixed"), "Summary should confirm fix was applied");
let content = fs::read_to_string(test_file_path).expect("Failed to read file");
assert!(
content.contains("*spaced emphasis*"),
"Spaces inside emphasis should be fixed in the file content"
);
}
#[test]
fn test_truly_unfixable_issues_labeling() {
let temp_dir = setup_test_files();
let test_file = temp_dir.path().join("unfixable_issue.md");
fs::write(
&test_file,
"# Truly Unfixable Issue\n\nThis paragraph contains a very long line that definitely exceeds the maximum line length limit and cannot be automatically fixed by the linter because line wrapping requires manual intervention.\n",
).unwrap();
let config_file = temp_dir.path().join("custom_rumdl.toml");
fs::write(&config_file, "[MD013]\nline_length = 20\n").unwrap();
let test_file_path = test_file.to_str().unwrap();
let config_file_path = config_file.to_str().unwrap();
let output = Command::new(env!("CARGO_BIN_EXE_rumdl"))
.args(["check", test_file_path, "--config", config_file_path])
.output()
.expect("Failed to execute command");
let stdout = String::from_utf8_lossy(&output.stdout);
println!("Truly unfixable issue output:\n{stdout}");
assert!(
stdout.contains("[MD013]") && stdout.contains("Line length"),
"Should detect line length issue"
);
if stdout.contains("[MD013]") {
let md013_line = stdout.lines().find(|line| line.contains("[MD013]")).unwrap_or("");
assert!(
!md013_line.contains("[*]"),
"MD013 should NOT have [*] label since it's not fixable"
);
}
let fix_output = Command::new(env!("CARGO_BIN_EXE_rumdl"))
.args(["check", test_file_path, "--fix", "--config", config_file_path])
.output()
.expect("Failed to execute command");
let fix_stdout = String::from_utf8_lossy(&fix_output.stdout);
println!("Truly unfixable issue with --fix output:\n{fix_stdout}");
assert!(
fix_stdout.contains("[MD013]") && fix_stdout.contains("Line length"),
"Line length issue should still be reported after fix attempt"
);
if fix_stdout.contains("[MD013]") {
let md013_line = fix_stdout.lines().find(|line| line.contains("[MD013]")).unwrap_or("");
assert!(
!md013_line.contains("[fixed]"),
"Unfixable issue (MD013) should NOT have [fixed] label"
);
}
let content = fs::read_to_string(test_file_path).expect("Failed to read file");
assert!(
content.contains("This paragraph contains a very long line"),
"Line length should NOT be fixed in the file content"
);
}
#[test]
fn test_mixed_fixable_unfixable_issues() {
let temp_dir = setup_test_files();
let test_file = temp_dir.path().join("mixed_issues.md");
fs::write(
&test_file,
"# Mixed Issues\nThis line has trailing spaces. \n\nThis paragraph contains * spaced emphasis * that should be fixable.\nThis line is extremely long and exceeds the maximum line length which cannot be automatically fixed because line wrapping requires manual intervention by the user.\nThis line should have a newline at the end but doesn't",
).unwrap();
let config_file = temp_dir.path().join("custom_rumdl.toml");
fs::write(&config_file, "[MD013]\nline_length = 20\n").unwrap();
let test_file_path = test_file.to_str().unwrap();
let config_file_path = config_file.to_str().unwrap();
let output = Command::new(env!("CARGO_BIN_EXE_rumdl"))
.args(["check", test_file_path, "--config", config_file_path])
.output()
.expect("Failed to execute command");
let stdout = String::from_utf8_lossy(&output.stdout);
println!("Mixed issues output:\n{stdout}");
assert!(
stdout.contains("[MD022]"),
"Should detect heading blank line issue (fixable)"
);
assert!(
stdout.contains("[MD047]"),
"Should detect missing newline issue (fixable)"
);
assert!(
stdout.contains("[MD037]"),
"Should detect spaces around emphasis issue (fixable)"
);
assert!(
stdout.contains("[MD013]"),
"Should detect line length issue (unfixable)"
);
assert!(
stdout.contains("[*]"),
"Should detect at least one fixable issue with [*] label"
);
if stdout.contains("[MD013]") {
let md013_line = stdout.lines().find(|line| line.contains("[MD013]")).unwrap_or("");
assert!(
!md013_line.contains("[*]"),
"MD013 should NOT have [*] label since it's not fixable"
);
}
let fix_output = Command::new(env!("CARGO_BIN_EXE_rumdl"))
.args(["check", test_file_path, "--fix", "--config", config_file_path])
.output()
.expect("Failed to execute command");
let fix_stdout = String::from_utf8_lossy(&fix_output.stdout);
println!("Mixed issues with --fix output:\n{fix_stdout}");
assert!(
fix_stdout.contains("[fixed]"),
"Fixable issues should show [fixed] label. stdout: {fix_stdout}"
);
if fix_stdout.contains("[MD037]") {
let md037_line = fix_stdout.lines().find(|line| line.contains("[MD037]")).unwrap_or("");
assert!(
md037_line.contains("[fixed]"),
"MD037 should have [fixed] label after applying fixes"
);
}
assert!(
fix_stdout.contains("[MD013]"),
"MD013 (unfixable) should remain in output. stdout: {fix_stdout}"
);
if fix_stdout.contains("[MD013]") {
let md013_line = fix_stdout.lines().find(|line| line.contains("[MD013]")).unwrap_or("");
assert!(
!md013_line.contains("[fixed]"),
"MD013 should NOT have [fixed] label as it cannot be fixed automatically"
);
}
assert!(!fix_stdout.contains("[*]"), "Fix mode should not show [*] labels");
let content = fs::read_to_string(test_file_path).expect("Failed to read file");
assert!(
content.contains("# Mixed Issues\n\n"),
"Heading should have a blank line below it after fixing"
);
assert!(
content.contains("*spaced emphasis*"),
"Spaces inside emphasis should be fixed in the file content"
);
assert!(
content.ends_with('\n'),
"Missing newline should be fixed in the file content"
);
assert!(
content.contains("This line is extremely long"),
"Long line should remain unfixed in the content"
);
}
#[test]
fn test_fix_mode_text_vs_json_output() {
let temp_dir = setup_test_files();
let test_file = temp_dir.path().join("text_vs_json.md");
fs::write(
&test_file,
"# Test\nThis paragraph contains * spaced emphasis * that is fixable.\nThis line is extremely long and exceeds the maximum line length which cannot be automatically fixed because line wrapping requires manual intervention by the user to decide where to break.\n",
).unwrap();
let config_file = temp_dir.path().join("tvj_config.toml");
fs::write(&config_file, "[MD013]\nline_length = 20\n").unwrap();
let test_file_path = test_file.to_str().unwrap();
let config_file_path = config_file.to_str().unwrap();
let text_output = Command::new(env!("CARGO_BIN_EXE_rumdl"))
.args(["check", test_file_path, "--fix", "--config", config_file_path])
.env("NO_COLOR", "1")
.output()
.expect("Failed to execute command");
let text_stdout = String::from_utf8_lossy(&text_output.stdout);
assert!(
text_stdout.contains("[fixed]"),
"Text fix mode must show [fixed] labels for fixed issues. stdout: {text_stdout}"
);
assert!(
text_stdout.contains("[MD037]"),
"Text fix mode must show MD037 (fixed). stdout: {text_stdout}"
);
assert!(
text_stdout.contains("[MD013]"),
"Text fix mode must show MD013 (unfixable). stdout: {text_stdout}"
);
let md037_line = text_stdout.lines().find(|l| l.contains("[MD037]")).unwrap_or("");
assert!(
md037_line.contains("[fixed]"),
"MD037 must have [fixed] label in text output. line: {md037_line}"
);
let md013_line = text_stdout.lines().find(|l| l.contains("[MD013]")).unwrap_or("");
assert!(
!md013_line.contains("[fixed]"),
"MD013 must NOT have [fixed] label. line: {md013_line}"
);
fs::write(
&test_file,
"# Test\nThis paragraph contains * spaced emphasis * that is fixable.\nThis line is extremely long and exceeds the maximum line length which cannot be automatically fixed because line wrapping requires manual intervention by the user to decide where to break.\n",
).unwrap();
let json_output = Command::new(env!("CARGO_BIN_EXE_rumdl"))
.args([
"check",
test_file_path,
"--fix",
"--output-format",
"json",
"--config",
config_file_path,
])
.output()
.expect("Failed to execute command");
let json_stdout = String::from_utf8_lossy(&json_output.stdout);
let parsed: serde_json::Value = serde_json::from_str(&json_stdout)
.unwrap_or_else(|e| panic!("Invalid JSON output: {e}. stdout: {json_stdout}"));
let warnings = parsed.as_array().expect("JSON output should be an array");
for w in warnings {
let rule = w["rule"].as_str().unwrap_or("");
assert_ne!(
rule, "MD037",
"JSON fix mode must NOT include fixed warnings (MD037). Got: {json_stdout}"
);
}
let has_md013 = warnings.iter().any(|w| w["rule"].as_str() == Some("MD013"));
assert!(
has_md013,
"JSON fix mode must include remaining unfixable warnings (MD013). Got: {json_stdout}"
);
for w in warnings {
assert!(
w.get("fixed").is_none(),
"JSON output must not contain 'fixed' field. Got: {w}"
);
}
}
#[test]
fn test_color_output_disabled() {
let temp_dir = setup_test_files();
let test_file = temp_dir.path().join("single_file.md");
let test_file_path = test_file.to_str().unwrap();
let output = Command::new(env!("CARGO_BIN_EXE_rumdl"))
.args(["check", test_file_path])
.env("NO_COLOR", "1")
.output()
.expect("Failed to execute command");
let stdout = String::from_utf8_lossy(&output.stdout);
println!("No color output:\n{stdout}");
assert!(
!stdout.contains("\x1b["),
"Output should not contain ANSI color codes when NO_COLOR is set"
);
}
#[test]
fn test_quiet_mode_output() {
let temp_dir = setup_test_files();
let test_file = temp_dir.path().join("single_file.md");
let test_file_path = test_file.to_str().unwrap();
let output = Command::new(env!("CARGO_BIN_EXE_rumdl"))
.args(["check", test_file_path, "--silent"])
.output()
.expect("Failed to execute command");
let stdout = String::from_utf8_lossy(&output.stdout);
println!("Silent mode output:\n{stdout}");
assert!(
stdout.is_empty(),
"Silent mode should suppress standard output, got: {stdout}"
);
let fix_output = Command::new(env!("CARGO_BIN_EXE_rumdl"))
.args(["check", test_file_path, "--silent", "--fix"])
.output()
.expect("Failed to execute command");
let fix_stdout = String::from_utf8_lossy(&fix_output.stdout);
assert!(
fix_stdout.is_empty(),
"Quiet mode with fix should suppress standard output, got: {fix_stdout}"
);
let content = fs::read_to_string(test_file_path).expect("Failed to read file");
assert!(
content.ends_with('\n'),
"File should have been fixed (newline added) even in quiet mode"
);
}
#[test]
fn test_verbose_mode_output() {
let temp_dir = setup_test_files();
let test_file = temp_dir.path().join("single_file.md");
let test_file_path = test_file.to_str().unwrap();
let output = Command::new(env!("CARGO_BIN_EXE_rumdl"))
.args(["check", test_file_path, "--verbose"])
.output()
.expect("Failed to execute command");
let stdout = String::from_utf8_lossy(&output.stdout);
println!("Verbose mode output:\n{stdout}");
assert!(
stdout.contains("Processing file:"),
"Verbose mode should include 'Processing file:' messages"
);
assert!(
stdout.contains("Enabled rules:"),
"Verbose mode should include list of enabled rules"
);
assert!(
stdout.contains("[MD"),
"Verbose mode should still show lint warnings in the output"
);
}
#[test]
fn test_exit_code_validation() {
let temp_dir = setup_test_files();
let clean_file = temp_dir.path().join("clean_file.md");
fs::write(&clean_file, "# Clean File\n\nThis file has no issues.\n").unwrap();
let issue_file = temp_dir.path().join("single_file.md");
let output_with_issues = Command::new(env!("CARGO_BIN_EXE_rumdl"))
.args(["check", issue_file.to_str().unwrap()])
.output()
.expect("Failed to execute command");
let output_no_issues = Command::new(env!("CARGO_BIN_EXE_rumdl"))
.args(["check", clean_file.to_str().unwrap()])
.output()
.expect("Failed to execute command");
assert_ne!(
output_with_issues.status.code(),
Some(0),
"Exit code should be non-zero when issues are found"
);
assert_eq!(
output_no_issues.status.code(),
Some(0),
"Exit code should be zero when no issues are found"
);
let output_fix = Command::new(env!("CARGO_BIN_EXE_rumdl"))
.args(["check", issue_file.to_str().unwrap(), "--fix"])
.output()
.expect("Failed to execute command");
let fix_stdout = String::from_utf8_lossy(&output_fix.stdout);
if !fix_stdout.contains("issues") || fix_stdout.contains("Fixed: 0/") {
assert_eq!(
output_fix.status.code(),
Some(0),
"Exit code should be 0 when no issues remain after fix"
);
}
}
#[test]
fn test_rule_with_configuration() {
let temp_dir = setup_test_files();
let test_file = temp_dir.path().join("configurable_issue.md");
fs::write(
&test_file,
"# Configurable Issue\n\nThis line is exactly 70 characters long which is within default limits.\nThis line is a bit longer and has exactly 75 characters which exceeds 70 chars.\nThis line is much longer and definitely exceeds the default limit of 80 characters by a substantial margin including many extra words that ensure it is well over the limit of 80 characters making it extremely long and definitely triggering the MD013 rule with its default configuration.\n",
).unwrap();
let test_file_path = test_file.to_str().unwrap();
let default_output = Command::new(env!("CARGO_BIN_EXE_rumdl"))
.args(["check", test_file_path])
.output()
.expect("Failed to execute command");
let default_stdout = String::from_utf8_lossy(&default_output.stdout);
println!("Default configuration output:\n{default_stdout}");
assert!(
default_stdout.contains("[MD013]"),
"Should detect line length issues over 80 characters with default configuration"
);
let config_file = temp_dir.path().join(".rumdl.toml");
fs::write(
&config_file,
r#"
[MD013]
line_length = 70
"#,
)
.unwrap();
let custom_output = Command::new(env!("CARGO_BIN_EXE_rumdl"))
.args(["check", test_file_path])
.current_dir(temp_dir.path()) .output()
.expect("Failed to execute command");
let custom_stdout = String::from_utf8_lossy(&custom_output.stdout);
println!("Custom configuration output:\n{custom_stdout}");
assert!(
custom_stdout.contains("[MD013]"),
"Should detect line length issues over 70 characters with custom configuration"
);
let default_issue_count = default_stdout.matches("[MD013]").count();
let custom_issue_count = custom_stdout.matches("[MD013]").count();
assert!(
custom_issue_count > default_issue_count,
"Custom configuration should detect more issues than default configuration"
);
}
#[test]
fn test_fixed_content_validation() {
let temp_dir = setup_test_files();
let test_file = temp_dir.path().join("fixable_issues.md");
fs::write(&test_file, "# Missing Newline\nThis line does not end with a newline").unwrap();
let test_file_path = test_file.to_str().unwrap();
let original_content = fs::read_to_string(&test_file).unwrap();
let check_output = Command::new(env!("CARGO_BIN_EXE_rumdl"))
.args(["check", test_file_path])
.output()
.expect("Failed to execute command");
let check_stdout = String::from_utf8_lossy(&check_output.stdout);
println!("Pre-fix output:\n{check_stdout}");
assert!(
check_stdout.contains("[MD047]") && check_stdout.contains("newline"),
"Should detect missing newline issue"
);
let fix_output = Command::new(env!("CARGO_BIN_EXE_rumdl"))
.args(["check", test_file_path, "--fix"])
.output()
.expect("Failed to execute command");
let fix_stdout = String::from_utf8_lossy(&fix_output.stdout);
println!("Fix output:\n{fix_stdout}");
let fixed_content = fs::read_to_string(&test_file).unwrap();
assert_ne!(
original_content, fixed_content,
"Content should be modified after fixing"
);
assert!(fixed_content.ends_with('\n'), "Fixed content should end with a newline");
let recheck_output = Command::new(env!("CARGO_BIN_EXE_rumdl"))
.args(["check", test_file_path])
.output()
.expect("Failed to execute command");
let recheck_stdout = String::from_utf8_lossy(&recheck_output.stdout);
println!("Post-fix check output:\n{recheck_stdout}");
assert!(!recheck_stdout.contains("[MD047]"), "MD047 issue should be fixed");
}