crates/gunnar-grpc/tests/decision_words.rs
//! **F (release-3 test2 mock 2026-09-29): the audit target speaks THREE decision words.**
//!
//! `gunnar-grpc` auth writes `decision = "allow" | "deny" | "narrow"` on
//! `event = "grpc.call"`, and that field is what a reader filters on — the
//! console's audit pane and the ops log gate (private-gunnar-ops
//! `log_gate::DECISIONS`). `service/mirror.rs` wrote `decision = "allowed"` for
//! the twin's `Mirror.LogHead`/`LogFetch` reads (176 + 54 lines per main on
//! the mock): the console rendered every ALLOWED read of the twin as "DENIED
//! Mirror.LogHead gunnar-twin", and the gate failed the receipt on it.
//!
//! The guard reads every source file of the crates that log `grpc.call`, so a
//! fourth word cannot come back unnoticed.
use std::path::{Path, PathBuf};
const WORDS: &[&str] = &["allow", "deny", "narrow"];
fn rust_files(dir: &Path, out: &mut Vec<PathBuf>) {
for e in std::fs::read_dir(dir)
.unwrap_or_else(|e| panic!("{}: {e}", dir.display()))
.flatten()
{
let p = e.path();
if p.is_dir() {
rust_files(&p, out);
} else if p.extension().is_some_and(|x| x == "rs") {
out.push(p);
}
}
}
#[test]
fn every_decision_word_on_the_audit_target_is_allow_deny_or_narrow() {
let crates = Path::new(env!("CARGO_MANIFEST_DIR"))
.parent()
.expect("crates/");
let mut files = Vec::new();
for c in ["gunnar-grpc", "gunnar-server", "gunnar-ssh", "gunnar-front"] {
rust_files(&crates.join(c).join("src"), &mut files);
}
assert!(
files.len() > 50,
"the crates were read: {} files",
files.len()
);
let mut bad = Vec::new();
let mut seen = 0;
for f in &files {
let text = std::fs::read_to_string(f).unwrap();
for (n, line) in text.lines().enumerate() {
let mut rest = line;
while let Some(i) = rest.find("decision = \"") {
let after = &rest[i + "decision = \"".len()..];
let word = after.split('"').next().unwrap_or("");
seen += 1;
if !WORDS.contains(&word) {
bad.push(format!("{}:{}: decision = \"{word}\"", f.display(), n + 1));
}
rest = after;
}
}
}
assert!(seen >= 20, "the decision fields were found: {seen}");
assert!(
bad.is_empty(),
"a decision word the audit target does not speak ({}):\n{}",
WORDS.join("|"),
bad.join("\n")
);
}