crates/knut-popviz/tests/robot_ui_atoms.rs
//! **Robot scenario — popviz UI atoms driven over the command seam.**
//!
//! Drives each popviz UI atom purely as data through [`PopvizState::update_json`]
//! and asserts the observable [`PopvizState::state_json`] — the remote-control
//! contract (LAW: every UI atom drivable in code, no display, no AI at runtime).
//! Runs headless in CI; the SAME shared `PopvizState` is what
//! `cargo run -p knut-popviz --features gui` renders on DISPLAY:0 for a visible
//! `watch`. Each atom also emits a `nornir-testmatrix` cell (a no-op without the
//! `testmatrix` feature, a real row with it).
use knut_popviz::{PopvizState, functional_status};
#[test]
fn robot_drives_popviz_ui_atoms_via_command_seam() {
let mut s = PopvizState::new(1.0);
let first = s.labels[0].name.clone();
// atom: target select (falkor ⇄ iceberg)
s.update_json(r#"{"select_target":"iceberg"}"#);
let ok = s.state_json()["target"] == "iceberg";
assert!(ok, "target select drives state_json");
functional_status("knut-popviz/ui_atom", "target_select", ok, "iceberg");
// atom: phase set (the load lifecycle the progress bars reflect)
s.update_json(r#"{"set_phase":"loading vertices"}"#);
let ok = s.state_json()["phase"] == "loading vertices";
assert!(ok, "phase set drives state_json");
functional_status("knut-popviz/ui_atom", "phase_set", ok, "vertices");
// atom: population-view progress advance (a per-label bar)
s.update_json(&format!(
r#"{{"advance":{{"label":"{first}","loaded":1}}}}"#
));
let ok = s.state_json()["labels"][&first]["loaded"] == 1;
assert!(ok, "advance drives per-label progress");
functional_status("knut-popviz/ui_atom", "progress_advance", ok, &first);
// atom: reach done
s.update_json(r#"{"set_phase":"done"}"#);
let ok = s.state_json()["phase"] == "done";
assert!(ok, "done phase");
functional_status("knut-popviz/ui_atom", "phase_done", ok, "done");
// atom: reset back to idle (zeroes progress; keeps the selected target)
s.update_json(r#"{"reset":true}"#);
let j = s.state_json();
let ok = j["phase"] == "idle" && j["labels"][&first]["loaded"] == 0;
assert!(ok, "reset returns to idle with zeroed progress");
functional_status("knut-popviz/ui_atom", "reset", ok, "idle");
// garbled / unknown input is a safe no-op (never panics, never corrupts)
s.update_json("not even json");
s.update_json(r#"{"select_target":"bogus"}"#);
assert_eq!(
s.state_json()["target"],
"iceberg",
"unknown input leaves the last valid selection intact"
);
}