use std::fs;
use std::process::Command;
use tempfile::tempdir;
fn setup_test_directory() -> tempfile::TempDir {
let temp_dir = tempdir().unwrap();
let base_path = temp_dir.path();
fs::write(base_path.join(".gitignore"), "ignored.md\n").unwrap();
fs::write(
base_path.join("ignored.md"),
"This file has no heading and should trigger MD041.\n",
)
.unwrap();
fs::write(
base_path.join("included.md"),
"This file also has no heading and should trigger MD041.\n",
)
.unwrap();
Command::new("git")
.current_dir(base_path)
.args(["init", "-q"])
.output()
.expect("Failed to init git repo");
Command::new("git")
.current_dir(base_path)
.args(["add", "included.md"])
.output()
.expect("Failed to add file to git");
temp_dir
}
#[test]
fn test_respect_gitignore_equals_true_is_accepted() {
let temp_dir = setup_test_directory();
let base_path = temp_dir.path();
let rumdl_exe = env!("CARGO_BIN_EXE_rumdl");
let output = Command::new(rumdl_exe)
.current_dir(base_path)
.args(["check", "--respect-gitignore=true", "."])
.output()
.expect("Failed to execute command");
let stderr = String::from_utf8_lossy(&output.stderr);
assert!(
!stderr.contains("unexpected value"),
"--respect-gitignore=true should be accepted, got: {stderr}"
);
assert!(
!stderr.contains("error:") || stderr.contains("Found"),
"--respect-gitignore=true should not cause a parse error, got: {stderr}"
);
}
#[test]
fn test_respect_gitignore_equals_false_is_accepted() {
let temp_dir = setup_test_directory();
let base_path = temp_dir.path();
let rumdl_exe = env!("CARGO_BIN_EXE_rumdl");
let output = Command::new(rumdl_exe)
.current_dir(base_path)
.args(["check", "--respect-gitignore=false", "."])
.output()
.expect("Failed to execute command");
let stderr = String::from_utf8_lossy(&output.stderr);
assert!(
!stderr.contains("unexpected value"),
"--respect-gitignore=false should be accepted, got: {stderr}"
);
assert!(
!stderr.contains("error:") || stderr.contains("Found"),
"--respect-gitignore=false should not cause a parse error, got: {stderr}"
);
}
#[test]
fn test_respect_gitignore_false_lints_ignored_files() {
let temp_dir = setup_test_directory();
let base_path = temp_dir.path();
let rumdl_exe = env!("CARGO_BIN_EXE_rumdl");
let output = Command::new(rumdl_exe)
.current_dir(base_path)
.args(["check", "--respect-gitignore=false", "."])
.output()
.expect("Failed to execute command");
let stdout = String::from_utf8_lossy(&output.stdout);
let stderr = String::from_utf8_lossy(&output.stderr);
let combined = format!("{stdout}{stderr}");
assert!(
combined.contains("ignored.md"),
"ignored.md should be linted when --respect-gitignore=false, got:\n{combined}"
);
assert!(
combined.contains("included.md"),
"included.md should be linted, got:\n{combined}"
);
}
#[test]
fn test_fmt_respect_gitignore_equals_false() {
let temp_dir = setup_test_directory();
let base_path = temp_dir.path();
let rumdl_exe = env!("CARGO_BIN_EXE_rumdl");
let output = Command::new(rumdl_exe)
.current_dir(base_path)
.args(["fmt", "--respect-gitignore=false", "--dry-run", "."])
.output()
.expect("Failed to execute command");
let stderr = String::from_utf8_lossy(&output.stderr);
assert!(
!stderr.contains("unexpected value"),
"fmt --respect-gitignore=false should be accepted, got: {stderr}"
);
}
#[test]
fn test_help_shows_respect_gitignore() {
let rumdl_exe = env!("CARGO_BIN_EXE_rumdl");
let output = Command::new(rumdl_exe)
.args(["check", "--help"])
.output()
.expect("Failed to execute command");
let stdout = String::from_utf8_lossy(&output.stdout);
assert!(
stdout.contains("--respect-gitignore"),
"Help should mention --respect-gitignore"
);
assert!(stdout.contains(".gitignore"), "Help should explain gitignore behavior");
}
#[test]
fn test_explicit_path_ignores_gitignore_setting() {
let temp_dir = setup_test_directory();
let base_path = temp_dir.path();
let rumdl_exe = env!("CARGO_BIN_EXE_rumdl");
let output = Command::new(rumdl_exe)
.current_dir(base_path)
.args(["check", "ignored.md"])
.output()
.expect("Failed to execute command");
let stdout = String::from_utf8_lossy(&output.stdout);
let stderr = String::from_utf8_lossy(&output.stderr);
let combined = format!("{stdout}{stderr}");
assert!(
combined.contains("ignored.md") || combined.contains("MD041"),
"Explicitly provided files should be linted regardless of gitignore, got:\n{combined}"
);
}
#[test]
fn test_respect_gitignore_without_equals_followed_by_path() {
let temp_dir = setup_test_directory();
let base_path = temp_dir.path();
let rumdl_exe = env!("CARGO_BIN_EXE_rumdl");
let output = Command::new(rumdl_exe)
.current_dir(base_path)
.args(["check", "--respect-gitignore", "."])
.output()
.expect("Failed to execute command");
let stderr = String::from_utf8_lossy(&output.stderr);
assert!(
!stderr.contains("invalid value '.'"),
"--respect-gitignore followed by path should work, got: {stderr}"
);
assert!(
!stderr.contains("error: unexpected"),
"--respect-gitignore should be accepted, got: {stderr}"
);
}
#[test]
fn test_respect_gitignore_default_value() {
let temp_dir = setup_test_directory();
let base_path = temp_dir.path();
let rumdl_exe = env!("CARGO_BIN_EXE_rumdl");
let output = Command::new(rumdl_exe)
.current_dir(base_path)
.args(["check", "."])
.output()
.expect("Failed to execute command");
let stdout = String::from_utf8_lossy(&output.stdout);
let stderr = String::from_utf8_lossy(&output.stderr);
let combined = format!("{stdout}{stderr}");
assert!(
!stderr.contains("error: unexpected") && !stderr.contains("error: invalid"),
"Default behavior should work, got: {stderr}"
);
assert!(
!combined.contains("ignored.md"),
"ignored.md should NOT be linted with default respect-gitignore=true, got:\n{combined}"
);
assert!(
combined.contains("included.md"),
"included.md should be linted, got:\n{combined}"
);
}
fn setup_nested_repository(pattern: &str) -> tempfile::TempDir {
let temp_dir = tempdir().unwrap();
fs::write(temp_dir.path().join(".gitignore"), format!("{pattern}\n")).unwrap();
let repo = temp_dir.path().join("repo");
fs::create_dir_all(repo.join("docs")).unwrap();
fs::create_dir_all(repo.join(".git")).unwrap();
fs::write(repo.join(".gitignore"), "/.rumdl_cache\n").unwrap();
fs::write(
repo.join(".rumdl.toml"),
"[global]\ninclude = [\n \"docs/**/*.md\",\n]\n",
)
.unwrap();
fs::write(repo.join("docs/guide.md"), "Body without a heading.\n").unwrap();
temp_dir
}
fn check_repo(temp_dir: &tempfile::TempDir, args: &[&str]) -> std::process::Output {
Command::new(env!("CARGO_BIN_EXE_rumdl"))
.current_dir(temp_dir.path().join("repo"))
.arg("check")
.arg("--no-cache")
.args(args)
.output()
.expect("Failed to execute command")
}
#[test]
fn gitignore_above_the_repository_root_does_not_hide_files_inside_it() {
for pattern in ["docs/", "*.md", "*"] {
let temp_dir = setup_nested_repository(pattern);
for args in [vec!["."], vec!["docs/"]] {
let output = check_repo(&temp_dir, &args);
let combined = format!(
"{}{}",
String::from_utf8_lossy(&output.stdout),
String::from_utf8_lossy(&output.stderr)
);
assert!(
combined.contains("guide.md"),
"'{pattern}' above the repository root hid guide.md from `check {}`:\n{combined}",
args.join(" ")
);
}
}
let temp_dir = setup_nested_repository("docs/");
fs::remove_dir(temp_dir.path().join("repo/.git")).unwrap();
let stderr = String::from_utf8_lossy(&check_repo(&temp_dir, &["."]).stderr).to_string();
assert!(
stderr.contains("by ignore files"),
"outside a repository the ignore file above still applies:\n{stderr}"
);
let temp_dir = setup_nested_repository("");
fs::write(temp_dir.path().join("repo/.gitignore"), "docs/\n").unwrap();
let stderr = String::from_utf8_lossy(&check_repo(&temp_dir, &["."]).stderr).to_string();
assert!(
stderr.contains("by ignore files"),
"a gitignore inside the repository still applies:\n{stderr}"
);
}
#[test]
fn test_config_file_respect_gitignore_false() {
let temp_dir = setup_test_directory();
let base_path = temp_dir.path();
let rumdl_exe = env!("CARGO_BIN_EXE_rumdl");
fs::write(base_path.join(".rumdl.toml"), "[global]\nrespect-gitignore = false\n").unwrap();
let output = Command::new(rumdl_exe)
.current_dir(base_path)
.args(["check", "."])
.output()
.expect("Failed to execute command");
let stdout = String::from_utf8_lossy(&output.stdout);
let stderr = String::from_utf8_lossy(&output.stderr);
let combined = format!("{stdout}{stderr}");
assert!(
combined.contains("ignored.md"),
"ignored.md should be linted when config has respect-gitignore=false, got:\n{combined}"
);
assert!(
combined.contains("included.md"),
"included.md should be linted, got:\n{combined}"
);
}