tests/viz_arch_subviews.rs
//! 🏛 **Architecture tab as a CONTAINER** — inject-and-assert (LAW #1) + a wgpu
//! snapshot PROOF (LAW #4) for the live, clickable, multi-functionality system
//! diagram.
//!
//! The 🏛 Architecture tab is a container with an internal sub-view selector
//! (Deps / Live system / Wiring / Hållpunkter). This test proves, WITHOUT pixels
//! (`state_json["architecture"]`):
//! 1. the tab lists every sub-view + which is active (the container contract);
//! 2. the injected wiring board renders with the right node/edge/per-kind counts;
//! 3. a node click resolves to a real `file:line` via the shared symbol
//! resolver (the click-to-code bridge) — asserted path + line for a known
//! symbol, plus the inline source-preview window;
//! 4. the Hållpunkter (boundary) landmarks include known ui / gRPC / emitter
//! functions, classified by the coverage feature's `classify` (reused).
//! Plus a wgpu golden PNG of the boundaries lens (LAW #4).
//!
//! Run: `cargo test --features viz --test viz_arch_subviews -- --nocapture`
#![cfg(feature = "viz")]
use std::path::{Path, PathBuf};
use egui_kittest::Harness;
use nornir::arch::{ArchEdge, ArchEdgeKind, ArchGraph, ArchNode, NodeKind};
use nornir::knowledge::symbols::SymbolRow;
use nornir::viz::UrdrThreadsApp;
const MEMBERS: [&str; 1] = ["nornir"];
/// The same four-layer fixture board the pane lays out.
fn fixture_board() -> ArchGraph {
let node = |id: &str, label: &str, kind: NodeKind| ArchNode {
id: id.into(),
label: label.into(),
kind,
};
let edge = |from: &str, to: &str, kind: ArchEdgeKind| ArchEdge {
from: from.into(),
to: to.into(),
kind,
};
ArchGraph {
nodes: vec![
node(
"component:ArchTabState",
"ArchTabState",
NodeKind::Component,
),
node("grpc:Viz.Architecture", "Viz.Architecture", NodeKind::Grpc),
node("corefn:nornir::arch", "nornir::arch", NodeKind::CoreFn),
node(
"table:architecture_wiring",
"architecture_wiring",
NodeKind::Table,
),
],
edges: vec![
edge(
"component:ArchTabState",
"table:architecture_wiring",
ArchEdgeKind::Reads,
),
edge(
"component:ArchTabState",
"corefn:nornir::arch",
ArchEdgeKind::Calls,
),
edge(
"grpc:Viz.Architecture",
"table:architecture_wiring",
ArchEdgeKind::Writes,
),
],
}
}
/// A small scanned-symbol set in a REAL temp file, so click-to-code resolves to
/// an actual `file:line` AND the inline source-preview can read the lines. The
/// `ArchTabState` symbol lives in `src/viz/arch_tab.rs`; the `state_json` symbol
/// is an emitter boundary; the `draw` symbol is a ui boundary; `architecture` in
/// the server file is a gRPC boundary.
fn fixture_symbols(file_rel: &str) -> Vec<SymbolRow> {
let sym = |module: &str, name: &str, kind: &str, file: &str, line: u32| SymbolRow {
crate_name: "nornir".into(),
module_path: module.into(),
item_kind: kind.into(),
item_name: name.into(),
visibility: "pub".into(),
file: file.into(),
line,
doc_lines: 0,
signature: None,
};
vec![
sym(
"nornir::viz::arch_tab",
"ArchTabState",
"struct",
file_rel,
3,
),
sym(
"nornir::viz::arch_tab::ArchTabState",
"draw",
"fn",
"src/viz/arch_tab.rs",
600,
),
sym(
"nornir::viz::arch_tab::ArchTabState",
"state_json",
"fn",
"src/viz/arch_tab.rs",
1100,
),
sym(
"nornir_server::viz",
"architecture",
"fn",
"src/bin/nornir-server.rs",
1200,
),
]
}
/// Build a harness over a fresh app with a real checkout root containing the
/// preview file, inject the board + symbols, drive to the Architecture tab.
fn harness(tag: &str) -> (Harness<'static, UrdrThreadsApp>, PathBuf) {
let pid = std::process::id();
let dir = std::env::temp_dir();
// TODO: Audit that the environment access only happens in single-threaded code.
unsafe {
std::env::set_var(
"NORNIR_VIZ_CMD",
dir.join(format!("nornir-archsv-cmd-{tag}-{pid}.json")),
)
};
// TODO: Audit that the environment access only happens in single-threaded code.
unsafe {
std::env::set_var(
"NORNIR_VIZ_STATE",
dir.join(format!("nornir-archsv-state-{tag}-{pid}.json")),
)
};
// TODO: Audit that the environment access only happens in single-threaded code.
unsafe { std::env::set_var("NORNIR_VIZ_NO_DURABLE_ACTIONLOG", "1") };
let warehouse_root = dir.join(format!("nornir-archsv-wh-{tag}-{pid}"));
let _ = std::fs::create_dir_all(&warehouse_root);
// A real checkout dir with the preview source file (so source_window reads it).
let checkout = dir.join(format!("nornir-archsv-co-{tag}-{pid}"));
let _ = std::fs::create_dir_all(checkout.join("nornir/src/viz"));
let file_rel = "src/viz/arch_tab.rs";
std::fs::write(
checkout.join("nornir").join(file_rel),
"// line 1\n// line 2\npub struct ArchTabState {\n field: u8,\n}\n// line 6\n",
)
.unwrap();
let mut app = UrdrThreadsApp::with_repos(
warehouse_root,
"nornir".to_string(),
checkout,
MEMBERS.iter().map(|s| s.to_string()).collect(),
);
app.inject_arch_for_test(
fixture_board(),
MEMBERS.iter().map(|s| s.to_string()).collect(),
);
app.inject_arch_symbols_for_test(fixture_symbols(file_rel));
let h = Harness::builder()
.with_size(egui::Vec2::new(1400.0, 900.0))
.wgpu()
.build_ui_state(|ui, app: &mut UrdrThreadsApp| app.draw_ui(ui), app);
(h, PathBuf::from(file_rel))
}
/// (a) The container lists all sub-views; (b) the board renders with the right
/// counts.
#[test]
fn arch_container_lists_subviews_and_renders_board() {
let (mut h, _) = harness("subviews");
for _ in 0..6 {
h.run_ok();
}
let state = h.state().state_json();
let arch = &state["architecture"];
// (a) every sub-view is enumerable + the default active one is reported.
let subs: Vec<&str> = arch["sub_views"]
.as_array()
.unwrap()
.iter()
.map(|v| v.as_str().unwrap())
.collect();
assert_eq!(
subs,
vec!["Deps", "Live", "Wiring", "Boundaries", "CallGraph", "Metro"],
"all six lenses listed (CallGraph restored as its own lens, separate from Metro)"
);
assert!(
arch["sub_view"].as_str().is_some(),
"an active sub-view is reported"
);
// (b) the injected board's counts.
assert_eq!(arch["node_count"].as_u64(), Some(4));
assert_eq!(arch["edge_count"].as_u64(), Some(3));
assert_eq!(arch["components"].as_u64(), Some(1));
assert_eq!(arch["grpc"].as_u64(), Some(1));
assert_eq!(arch["tables"].as_u64(), Some(1));
}
/// (c) Clicking a node resolves to a real `file:line` via the shared resolver,
/// readable back in `state_json["architecture"]["last_open"]` (no pixels).
#[test]
fn arch_click_resolves_symbol_to_file_line() {
let (mut h, file_rel) = harness("click");
for _ in 0..3 {
h.run_ok();
}
// Drive a click-to-code on the ArchTabState component (best-effort editor:
// `code` may be absent in CI — preview + resolution are the asserted parts).
let found = h.state_mut().arch_open_symbol_for_test("ArchTabState");
assert!(found, "the symbol resolved to a location");
for _ in 0..3 {
h.run_ok();
}
let state = h.state().state_json();
let open = &state["architecture"]["last_open"];
assert_eq!(open["query"], "ArchTabState");
assert_eq!(
open["file"].as_str(),
Some(file_rel.to_str().unwrap()),
"resolved to the right file"
);
assert_eq!(
open["line"].as_u64(),
Some(3),
"resolved to the struct's line"
);
assert_eq!(open["symbol"], "ArchTabState");
// The inline source-preview window read real lines around line 3.
assert!(
open["preview_lines"].as_u64().unwrap() >= 3,
"preview window has lines: {open}"
);
}
/// (d) The Hållpunkter (boundary) landmarks include known ui / gRPC / emitter
/// functions, classified by the coverage feature's `classify` (reused).
#[test]
fn arch_boundary_landmarks_cover_ui_grpc_emitter() {
let (mut h, _) = harness("bnd");
// Switch to the Boundaries lens.
assert!(h.state_mut().arch_set_sub_view_for_test("Boundaries"));
for _ in 0..6 {
h.run_ok();
}
let state = h.state().state_json();
let b = &state["architecture"]["boundaries"];
assert!(b["ui"].as_u64().unwrap() >= 1, "a ui landmark (draw)");
assert!(
b["grpc"].as_u64().unwrap() >= 1,
"a gRPC landmark (architecture handler)"
);
assert!(
b["emitter"].as_u64().unwrap() >= 1,
"an emitter landmark (state_json)"
);
let labels: Vec<&str> = b["landmarks"]
.as_array()
.unwrap()
.iter()
.filter_map(|l| l["label"].as_str())
.collect();
assert!(
labels.contains(&"draw"),
"ui draw landmark present: {labels:?}"
);
assert!(
labels.contains(&"state_json"),
"emitter state_json landmark present: {labels:?}"
);
assert!(
labels.contains(&"architecture"),
"gRPC handler landmark present: {labels:?}"
);
// LAW #4: the boundaries lens renders to a committed PNG.
let dir = Path::new(env!("CARGO_MANIFEST_DIR")).join("tests/snapshots");
std::fs::create_dir_all(&dir).expect("create tests/snapshots");
let image = h.render().expect("wgpu render failed");
let png = dir.join("viz_architecture_boundaries.png");
image.save(&png).expect("write boundaries PNG");
let size = std::fs::metadata(&png).map(|m| m.len()).unwrap_or(0);
assert!(
size > 3_000,
"boundaries PNG suspiciously small ({size} bytes)"
);
h.snapshot("viz_architecture_boundaries");
let results = h.take_snapshot_results();
if let Err(results) = results.into_result() {
let inner: Vec<_> = results.into();
for err in &inner {
let msg = err.to_string();
assert!(
msg.contains("Missing snapshot"),
"golden snapshot diff: {msg}"
);
}
}
}