use std::path::{Path, PathBuf};
use std::process::{Command, Output};
fn alint() -> PathBuf {
PathBuf::from(env!("CARGO_BIN_EXE_alint"))
}
fn run(dir: &Path, args: &[&str]) -> Output {
Command::new(alint())
.args(args)
.current_dir(dir)
.output()
.expect("spawn alint")
}
fn code(o: &Output) -> i32 {
o.status.code().unwrap_or(-1)
}
fn fixture() -> tempfile::TempDir {
let d = tempfile::tempdir().unwrap();
std::fs::write(
d.path().join(".alint.yml"),
"version: 1\n\
rules:\n\
\x20 - id: r\n\
\x20 kind: final_newline\n\
\x20 paths: [\"**/*.txt\"]\n\
\x20 level: error\n",
)
.unwrap();
std::fs::write(d.path().join("a.txt"), "no newline").unwrap();
d
}
const NON_ONLY_SUBCOMMANDS: &[&[&str]] = &[
&["list"],
&["explain", "r"],
&["facts"],
&["validate-config"],
&["suggest"],
&["baseline"],
];
#[test]
fn only_is_rejected_on_subcommands_that_do_not_honor_it() {
let d = fixture();
for sub in NON_ONLY_SUBCOMMANDS {
let mut args: Vec<&str> = sub.to_vec();
args.push("--only");
args.push("some-id");
let out = run(d.path(), &args);
assert_ne!(
code(&out),
0,
"`alint {} --only ...` must be rejected, not silently ignored\nstderr: {}",
sub.join(" "),
String::from_utf8_lossy(&out.stderr),
);
}
assert_ne!(
code(&run(d.path(), &["check", "--only", "r"])),
2,
"`check --only <real-id>` must be accepted",
);
}
#[test]
fn format_limited_subcommands_reject_unsupported_formats() {
let d = fixture();
let unsupported = ["sarif", "github", "junit", "gitlab", "markdown", "agent"];
let cases: &[&[&str]] = &[
&["list"],
&["facts"],
&["explain", "r"],
&["validate-config"],
];
for base in cases {
for fmt in unsupported {
let mut args: Vec<&str> = base.to_vec();
args.push("--format");
args.push(fmt);
let out = run(d.path(), &args);
assert_eq!(
code(&out),
2,
"`alint {} --format {fmt}` must be rejected (exit 2), not degraded to human",
base.join(" "),
);
}
for fmt in ["human", "json"] {
let mut args: Vec<&str> = base.to_vec();
args.push("--format");
args.push(fmt);
assert_ne!(
code(&run(d.path(), &args)),
2,
"`alint {} --format {fmt}` must be accepted",
base.join(" "),
);
}
}
}
#[test]
fn check_fix_baseline_reject_a_file_path() {
let d = fixture();
for sub in ["check", "fix", "baseline"] {
let out = run(d.path(), &[sub, "a.txt"]);
assert_ne!(
code(&out),
0,
"`alint {sub} a.txt` (a file, not a dir) must error, not silently pass",
);
let stderr = String::from_utf8_lossy(&out.stderr);
assert!(
stderr.contains("file") || stderr.contains("directory"),
"`{sub}` file-path error should explain the directory requirement: {stderr}",
);
}
assert_ne!(
code(&run(d.path(), &["check", "."])),
2,
"`check .` (a directory) must be accepted",
);
}