mod common;
use common::Repo;
#[test]
fn passes_when_both_are_staged() {
let r = Repo::new();
r.stage("package.json", "{}\n");
r.stage("package-lock.json", "{}\n");
assert!(r.hook("pre-commit-package-lock", &[]).passed());
}
#[test]
fn rejects_a_lockfile_staged_without_its_package_json() {
let r = Repo::new();
r.stage("package-lock.json", "{}\n");
let run = r.hook("pre-commit-package-lock", &[]);
assert!(!run.passed());
assert!(run.says("without its package.json"));
}
#[test]
fn a_package_json_with_no_lockfile_on_disk_demands_nothing() {
let r = Repo::new();
r.stage("package.json", "{}\n");
assert!(r.hook("pre-commit-package-lock", &[]).passed());
}
#[test]
fn a_forgotten_lockfile_blocks_without_asking() {
let r = Repo::new();
r.write("package-lock.json", "{}\n");
r.git(&["add", "package-lock.json"]);
r.commit("chore: seed the lockfile");
r.stage("package.json", "{\"name\":\"t\"}\n");
let child = std::process::Command::new(env!("CARGO_BIN_EXE_amont"))
.arg("--hooks-dir")
.arg(r.path(".git/hooks"))
.arg("pre-commit-package-lock")
.current_dir(&r.dir)
.stdin(std::process::Stdio::inherit())
.stdout(std::process::Stdio::piped())
.stderr(std::process::Stdio::piped())
.spawn()
.expect("spawn");
let (tx, rx) = std::sync::mpsc::channel();
std::thread::spawn(move || {
let _ = tx.send(child.wait_with_output());
});
let out = rx
.recv_timeout(std::time::Duration::from_secs(10))
.expect("the check never returned — it is asking a question again")
.expect("wait on the child");
assert!(!out.status.success(), "a forgotten lockfile must block");
let text = format!(
"{}{}",
String::from_utf8_lossy(&out.stdout),
String::from_utf8_lossy(&out.stderr)
);
assert!(
text.contains("amont.severity.package-lock"),
"the message must offer the one-time replacement for answering y: {text}"
);
assert!(text.contains("hook.skip=package-lock"), "{text}");
assert!(
!text.contains("Commit anyway"),
"it asked a question from a worker thread: {text}"
);
}
#[test]
fn severity_warn_lets_a_forgotten_lockfile_through() {
let r = Repo::new();
r.write("package-lock.json", "{}\n");
r.git(&["add", "package-lock.json"]);
r.commit("chore: seed the lockfile");
r.git(&["config", "amont.severity.package-lock", "warn"]);
r.stage("package.json", "{\"name\":\"t\"}\n");
let run = r.hook("pre-commit-package-lock", &[]);
assert!(run.passed(), "warn must not block:\n{}", run.output());
assert!(
run.says("package-lock.json is not staged"),
"{}",
run.output()
);
}
#[test]
fn scoping_is_per_directory() {
let r = Repo::new();
r.write("package-lock.json", "{}\n"); r.stage("apps/web/package.json", "{}\n");
assert!(r.hook("pre-commit-package-lock", &[]).passed());
}