mod common;
use common::Repo;
use std::io::Write;
use std::process::{Command, Stdio};
fn conflicted() -> String {
format!(
"{}\nours\n{}\ntheirs\n{}\n",
"<".repeat(7),
"=".repeat(7),
">".repeat(7)
)
}
fn pre_push(r: &Repo) -> (i32, String) {
let mut child = Command::new(env!("CARGO_BIN_EXE_amont"))
.arg("--hooks-dir")
.arg(r.path(".git/hooks"))
.arg("pre-push")
.current_dir(&r.dir)
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.spawn()
.expect("spawn");
child
.stdin
.as_mut()
.expect("stdin")
.write_all(
b"refs/heads/nonsense-branch-name aaa refs/heads/nonsense-branch-name \
0000000000000000000000000000000000000000\n",
)
.expect("write");
let out = child.wait_with_output().expect("wait");
(
out.status.code().unwrap_or(-1),
format!(
"{}{}",
String::from_utf8_lossy(&out.stdout),
String::from_utf8_lossy(&out.stderr)
),
)
}
#[test]
fn the_check_blocks_by_default() {
let r = Repo::new();
r.stage("bad.txt", &conflicted());
let run = r.hook("pre-commit", &[]);
assert!(!run.passed(), "expected a block, got:\n{}", run.output());
}
#[test]
fn severity_warn_reports_without_blocking() {
let r = Repo::new();
r.git(&["config", "amont.severity.pre-commit-merge-conflict", "warn"]);
r.stage("bad.txt", &conflicted());
let run = r.hook("pre-commit", &[]);
assert!(run.passed(), "should not block:\n{}", run.output());
assert!(
run.says("conflict"),
"the check went quiet instead of warning:\n{}",
run.output()
);
assert!(
run.says("set to warn"),
"nothing explained why an error message did not block:\n{}",
run.output()
);
}
#[test]
fn severity_block_overrides_a_broader_warn() {
let r = Repo::new();
r.git(&["config", "amont.severity.pre-commit-merge-conflict", "warn"]);
r.git(&[
"config",
"--replace-all",
"amont.severity.pre-commit-merge-conflict",
"block",
]);
r.stage("bad.txt", &conflicted());
assert!(!r.hook("pre-commit", &[]).passed());
}
#[test]
fn an_unrecognised_severity_keeps_the_declared_one() {
let r = Repo::new();
r.git(&[
"config",
"amont.severity.pre-commit-merge-conflict",
"advisory",
]);
r.stage("bad.txt", &conflicted());
let run = r.hook("pre-commit", &[]);
assert!(
!run.passed(),
"a misspelt severity turned the check off:\n{}",
run.output()
);
}
#[test]
fn a_warn_failure_does_not_end_the_pre_push_chain() {
let r = Repo::new();
r.stage("a.txt", "x\n");
r.commit("init");
r.git(&["checkout", "-q", "-b", "nonsense-branch-name"]);
let (code, out) = pre_push(&r);
assert_ne!(code, 0, "branch-pattern was expected to block:\n{out}");
r.git(&["config", "amont.severity.pre-push-branch-pattern", "warn"]);
let (code, out) = pre_push(&r);
assert_eq!(code, 0, "the chain stopped at a warning:\n{out}");
assert!(
out.contains("severity warn"),
"the downgrade was not announced:\n{out}"
);
}
#[test]
fn the_override_is_scoped_to_one_check() {
let r = Repo::new();
r.git(&["config", "amont.severity.pre-commit-ban-terms", "warn"]);
r.stage("bad.txt", &conflicted());
assert!(
!r.hook("pre-commit", &[]).passed(),
"downgrading one check downgraded another"
);
}