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

nordisk/facett

facett-lineage/tests/robot.rs

//! **Robot scenario + render proof for the LINEAGE viewer.** Drives the lineage view
//! end-to-end through the inner GRAPH NAVIGATOR's `Msg` surface (query the lineage DAG →
//! select a dataset → highlight a cross-system provenance path → switch layout → export),
//! asserting the lineage `state_json` after each step, then runs a real `egui_kittest`
//! frame so the canvas painter + AccessKit nodes execute (a dataset rides in as a
//! queryable Button).

use egui_kittest::Harness;
use egui_kittest::kittest::{NodeT, Queryable};

use facett_core::Facet;
use facett_lineage::{ExportFormat, LayoutKind, LineageView, Msg};

#[test]
fn robot_drives_the_lineage_dag() {
    let mut view = LineageView::demo();

    // The lineage summary is exposed before any interaction.
    let st = view.state_json();
    assert_eq!(st["lineage"]["events"], 6);
    assert_eq!(st["lineage"]["datasets"], 5);
    assert_eq!(st["lineage"]["runs"], 5);

    // 1. Query the DAG for the warehouse-local datasets (label "skade").
    view.update(Msg::SetQuery("g.V().hasLabel('skade')".into()));
    view.update(Msg::RunQuery);
    assert_eq!(view.state_json()["query"]["result_nodes"], 4, "raw/clean/metrics/gold are skade-local");

    // 2. Select a dataset node → the inspector names it (caption = the table).
    view.update(Msg::SelectNode("main.raw".into()));
    assert_eq!(view.state_json()["inspector"]["caption"], "main.raw");

    // 3. Highlight the cross-system provenance path: spark events → … → main.gold.
    view.update(Msg::SetPath(Some(("spark:app-42/events".into(), "main.gold".into()))));
    let path = view.state_json()["path"]["nodes"].as_array().unwrap().len();
    assert!(path >= 3, "the ingest→clean→gold provenance chain is found");

    // 4. Switch the canvas to the hierarchical (DAG) layout.
    view.update(Msg::SetLayout(LayoutKind::Hierarchical));
    assert_eq!(view.state_json()["canvas"]["layout"], "hierarchical");

    // 5. Export the current lineage graph as DOT.
    view.update(Msg::Export(ExportFormat::Dot));
    assert_eq!(view.state_json()["export"]["last"], "dot");
}

#[test]
fn canvas_paints_a_dataset_into_the_accesskit_tree() {
    let mut view = LineageView::demo();
    // Grid keeps the labelled dataset buttons well-separated on the canvas.
    view.update(Msg::SetLayout(LayoutKind::Grid));
    let mut harness = Harness::builder().with_size(egui::vec2(600.0, 480.0)).build_ui(move |ui| {
        view.ui(ui);
    });
    harness.run();
    // A skade dataset rode into the tree as a queryable Button (caption · label).
    let node = harness.get_by_label("main.raw · skade").accesskit_node();
    assert_eq!(node.role(), egui::accesskit::Role::Button, "a dataset node is a queryable Button");
    // The cross-system spark dataset is present too.
    assert!(harness.query_by_label("events · spark").is_some());
}

#[test]
fn state_json_is_deterministic_and_carries_lineage() {
    let a = LineageView::demo().state_json();
    let b = LineageView::demo().state_json();
    assert_eq!(a, b, "same lineage ⇒ byte-identical state");
    assert!(a["lineage"]["operations"]["append"] == 3);
}

/// **The functional test-matrix cell** — drives the whole lineage-DAG gesture chain
/// and emits one `nornir test` **functional** row per *meaningful* check
/// (`facett_core::testmatrix::emit`, no-op in release / feature off). Before this
/// the Palantir lineage pane was invisible to the matrix — a regression that blanked
/// it showed no red cell. Each row asserts a REAL measured signal (Law 1: never
/// "didn't panic"): the DAG built datasets + runs, a label query narrowed the set, a
/// cross-system provenance path was found, and a DOT export was produced. Mirrors
/// `facett-graphnav`'s emit pattern; the `testmatrix` passthrough feature was already
/// declared in Cargo.toml, so this only supplies the emit the crate had been missing.
#[test]
fn testmatrix_cell_lineage_dag() {
    let mut view = LineageView::demo();

    // 1. The DAG built: 5 datasets (nodes) and 5 runs (edges) from 6 events.
    let st = view.state_json();
    let datasets = st["lineage"]["datasets"].as_u64().unwrap_or(0);
    let runs = st["lineage"]["runs"].as_u64().unwrap_or(0);
    let built = datasets == 5 && runs == 5;
    facett_core::testmatrix::emit(
        "facett-lineage/dag",
        "builds_datasets_and_runs",
        built,
        &format!("datasets={datasets} runs={runs}"),
    );
    assert!(built, "lineage DAG built {datasets} datasets / {runs} runs");

    // 2. A label query narrows to the 4 warehouse-local (skade) datasets.
    view.update(Msg::SetQuery("g.V().hasLabel('skade')".into()));
    view.update(Msg::RunQuery);
    let hits = view.state_json()["query"]["result_nodes"].as_u64().unwrap_or(0);
    let queried = hits == 4;
    facett_core::testmatrix::emit(
        "facett-lineage/query",
        "label_query_narrows_the_dag",
        queried,
        &format!("hasLabel('skade')→{hits} nodes"),
    );
    assert!(queried, "the skade-local query matched {hits} datasets");

    // 3. The cross-system provenance path (spark ingest → … → main.gold) is found.
    view.update(Msg::SetPath(Some(("spark:app-42/events".into(), "main.gold".into()))));
    let path_len = view.state_json()["path"]["nodes"].as_array().map(|a| a.len()).unwrap_or(0);
    let path_found = path_len >= 3;
    facett_core::testmatrix::emit(
        "facett-lineage/path",
        "cross_system_provenance_path",
        path_found,
        &format!("ingest→gold chain length={path_len}"),
    );
    assert!(path_found, "provenance chain length {path_len}");

    // 4. A DOT export of the current lineage graph is produced.
    view.update(Msg::SetLayout(LayoutKind::Hierarchical));
    view.update(Msg::Export(ExportFormat::Dot));
    let exported = view.state_json()["export"]["last"] == "dot";
    facett_core::testmatrix::emit(
        "facett-lineage/export",
        "exports_lineage_as_dot",
        exported,
        "Export(Dot) → export.last=dot",
    );
    assert!(exported, "lineage exported as DOT");
}