dwarves-gui/tests/robot_dataview.rs
//! **ROBOT SCENARIO — the 🏢 Data view tab: the warehouse deck wall + the DataFusion
//! SQL prompt.**
//!
//! Proves the tab that brought the facett warehouse dashboard back under Mímir:
//! 1. Scanning binds the Mímir/warehouse tables into a `facett_warehousedeck` WALL of
//! ≥N panes (a 3D graph, a chart, grids), and the wall RENDERS for real in an
//! `egui_kittest` frame (the deck toolbar + the SQL prompt are in the AccessKit tree,
//! not merely `state_json`).
//! 2. The SQL prompt runs a known query (`SELECT repo, count(*) FROM git_heat_facts GROUP
//! BY repo`) and shows a result grid (behind `--features dataview-sql`, the DataFusion
//! engine skade re-exports).
//! 3. A bad SQL shows an HONEST error — never a crash, never an empty-as-ok.
//!
//! It drives the pane's REAL `Facet` seam over a deterministic fixture (no server), and
//! emits a `nornir-testmatrix` `functional_status` cell under `--features testmatrix`.
use std::cell::RefCell;
use dwarves_gui::dataview::{DataViewPane, DataViewSource};
use facett_core::Facet;
use nornir_warehouse_embed::WarehouseScan;
/// A never-blank fixture warehouse of tables that are still written: git heat across two
/// repos (a chart; the sample GROUP BY yields two rows), the dependency graph (a 3D
/// graph), coverage and test outcomes (grids). The rest of the key tables are "not
/// served" so a missing table reads as a named absence. (`symbol_facts` / `call_edges`
/// have no writer since Mímir v2: the wall does not ask for them.)
struct FixtureSource;
impl FixtureSource {
fn s(cols: &[&str], rows: &[&[&str]]) -> WarehouseScan {
WarehouseScan {
columns: cols.iter().map(|s| s.to_string()).collect(),
rows: rows
.iter()
.map(|r| r.iter().map(|s| s.to_string()).collect())
.collect(),
}
}
}
impl DataViewSource for FixtureSource {
fn scan(&self, table: &str) -> Result<WarehouseScan, String> {
match table {
"test_outcomes" => Ok(Self::s(
&["repo", "test_name", "outcome"],
&[
&["znippy", "parse_stream_round_trips", "ok"],
&["korp", "spark_ingest_smoke", "failed"],
],
)),
"dep_graph_edges" => Ok(Self::s(
&["from_repo", "to_repo"],
&[
&["korp", "znippy"],
&["korp", "skade"],
&["skade", "znippy"],
],
)),
"git_heat_facts" => Ok(Self::s(
&["repo", "file", "commits_90d"],
&[
&["znippy", "src/hot.rs", "37"],
&["znippy", "src/lib.rs", "12"],
&["korp", "src/spark.rs", "5"],
],
)),
"coverage" => Ok(Self::s(
&["repo", "pct"],
&[&["znippy", "84"], &["korp", "61"]],
)),
other => Err(format!("nornir warehouse not_served: no table `{other}`")),
}
}
fn describe(&self) -> String {
"fixture (demo)".to_string()
}
}
#[test]
fn dataview_renders_the_deck_wall_and_runs_the_sql_prompt() {
use egui_kittest::kittest::Queryable;
use egui_kittest::Harness;
let mut pane = DataViewPane::with_source(Box::new(FixtureSource));
// 1) Scan binds the served key tables into a deck wall of ≥3 panes.
pane.update_json("\"scan\"");
let s = pane.state_json();
assert_eq!(s["scanned"], true, "scan built the deck");
let panes = s["pane_count"].as_u64().unwrap();
assert!(
panes >= 3,
"the wall binds ≥3 Mímir tables to panes (got {panes})"
);
assert!(
s["deck"]["graph3d_count"].as_u64().unwrap() >= 1,
"dep_graph_edges → a 3D graph pane"
);
assert!(
s["deck"]["chart_count"].as_u64().unwrap() >= 1,
"git_heat_facts → a chart pane"
);
assert!(
s["deck"]["grid_count"].as_u64().unwrap() >= 1,
"coverage/test_outcomes → grid panes"
);
// This pane was given no code room (`with_source` only): the wall has no code-room
// pane and claims none. The room's two panes are driven in `robot_dataview_room.rs`.
assert!(
s["room"]["symbols"].is_null() && s["room"]["calls"].is_null(),
"no room was asked: {}",
s["room"]
);
assert!(
s["room"]["symbols_shown"].is_null() && s["room"]["call_nodes"].is_null(),
"…so nothing is counted: {}",
s["room"]
);
// 2) The wall RENDERS for real in an egui frame (not merely in state_json).
let cell = RefCell::new(pane);
{
let mut h = Harness::builder()
.with_size(egui::vec2(1200.0, 900.0))
.build_ui(|ui| Facet::ui(&mut *cell.borrow_mut(), ui));
// The 3D-graph panes animate (force layout keeps requesting repaint), so a fixed
// number of steps rather than `run()`-to-stable — the wall never goes idle.
for _ in 0..3 {
h.step();
}
assert!(
h.get_all_by_label_contains("Data view").next().is_some(),
"the tab heading drew"
);
assert!(
h.get_all_by_label_contains("Run SQL").next().is_some(),
"the SQL prompt drew"
);
}
// 3) The SQL prompt: a known query → a result; a bad query → an honest error.
let mut pane = cell.into_inner();
let sql_ok;
let bad_ok;
#[cfg(feature = "dataview-sql")]
{
let before = pane.state_json()["pane_count"].as_u64().unwrap();
pane.update_json(
r#"{"sql":"SELECT repo, count(*) AS n FROM git_heat_facts GROUP BY repo"}"#,
);
let q = pane.state_json();
sql_ok = q["sql_ok"] == true && q["sql_rows"] == 2;
assert!(
sql_ok,
"the known query ran and grouped into two repos: {}",
q["sql_status"]
);
assert_eq!(
q["pane_count"].as_u64().unwrap(),
before + 1,
"the SQL result fed the deck as a new pane"
);
pane.update_json(r#"{"sql":"SELECT * FROM no_such_table"}"#);
let b = pane.state_json();
bad_ok = b["sql_ok"] == false && b["sql_error"].is_string();
assert!(
bad_ok,
"a bad query shows an honest error, no crash: {}",
b["sql_status"]
);
}
#[cfg(not(feature = "dataview-sql"))]
{
// Engine off: the prompt says so honestly (names the feature). Still no crash.
pane.update_json(r#"{"run_sql":true}"#);
let q = pane.state_json();
sql_ok = q["sql_enabled"] == false;
bad_ok = q["sql_error"]
.as_str()
.map(|e| e.contains("dataview-sql"))
.unwrap_or(false);
assert!(
sql_ok && bad_ok,
"off-build names the feature to rebuild with: {}",
q["sql_error"]
);
}
#[cfg(feature = "testmatrix")]
nornir_testmatrix::functional_status(
"dwarves-gui",
"dataview_deck_wall_and_sql_prompt",
panes >= 3 && sql_ok && bad_ok,
"the 🏢 Data view tab binds ≥3 Mímir/warehouse tables into a facett-warehousedeck \
wall (3D graph + chart + grids) that renders in a real egui frame, and its \
DataFusion SQL prompt runs a known query into a result (fed to a deck pane) while \
a bad query shows an honest error — the facett warehouse dashboard is back under Mímir",
);
let _ = (sql_ok, bad_ok);
}