facett-graphview/tests/metro_view.rs
//! **Metro view — inject-assert headless test.** Build a 2-line [`MetroMap`]
//! (one all-lit → green, one with an unlit emitter → red), drive [`MetroView`]
//! one headless egui frame through the shared `facett_core::harness`, and assert
//! its `state_json` reports exactly what was drawn — line A green, line B red
//! with the unlit station named — without a single pixel. Real data in, real
//! verdict out (not no-crash).
use facett_core::{Facet, harness};
use facett_graphview::{MetroBranch, MetroLine, MetroMap, MetroStation, MetroView, StationKind};
/// The injected fixture: A is fully lit (green) and its gRPC verb FORKS to BOTH
/// caller arms (UI viz + CLI, both lit); B has one unlit emitter (red).
fn two_lines() -> MetroMap {
let line_a = MetroLine::new(
"A",
"Bench Run",
vec![
MetroStation::new("ui::run", "Run", StationKind::Start, true),
MetroStation::new("bench::collect", "collect", StationKind::Emitter, true),
MetroStation::new("bench::normalize", "normalize", StationKind::PassThrough, true),
MetroStation::with_branches(
"grpc::Submit",
"Bench.Submit",
StationKind::Grpc,
true,
vec![
MetroBranch::new("caller::ui::Submit", "viz", StationKind::UiClient, true),
MetroBranch::new("caller::cli::Submit", "nornir", StationKind::CliClient, true),
],
),
MetroStation::new("wh::bench_runs", "bench_runs", StationKind::Terminus, true),
],
);
let line_b = MetroLine::new(
"B",
"Docs Export",
vec![
MetroStation::new("ui::export", "Export", StationKind::Start, true),
MetroStation::new("docs::gather", "gather", StationKind::Emitter, true),
MetroStation::new("docs::render_svg", "render_svg", StationKind::Emitter, false), // UNLIT
MetroStation::new("grpc::Render", "Docs.Render", StationKind::Grpc, true),
MetroStation::new("wh::doc_exports", "doc_exports", StationKind::Terminus, true),
],
);
MetroMap::new(vec![line_a, line_b])
}
#[test]
fn headless_render_reports_green_and_red_lines() {
let mut view = MetroView::new("Metro");
view.set_map(two_lines());
// Drive one real headless egui frame — this IS the render the pixels come
// from, so the vertex count proves it actually drew the tracks/stops.
let report = harness::headless_render(&mut view);
assert_eq!(report.title, "Metro");
assert!(report.drew(), "the metro lines tessellated to vertices");
// "green and red lines" is a claim about COLOUR, and `drew()` cannot see colour —
// every assertion below reads state_json, i.e. the DATA, so the paint half of this
// test's name was unverified. Assert the palette the painter actually emitted
// contains both a green-dominant and a red-dominant ink.
// The thresholds are deliberately STRICT. A loose `r > g + 24` also matches the
// amber warning ink (#D6A046) this view paints, and a guard that accepts amber for
// red is itself hollow — verified: with the red verdict colour deleted the loose
// form stayed green, the form below goes red.
let chan = |c: &egui::Color32| (c.r() as i16, c.g() as i16, c.b() as i16);
let greenish = report.ink_colors.iter().any(|(c, _)| { let (r, g, b) = chan(c); g > r + 80 && g > b + 24 });
let reddish = report.ink_colors.iter().any(|(c, _)| { let (r, g, b) = chan(c); r > g + 80 && r > b + 80 });
assert!(
greenish && reddish,
"the render must actually contain a GREEN line and a RED one, got \
greenish={greenish} reddish={reddish} from {:?}",
report.ink_colors,
);
let s = &report.state;
assert_eq!(s["lines"], 2, "two lines drawn");
assert_eq!(s["green_lines"], 1, "exactly one green line");
assert_eq!(s["green"], false, "the map is not fully green");
let lines = s["line"].as_array().expect("line array");
assert_eq!(lines.len(), 2);
// Line A: green, no unlit stations.
let a = &lines[0];
assert_eq!(a["id"], "A");
assert_eq!(a["green"], true, "line A is green (every gating stop lit)");
assert!(a["unlit"].as_array().unwrap().is_empty(), "line A has no unlit stops");
// The pass-through is present but doesn't count as a station.
assert_eq!(a["station_count"], 4, "4 gating stops; the pass-through isn't one");
// Line B: red, the unlit emitter named.
let b = &lines[1];
assert_eq!(b["id"], "B");
assert_eq!(b["green"], false, "line B is red (an emitter never fired)");
let unlit: Vec<&str> = b["unlit"].as_array().unwrap().iter().map(|v| v.as_str().unwrap()).collect();
assert_eq!(unlit, ["render_svg"], "the unlit station is named");
// And the unlit stop is the emitter, reported lit=false in the station list.
let render_svg = b["stations"]
.as_array()
.unwrap()
.iter()
.find(|st| st["label"] == "render_svg")
.expect("render_svg station present");
assert_eq!(render_svg["kind"], "emitter");
assert_eq!(render_svg["lit"], false);
// FORK: line A's gRPC verb diverges to TWO marked caller arms (UI + CLI), both
// lit — asserted on the serialised branch topology (LAW 6), through the real
// headless render above (so the diverging arms actually tessellated).
let grpc_a = a["stations"].as_array().unwrap().iter().find(|st| st["kind"] == "grpc").unwrap();
let arms = grpc_a["branches"].as_array().expect("the gRPC stop carries caller arms");
assert_eq!(arms.len(), 2, "called by BOTH ui+cli → two arms");
let markers: Vec<&str> = arms.iter().map(|x| x["marker"].as_str().unwrap()).collect();
assert!(markers.contains(&"UI") && markers.contains(&"CLI"), "both markers present: {markers:?}");
assert!(arms.iter().all(|x| x["lit"].as_bool().unwrap()), "both arms exercised (lit)");
}
#[test]
fn discovery_local_ctor_renders_two_lines() {
// The discovery-contract ctor must produce a real, renderable map.
let mut view = MetroView::local();
let report = harness::headless_render(&mut view);
assert!(report.drew());
assert_eq!(report.state["lines"], 2);
assert_eq!(report.state["green_lines"], 1);
// Themeable + resizable + selectable per the contract.
let caps = view.caps();
assert!(caps.themeable && caps.resizable && caps.selectable);
}