crates/nornir-testmatrix/src/appliance.rs
//! **The appliance (product × proof) rollup** — ONE place that answers "which
//! products have a PROVEN identity ceremony, and how far up the ladder?".
//!
//! The suite's appliance work (the wg-appliance ceremony, the container robot,
//! the installer KVM proofs, Secure Boot arming) emits ordinary
//! [`functional_status`](crate::functional_status) rows under a NAMING
//! CONVENTION this module owns:
//!
//! ```text
//! component = "<product>-appliance" (the host/server form)
//! or "<product>-appliance/<form>" (a specific form: container, iso …)
//! check = the proof name (identity_ready, ping_through_tunnel, …)
//! ```
//!
//! [`ApplianceRollup::from_rows`] folds any row set into a grid of
//! [`ProofVerdict`]s keyed `(product, proof)`. Three verdicts, and the third is
//! the load-bearing one:
//!
//! * [`ProofVerdict::Green`] — every matching row passed.
//! * [`ProofVerdict::Red`] — ANY matching row failed. Red wins over green
//! (a proof that passed on one form and failed on another is NOT proven).
//! * [`ProofVerdict::Absent`] — no row at all. A product that never emitted is
//! a visible HOLE in the grid, never silent green — "no evidence" and
//! "proven" must be impossible to confuse (the same law as the gated-tests
//! guard: silence is not success).
//!
//! The canonical rosters ([`APPLIANCE_PRODUCTS`], [`APPLIANCE_PROOFS`]) pin the
//! grid's SHAPE: the rollup always shows all three products against the full
//! proof ladder, so korp's empty row is on the wall next to holger's green one.
//! Extra products/proofs found in the rows are appended after the canonical
//! ones — the roster floors the grid, it does not cap it.
use std::collections::BTreeMap;
use crate::model::{status, TestResultRow};
/// The products the suite ships as appliances. The grid ALWAYS carries all of
/// them — an unproven product is a row of `Absent`, not a missing row.
///
/// These are the nordisk RUNNABLES, and the roster is a **FLOOR, not a cap**:
/// discovery appends anything else it finds.
///
/// ⚠ The four (five) names are NOT the rows. Each product exists in several
/// runnable FORMS in the Build Thing — server, ui, cli, solo, service,
/// container, iso, installer — and **the forms are the rows**
/// ([`crate::chain::BuildThing`]). This roster only fixes which products must
/// always be REPRESENTED.
///
/// `dwarves` is a product here (it ships `dwarves-server`), not merely the forge
/// that builds the others. `nornir` is last on purpose: it is a real product but
/// mostly embedded in dwarves, so it is the lowest-priority standalone and is
/// expected to sit `Absent` — visible, not missing, and not a wall.
///
/// The LIBRARIES are deliberately NOT rows: facett, edda, tunnr, knut, znippy,
/// skade, Skidbladnir, draupnir, modgunn. They are the MACHINERY the matrix uses
/// — draupnir instantiates, nornir-build-thing enumerates and builds,
/// wg-appliance-core owns the ceremony.
pub const APPLIANCE_PRODUCTS: [&str; 5] = ["gunnar", "holger", "dwarves", "korp", "nornir"];
/// The proof ladder, in ceremony order — each name is a `check` some proof
/// emits (or will emit; an emitter that does not exist yet shows `Absent`,
/// which is exactly the honest answer):
///
/// * `identity_ready` — the server binary ran the ceremony (host form).
/// * `identity_minted` / `identity_survives_replacement` /
/// `identity_survives_upgrade` — the container robot's durability arms.
/// * `tunnel_up_traffic_crossed` — handshake seen + ≥1 packet sealed.
/// * `ping_through_tunnel` — ICMP echo through the tunnel, both directions.
/// * `install_ok` — the installer ISO laid the product onto a disk under KVM.
/// * `secure_boot_armed` — enforcing firmware boots the signed installer and
/// refuses the unsigned/foreign-keyed one.
pub const APPLIANCE_PROOFS: [&str; 8] = [
"identity_ready",
"identity_minted",
"identity_survives_replacement",
"identity_survives_upgrade",
"tunnel_up_traffic_crossed",
"ping_through_tunnel",
"install_ok",
"secure_boot_armed",
];
/// The `-appliance` component-name marker the convention hangs on.
const COMPONENT_SUFFIX: &str = "-appliance";
/// One cell of the grid — and, since the chain matrix
/// ([`crate::chain`]), of THAT grid too. ONE verdict vocabulary for the whole
/// suite, deliberately: two enums that mean almost the same thing is exactly how
/// "no evidence" and "proven" start reading alike again.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum ProofVerdict {
/// It RAN and every matching row passed.
Green,
/// It RAN and at least one matching row failed — red wins over green.
Red,
/// No row matched — it NEVER RAN. No evidence, visibly so. Never green.
Absent,
/// **This kind has no such stage.** Not a hole anyone can go fill: a library
/// was never going to boot off a disk, and a place where no green was ever
/// possible must not sit in the report looking like unfinished work.
///
/// A product DECLARES it, out loud, per rung: a `skip` row whose message
/// opens with [`NA_MARKER`](crate::functional::NA_MARKER) — what
/// [`functional_na`](crate::functional::functional_na) emits — and the
/// reason travels with it. Nothing infers an `Na`; silence stays
/// [`Absent`](ProofVerdict::Absent).
///
/// REVISED 2026-09-10: this enum's old note said the (product × proof)
/// rollup never produces one, on the theory that every rung applies to
/// every product. It does not: nornir is embedded in dwarves and ships no
/// installer ISO, so `install_ok` is not a hole in nornir's work — it is a
/// stage nornir has not got. The alternative was leaving it `Absent`
/// forever, which is the same dash as "nobody wrote it yet".
Na,
}
impl ProofVerdict {
/// The single-glyph cell rendering: `✔` / `✘` / `—` / `·`.
///
/// `—` (absent) and `·` (n/a) MUST stay visually distinct: one is a hole
/// someone must go fill, the other never was one.
pub fn glyph(self) -> &'static str {
match self {
ProofVerdict::Green => "✔",
ProofVerdict::Red => "✘",
ProofVerdict::Absent => "—",
ProofVerdict::Na => "·",
}
}
}
/// Parse a component name against the convention: `holger-appliance` and
/// `holger-appliance/container` both yield `Some("holger")`; anything without
/// the `-appliance` marker (or with an EMPTY product) is `None` — foreign
/// components never leak into the grid.
pub fn appliance_product(component: &str) -> Option<&str> {
let base = component.split('/').next().unwrap_or(component);
let product = base.strip_suffix(COMPONENT_SUFFIX)?;
if product.is_empty() {
return None;
}
Some(product)
}
/// The folded (product × proof) grid.
#[derive(Debug, Clone)]
pub struct ApplianceRollup {
/// Row order: the canonical products first, then any extras seen in the
/// rows (sorted). Same floor-not-cap rule for `proofs`.
pub products: Vec<String>,
/// Column order: the canonical ladder first, then extras (sorted).
pub proofs: Vec<String>,
verdicts: BTreeMap<(String, String), ProofVerdict>,
}
impl ApplianceRollup {
/// Fold rows into the grid. Rows whose component does not match the
/// convention are ignored; `pass` folds green, a `skip` that DECLARES
/// itself inapplicable ([`NA_MARKER`](crate::functional::NA_MARKER)) folds
/// `Na`, and anything else that matched a proof folds RED (a stalled or
/// failed proof is not proven).
///
/// Precedence across forms is red > green > n/a: one form failing sinks the
/// proof, and a form that DID climb the rung outranks another form's claim
/// that the rung is not there.
pub fn from_rows<'a, I: IntoIterator<Item = &'a TestResultRow>>(rows: I) -> Self {
let mut verdicts: BTreeMap<(String, String), ProofVerdict> = BTreeMap::new();
let mut extra_products: Vec<String> = Vec::new();
let mut extra_proofs: Vec<String> = Vec::new();
for row in rows {
let Some(product) = appliance_product(&row.suite) else {
continue;
};
let proof = row.test_name.as_str();
let seen = if row.status == status::PASS {
ProofVerdict::Green
} else if row.status == status::SKIP
&& row.message.trim_start().starts_with(crate::functional::NA_MARKER)
{
ProofVerdict::Na
} else {
ProofVerdict::Red
};
let key = (product.to_string(), proof.to_string());
let held = verdicts.entry(key).or_insert(seen);
*held = match (*held, seen) {
(ProofVerdict::Red, _) | (_, ProofVerdict::Red) => ProofVerdict::Red,
(ProofVerdict::Green, _) | (_, ProofVerdict::Green) => ProofVerdict::Green,
_ => ProofVerdict::Na,
};
if !APPLIANCE_PRODUCTS.contains(&product) && !extra_products.iter().any(|p| p == product) {
extra_products.push(product.to_string());
}
if !APPLIANCE_PROOFS.contains(&proof) && !extra_proofs.iter().any(|p| p == proof) {
extra_proofs.push(proof.to_string());
}
}
extra_products.sort();
extra_proofs.sort();
let products = APPLIANCE_PRODUCTS
.iter()
.map(|p| p.to_string())
.chain(extra_products)
.collect();
let proofs = APPLIANCE_PROOFS
.iter()
.map(|p| p.to_string())
.chain(extra_proofs)
.collect();
Self { products, proofs, verdicts }
}
/// The verdict for one cell (any product/proof string — off-grid asks are
/// `Absent`, the same answer as an on-grid hole).
pub fn verdict(&self, product: &str, proof: &str) -> ProofVerdict {
self.verdicts
.get(&(product.to_string(), proof.to_string()))
.copied()
.unwrap_or(ProofVerdict::Absent)
}
/// True when every canonical (product × proof) place is `Green` **or a
/// declared `Na`** — the suite-wide "all appliances fully proven" gate.
/// Today this is FALSE and the grid shows exactly which places keep it
/// false.
///
/// An `Na` never holds the gate, for the same reason a needs-real-cloud
/// preset never holds [`ApplianceBootGrid::kvm_all_green`]: a stage the
/// product has not got cannot be climbed, so demanding it would make the
/// gate permanently red for a reason nobody can act on. `Absent` is the
/// opposite case and still holds it red — that IS actionable.
pub fn all_green(&self) -> bool {
APPLIANCE_PRODUCTS.iter().all(|product| {
APPLIANCE_PROOFS.iter().all(|proof| {
matches!(self.verdict(product, proof), ProofVerdict::Green | ProofVerdict::Na)
})
})
}
/// Render the grid as a fixed-width text table — products as rows, proofs
/// as columns (numbered, with a legend, so the table stays terminal-width).
pub fn render(&self) -> String {
let mut out = String::new();
out.push_str(
"appliance (product × proof) — ✔ proven · ✘ FAILED · — no evidence · · no such stage\n",
);
let width = self.products.iter().map(|p| p.len()).max().unwrap_or(0).max(7);
out.push_str(&format!("{:width$} ", ""));
for i in 1..=self.proofs.len() {
out.push_str(&format!("{i:>3}"));
}
out.push('\n');
for product in &self.products {
out.push_str(&format!("{product:width$} "));
for proof in &self.proofs {
out.push_str(&format!("{:>3}", self.verdict(product, proof).glyph()));
}
out.push('\n');
}
for (i, proof) in self.proofs.iter().enumerate() {
out.push_str(&format!(" {:>2} = {proof}\n", i + 1));
}
out
}
}
// ─────────────── appliance-boot: one row per TARGET PRESET ──────────────────
/// The component prefix of the per-machine boot rows: `appliance-boot/<preset>`.
///
/// The `<preset>` word is a name from `nornir_build_thing::target::presets::PRESETS`
/// — the ONE roster of cloud machine shapes (UpCloud, qemu-local, GCE …). This
/// crate never spells those names itself (the dependency runs the other way,
/// see `tests/chain_matrix.rs`): the caller hands the grid a [`BootPreset`]
/// roster built FROM that code (`presets::boot_roster()`), and the rows come
/// from the proof doors (`gunnar-installtest installer --target <preset>`, a
/// real cloud deploy) under this naming convention.
pub const APPLIANCE_BOOT_COMPONENT: &str = "appliance-boot";
/// The proof ladder of one machine shape, in door order — the three legs of
/// `gunnar-installtest installer`: B1 install onto the disk, B2 boot the
/// installed disk alone and ANSWER, B3 power-cut and come back.
pub const APPLIANCE_BOOT_PROOFS: [&str; 3] = ["install_ok", "serves", "ready"];
/// Can this machine shape be measured on the proof box at all?
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum BootReach {
/// QEMU/KVM can mimic it — a hole the installtest fills.
Kvm,
/// No QEMU model for a device the guest must drive (or not QEMU at all):
/// the row is declared and stays `Absent (needs real cloud: …)` until a
/// real deploy emits under its name. The reason is the preset's own.
RealCloud(String),
}
/// **One device QEMU had to stand in for**, as the roster's code derives it
/// (`nornir_build_thing::target::presets::MimicAxes::substitutions`).
///
/// A proof row for a preset with a non-empty list proved a SHAPE — the boot
/// order, the firmware handoff, the partition scan, the address plumbing —
/// and NOT the named devices. The row has to say so; see
/// [`BootPreset::unnamed_substitutions`].
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct BootSubstitution {
/// Which facet: `firmware` | `chipset` | `disk` | `nic` | `medium`.
pub facet: String,
/// The device the real machine presents.
pub missing: String,
/// What QEMU was asked to present instead.
pub with: String,
}
impl BootSubstitution {
/// The exact phrase a row must contain — `nic: hv-netvsc -> virtio-net-pci`.
/// One writer, so the emitter and the checker cannot spell it differently.
pub fn label(&self) -> String {
format!("{}: {} -> {}", self.facet, self.missing, self.with)
}
}
/// One machine shape the grid MUST show — the floor, handed in from the code
/// that declares it. Never typed out here.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct BootPreset {
/// The `<preset>` word.
pub name: String,
pub reach: BootReach,
/// Where/when the shape was measured, when it was — the roster's own
/// evidence, quoted in the detail of a row that has no fresh proof yet.
/// The string says which KIND of measurement it was (`real cloud: …` /
/// `qemu rig: …`); the roster is the one writer of that word.
pub measured: Option<String>,
/// The devices QEMU stands in for on this shape. Empty = a green here
/// means the machine; non-empty = a green means the shape, and the row
/// must NAME each one.
pub substitutions: Vec<BootSubstitution>,
}
/// **The word a row uses to say it ran on the real platform**, not on a mimic.
///
/// The same vocabulary the roster's own evidence uses
/// (`nornir_build_thing::target::presets::MeasuredOn::RealCloud` renders
/// `real cloud: …`), so there is ONE spelling of this claim in the
/// constellation rather than one per emitter.
pub const REAL_DEPLOY_MARKER: &str = "real cloud";
impl BootPreset {
/// **Which substitutions a passing row failed to account for.**
///
/// The load-bearing guard of this module's second half: a `pass` whose
/// message does not spell every swapped device is not evidence that the
/// machine booted, it is evidence that SOMETHING booted.
///
/// Two ways for a row to be honest, because there are two ways to earn a
/// green on a substituted shape:
///
/// * it NAMES every substitution — a mimic run, and the row says what it
/// stood in for; or
/// * it declares [`REAL_DEPLOY_MARKER`] — it ran on the actual platform, so
/// nothing was substituted and the devices really were there.
///
/// A preset with no substitutions is always honest, so nothing at all is
/// demanded of an exactly-mimicked shape.
pub fn unnamed_substitutions(&self, message: &str) -> Vec<String> {
if message.contains(REAL_DEPLOY_MARKER) {
return Vec::new();
}
self.substitutions
.iter()
.map(BootSubstitution::label)
.filter(|label| !message.contains(label.as_str()))
.collect()
}
}
/// Parse `appliance-boot/<preset>` → `Some("<preset>")`; anything else (the
/// bare prefix, an empty preset, a foreign component) is `None`.
pub fn appliance_boot_preset(component: &str) -> Option<&str> {
let rest = component.strip_prefix(APPLIANCE_BOOT_COMPONENT)?.strip_prefix('/')?;
if rest.is_empty() || rest.contains('/') {
return None;
}
Some(rest)
}
/// The (preset × proof) grid: every declared machine shape against the three
/// boot proofs. Same verdict vocabulary as the rest of the suite
/// ([`ProofVerdict`]); the only twist is WHY a cell is `Absent`, which this
/// grid carries as a detail string so `needs real cloud` and `never ran` are
/// never the same dash.
#[derive(Debug, Clone)]
pub struct ApplianceBootGrid {
/// Row order: the roster first (its order), then any preset seen only in
/// rows (sorted) — floor, not cap.
pub presets: Vec<BootPreset>,
/// Column order: the canonical ladder, then extras (sorted).
pub proofs: Vec<String>,
cells: BTreeMap<(String, String), (ProofVerdict, String)>,
}
impl ApplianceBootGrid {
/// Fold rows onto the roster. `pass` is green, any other matched status
/// is red; a roster cell with no row is `Absent` with the reason the
/// roster gives (`needs real cloud: …` or `no KVM proof emitted yet`).
pub fn from_rows<'a, I: IntoIterator<Item = &'a TestResultRow>>(roster: Vec<BootPreset>, rows: I) -> Self {
let mut cells: BTreeMap<(String, String), (ProofVerdict, String)> = BTreeMap::new();
let mut extra_presets: Vec<String> = Vec::new();
let mut extra_proofs: Vec<String> = Vec::new();
for row in rows {
let Some(preset) = appliance_boot_preset(&row.suite) else { continue };
let proof = row.test_name.as_str();
// **A pass must name what QEMU stood in for.** The roster knows
// which devices this shape substitutes; a `pass` whose message is
// silent about one of them claims a machine it did not boot. Such
// a row is folded RED with the missing names, because "green but
// do not believe it" is not a verdict this grid can render — and a
// silent green is the one outcome this crate exists to refuse.
let unnamed = roster
.iter()
.find(|p| p.name == preset)
.map(|p| p.unnamed_substitutions(&row.message))
.unwrap_or_default();
let ok = row.status == status::PASS && unnamed.is_empty();
let message = if row.status == status::PASS && !unnamed.is_empty() {
format!(
"row says pass but does not name what QEMU stood in for: {} — a KVM green on a \
substituted shape proves the shape, not the machine, so the row must name every \
swap or say `{REAL_DEPLOY_MARKER}` (message was: {:?})",
unnamed.join("; "),
row.message
)
} else {
row.message.clone()
};
let cell = cells
.entry((preset.to_string(), proof.to_string()))
.or_insert((ProofVerdict::Green, String::new()));
if !ok {
*cell = (ProofVerdict::Red, message);
} else if cell.0 == ProofVerdict::Green {
cell.1 = message;
}
if !roster.iter().any(|p| p.name == preset) && !extra_presets.iter().any(|p| p == preset) {
extra_presets.push(preset.to_string());
}
if !APPLIANCE_BOOT_PROOFS.contains(&proof) && !extra_proofs.iter().any(|p| p == proof) {
extra_proofs.push(proof.to_string());
}
}
extra_presets.sort();
extra_proofs.sort();
let mut presets = roster;
presets.extend(extra_presets.into_iter().map(|name| BootPreset {
name,
reach: BootReach::Kvm,
measured: None,
// An extra preset is one the roster never declared, so nothing here
// knows what it substitutes. No claim is made either way.
substitutions: Vec::new(),
}));
let proofs: Vec<String> =
APPLIANCE_BOOT_PROOFS.iter().map(|p| p.to_string()).chain(extra_proofs).collect();
// Every roster hole gets its reason NOW, so a render never invents one.
for p in &presets {
for proof in &proofs {
cells.entry((p.name.clone(), proof.clone())).or_insert_with(|| {
let why = match &p.reach {
BootReach::RealCloud(why) => format!("needs real cloud: {why}"),
BootReach::Kvm => match &p.measured {
Some(m) => format!("no KVM proof emitted this run (roster says measured: {m})"),
None => "no KVM proof emitted yet".to_string(),
},
};
(ProofVerdict::Absent, why)
});
}
}
Self { presets, proofs, cells }
}
pub fn verdict(&self, preset: &str, proof: &str) -> ProofVerdict {
self.cells
.get(&(preset.to_string(), proof.to_string()))
.map(|c| c.0)
.unwrap_or(ProofVerdict::Absent)
}
/// The reason behind a cell — the gap for an `Absent`, the message for a
/// red or green.
pub fn detail(&self, preset: &str, proof: &str) -> &str {
self.cells
.get(&(preset.to_string(), proof.to_string()))
.map(|c| c.1.as_str())
.unwrap_or("")
}
/// Every cell as an ordinary functional row — `pass` / `fail` / `skip`
/// with the reason in `message` — so the warehouse carries the holes and
/// the `needs real cloud` gaps as rows, never as silence.
pub fn rows(&self) -> Vec<TestResultRow> {
let mut out = Vec::new();
for p in &self.presets {
for proof in &self.proofs {
let (verdict, detail) = &self.cells[&(p.name.clone(), proof.clone())];
let st = match verdict {
ProofVerdict::Green => status::PASS,
ProofVerdict::Red => status::FAIL,
ProofVerdict::Absent | ProofVerdict::Na => status::SKIP,
};
out.push(TestResultRow {
run_id: String::new(),
repo: String::new(),
suite: format!("{APPLIANCE_BOOT_COMPONENT}/{}", p.name),
test_name: proof.clone(),
status: st.to_string(),
duration_ms: 0.0,
ts_micros: crate::harness::now_micros(),
message: detail.clone(),
aspect: crate::functional::ASPECT_FUNCTIONAL.to_string(),
metric: if *verdict == ProofVerdict::Red { 1.0 } else { 0.0 },
});
}
}
out
}
/// True when every KVM-reachable preset is green on every proof. A
/// `RealCloud` preset never holds the gate — its gap is shown, not scored.
pub fn kvm_all_green(&self) -> bool {
self.presets.iter().filter(|p| p.reach == BootReach::Kvm).all(|p| {
self.proofs.iter().all(|proof| self.verdict(&p.name, proof) == ProofVerdict::Green)
})
}
/// Fixed-width text: presets as rows, proofs as columns, plus one reason
/// line per preset that is not fully green.
pub fn render(&self) -> String {
let mut out = String::new();
out.push_str("appliance-boot (preset × proof) — ✔ proven · ✘ FAILED · — no evidence\n");
let width = self.presets.iter().map(|p| p.name.len()).max().unwrap_or(0).max(7);
out.push_str(&format!("{:width$} ", ""));
for i in 1..=self.proofs.len() {
out.push_str(&format!("{i:>3}"));
}
out.push_str(" reach\n");
for p in &self.presets {
out.push_str(&format!("{:width$} ", p.name));
for proof in &self.proofs {
out.push_str(&format!("{:>3}", self.verdict(&p.name, proof).glyph()));
}
let reach = match &p.reach {
BootReach::Kvm => "kvm".to_string(),
BootReach::RealCloud(why) => format!("needs real cloud: {why}"),
};
// The wall says what a green on this row would and would not mean.
let subs = if p.substitutions.is_empty() {
String::new()
} else {
format!(
" [substitutes {}]",
p.substitutions.iter().map(BootSubstitution::label).collect::<Vec<_>>().join("; ")
)
};
out.push_str(&format!(" {reach}{subs}\n"));
}
for (i, proof) in self.proofs.iter().enumerate() {
out.push_str(&format!(" {:>2} = {proof}\n", i + 1));
}
out
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::functional::functional_row;
// RED-BEFORE-GREEN: the hole is the load-bearing verdict — a product with
// no rows is Absent in EVERY cell, and all_green() refuses.
#[test]
fn a_product_that_never_emitted_is_a_visible_hole_not_silent_green() {
let rows =
vec![functional_row("fixture-appliance", "identity_ready", true, "pubkey=… minted=false")];
let grid = ApplianceRollup::from_rows(&rows);
assert_eq!(grid.verdict("korp", "identity_ready"), ProofVerdict::Absent);
assert_eq!(grid.verdict("gunnar", "ping_through_tunnel"), ProofVerdict::Absent);
assert!(!grid.all_green(), "holes must hold the gate red");
let rendered = grid.render();
assert!(rendered.contains("korp"), "korp's empty row is ON the wall:\n{rendered}");
assert!(rendered.contains('—'), "absence renders as —:\n{rendered}");
}
/// RED-BEFORE-GREEN: a rung the product HAS NOT GOT is its own verdict.
///
/// The three ways it must not be reported: as a pass (nothing was proven),
/// as a fail (nothing is broken), or as the same dash as a rung nobody has
/// written the test for yet — that last one is why `Absent` and `Na` render
/// with different glyphs.
#[test]
fn a_declared_n_a_rung_is_not_a_hole_and_is_not_green() {
use crate::functional::functional_na_row;
let rows = vec![
functional_na_row("fixture-appliance", "install_ok", "ships no installer ISO"),
functional_row("fixture-appliance", "identity_ready", true, "ceremony ran"),
];
let grid = ApplianceRollup::from_rows(&rows);
assert_eq!(grid.verdict("fixture", "install_ok"), ProofVerdict::Na);
assert_eq!(grid.verdict("fixture", "identity_ready"), ProofVerdict::Green);
assert_eq!(
grid.verdict("fixture", "secure_boot_armed"),
ProofVerdict::Absent,
"an UNDECLARED rung stays a hole — n/a is a claim somebody makes, never a default"
);
assert_ne!(
ProofVerdict::Na.glyph(),
ProofVerdict::Absent.glyph(),
"the hole and the missing stage must not read alike"
);
// A `skip` that does not declare itself is still red: silence about the
// reason is the failure mode, not the skip.
let mut mute = functional_na_row("fixture-appliance", "install_ok", "x");
mute.message = String::new();
assert_eq!(ApplianceRollup::from_rows(&[mute]).verdict("fixture", "install_ok"), ProofVerdict::Red);
// Precedence: a form that really climbed the rung outranks another
// form's n/a; a red sinks both.
let mixed = vec![
functional_na_row("fixture-appliance/container", "ping_through_tunnel", "no tunnel in this form"),
functional_row("fixture-appliance", "ping_through_tunnel", true, "8 bytes back"),
];
assert_eq!(ApplianceRollup::from_rows(&mixed).verdict("fixture", "ping_through_tunnel"), ProofVerdict::Green);
let sunk = vec![
functional_na_row("fixture-appliance/container", "ping_through_tunnel", "no tunnel in this form"),
functional_row("fixture-appliance", "ping_through_tunnel", false, "timed out"),
];
assert_eq!(ApplianceRollup::from_rows(&sunk).verdict("fixture", "ping_through_tunnel"), ProofVerdict::Red);
}
#[test]
fn red_wins_over_green_across_forms() {
// Same proof, two forms: host passed, container FAILED → the fold is red.
let rows = vec![
functional_row("fixture-appliance", "identity_ready", true, "host ok"),
functional_row("fixture-appliance/container", "identity_ready", false, "container broke"),
];
let grid = ApplianceRollup::from_rows(&rows);
assert_eq!(grid.verdict("fixture", "identity_ready"), ProofVerdict::Red);
}
// The three FORM SHAPES a product's emitters use — the bare product (the
// host form), a `/container` form and an `/iso-kvm` form — against the full
// ladder, so the fold is pinned for every rung at once.
//
// REFRESHED 2026-08-15: the ISO form is `iso-kvm`, NOT `iso`. holger stopped
// emitting a bare `…-appliance/iso` when the kvm/metal split landed — a
// green earned inside a faked machine must never sit where a real-hardware
// green would (`crate::chain::Form`). The fold itself is form-blind
// (`appliance_product` splits on `/`), which is exactly why a stale fixture
// here could go unnoticed — so both forms are pinned.
//
// REFRESHED 2026-09-10: the fixture product used to be spelled `holger`,
// with a comment saying it quoted holger's real strings. It did — and the
// inventory then read these eight sample rows as holger's EVIDENCE and
// reported the grid 8/8 green while holger's own repo emitted one rung.
// A product's vocabulary is pinned where the product emits it, in holger's
// own tests; the instrument's fixture names no product at all.
#[test]
fn the_form_vocabulary_folds_onto_the_canonical_grid() {
let rows = vec![
functional_row("fixture-appliance", "identity_ready", true, ""),
functional_row("fixture-appliance/container", "identity_minted", true, ""),
functional_row("fixture-appliance/container", "identity_survives_replacement", true, ""),
functional_row("fixture-appliance/container", "identity_survives_upgrade", true, ""),
functional_row("fixture-appliance/container", "tunnel_up_traffic_crossed", true, ""),
functional_row("fixture-appliance/container", "ping_through_tunnel", true, ""),
functional_row("fixture-appliance/iso-kvm", "install_ok", true, ""),
functional_row("fixture-appliance/iso-kvm", "secure_boot_armed", true, ""),
];
// Every form reads as the product, and a form that is not a form (the
// bare product) does too.
for c in ["fixture-appliance", "fixture-appliance/container", "fixture-appliance/iso-kvm"] {
assert_eq!(appliance_product(c), Some("fixture"), "{c} must fold onto fixture");
}
let grid = ApplianceRollup::from_rows(&rows);
for proof in APPLIANCE_PROOFS {
assert_eq!(
grid.verdict("fixture", proof),
ProofVerdict::Green,
"{proof} must fold onto the canonical column"
);
}
// No stray columns: every proof emitted here IS canonical.
assert_eq!(grid.proofs.len(), APPLIANCE_PROOFS.len());
}
#[test]
fn foreign_components_never_leak_into_the_grid() {
let rows = vec![
functional_row("holger-mvp", "build", true, "a build tier, not an appliance"),
functional_row("appliance", "identity_ready", true, "no product prefix"),
functional_row("-appliance", "identity_ready", true, "EMPTY product"),
];
let grid = ApplianceRollup::from_rows(&rows);
assert_eq!(grid.products.len(), APPLIANCE_PRODUCTS.len(), "only the canonical roster");
for product in APPLIANCE_PRODUCTS {
for proof in APPLIANCE_PROOFS {
assert_eq!(grid.verdict(product, proof), ProofVerdict::Absent);
}
}
}
#[test]
fn extra_products_and_proofs_floor_not_cap() {
let rows = vec![functional_row("skrymt-appliance", "quantum_ready", false, "a fourth product")];
let grid = ApplianceRollup::from_rows(&rows);
assert!(grid.products.iter().any(|p| p == "skrymt"), "extras appended");
assert!(grid.proofs.iter().any(|p| p == "quantum_ready"));
assert_eq!(grid.verdict("skrymt", "quantum_ready"), ProofVerdict::Red);
// …and the canonical roster still leads the row order, nornir last.
assert_eq!(&grid.products[..5], &["gunnar", "holger", "dwarves", "korp", "nornir"]);
}
#[test]
fn product_parsing_handles_forms_and_refuses_nonmatches() {
assert_eq!(appliance_product("holger-appliance"), Some("holger"));
assert_eq!(appliance_product("holger-appliance/container"), Some("holger"));
assert_eq!(appliance_product("korp-appliance/iso"), Some("korp"));
assert_eq!(appliance_product("holger-mvp"), None);
assert_eq!(appliance_product("-appliance"), None);
assert_eq!(appliance_product("appliance"), None);
}
// ── appliance-boot ──
fn roster() -> Vec<BootPreset> {
vec![
BootPreset { name: "qemu-local".into(), reach: BootReach::Kvm, measured: Some("qemu rig: before 2026-08-28".into()), substitutions: vec![] },
BootPreset { name: "upcloud".into(), reach: BootReach::Kvm, measured: Some("real cloud: 2026-08-29 se-sto1".into()), substitutions: vec![] },
BootPreset { name: "hetzner-cloud".into(), reach: BootReach::Kvm, measured: None, substitutions: vec![] },
BootPreset {
name: "aws-nitro".into(),
reach: BootReach::RealCloud("ENA has no QEMU model".into()),
measured: None,
substitutions: vec![BootSubstitution {
facet: "nic".into(),
missing: "ena".into(),
with: "virtio-net-pci".into(),
}],
},
]
}
/// A shape with substitutions, shaped exactly like the real `azure-gen2`
/// row: Hyper-V synthetic SCSI and synthetic NIC, both stood in for.
fn azure_like() -> Vec<BootPreset> {
vec![BootPreset {
name: "azure-gen2".into(),
reach: BootReach::Kvm,
measured: None,
substitutions: vec![
BootSubstitution { facet: "disk".into(), missing: "hv-scsi".into(), with: "virtio-scsi-pci".into() },
BootSubstitution { facet: "nic".into(), missing: "hv-netvsc".into(), with: "virtio-net-pci".into() },
],
}]
}
/// **RED: a `pass` that does not say what QEMU stood in for is not a pass.**
///
/// The whole point of carrying substitutions on the row. A KVM green for
/// `azure-gen2` proves the boot order, the firmware handoff, the partition
/// scan and the root mount — it does NOT prove `hv_netvsc` or `hv_storvsc`,
/// because neither was there. A row that stays silent about that is
/// claiming a machine it never booted, so the grid folds it RED and names
/// what is missing.
#[test]
fn a_pass_that_does_not_name_its_substitutions_is_red() {
// Silent pass → RED, and the detail names the missing swap.
let silent = vec![functional_row("appliance-boot/azure-gen2", "install_ok", true, "B1 install 2002 ms INSTALL-OK")];
let grid = ApplianceBootGrid::from_rows(azure_like(), &silent);
assert_eq!(grid.verdict("azure-gen2", "install_ok"), ProofVerdict::Red, "a silent green must not stand");
let why = grid.detail("azure-gen2", "install_ok");
assert!(why.contains("nic: hv-netvsc -> virtio-net-pci"), "the refusal must NAME the swap: {why}");
assert!(why.contains("disk: hv-scsi -> virtio-scsi-pci"), "both swaps, not just the first: {why}");
assert!(!grid.kvm_all_green());
// HALF-honest is still red: naming one swap and hiding the other is the
// same lie, smaller.
let half = vec![functional_row("appliance-boot/azure-gen2", "install_ok", true, "INSTALL-OK; disk: hv-scsi -> virtio-scsi-pci")];
let grid = ApplianceBootGrid::from_rows(azure_like(), &half);
assert_eq!(grid.verdict("azure-gen2", "install_ok"), ProofVerdict::Red);
let why = grid.detail("azure-gen2", "install_ok");
assert!(why.contains("nic: hv-netvsc -> virtio-net-pci") && !why.contains("row says pass but does not name what QEMU stood in for: disk"), "{why}");
// GREEN, same verdict word, once the row tells the truth — proof the
// red above is the guard firing and not the fold being broken.
let honest = vec![functional_row(
"appliance-boot/azure-gen2",
"install_ok",
true,
"B1 install 2002 ms INSTALL-OK (shape only — disk: hv-scsi -> virtio-scsi-pci; nic: hv-netvsc -> virtio-net-pci)",
)];
let grid = ApplianceBootGrid::from_rows(azure_like(), &honest);
assert_eq!(grid.verdict("azure-gen2", "install_ok"), ProofVerdict::Green);
// GREEN the other way: a row that ran on the REAL platform substituted
// nothing, and says so. This is the escape hatch a real Azure deploy
// takes — and it must be a claim the row makes out loud, not a default.
let deployed = vec![functional_row("appliance-boot/azure-gen2", "install_ok", true, "real cloud: Standard_D2s_v5, westeurope")];
let grid = ApplianceBootGrid::from_rows(azure_like(), &deployed);
assert_eq!(grid.verdict("azure-gen2", "install_ok"), ProofVerdict::Green);
// And a preset that substitutes NOTHING is asked for nothing: an
// exactly-mimicked shape's bare `pass` stays green.
let bare = vec![functional_row("appliance-boot/upcloud", "install_ok", true, "SeaBIOS + -serial none")];
let grid = ApplianceBootGrid::from_rows(roster(), &bare);
assert_eq!(grid.verdict("upcloud", "install_ok"), ProofVerdict::Green);
// A FAIL is still a fail, and keeps its own message — the substitution
// guard must not overwrite a genuine red's reason.
let failed = vec![functional_row("appliance-boot/azure-gen2", "serves", false, "B2 never answered in 180 s")];
let grid = ApplianceBootGrid::from_rows(azure_like(), &failed);
assert_eq!(grid.verdict("azure-gen2", "serves"), ProofVerdict::Red);
assert_eq!(grid.detail("azure-gen2", "serves"), "B2 never answered in 180 s");
}
/// The wall says, per row, what a green there would and would not mean.
#[test]
fn the_wall_names_the_substituted_devices() {
let rendered = ApplianceBootGrid::from_rows(roster(), &[]).render();
assert!(rendered.contains("[substitutes nic: ena -> virtio-net-pci]"), "{rendered}");
// A row with nothing to substitute says nothing — no noise.
let upcloud_line = rendered.lines().find(|l| l.starts_with("upcloud")).unwrap();
assert!(!upcloud_line.contains("substitutes"), "{upcloud_line}");
}
// RED-BEFORE-GREEN: an empty row set is a grid full of NAMED holes — the
// real-cloud gap and the never-ran hole are different reasons on the same
// dash, and neither is green.
#[test]
fn appliance_boot_holes_carry_their_reason_and_never_read_green() {
let grid = ApplianceBootGrid::from_rows(roster(), &[]);
assert_eq!(grid.presets.len(), 4);
assert_eq!(grid.proofs, APPLIANCE_BOOT_PROOFS.map(String::from).to_vec());
for p in ["qemu-local", "upcloud", "hetzner-cloud", "aws-nitro"] {
for proof in APPLIANCE_BOOT_PROOFS {
assert_eq!(grid.verdict(p, proof), ProofVerdict::Absent, "{p}/{proof}");
}
}
assert!(grid.detail("aws-nitro", "install_ok").starts_with("needs real cloud: ENA"));
assert_eq!(grid.detail("hetzner-cloud", "install_ok"), "no KVM proof emitted yet");
assert!(grid.detail("upcloud", "serves").contains("roster says measured: real cloud: 2026-08-29"));
assert!(!grid.kvm_all_green());
let rows = grid.rows();
assert_eq!(rows.len(), 4 * 3);
assert!(rows.iter().all(|r| r.status == status::SKIP), "absent emits `skip`, never `pass`");
assert!(rows.iter().all(|r| appliance_boot_preset(&r.suite).is_some()));
let rendered = grid.render();
assert!(rendered.contains("aws-nitro") && rendered.contains("needs real cloud"), "{rendered}");
}
#[test]
fn appliance_boot_folds_the_door_rows_and_red_wins() {
let rows = vec![
functional_row("appliance-boot/upcloud", "install_ok", true, "B1 1.5 s"),
functional_row("appliance-boot/upcloud", "serves", false, "B2 never answered in 180 s"),
functional_row("appliance-boot/qemu-local", "install_ok", true, ""),
functional_row("appliance-boot/qemu-local", "serves", true, ""),
functional_row("appliance-boot/qemu-local", "ready", true, ""),
functional_row("appliance-boot/qemu-local", "ready", false, "one red arm"),
// A real deploy CAN land a green on a needs-real-cloud row — and it
// says so, which is what excuses it from naming substitutions: on
// the real platform nothing was stood in for.
functional_row("appliance-boot/aws-nitro", "install_ok", true, "real cloud: imported AMI booted"),
// Foreign components never leak.
functional_row("fixture-appliance/iso-kvm", "install_ok", true, ""),
functional_row("appliance-boot", "install_ok", true, "no preset"),
functional_row("appliance-boot/", "install_ok", true, "empty preset"),
];
let grid = ApplianceBootGrid::from_rows(roster(), &rows);
assert_eq!(grid.verdict("upcloud", "install_ok"), ProofVerdict::Green);
assert_eq!(grid.verdict("upcloud", "serves"), ProofVerdict::Red);
assert_eq!(grid.detail("upcloud", "serves"), "B2 never answered in 180 s");
assert_eq!(grid.verdict("upcloud", "ready"), ProofVerdict::Absent);
assert_eq!(grid.verdict("qemu-local", "ready"), ProofVerdict::Red, "red wins over green");
assert_eq!(grid.verdict("aws-nitro", "install_ok"), ProofVerdict::Green);
assert_eq!(grid.verdict("aws-nitro", "serves"), ProofVerdict::Absent);
assert_eq!(grid.presets.len(), 4, "no stray rows from foreign components");
assert!(!grid.kvm_all_green());
}
#[test]
fn appliance_boot_extras_floor_not_cap_and_the_gate_is_kvm_only() {
let rows = vec![functional_row("appliance-boot/hetzner-cloud", "vnc_screendump", true, "banner seen")];
let grid = ApplianceBootGrid::from_rows(roster(), &rows);
assert_eq!(grid.proofs.len(), 4, "extra proof appended");
let mut all_kvm_green = Vec::new();
for p in roster().into_iter().filter(|p| p.reach == BootReach::Kvm) {
for proof in grid.proofs.clone() {
all_kvm_green.push(functional_row(&format!("appliance-boot/{}", p.name), &proof, true, ""));
}
}
let grid = ApplianceBootGrid::from_rows(roster(), &all_kvm_green);
assert!(grid.kvm_all_green(), "a needs-real-cloud preset never holds the KVM gate:\n{}", grid.render());
assert_eq!(grid.verdict("aws-nitro", "install_ok"), ProofVerdict::Absent);
// A preset seen only in rows is appended after the roster.
let stray = vec![functional_row("appliance-boot/moon-base", "install_ok", true, "")];
let grid = ApplianceBootGrid::from_rows(roster(), &stray);
assert_eq!(grid.presets.last().unwrap().name, "moon-base");
}
#[test]
fn appliance_boot_component_parsing() {
assert_eq!(appliance_boot_preset("appliance-boot/upcloud"), Some("upcloud"));
assert_eq!(appliance_boot_preset("appliance-boot/"), None);
assert_eq!(appliance_boot_preset("appliance-boot"), None);
assert_eq!(appliance_boot_preset("appliance-boot/a/b"), None);
assert_eq!(appliance_boot_preset("holger-appliance/iso-kvm"), None);
}
}