dwarves-gui/tests/common/mod.rs
//! **A canned code room behind a REAL forge proxy** — the stand every code-graph
//! pane's robot test drives the SHIPPED shell on:
//!
//! ```text
//! mock nornir (Warehouse + CodeRoom) ── dwarves warehouse-proxy (:auto) ── the shell
//! ```
//!
//! The room's answers are written here ONCE, in the reader's own shapes, and are
//! consistent with each other (one small workspace: repo `shop`, crates `shop` and
//! `shop-core`, the dependency `serde`). A test asserts that what a pane's
//! `state_json` holds IS these values — and that a refused answer leaves the state
//! and no number.
#![allow(dead_code)]
use nornir_warehouse_embed::testkit::{spawn_mock_nornir, MockCodeRoom, MockWarehouseServer};
use serde_json::{json, Value};
/// The workspace the shell selects.
pub const WS: &str = "shop-ws";
/// The room line every pane prints for an answer read from [`room_id`].
pub const ROOM_LINE: &str = "workspace shop-ws · shop@2 (0123456789) · 985 nodes · 2310 edges";
/// The sentence nornir gives for the two world-ring surfaces.
pub const COMPAT_NOTE: &str = "Coderoom.Compat: the world ring (crates.io sources of other crates, COMPATIBLE edges, semver diffs) has no v2 producer (gap P5)";
pub const BREAKS_NOTE: &str = "Coderoom.WhatBreaks: the registry versions of other crates (the world ring) are not ingested (gap P5)";
/// The sentence of a workspace nothing was consumed into.
pub const NOT_CONSUMED: &str = "code room of `shop-ws`: no repo has a consumer offset yet — nothing of this workspace was consumed";
pub fn room_id() -> Value {
json!({
"workspace": WS, "universe": false, "nodes": 985, "edges_total": 2310,
"repos": [{ "repo": "shop", "seq": 2, "sha": "0123456789abcdef0123" }],
"edges": { "CALLS": 1400, "CONTAINS": 700, "DEPENDS_ON": 3 },
})
}
fn evidence(notes: &[&str]) -> Value {
json!({ "missing": [], "notes": notes })
}
pub fn refusal(state: &str, note: &str) -> Value {
json!({ "state": state, "refused": true, "note": note })
}
/// `Viz.CrateGraph`.
pub fn crate_graph() -> Value {
json!({
"state": "complete",
"crates": [
{ "id": "serde@1.0.228", "name": "serde", "version": "1.0.228", "repo": null, "local": false, "nodes": 900, "symbols": 310, "fns": 200, "files": 0, "calls_inside": 0 },
{ "id": "shop-core@0.1.0", "name": "shop-core", "version": "0.1.0", "repo": "shop", "local": true, "nodes": 25, "symbols": 12, "fns": 9, "files": 2, "calls_inside": 5 },
{ "id": "shop@0.1.0", "name": "shop", "version": "0.1.0", "repo": "shop", "local": true, "nodes": 60, "symbols": 41, "fns": 30, "files": 4, "calls_inside": 22 },
],
"edges": [
{ "from": "shop-core@0.1.0", "to": "serde@1.0.228", "depends_on": 1, "calls": 0, "call_sites": 0, "uses_type": 0 },
{ "from": "shop@0.1.0", "to": "serde@1.0.228", "depends_on": 1, "calls": 3, "call_sites": 4, "uses_type": 0 },
{ "from": "shop@0.1.0", "to": "shop-core@0.1.0", "depends_on": 1, "calls": 7, "call_sites": 19, "uses_type": 2 },
],
"repos": [{ "repo": "shop", "seq": 2, "sha": "0123456789abcdef0123", "crates": ["shop-core@0.1.0", "shop@0.1.0"], "consumes": ["serde", "shop-core"] }],
"repo_edges": [],
"evidence": evidence(&["calls through operators, closures and fn pointers are not recorded: a lower bound"]),
"room": room_id(),
})
}
/// `Coderoom.SnapshotDiff` for `shop`.
pub fn snapshot_diff() -> Value {
json!({
"state": "complete", "from": "shop@1", "to": "shop@2",
"added_nodes": 3, "removed_nodes": 1, "changed_nodes": 2,
"by_crate": {
"crates_added": ["log@0.4.29"], "crates_removed": [],
"crates": [{ "crate": "shop@0.1.0", "symbols_added": 2, "symbols_removed": 1, "symbols_changed": 2, "nodes_added": 3, "nodes_removed": 1, "nodes_changed": 2 }],
},
"evidence": evidence(&[]),
})
}
/// `Viz.FunctionTier`.
pub fn function_tier() -> Value {
json!({
"state": "complete",
"repos": [{
"repo": "shop",
"crates": [
{ "crate": "shop", "version": "0.1.0", "total_fns": 3, "total_calls": 2,
"fns": [{ "id": "shop::checkout", "name": "checkout" }, { "id": "shop::pay::charge", "name": "charge" }, { "id": "shop::pay::audit", "name": "audit" }],
"calls": [[0, 1, 3], [1, 2, 2]] },
{ "crate": "shop-core", "version": "0.1.0", "total_fns": 1, "total_calls": 0, "fns": [{ "id": "shop_core::total", "name": "total" }], "calls": [] },
],
}],
"total_fns": 4, "total_calls": 2,
"evidence": evidence(&["a lower bound"]),
"room": room_id(),
})
}
/// `Knowledge.Symbols`.
pub fn symbols() -> Value {
let sym = |name: &str, line: u32, end: u32| {
json!({
"repo": "shop", "crate_name": "shop", "module_path": "shop::pay", "item_kind": "fn", "item_name": name, "visibility": "pub",
"file": "src/pay.rs", "line": line, "end_line": end, "path": format!("shop::pay::{name}"), "moniker": format!("m:{name}"),
})
};
json!({
"state": "complete", "total": 53, "by_kind": { "fn": 39, "struct": 6 },
"symbols": [sym("charge", 7, 22), sym("audit", 24, 30), sym("open", 32, 36)],
"evidence": { "missing": [], "notes": [], "capped": { "shown": 3, "total": 53 } },
"room": room_id(),
})
}
/// `Viz.CallGraph`.
pub fn call_graph() -> Value {
let node = |id: &str, m: &str| json!({ "id": id, "moniker": m, "crate": "shop", "kind": "fn", "provenance": "source_only", "file": "src/pay.rs", "line": 7 });
json!({
"state": "complete", "total_edges": 31,
"nodes": [node("shop::checkout", "m:checkout"), node("shop::pay::charge", "m:charge"), node("shop::pay::audit", "m:audit")],
"edges": [{ "from": 0, "to": 1, "sites": 3 }, { "from": 1, "to": 2, "sites": 2 }],
"evidence": { "missing": [], "notes": ["a lower bound"], "capped": { "shown": 2, "total": 31 } },
"room": room_id(),
})
}
/// `Knowledge.Source` (whatever node is asked: the mock answers per surface).
pub fn source() -> Value {
json!({
"state": "complete",
"node": { "path": "shop::pay::charge", "moniker": "m:charge", "kind": "fn", "crate": "shop", "repo": "shop", "file": "src/pay.rs", "line": 7, "end_line": 9, "defined": true },
"candidates": [{ "path": "shop::pay::charge" }],
"repo": "shop", "file": "src/pay.rs", "start_line": 7, "end_line": 9, "sha": "0123456789abcdef0123",
"lines": ["pub fn charge(cart: &Cart) -> Receipt {", " audit(cart);", "}"],
"file_lines": 48, "truncated": false,
"evidence": evidence(&[]),
"room": room_id(),
})
}
/// `Knowledge.TestReach`: four fns of `shop` (two reached), one test.
pub fn test_reach() -> Value {
let f = |module: &str,
name: &str,
file: &str,
line: u32,
reachable: bool,
fan_in: u32,
test_code: bool| {
json!({
"repo": "shop", "crate_name": "shop", "module": module, "module_path": module, "item_name": name, "fqn": format!("{module}::{name}"),
"kind": "fn", "visibility": "pub", "file": file, "line": line, "test_code": test_code, "test_reachable": reachable, "fan_in": fan_in,
})
};
json!({
"state": "complete",
"files": [{ "repo": "shop", "file": "src/pay.rs" }, { "repo": "shop", "file": "src/lib.rs" }],
"fns": [
f("shop", "checkout", "src/lib.rs", 10, true, 1, false),
f("shop::pay", "charge", "src/pay.rs", 7, true, 4, false),
f("shop::pay", "audit", "src/pay.rs", 24, false, 2, false),
f("shop::pay", "open", "src/pay.rs", 32, false, 0, false),
f("shop::pay", "charges_once", "src/pay.rs", 40, false, 0, true),
],
"tests": [{
"repo": "shop", "crate_name": "shop", "module": "shop::pay", "module_path": "shop::pay", "test_name": "charges_once", "fqn": "shop::pay::charges_once",
"file": "src/pay.rs", "line": 40, "calls_out": 1, "names": 1, "reached": 2, "files": [0, 1],
}],
"surface_fns": 4, "test_reachable": 2, "tests_total": 1,
"evidence": evidence(&["calls through operators, closures and fn pointers are not recorded: a lower bound"]),
"room": room_id(),
})
}
/// Every surface the panes ask, answered.
///
/// **The red-proof knob.** `DWARVES_BREAK_FEED=<surface>[,<surface>…]` leaves those
/// surfaces UNFED — the mock then answers them as the reader answers a name it does not
/// serve (a refusal). Each `robot_*_room` test asserts the pane's VALUES, so with its
/// surface broken it must go red: that is the check that what the pane shows came from
/// the feed and from nowhere else. E.g.
/// `DWARVES_BREAK_FEED=Viz.CrateGraph cargo test -p dwarves-gui --test robot_coderoom_room`.
pub fn full_room() -> MockCodeRoom {
let broken: Vec<String> = std::env::var("DWARVES_BREAK_FEED")
.map(|v| v.split(',').map(|s| s.trim().to_string()).collect())
.unwrap_or_default();
let fed: Vec<(&str, Value)> = vec![
("Viz.CrateGraph", crate_graph()),
("Coderoom.SnapshotDiff", snapshot_diff()),
("Coderoom.Compat", refusal("not_wired", COMPAT_NOTE)),
("Coderoom.WhatBreaks", refusal("not_wired", BREAKS_NOTE)),
("Viz.FunctionTier", function_tier()),
("Knowledge.Symbols", symbols()),
("Viz.CallGraph", call_graph()),
("Knowledge.Source", source()),
("Knowledge.TestReach", test_reach()),
("Viz.Binary", binary()),
]
.into_iter()
.filter(|(s, _)| !broken.iter().any(|b| b == s))
.collect();
MockCodeRoom::answering(&fed)
}
/// `Viz.Binary`: the binary half of the room — the `shop` artifact, its bytes by
/// crate, its largest symbols and two releases of its ledger.
pub fn binary() -> Value {
json!({
"state": "complete",
"artifacts": [
{ "artifact": "shop@x86_64-unknown-linux-gnu:release", "binary": "shop", "symbols": 3, "bytes": 5200, "imports": 2, "file_size": 91000, "text_size": 40000 },
{ "artifact": "shopctl@x86_64-unknown-linux-gnu:release", "binary": "shopctl", "symbols": 1, "bytes": 800, "imports": 1, "file_size": 21000, "text_size": 9000 },
],
"artifact": "shop@x86_64-unknown-linux-gnu:release",
"crates": [{ "crate": "shop", "bytes": 4100, "symbols": 2 }, { "crate": "serde", "bytes": 1100, "symbols": 1 }],
"symbols": [
{ "symbol": "shop::pay::charge", "mangled": "_RNvNtCs1_4shop3pay6charge", "crate": "shop", "path": "shop::pay::charge", "role": "item", "section": ".text", "bytes": 3000 },
{ "symbol": "shop::checkout", "mangled": "_RNvCs1_4shop8checkout", "crate": "shop", "path": "shop::checkout", "role": "item", "section": ".text", "bytes": 1100 },
{ "symbol": "<serde::de::Visitor>::visit_str", "mangled": "_RNv…", "crate": "serde", "path": "serde::de::Visitor::visit_str", "role": "mono", "section": ".text", "bytes": 1100 },
],
"total_symbols": 3,
"history": [
{ "repo": "shop", "artifact": "shop@x86_64-unknown-linux-gnu:release", "rev": 1, "seq": 1, "sha": "aaaa", "status": "ok", "file_size": 88000, "text_size": 39000 },
{ "repo": "shop", "artifact": "shop@x86_64-unknown-linux-gnu:release", "rev": 2, "seq": 2, "sha": "0123456789abcdef0123", "status": "ok", "file_size": 91000, "text_size": 40000 },
],
"room": room_id(),
"evidence": evidence(&["hardening (RELRO · PIE · NX · stack canary · fortify) is not in the binary layer: mimir-binary joins symbols, it does not grade the ELF"]),
})
}
/// A room nothing was consumed into: every surface that needs a room refuses
/// `not_consumed`; the two world-ring surfaces stay `not_wired`.
pub fn unconsumed_room() -> MockCodeRoom {
let room = MockCodeRoom::default();
for s in [
"Viz.CrateGraph",
"Coderoom.SnapshotDiff",
"Viz.FunctionTier",
"Knowledge.Symbols",
"Viz.CallGraph",
"Knowledge.Source",
"Knowledge.TestReach",
"Viz.Binary",
] {
room.set(s, refusal("not_consumed", NOT_CONSUMED));
}
room.set("Coderoom.Compat", refusal("not_wired", COMPAT_NOTE));
room.set("Coderoom.WhatBreaks", refusal("not_wired", BREAKS_NOTE));
room
}
fn to_s(v: &[&str]) -> Vec<String> {
v.iter().map(|s| s.to_string()).collect()
}
/// The warehouse tables the stand serves (the ones that are still tables).
pub fn tables() -> Vec<(String, Vec<String>, Vec<Vec<String>>)> {
vec![
(
"git_heat_facts".to_string(),
to_s(&["repo", "file", "commits_90d"]),
vec![
to_s(&["shop", "src/pay.rs", "12"]),
to_s(&["shop", "src/lib.rs", "3"]),
],
),
(
"dep_graph_edges".to_string(),
to_s(&["from_repo", "to_repo"]),
vec![to_s(&["web", "shop"])],
),
(
"bench_runs".to_string(),
to_s(&[
"run_id",
"repo",
"ts_micros",
"date",
"version",
"machine",
"cores",
]),
vec![to_s(&[
"r1",
"shop",
"1784000000000000",
"2026-07-22",
"0.1.0",
"oden",
"32",
])],
),
(
"bench_results".to_string(),
to_s(&["run_id", "result_name", "metric_name", "metric_value"]),
vec![to_s(&["r1", "shop.pay_charge", "ms", "4"])],
),
]
}
/// The stand: a mock nornir, the real forge proxy in front of it, and the room handle.
pub struct Stand {
pub room: MockCodeRoom,
/// The forge endpoint — the ONLY address the shell is given.
pub host: String,
_proxy: dwarves::warehouse_proxy::ProxyServer,
_upstream: MockWarehouseServer,
}
pub fn stand(room: MockCodeRoom) -> Stand {
let upstream = spawn_mock_nornir(tables(), room.clone());
let proxy = dwarves::warehouse_proxy::spawn_proxy(upstream.endpoint.clone(), String::new());
Stand {
room,
host: proxy.endpoint.clone(),
_proxy: proxy,
_upstream: upstream,
}
}
/// The address the shell must NEVER adopt — poisoned in the env as a booby-trap.
pub const NORNIR_POISON: &str = "http://nornir-poison.example:7878";
/// The SHIPPED shell (`build_native_app`, the builder `main` runs), given only the forge.
pub fn shell(host: &str) -> dwarves_gui::tabbed::TabbedView {
// SAFETY: each robot file is its own test binary and holds ONE test that builds a shell.
unsafe {
std::env::set_var("NORNIR_SERVER", NORNIR_POISON);
std::env::set_var("DWARVES_SERVER", host);
std::env::set_var("DWARVES_WAREHOUSES", WS);
std::env::set_var("NORNIR_WORKSPACE", WS);
}
dwarves_gui::build_native_app(
host.to_string(),
std::path::PathBuf::from("/nonexistent/dwarves-gui/room-stand"),
)
}
/// Every `(workspace header, surface)` the mock was asked, in order.
pub fn asked(room: &MockCodeRoom) -> Vec<(String, String)> {
room.asked
.lock()
.unwrap()
.iter()
.map(|a| (a.0.clone(), a.1.clone()))
.collect()
}
/// The params the mock got for the LAST ask of `surface`.
pub fn params_of(room: &MockCodeRoom, surface: &str) -> Option<Value> {
room.asked
.lock()
.unwrap()
.iter()
.rev()
.find(|a| a.1 == surface)
.and_then(|a| serde_json::from_str(&a.2).ok())
}
#[allow(unused_variables)]
pub fn matrix(check: &str, ok: bool, detail: &str) {
#[cfg(feature = "testmatrix")]
nornir_testmatrix::functional_status("dwarves-gui", check, ok, detail);
}