Your browser sucks, JavaScript disabled! Gunnar's console needs JavaScript and WebAssembly. Git over SSH on port 2222 does not.

nordisk/gunnar

crates/gunnar-policy/tests/gates.rs

//! Both directions, for every gate.
//!
//! The pre-receive gate's rule: *every gate must be seen to REJECT a crafted bad push before it is
//! trusted to allow a good one.* So this file runs the crafted-bad-push set and
//! asserts each one is refused **with the right reason**, then runs the
//! good-push set and asserts each one is let through.
//!
//! Neither half is sufficient alone. A gate that always allows passes the second
//! half; a gate that always rejects passes the first. `tests/blind.rs` proves
//! this file would notice either.

mod common;

use common::{bad_pushes, good_pushes};

#[test]
fn every_crafted_bad_push_is_refused_with_the_right_reason() {
    let cases = bad_pushes();
    assert!(
        cases.len() >= 15,
        "the crafted-bad-push set has shrunk to {}; a gate has lost its red case",
        cases.len()
    );

    let mut failures = Vec::new();
    for case in &cases {
        if let Err(err) = (case.run)(case.gate.as_ref()) {
            failures.push(format!("  {}\n    {err}", case.name));
        }
    }
    assert!(
        failures.is_empty(),
        "{} of {} crafted bad pushes were not refused correctly:\n{}",
        failures.len(),
        cases.len(),
        failures.join("\n")
    );
}

#[test]
fn every_good_push_is_allowed() {
    let cases = good_pushes();
    assert!(cases.len() >= 6, "the good-push set has shrunk");

    let mut failures = Vec::new();
    for case in &cases {
        if let Err(err) = (case.run)(case.gate.as_ref()) {
            failures.push(format!("  {}\n    {err}", case.name));
        }
    }
    assert!(
        failures.is_empty(),
        "{} of {} good pushes were refused:\n{}",
        failures.len(),
        cases.len(),
        failures.join("\n")
    );
}

#[test]
fn every_gate_has_at_least_one_case_in_each_direction() {
    // A gate with only bad cases has never been seen to allow anything, and one
    // with only good cases has never been seen to refuse. Either is the failure
    // this crate is most exposed to, so the coverage itself is asserted rather
    // than left to whoever adds the next gate.
    use std::collections::BTreeSet;
    let bad: BTreeSet<String> = bad_pushes()
        .iter()
        .map(|c| c.gate.name().to_owned())
        .collect();
    let good: BTreeSet<String> = good_pushes()
        .iter()
        .map(|c| c.gate.name().to_owned())
        .collect();
    let expected: BTreeSet<String> = [
        "branch-protection",
        "ref-name",
        "signed-commits",
        "limits",
        "ai-trailers",
    ]
    .into_iter()
    .map(str::to_owned)
    .collect();

    assert_eq!(bad, expected, "gates missing a rejection case");
    assert_eq!(good, expected, "gates missing an allow case");
}