Your browser sucks, JavaScript disabled! Gunnar's console needs JavaScript and WebAssembly. Git over SSH on port 2222 does not.

nordisk/facett

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"));
}