facett-brokkrview/tests/headless.rs
//! **Backend (4) headless drive** for facett-brokkrview.
//!
//! Drives the view through the constellation-wide [`facett_core::Panel`] seam
//! (the blanket impl makes `BrokkrView` a `Panel` for free) — applying a `Vec` of
//! **JSON** messages via `update_json` and asserting the `state_json` transition
//! *sequence*. This is the exact wire the nornir robot-UI harness + test-matrix
//! consume, so one drive here proves the pane across every backend.
use facett_brokkrview::BrokkrView;
use facett_core::{panel, Panel};
#[test]
fn json_drive_switches_views_runs_and_folds_a_finished_run() {
let mut view = BrokkrView::demo("Brokkr");
// A run-finished payload that greens a fresh "deploy" step is not in the demo;
// instead we replay a finished run over the demo's OWN pipeline and assert the
// roster + log grow and the DAG stays green.
let run_finished = r#"{
"run_finished": {
"run_id": "run-9",
"pipeline": "nornir-ci",
"status": "passed",
"trigger": "manual",
"sha": "0badf00d9999",
"passed": 4,
"total": 4,
"duration_ms": 9800,
"steps": { "compile": "passed", "unit": "passed", "lint": "passed", "release": "passed" },
"dispositions": { "compile": "ran", "unit": "cache-hit" },
"cached": ["unit"]
}
}"#;
let msgs = [
r#"{"set_view":"tree"}"#,
r#"{"set_view":"dag"}"#,
r#"{"set_view":"roster"}"#,
r#"{"run":"nornir-ci"}"#,
run_finished,
r#"{"select_run":"run-9"}"#,
];
// Drive through the Panel trait object — one snapshot per message, in order.
let snaps = panel::drive(&mut view as &mut dyn Panel, msgs);
assert_eq!(snaps.len(), 6, "one state snapshot per driven message");
// The three view toggles took effect in order.
assert_eq!(snaps[0]["view"], "tree");
assert_eq!(snaps[1]["view"], "dag");
assert_eq!(snaps[2]["view"], "roster");
// Pressing Run logged the intent (the effect is host-side; the log records it).
assert!(
snaps[3]["log"].as_array().unwrap().iter().any(|l| l.as_str().unwrap().contains("running 'nornir-ci'")),
"Run logged the request"
);
// The finished run grew the roster (demo had 3) and stayed all-green.
assert_eq!(snaps[4]["run_count"], 4, "the finished run was prepended to the roster");
assert_eq!(snaps[4]["dag"]["all_green"], true);
assert_eq!(snaps[4]["green_count"], 3, "the two demo greens + the new green run");
// Selecting the new run set the selection.
assert_eq!(snaps[5]["selected"], "run-9");
}
#[test]
fn unknown_or_malformed_json_is_a_no_op_not_a_panic() {
let mut view = BrokkrView::demo("Brokkr");
let before = Panel::state_json(&view);
// Not a known message / not even JSON — the update_json seam ignores both.
Panel::update_json(&mut view, r#"{"nonsense":true}"#);
Panel::update_json(&mut view, "this is not json [[[");
let after = Panel::state_json(&view);
assert_eq!(before, after, "an undriven/malformed message changes nothing");
}