use super::*;
fn listing_snapshot(records: &[(&str, &str)]) -> ConfigSnapshot {
let mut bytes: Vec<u8> = Vec::new();
for (scope, record) in records {
bytes.extend_from_slice(scope.as_bytes());
bytes.push(0);
bytes.extend_from_slice(record.as_bytes());
bytes.push(0);
}
return ConfigSnapshot::parse(bytes.as_slice(), true).expect("a well-formed listing");
}
#[test]
fn core_fsmonitor_is_not_reported_as_a_hook() {
for value in ["/usr/local/bin/watch", "true", "false"] {
let snapshot: ConfigSnapshot = listing_snapshot(&[("local", format!("core.fsmonitor\n{value}").as_str())]);
assert!(configured_hooks(&snapshot).is_empty(), "{value}");
assert_eq!(external_hooks_warning(Some(&snapshot)), None, "{value}");
}
}
struct RefusingStream;
impl Write for RefusingStream {
fn write(&mut self, _buffer: &[u8]) -> std::io::Result<usize> {
return Err(std::io::Error::other("closed"));
}
fn flush(&mut self) -> std::io::Result<()> {
return Err(std::io::Error::other("closed"));
}
}
#[test]
fn hooks_group_by_name_with_their_events_and_commands() {
let snapshot: ConfigSnapshot = listing_snapshot(&[
("local", "hook.review.event\npost-commit"),
("local", "hook.review.command\nrun it"),
("local", "hook.lint.event\npre-commit"),
("local", "hook.lint.event\ncommit-msg"),
("local", "hook.reset.event\npre-push"),
("local", "hook.reset.event\n"),
("local", "hook.reset.event\npost-merge"),
("local", "hook.orphan.command\ntrue"),
("local", "hook.a.b.event\nupdate"),
]);
let hooks: Vec<ConfiguredHook> = configured_hooks(&snapshot);
let names: Vec<&str> = hooks.iter().map(|hook| { return hook.name.as_str(); }).collect();
assert_eq!(names, vec!["review", "lint", "reset", "orphan", "a.b"]);
assert_eq!(hooks[0].events, vec![String::from("post-commit")]);
assert!(hooks[0].has_command);
assert_eq!(hooks[1].events, vec![String::from("pre-commit"), String::from("commit-msg")]);
assert!(!hooks[1].has_command);
assert_eq!(hooks[2].events, vec![String::from("post-merge")]);
assert!(hooks[3].events.is_empty() && hooks[3].has_command);
assert_eq!(describe_hook(&hooks[3]), "hook.orphan (no event)");
assert_eq!(describe_hook(&hooks[1]), "hook.lint (events: pre-commit, commit-msg)");
}
#[test]
fn switches_and_other_keys_name_no_hook() {
let snapshot: ConfigSnapshot = listing_snapshot(&[
("local", "hook.pre-commit.enabled\nfalse"),
("local", "hook.jobs\n4"),
("local", "hook.mine.parallel\ntrue"),
("local", "core.hookspath\n/x"),
("local", "hooks.concurrentcommits\ntrue"),
]);
assert_eq!(configured_hooks(&snapshot), Vec::new());
assert_eq!(external_hooks_warning(Some(&snapshot)), None);
assert_eq!(external_hooks_warning(None), None);
}
#[test]
fn a_hook_switched_off_by_name_is_not_reported() {
let snapshot: ConfigSnapshot = listing_snapshot(&[
("local", "hook.off.event\npost-commit"),
("local", "hook.off.enabled\nfalse"),
("local", "hook.on.event\npost-commit"),
("local", "hook.on.enabled\ntrue"),
]);
let hooks: Vec<ConfiguredHook> = configured_hooks(&snapshot);
assert_eq!(hooks.len(), 1);
assert_eq!(hooks[0].name, "on");
}
#[test]
fn the_warning_names_the_hooks_and_the_ways_forward() {
let snapshot: ConfigSnapshot = listing_snapshot(&[
("local", "hook.review.event\npost-commit"),
("local", "hook.review.command\nrun it"),
("global", "hook.lint.event\npre-commit"),
]);
let line: String = external_hooks_warning(Some(&snapshot)).expect("warning");
assert!(line.ends_with('\n') && line.matches('\n').count() == 1, "one line: {line:?}");
assert!(line.contains("\"type\":\"configuration-warning\"") && line.contains(EXTERNAL_HOOKS_UNSUPPORTED_CODE), "{line}");
for expected in ["hook.review (events: post-commit)", "hook.lint (events: pre-commit)", "--unset-all", "runs no program named by any configuration", "policies", "git cli-git doctor"] {
assert!(line.contains(expected), "{expected} in {line}");
}
}
#[test]
fn the_writer_emits_one_line_or_nothing() {
let snapshot: ConfigSnapshot = listing_snapshot(&[("local", "hook.a.event\npost-commit")]);
let mut written: Vec<u8> = Vec::new();
warn_external_hooks(Some(&snapshot), &mut written);
assert_eq!(written, external_hooks_warning(Some(&snapshot)).expect("warning").into_bytes());
let mut nothing: Vec<u8> = Vec::new();
warn_external_hooks(None, &mut nothing);
assert!(nothing.is_empty());
warn_external_hooks(Some(&snapshot), &mut RefusingStream);
}