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/blind.rs

//! The permanent proof that `tests/gates.rs` can go red.
//!
//! # What this file is for
//!
//! The pre-receive gate's design asks for each gate to be *"deliberately stubbed to always-Allow"* and
//! its rejection test *"confirmed to go red"*. Doing that by hand proves the
//! assertion could fail on the day somebody did it, and proves nothing on any
//! day after.
//!
//! So it is done here instead, on every run, against the *same* fixtures:
//!
//! * [`AlwaysAllow`] stands in for each real gate. Every crafted bad push is
//!   replayed through it, and the harness must come back `Err`. If it does not,
//!   the assertion in `tests/gates.rs` was never capable of failing, and the
//!   green there means nothing.
//! * [`AlwaysReject`] stands in for each gate in the allow direction, and the
//!   harness must come back `Err` there too, because a gate that refuses
//!   everything passes every rejection test in the suite and is just as broken.
//!
//! # What this file does NOT catch, checked by mutation rather than assumed
//!
//! It does not catch a gate that has gone blind, and it does not catch a fixture
//! that has stopped being a bad push. Both of those make the *real* gate allow,
//! and `tests/gates.rs` is what notices. Verified by mutating each in turn: the
//! gate stub and the softened fixture both leave this file green and take
//! `tests/gates.rs` red.
//!
//! What is left is the one failure `tests/gates.rs` structurally cannot see:
//! **an assertion that has stopped being able to fail.** A harness whose
//! `expect_reject` returned `Ok` unconditionally would leave every rejection
//! test in the crate green and worthless, and only a replay through a gate known
//! to allow can observe it. That mutation was applied too, and this file went
//! red with "16 of 16 rejection cases would pass".

mod common;

use common::{bad_pushes, good_pushes};
use gunnar_policy::blind::{AlwaysAllow, AlwaysReject};

#[test]
fn a_blind_gate_allows_every_crafted_bad_push_and_the_harness_says_so() {
    let cases = bad_pushes();
    let mut undetected = Vec::new();

    for case in &cases {
        let blind = AlwaysAllow::instead_of(case.gate.as_ref());
        match (case.run)(&blind) {
            Err(message) => {
                // This is the point of the file: the harness noticed. Also check
                // it noticed for the right reason, so a harness that always
                // returns `Err` cannot pass either.
                assert!(
                    message.contains("ALLOWED a push that must be refused"),
                    "case {:?}: the harness failed, but not because the blind gate allowed the \
                     push. It said: {message}",
                    case.name
                );
            }
            Ok(verdict) => undetected.push(format!(
                "  {}: an always-allow gate satisfied this case (verdict {verdict:?}). \
                 Either the fixture is no longer a bad push, or the assertion cannot fail.",
                case.name
            )),
        }
    }

    assert!(
        undetected.is_empty(),
        "{} of {} rejection cases would pass with the gate replaced by a stub that always \
         allows, so those cases prove nothing:\n{}",
        undetected.len(),
        cases.len(),
        undetected.join("\n")
    );
}

#[test]
fn a_gate_that_refuses_everything_fails_the_allow_direction() {
    let cases = good_pushes();
    let mut undetected = Vec::new();

    for case in &cases {
        let blind = AlwaysReject::instead_of(case.gate.as_ref());
        match (case.run)(&blind) {
            Err(message) => assert!(
                message.contains("REFUSED a push that must be allowed"),
                "case {:?}: the harness failed for the wrong reason: {message}",
                case.name
            ),
            Ok(()) => undetected.push(format!(
                "  {}: an always-reject gate satisfied this case",
                case.name
            )),
        }
    }

    assert!(
        undetected.is_empty(),
        "{} of {} allow cases would pass with the gate replaced by a stub that refuses \
         everything:\n{}",
        undetected.len(),
        cases.len(),
        undetected.join("\n")
    );
}

#[test]
fn the_engine_treats_a_blind_gate_as_live_and_a_real_one_as_deciding() {
    // The engine must not quietly special-case anything. A blind gate is a gate:
    // it is listed, it runs, and it allows. The value of this assertion is that
    // `gate_names()`, what `Policy.ListGates` answers with, is proved to
    // reflect what actually runs.
    use gunnar_policy::PolicyEngine;
    let engine = PolicyEngine::new(vec![Box::new(AlwaysAllow::named("branch-protection"))]);
    assert_eq!(engine.gate_names(), vec!["branch-protection"]);
    assert_eq!(engine.len(), 1);
    assert!(!engine.is_empty());
}