facett-fleet-view/tests/robot.rs
//! **Robot state_json-drive scenarios for the fleet coverage board**, driven
//! through the SHARED generic-Facet probe in `facett_core::harness` (no
//! hand-rolled render+emit boilerplate — the exact idiom `facett-matrix`'s robot
//! test uses). Two red-when-broken scenarios, asserted over `state_json`, NOT
//! "it rendered":
//!
//! (a) a **fully-covered** run → the GREEN `Gap == ∅` banner (`gate_green`) and
//! every cell green;
//! (b) a run **missing one surface** (`viz_tab:chat@thin`) → the RED banner names
//! the exact missing node and that cell is red.
//!
//! The probe also feeds the `update_json` seam a scripted member drill-in →
//! clear, proving the input surface is LIVE (a dead seam is a regression), and
//! emits the shared `headless_render` + `responds_to_input` functional cells (a
//! no-op unless built with `--features testmatrix`).
use std::collections::BTreeSet;
use facett_fleet_view::model::MemberHealth;
use facett_fleet_view::{status, FleetMember, FleetView, GateReport, TestResultRow};
use nornir_testmatrix::coverage::Allowlist;
use nornir_testmatrix::discover::{viz_tabs, Surface};
/// The fleet surface = `chat` + `board` tabs, each × {fat, thin}. `covered` = all
/// keys minus the optional `hole` (the one surface a run failed to cover).
fn report(hole: Option<&str>) -> GateReport {
let mut surface = Surface::new();
surface.extend(viz_tabs(["chat", "board"]));
let covered: BTreeSet<String> = surface
.nodes
.iter()
.map(|n| n.key_str())
.filter(|k| hole != Some(k.as_str()))
.collect();
GateReport::compute("run-1", "nordisk", &surface, &covered, &Allowlist::new())
}
fn row(member: &str, surface: &str, st: &str) -> TestResultRow {
TestResultRow {
run_id: "run-1".into(),
repo: member.into(),
suite: member.into(),
test_name: format!("{member}::{surface}"),
status: st.into(),
duration_ms: 1.0,
ts_micros: 100,
message: if status::is_red(st) { "boom".into() } else { String::new() },
aspect: surface.into(),
metric: 0.0,
}
}
const SURFACES: [&str; 4] =
["viz_tab:chat@fat", "viz_tab:chat@thin", "viz_tab:board@fat", "viz_tab:board@thin"];
/// (a) A fully-covered fleet run: GREEN `Gap == ∅` banner + every cell green.
#[test]
fn fully_covered_run_is_green_gap_and_all_cells_green() {
let mut rows = Vec::new();
for m in ["nornir", "dwarves"] {
for s in SURFACES {
rows.push(row(m, s, status::PASS));
}
}
let members = vec![
FleetMember::new("nornir", MemberHealth::Ready),
FleetMember::new("dwarves", MemberHealth::Ready),
];
let mut view = FleetView::new("Fleet").with_run(report(None), rows, members);
// Drive the member drill-in / clear through the LIVE update_json seam.
let script = [r#"{"select_member":"nornir"}"#, r#""clear_selection""#];
let probe = facett_core::testmatrix::probe_and_emit("facett-fleet-view", &mut view, &script);
assert!(probe.drew(), "the board must tessellate to vertices");
assert!(probe.cardinality() > 0, "state_json reports the board");
assert!(probe.responded(), "update_json must drive state — a dead seam is a regression");
// The GREEN Gap == ∅ verdict, read as data.
let s = &probe.initial_state;
assert_eq!(s["gate_green"], true, "fleet fully covered ⇒ GREEN banner");
assert_eq!(s["gap_clean"], true);
assert_eq!(s["missing"].as_array().unwrap().len(), 0, "no missing surface");
// Every cell green: none red, none not-driven.
assert_eq!(s["cells_red"], 0, "no red cells in a fully-covered run");
assert_eq!(s["cells_not_driven"], 0, "every surface was driven");
assert!(s["cells_green"].as_u64().unwrap() >= 8, "all 2×4 cells green");
for m in s["members"].as_array().unwrap() {
for c in m["cells"].as_array().unwrap() {
assert_eq!(c["green"], true, "cell {} / {} is green", m["name"], c["surface"]);
}
}
// The drill-in lands, the clear removes it.
assert_eq!(probe.steps[0].state["selected"], "nornir");
assert!(probe.steps[0].changed, "select_member moved state");
assert_eq!(probe.final_state["selected"], serde_json::Value::Null, "clear_selection cleared it");
}
/// (b) A run missing ONE surface: RED banner names the exact node + that cell red.
#[test]
fn missing_surface_run_is_red_banner_naming_the_node_and_a_red_cell() {
let hole = "viz_tab:chat@thin";
// The failing drive is WHY the surface is uncovered: nornir's chat@thin red;
// every other surface passes.
let mut rows = Vec::new();
for s in SURFACES {
let st = if s == hole { status::FAIL } else { status::PASS };
rows.push(row("nornir", s, st));
}
let members = vec![FleetMember::new("nornir", MemberHealth::Ready)];
let mut view = FleetView::new("Fleet").with_run(report(Some(hole)), rows, members);
let probe = facett_core::testmatrix::probe_and_emit("facett-fleet-view", &mut view, &[]);
assert!(probe.drew(), "the board must tessellate to vertices");
let s = &probe.final_state;
// The RED Gap != ∅ verdict names the EXACT missing node.
assert_eq!(s["gate_green"], false, "a missing surface ⇒ RED banner");
assert_eq!(s["gap_clean"], false);
let missing: Vec<&str> = s["missing"].as_array().unwrap().iter().map(|v| v.as_str().unwrap()).collect();
assert_eq!(missing, vec![hole], "missing == [viz_tab:chat@thin]");
// That surface's cell for nornir is RED.
let nornir = s["members"].as_array().unwrap().iter().find(|m| m["name"] == "nornir").unwrap();
let cell = nornir["cells"].as_array().unwrap().iter().find(|c| c["surface"] == hole).unwrap();
assert_eq!(cell["red"], true, "the missing surface's cell is red");
assert_eq!(cell["verdict"], "failed");
assert_eq!(nornir["row_red"], true, "a member with a red cell reads as a red row");
assert!(s["cells_red"].as_u64().unwrap() >= 1);
}
/// A dead member (spin FAILED, no rows) is a RED row — not a false blank.
#[test]
fn dead_member_reads_as_a_red_row() {
let rows = vec![row("nornir", "viz_tab:chat@fat", status::PASS)];
let members =
vec![FleetMember::new("nornir", MemberHealth::Ready), FleetMember::new("korp", MemberHealth::Failed)];
let mut view = FleetView::new("Fleet").with_run(report(None), rows, members);
let probe = facett_core::testmatrix::probe_and_emit("facett-fleet-view", &mut view, &[]);
let s = &probe.final_state;
let korp = s["members"].as_array().unwrap().iter().find(|m| m["name"] == "korp").unwrap();
assert_eq!(korp["health"], "failed");
assert_eq!(korp["row_red"], true, "a dead member is a red row");
for c in korp["cells"].as_array().unwrap() {
assert_eq!(c["red"], true, "a dead member's cells are red, not blank");
}
}