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

nordisk/facett

facett-releaseview/tests/vault.rs

//! **RED-when-broken proofs for the CodeVault hero** (feature `graph3d`) — the
//! `Planes3D` mode rendered through the shared facett-graph3d CodeVault, plus the
//! headless `TriBadge2D` fallback that must survive with the feature on.
//!
//! Run: `cargo test -p facett-releaseview --features graph3d`.
#![cfg(feature = "graph3d")]

use std::cell::RefCell;
use std::rc::Rc;

use egui_kittest::Harness;
use egui_kittest::kittest::{By, Queryable};
use facett_core::Facet;
use facett_releaseview::model::{CrateConvergence, DepEdge, ReleaseConvergence, ReleaseEvent};
use facett_releaseview::vault::{convergence_to_codegraph, vault_state_json};
use facett_releaseview::{ReleaseView, ViewMode};

/// A 4-crate constellation starting DIVERGED: two need-release (red), one bump-drift
/// (amber), one converged (green).
fn sample() -> ReleaseConvergence {
    ReleaseConvergence::new("nordisk release-3")
        .with_node(CrateConvergence::new("skade", "0.5.1", "0.5.1", "0.5.1")) // converged
        .with_node(CrateConvergence::new("dwarves", "0.1.4", "0.1.4", "0.1.3")) // needs-release
        .with_node(CrateConvergence::new("holger", "0.6.9", "0.6.9", "0.6.8")) // needs-release
        .with_node(CrateConvergence::new("nornir", "0.5.3", "0.5.4", "0.5.4")) // bump-drift
        .with_edge(DepEdge::new(1, 0))
        .with_edge(DepEdge::new(2, 1))
        .with_edge(DepEdge::new(3, 2))
}

#[test]
fn planes3d_renders_through_the_codevault_and_reports_collapse() {
    // The hero path: Planes3D under `graph3d` lowers the model onto the shared
    // CodeVault and renders it headless (CPU projection — no GPU). The CodeVault's own
    // a11y ("Code Vault — N boxes …") proves the reused renderer drew it, not the CPU
    // parallelogram; state_json.vault carries the convergence-collapse as data.
    let view = Rc::new(RefCell::new(ReleaseView::from_model(sample()).with_mode(ViewMode::Planes3D).with_clock(0.0)));
    let ui_view = view.clone();
    let mut harness = Harness::builder().with_size(egui::vec2(1000.0, 720.0)).build_ui(move |ui| {
        ui_view.borrow_mut().ui(ui);
    });
    harness.run();
    harness.run();

    // The CodeVault renderer is the one drawing (its a11y image label).
    assert!(harness.query_all(By::new().label_contains("Code Vault")).count() >= 1, "the shared CodeVault must render the 3-plane hero");

    let sj = view.borrow().state_json();
    assert_eq!(sj["mode"], "3-plane");
    let vault = &sj["vault"];
    assert_eq!(vault["renderer"], "codevault");
    assert_eq!(vault["crate_count"], 4);
    // The sample has two needs-release (red) crates + a bump-drift → separated planes.
    assert_eq!(vault["collapsed"], false, "a diverged release has separated planes");
    assert_eq!(vault["glitch_count"], 2, "the two needs-release crates throb red");
    assert!(vault["max_abs_z"].as_f64().unwrap() > 0.0, "at least one plane is pushed off z=0");

    // dwarves (crates.io behind) has its separated box on the CRATES_IO plane.
    let dwarves = vault["crates"].as_array().unwrap().iter().find(|c| c["krate"] == "dwarves").unwrap();
    let lag = dwarves["laggards"].as_array().unwrap();
    assert!(lag.iter().any(|l| l["plane"] == "cratesio"), "dwarves is separated on the crates.io plane");
    assert!(lag.iter().any(|l| l["z"].as_f64().unwrap().abs() > 0.0), "the laggard is off z=0");
}

#[test]
fn all_agree_collapses_the_planes_into_one() {
    // Convergence complete ⇒ every crate is a single box on the merged plane; the
    // state_json reports collapsed=true and convergence 1.0. RED-when-broken: if a
    // converged crate spuriously split, collapsed flips false.
    let converged = ReleaseConvergence::new("done")
        .with_node(CrateConvergence::new("a", "1.0.0", "1.0.0", "1.0.0"))
        .with_node(CrateConvergence::new("b", "2.0.0", "2.0.0", "2.0.0"))
        .with_edge(DepEdge::new(1, 0));
    let v = ReleaseView::from_model(converged).with_mode(ViewMode::Planes3D).with_clock(0.0);
    let sj = v.state_json();
    assert_eq!(sj["converged"], true);
    let vault = &sj["vault"];
    assert_eq!(vault["collapsed"], true, "all-agree collapses every box onto the merged plane");
    assert_eq!(vault["box_count"], 2, "one box per converged crate");
    assert_eq!(vault["glitch_count"], 0, "nothing is throbbing when converged");
    assert!((vault["converged_fraction"].as_f64().unwrap() - 1.0).abs() < 1e-9);
}

#[test]
fn tribadge_fallback_still_renders_headless_with_graph3d_on() {
    // The cheap 2D fallback must keep working even when the GPU hero feature is
    // compiled in (headless / no-GPU / wasm reach for this path).
    let view = Rc::new(RefCell::new(ReleaseView::from_model(sample()).with_mode(ViewMode::TriBadge2D).with_clock(0.0)));
    let ui_view = view.clone();
    let mut harness = Harness::builder().with_size(egui::vec2(1000.0, 720.0)).build_ui(move |ui| {
        ui_view.borrow_mut().ui(ui);
    });
    harness.run();
    harness.run();

    for krate in ["skade", "dwarves", "holger", "nornir"] {
        assert!(harness.query_all(By::new().label_contains(krate)).count() >= 1, "tri-badge fallback shows `{krate}`");
    }
    // It is NOT the CodeVault (the fallback stays the flat CPU DAG).
    assert_eq!(harness.query_all(By::new().label_contains("Code Vault")).count(), 0, "TriBadge2D is the CPU fallback, not the vault");
    assert_eq!(view.borrow().state_json()["mode"], "tri-badge");
}

#[test]
fn live_cascade_collapses_the_vault_scene() {
    // The animation IS the convergence: as publish events land, the separated planes
    // collapse. Assert the box tally + collapse flag follow the model node-by-node.
    let mut v = ReleaseView::from_model(sample());
    let before = vault_state_json(v.model(), 1.0);
    assert_eq!(before["collapsed"], false);
    let before_boxes = before["box_count"].as_u64().unwrap();

    v.apply_event(ReleaseEvent::Published { krate: "dwarves".into(), version: "0.1.4".into() });
    v.apply_event(ReleaseEvent::Published { krate: "holger".into(), version: "0.6.9".into() });
    v.apply_event(ReleaseEvent::LocalBumped { krate: "nornir".into(), version: "0.5.4".into() });

    let after = vault_state_json(v.model(), 1.0);
    assert_eq!(after["collapsed"], true, "the whole scene collapses once every crate converges");
    assert_eq!(after["glitch_count"], 0);
    assert!(after["box_count"].as_u64().unwrap() < before_boxes, "laggard boxes merge away as crates converge");
    // The mapping itself agrees: no laggards remain.
    let (_, positions, meta) = convergence_to_codegraph(v.model(), 1.0);
    assert!(meta.iter().all(|m| m.target), "no separated planes left after convergence");
    assert!(positions.iter().all(|p| p[2].abs() < 1e-4), "every box on the merged plane");
}