facett-gantt/tests/matrix.rs
//! **The functional matrix cell for facett-gantt** — the pane emitted NO
//! testmatrix row of either aspect before this file (see
//! `.nornir/docs/test-matrix-inventory-history.md`, "~10 panes emit no cell").
//!
//! It is ONE call: the shared generic-`Facet` probe in `facett_core::harness`
//! headless-renders the pane, drives the JSON robot seam, and
//! `facett_core::testmatrix::probe_and_emit` turns the result into the
//! `headless_render` + `responds_to_input` rows. No bespoke harness, no twinned
//! sink (L5).
use facett_gantt::Gantt;
#[test]
fn gantt_probe_emits_render_and_input_cells() {
let mut view = Gantt::local();
let script = [
r#"{"select":"komm"}"#, // select a task by id (headless click)
r#"{"select":"beslut"}"#, // move the selection
r#""clear_selection""#, // and drop it
];
let probe = facett_core::testmatrix::probe_and_emit("facett-gantt", &mut view, &script);
assert!(probe.drew(), "the gantt chart must tessellate to vertices");
assert!(probe.cardinality() > 0, "state_json reports the tasks");
assert!(probe.responded(), "update_json must drive state — a dead seam is a regression");
// EXACT state transitions, not just "something changed".
assert_eq!(probe.steps[0].state["selected"], "komm");
assert_eq!(probe.steps[1].state["selected"], "beslut");
assert!(probe.steps[2].state["selected"].is_null(), "clear_selection empties it");
assert_eq!(probe.initial_state["task_count"], 4);
// The critical path is the dependency chain, terminating at the milestone.
assert_eq!(
probe.initial_state["critical_path"],
serde_json::json!(["utred", "komm", "beslut", "launch"])
);
}
/// RED-when-broken regression pin: `critical_path()` used to scan a `HashMap`, so
/// two tasks TYING on the finish date (`beslut`/`launch`, both 2026-06-18) picked
/// a winner by hash-iteration order — `state_json` differed between two identical
/// boards in the SAME process. Reintroducing the `finish.iter().max_by_key(..)`
/// scan makes this go RED (it did: the pre-fix run disagreed on the 4th element).
#[test]
fn critical_path_is_deterministic_across_identical_boards() {
let first = Gantt::local().critical_path();
for _ in 0..64 {
assert_eq!(Gantt::local().critical_path(), first, "critical path must not depend on hash order");
}
assert_eq!(first, vec!["utred", "komm", "beslut", "launch"], "the tie resolves to the downstream task");
}
/// RED-when-broken: an id that does not exist must NOT move the selection, so a
/// robot script with a typo cannot fake `responds_to_input`.
#[test]
fn unknown_task_id_is_a_no_op() {
let mut view = Gantt::local();
assert!(!view.select_id("no-such-task"));
assert_eq!(facett_core::Facet::selection_json(&view), serde_json::Value::Null);
assert!(view.select_id("utred"));
assert_eq!(facett_core::Facet::selection_json(&view), serde_json::json!("utred"));
}