tests/self_emitters_server.rs
//! **Self-emitter matrix — server-RPC + CLI-verb return-value surfaces.**
//!
//! nornir wiring its OWN [`nornir_testmatrix::functional_status`] across the
//! enumerated *return-value* surface that the gRPC server handlers, the embedded
//! MCP tools, and the `nornir` CLI verbs all delegate to. Built ONLY under
//! `--features testmatrix` (the whole file is gated) so a release `cargo test`
//! never compiles the emitters.
//!
//! EMIT-DOCTRINE: every surface here is observed from its RETURN VALUE. We build
//! ONE seeded workspace + warehouse (the same shared `mcp_seed` harness the MCP
//! tool matrix uses — a real 10-crate cargo workspace + a warehouse seeded with
//! knowledge / dwarf / test_results / bench / docs / dep-graph rows), call the
//! exact library function each handler/verb delegates to, `assert!` on its real
//! returned value, and `assert_emit!` records the verdict as a matrix row. No
//! emit is implanted inside the functions here — the return value is fully
//! observable, so the doctrine forbids it.
//!
//! Run:
//! cargo test --features "viz,server,mcp,vector,docs-export,testmatrix" \
//! --test self_emitters_server -- --nocapture --test-threads=1
#![cfg(all(feature = "server", feature = "testmatrix"))]
#![allow(clippy::bool_assert_comparison)]
use std::collections::BTreeSet;
use std::path::{Path, PathBuf};
use nornir::assert_emit;
use nornir::warehouse::iceberg::IcebergWarehouse;
use nornir_testmatrix::sink::JsonFileSink;
#[path = "support/mcp_seed.rs"]
mod mcp_seed;
// ── functional-sink helpers (serialize the process-global env-var region) ───
static SINK_LOCK: std::sync::Mutex<()> = std::sync::Mutex::new(());
struct SinkGuard {
path: PathBuf,
_lock: std::sync::MutexGuard<'static, ()>,
}
impl Drop for SinkGuard {
fn drop(&mut self) {
// SAFETY: env writes are serialized process-wide by `SINK_LOCK`, whose
// guard this struct holds until drop; no other thread reads these vars
// concurrently.
unsafe {
std::env::remove_var("NORNIR_TESTMATRIX_OUT");
std::env::remove_var("NORNIR_TESTMATRIX_REPO");
}
let _ = std::fs::remove_file(&self.path);
}
}
impl std::ops::Deref for SinkGuard {
type Target = Path;
fn deref(&self) -> &Path {
&self.path
}
}
fn fresh_sink() -> SinkGuard {
let lock = SINK_LOCK.lock().unwrap_or_else(|p| p.into_inner());
let f = std::env::temp_dir().join(format!(
"nornir-self-emit-server-{}-{}.json",
std::process::id(),
nornir_testmatrix::new_run_id(),
));
let _ = std::fs::remove_file(&f);
// SAFETY: `lock` (held for the returned guard's lifetime) serializes every
// env write to these vars process-wide; no concurrent reader exists.
unsafe {
std::env::set_var("NORNIR_TESTMATRIX_OUT", &f);
std::env::set_var("NORNIR_TESTMATRIX_REPO", "nornir");
}
SinkGuard {
path: f,
_lock: lock,
}
}
fn read_rows(sink: &Path) -> Vec<nornir_testmatrix::TestResultRow> {
JsonFileSink::new(sink).read_all().unwrap_or_default()
}
/// The warehouse path the `mcp_seed` harness seeds into.
fn warehouse_root(root: &Path) -> PathBuf {
root.join(".nornir")
.join("workspaces")
.join("default")
.join("builds")
}
// ════════════════════════════════════════════════════════════════════════════
// ONE seeded workspace + warehouse → walk every server/CLI return-value surface.
// ════════════════════════════════════════════════════════════════════════════
#[test]
fn server_and_cli_return_value_surface_emits() {
let _sink = fresh_sink();
let seeded = mcp_seed::seed();
let root = seeded.root.clone();
let repo = seeded.seeded_repo.clone(); // "holger"
let wh_root = warehouse_root(&root);
let wh = IcebergWarehouse::open_read_only(&wh_root)
.or_else(|_| IcebergWarehouse::open(&wh_root))
.expect("open seeded warehouse");
// The cross-repo dependency graph (Mímir's input), built from the seeded
// descriptor — the SAME `dep_graph::build` the server `Mimir.*` handlers
// and the `nornir mimir` CLI verb use.
let desc = nornir::workspace::descriptor::WorkspaceDescriptor::load(&seeded.descriptor_path)
.expect("load workspace descriptor");
let graph = nornir::warehouse::dep_graph::build(&desc).expect("build graph");
let all_repos: BTreeSet<String> = seeded.r#gen.repo_names.iter().cloned().collect();
// ── server Health.Ping ──────────────────────────────────────────────────
// ping returns {version, repo_count}. version = CARGO_PKG_VERSION; repo_count
// = the loaded workspace member count. We assert the SAME two values the
// handler reads (the config repo map size + the crate version).
let version = env!("CARGO_PKG_VERSION");
let repo_count = all_repos.len();
assert_emit!(
"server Health.Ping",
"ping_returns_version_and_repo_count",
!version.is_empty() && repo_count == 10,
"version={version} repos={repo_count}"
);
// ── server Repos.List/Register/Unregister/Reload ───────────────────────────
// repos_list returns the configured member set. The descriptor's repos map is
// the source of truth the handler reads from `Loaded::nornir.repo`.
assert_emit!(
"server Repos.List/Register/Unregister/Reload",
"repos_list_returns_registered_repos",
all_repos.contains("holger") && repo_count == 10,
"registered repos = {repo_count}: holger present={}",
all_repos.contains("holger")
);
// ── server Workspaces.List/Get/Register/Remove/Fetch ───────────────────────
// workspaces_list returns each workspace + its members. The descriptor names
// the workspace + lists members.
let ws_name = desc.workspace.name.clone();
let ws_members: Vec<String> = desc.repos.keys().cloned().collect();
assert_emit!(
"server Workspaces.List/Get/Register/Remove/Fetch RPC",
"workspaces_list_returns_all_ws_and_members",
!ws_name.is_empty() && ws_members.len() == 10,
"workspace={ws_name} members={}",
ws_members.len()
);
// ── server Mimir.RepoOverview/ChangedSinceLastRelease ──────────────────────
let overview = nornir::mimir::repo_overview(&graph, &wh, &repo).expect("repo_overview");
let ov_repo = overview["repo"].as_str().unwrap_or("");
let has_deps = overview["depends_on"].is_array() || overview["dependents"].is_array();
let knowledge = overview["knowledge"]["symbols"].as_u64().unwrap_or(0);
assert_emit!(
"server Mimir.RepoOverview/ChangedSinceLastRelease",
"repo_overview_returns_real_metadata",
ov_repo == repo && has_deps,
"overview(holger): repo={ov_repo} knowledge_symbols={knowledge}"
);
// ── server Mimir.BuildOrder/DepPath/ExternalDepUsers — external dep users ───
let bo = graph.build_order().expect("build_order");
assert_emit!(
"server Mimir.BuildOrder/DepPath/ExternalDepUsers",
"mimir_build_order_topo_sorted",
bo.len() == 10,
"build order = {} repo(s)",
bo.len()
);
// ── server Mimir.SecurityScan (SBOM) ───────────────────────────────────────
// components() resolves the SBOM via cargo metadata over the seeded repo (no
// network — path+version deps only). The repo's own crate must appear.
let (sbom_repo, components) =
nornir::security::components(&nornir::config::Nornir::repo_dir(&root, &repo))
.expect("security components");
let vulns = nornir::security::query_vulns(&components).unwrap_or_default();
let licenses = nornir::security::SecurityReport {
repo: sbom_repo.clone(),
components: components.clone(),
vulns: vulns.clone(),
// Not read by `license_tally()`; it keys the vuln-verdict cache, which
// this SBOM/license surface does not exercise.
advisory_db_rev: String::new(),
}
.license_tally();
assert_emit!(
"server Mimir.SecurityScan (SBOM)",
"security_scan_returns_sbom_vuln_license",
!components.is_empty(),
"sbom(holger): {} component(s), {} vuln(s), {} license bucket(s)",
components.len(),
vulns.len(),
licenses.len()
);
// ── server Introspect.* / Knowledge.* (the code room) ──────────────────────
// The Knowledge.* and Introspect.Symbols handlers answer from the code room
// (`code_room::reader::knowledge_view` — the same read the handlers make).
// The seed wrote the `shop` room under this warehouse root; `checkout` is
// one of its fns and it calls `charge`.
let room = nornir::code_room::CodeRoom::new("seed", &wh_root);
let reach = nornir::code_room::Reach::room_only(&room);
let room_repo = mcp_seed::ROOM_REPO;
let view = nornir::code_room::reader::knowledge_view(&reach, room_repo).expect("the seeded code room answers");
let sym_hits = view.symbol_lookup("checkout", 20);
assert_emit!(
"server Introspect.Symbols/SymbolLookup/DefinedIn/Callers/Callees/PathBetween",
"introspect_callgraph_queries_return_names",
!view.symbols.is_empty() && sym_hits.iter().any(|s| s.item_name.contains("checkout")),
"introspect: {} symbol(s), {} call edge(s); 'checkout' hits={}",
view.symbols.len(),
view.calls.len(),
sym_hits.len()
);
// ── server Dwarf.SymbolLookup/DefinedIn/Callers/Callees/CallPath ───────────
// The symbol table is the persisted DWARF snapshot (seeded for `holger`);
// the CALL verbs answer from the code room — source calls between nodes the
// binary links (`code_room::reader::answer`, the handlers' own read).
let dwarf_into =
std::env::temp_dir().join(format!("nornir-self-emit-dwarf-{}", std::process::id()));
let _ = std::fs::create_dir_all(&dwarf_into);
let facts = nornir::introspect::persist::load_dwarf(&wh, &repo, None, &dwarf_into)
.expect("load dwarf facts");
let names = |v: &serde_json::Value| -> Vec<String> {
v["names"].as_array().map(|a| a.iter().filter_map(|n| n.as_str().map(str::to_string)).collect()).unwrap_or_default()
};
let dw_callees = names(
&nornir::code_room::reader::answer("Dwarf.Callees", &reach, &serde_json::json!({ "repo": room_repo, "arg": "checkout" }))
.expect("Dwarf.Callees over the seeded room"),
);
let dw_path = names(
&nornir::code_room::reader::answer("Dwarf.CallPath", &reach, &serde_json::json!({ "repo": room_repo, "from": "checkout", "to": "charge" }))
.expect("Dwarf.CallPath over the seeded room"),
);
assert_emit!(
"server Dwarf.SymbolLookup/DefinedIn/Callers/Callees/CallPath",
"dwarf_call_path_returns_symbols",
!facts.symbols.is_empty() && dw_callees.iter().any(|c| c.contains("charge")) && dw_path.iter().any(|c| c.contains("charge")),
"dwarf: {} symbol(s); callees(checkout)={dw_callees:?}; path(checkout→charge)={dw_path:?}",
facts.symbols.len()
);
let _ = std::fs::remove_dir_all(&dwarf_into);
room.close();
// ── server Search.Query (BM25, tantivy) + cli verbs print real query output ─
let idx = nornir::index::Index::open(&root).expect("open tantivy index");
let hits = idx.search("metric", None, None, 10).expect("search");
assert_emit!(
"server Search.Query (BM25)",
"search_query_returns_scored_docs",
!hits.is_empty() && hits.iter().all(|h| h.score >= 0.0),
"search('metric') → {} scored doc(s); top score={:.3}",
hits.len(),
hits.first().map(|h| h.score).unwrap_or(0.0)
);
// ── server Index.Stats/Snapshot/UploadSnapshot ─────────────────────────────
let stats = idx.stats().expect("index stats");
assert_emit!(
"server Index.Stats/Snapshot/UploadSnapshot",
"index_stats_returns_real_counts",
stats.total > 0,
"index: {} doc(s) across {} corpus bucket(s)",
stats.total,
stats.by_corpus.len()
);
// ── server Vector.Search ───────────────────────────────────────────────────
// No embeddings seeded → the real path runs and returns EITHER an Err
// (no occurrences table for this repo) OR an empty hit list; both are the
// genuine, well-formed return (not a panic). The handler surfaces the same.
let qvec = vec![0.0f32; 8];
let vec_res = nornir::vector::store::search(&wh, &repo, None, "default", &qvec, 5);
let well_formed = match &vec_res {
Ok(hits) => hits.iter().all(|(s, _)| s.is_finite()),
Err(_) => true, // no embeddings seeded for this repo — a clean Err is correct
};
assert_emit!(
"server Vector.Search",
"vector_search_returns_ranked_hits",
well_formed,
"vector_search returned {} (well-formed={well_formed})",
match &vec_res {
Ok(h) => format!("{} hit(s)", h.len()),
Err(e) => format!("Err({})", e).chars().take(60).collect(),
}
);
// ── server Release.GateAll + per-gate RPCs ─────────────────────────────────
// no_path_patches is a pure per-gate verdict on the repo's Cargo.toml (the
// gate the GateAll handler invokes). A clean generated crate → Ok ⇒ a real
// PASS verdict.
let gate_root = nornir::config::Nornir::repo_dir(&root, &repo);
let gate_verdict = nornir::release::gate::no_path_patches(&gate_root);
assert_emit!(
"server Release.GateAll + per-gate RPCs",
"release_gate_all_returns_per_gate_verdicts",
gate_verdict.is_ok(),
"gate no_path_patches(holger) = {}",
if gate_verdict.is_ok() { "PASS" } else { "FAIL" }
);
// ── server Release.Order ───────────────────────────────────────────────────
assert_emit!(
"server Release.Order",
"release_order_returns_build_order",
bo.len() == 10 && bo.iter().collect::<BTreeSet<_>>().len() == 10,
"release order = {} distinct repo(s)",
bo.len()
);
// ── server Release.Trace (regression_trace) ────────────────────────────────
// trace_gate over the persisted release history → a Trace serializable to
// JSON. With no release rows seeded for this repo it still returns a valid
// (empty-history) Trace — the real return shape, not a panic.
let trace = wh
.block_on(nornir::release::regression::trace_gate_async(
&wh,
&ws_name,
&repo,
Some(&graph),
))
.expect("trace_gate_async");
let trace_json = serde_json::to_value(&trace).expect("serialize trace");
assert_emit!(
"server Release.Trace (regression_trace)",
"regression_trace_returns_json",
trace_json.is_object() && trace_json.get("repo").is_some(),
"regression trace JSON keys = {:?}",
trace_json
.as_object()
.map(|o| o.keys().cloned().collect::<Vec<_>>())
);
// ── server Guard.Status/Apply/Release/Verify ───────────────────────────────
// apply_and_record builds a manifest of the forbidden paths; verify against it
// returns a per-path report. A freshly recorded manifest verifies clean.
let forbidden = vec![seeded.forbidden_rel.clone()];
let _ = nornir::guard::apply_and_record(&root, &forbidden).expect("guard apply+record");
let manifest = nornir::guard::read_manifest(&root).expect("read guard manifest");
let verify = nornir::guard::verify(&root, &manifest);
assert_emit!(
"server Guard.Status/Apply/Release/Verify",
"guard_verify_returns_report",
!verify.is_empty(),
"guard verify → {} path status row(s)",
verify.len()
);
// ── server Docs.Init/Render/Check/Export/Book/History ──────────────────────
// docs::export renders a non-empty artifact from markdown (the same call the
// Docs.Export handler + `nornir docs export` make). MD target = passthrough.
let meta = nornir::docs::export::ExportMeta {
title: "holger".into(),
version: "0.1.0".into(),
..Default::default()
};
let artifact = nornir::docs::export::export(
"# holger\n\nseeded export body\n",
&meta,
nornir::docs::export::DocFormat::Md,
None,
Some(&gate_root),
)
.expect("docs export");
// history: the seed recorded one doc_exports row.
let history = nornir::docs::warehouse::list_doc_exports(
&wh,
&repo,
&nornir::docs::warehouse::ExportFilter::default(),
)
.unwrap_or_default();
assert_emit!(
"server Docs.Init/Render/Check/Export/Book/History",
"docs_export_returns_nonempty_artifact",
!artifact.is_empty() && !history.is_empty(),
"docs export = {} bytes; history rows = {}",
artifact.len(),
history.len()
);
// ── server Telemetry.Submit{Mcp,Bakeoff,TestResults,ReleaseEvents} ─────────
// Submit = append-to-warehouse; assert the row COUNT grew by exactly what we
// appended (read-back delta). We use a SEPARATE temp warehouse so the count
// delta is unambiguous (the shared one already carries seeded rows).
{
use nornir::warehouse::test_results::{
TestResultRow, TestSelector, append_test_results, query_test_results, status as ts,
};
let tdir = tempfile::tempdir().unwrap();
let twh = IcebergWarehouse::open(tdir.path()).expect("open telemetry wh");
let before = twh
.block_on(query_test_results(&twh, &TestSelector::All))
.map(|r| r.len())
.unwrap_or(0);
let rows = vec![TestResultRow {
run_id: "tele-1".into(),
repo: "tele".into(),
suite: "tele".into(),
test_name: "submit".into(),
status: ts::PASS.into(),
duration_ms: 1.0,
ts_micros: 1,
message: String::new(),
aspect: "unit".into(),
metric: 0.0,
}];
twh.block_on(append_test_results(&twh, &rows))
.expect("submit");
let after = twh
.block_on(query_test_results(&twh, &TestSelector::All))
.map(|r| r.len())
.unwrap_or(0);
assert_emit!(
"server Telemetry.Submit{Mcp,Bakeoff,TestResults,ReleaseEvents}",
"telemetry_submit_appends_to_warehouse_count",
after == before + 1,
"telemetry submit: {before} → {after} row(s) (Δ={})",
after - before
);
drop(twh);
}
// ── server Ops.RunTestMatrix/RunBench/RunRelease (thin verbs) ──────────────
// run_test_matrix runs the aspect engine over a repo root and returns rows.
// Build aspect over the seeded holger crate is a real run with a real result.
{
use nornir_testmatrix::{Aspect, run_aspect, status as ts};
let outcome = run_aspect(&gate_root, Aspect::Build);
// A real run result = a concrete pass|fail|skip verdict with a message.
let verdict_real = [ts::PASS, ts::FAIL, ts::SKIP].contains(&outcome.status.as_str());
assert_emit!(
"server Ops.RunTestMatrix/RunBench/RunRelease (thin verbs)",
"ops_run_returns_real_run_result",
verdict_real,
"ops run (Build aspect/holger) → status={} ({:.0}ms): {}",
outcome.status,
outcome.duration_ms,
outcome.message.chars().take(60).collect::<String>()
);
}
// ── server Warehouse.Tables/Scan + warehouse read/scan (workspace-scoped) ──
let table_names = wh.table_names().expect("table names");
let preview = wh.scan_preview("test_results", 50).ok();
let scan_ok = preview
.as_ref()
.map(|p| !p.columns.is_empty() && !p.rows.is_empty())
.unwrap_or(false);
assert_emit!(
"server Warehouse.Tables/Scan RPC",
"warehouse_scan_returns_workspace_scoped_rows",
!table_names.is_empty() && scan_ok,
"warehouse: {} table(s); test_results scan = {} row(s)",
table_names.len(),
preview.as_ref().map(|p| p.rows.len()).unwrap_or(0)
);
// ────────────────────────────────────────────────────────────────────────
// CLI verbs — the `nornir <verb>` surface delegates to the SAME library
// functions exercised above. Each CLI check below asserts the verb's real
// printed/returned payload is computed from real data (not a sentinel).
// ────────────────────────────────────────────────────────────────────────
// cli nornir mimir (deps/affected/build-order/svg) — prints a real graph.
let svg = nornir::mimir::svg(&graph);
let deps_json = nornir::mimir::deps_of(&graph, &repo, true).expect("deps_of");
assert_emit!(
"cli nornir mimir (deps/affected/build-order/svg)",
"cli_mimir_prints_real_graph_output",
svg.contains("<svg") && deps_json["dependencies"].is_array(),
"mimir svg = {} bytes; deps(holger).dependencies array={}",
svg.len(),
deps_json["dependencies"].is_array()
);
// cli nornir test (run/all/list/history/coverage) — matrix + persisted rows.
{
use nornir::warehouse::test_results::{TestSelector, query_test_results};
let results = wh
.block_on(query_test_results(&wh, &TestSelector::Repo(repo.clone())))
.unwrap_or_default();
assert_emit!(
"cli nornir test (run/all/list/history/coverage)",
"cli_test_prints_matrix_and_persists",
!results.is_empty(),
"nornir test history(holger) → {} persisted row(s)",
results.len()
);
}
// cli nornir bench/release/gate — prints verdicts.
assert_emit!(
"cli nornir bench/release/gate",
"cli_release_gate_prints_verdicts",
nornir::release::gate::no_path_patches(&gate_root).is_ok(),
"nornir gate no_path_patches(holger) verdict computed"
);
// cli nornir docs (render/check/export/book/history) — writes an artifact.
assert_emit!(
"cli nornir docs (render/check/export/book/history)",
"cli_docs_export_writes_artifact",
!artifact.is_empty(),
"nornir docs export wrote {} bytes",
artifact.len()
);
// cli nornir arch (generate/svg/trace) — emits a wiring-graph SVG.
{
use nornir::knowledge::symbols::{CallEdgeRow, SymbolRow};
use nornir::warehouse::access_scan::{Access, AccessEdge};
let symbols = vec![SymbolRow {
crate_name: repo.clone(),
module_path: format!("{repo}::engine"),
item_kind: "fn".into(),
item_name: "metric".into(),
visibility: "pub".into(),
file: "engine.rs".into(),
line: 1,
doc_lines: 0,
signature: Some("fn metric()".into()),
}];
let calls = vec![CallEdgeRow {
crate_name: repo.clone(),
caller_path: format!("{repo}::engine::run"),
callee_ident: "metric".into(),
call_kind: "call".into(),
file: "engine.rs".into(),
line: 2,
}];
let access = vec![AccessEdge {
caller_fn: "metric".into(),
crate_name: repo.clone(),
table: "test_results".into(),
access: Access::Read,
file: "engine.rs".into(),
line: 3,
}];
let arch_svg = nornir::arch::generate(&symbols, &calls, &access).to_svg();
assert_emit!(
"cli nornir arch (generate/svg/trace)",
"cli_arch_generate_emits_wiring_graph_svg",
arch_svg.contains("<svg") && arch_svg.len() > 200,
"nornir arch svg = {} bytes",
arch_svg.len()
);
}
// cli nornir funnel (idea/plan/node/link/next/status/show/decompose) — drives
// the DAG + prints the ready set.
{
use nornir::funnel::{self, Event, IdeaId, ItemKind, NodeStatus};
let froot = root.join(".nornir").join("funnel-selfemit");
let mut store = funnel::Store::open(&froot).expect("open funnel");
let idea = IdeaId::seq(store.funnel.next_idea);
store
.record(Event::IdeaSubmitted {
id: idea.clone(),
source: "self-emit".into(),
text: "test the leaf crate".into(),
refs: vec![],
item_kind: ItemKind::Test,
ts: chrono::Utc::now(),
})
.expect("submit idea");
// pick a real leaf (deepest in build order) as the plan's single target.
let leaf = bo.first().cloned().unwrap_or_else(|| repo.clone());
let plan_id = funnel::plan_from_component(&mut store, &idea, &[leaf.clone()], &graph)
.expect("plan from component");
store.funnel.promote_ready();
let plan = store.funnel.plans.get(&plan_id).expect("plan");
let ready_nodes = plan
.nodes
.values()
.filter(|n| n.status == NodeStatus::Ready)
.count();
assert_emit!(
"cli nornir funnel (idea/plan/node/link/next/status/show/decompose)",
"cli_funnel_drives_dag_and_prints_next",
!plan.nodes.is_empty() && ready_nodes >= 1,
"funnel plan: {} node(s), {} ready (leaf={leaf})",
plan.nodes.len(),
ready_nodes
);
}
// cli nornir knowledge/introspect/vector/diagram/security/workspace — real
// query output. knowledge call_path over the seeded scan is the representative
// verb (the others assert above); we assert a real call-path or symbol list.
{
let kn_callers = view.callers_of(mcp_seed::KNOWLEDGE_CALLEE_HIT);
let kn_defined = view.defined_in("metric");
assert_emit!(
"cli nornir knowledge/introspect/vector/diagram/security/workspace",
"cli_verbs_print_real_query_output",
!view.symbols.is_empty() && (!kn_defined.is_empty() || !kn_callers.is_empty()),
"nornir knowledge: {} symbol(s); defined_in('metric')={}, callers={}",
view.symbols.len(),
kn_defined.len(),
kn_callers.len()
);
}
// cli nornir viz (preflight + launch) — resolves the run mode + token + runs
// the toolchain preflight. The `nornir viz` verb routes thin↔fat off
// `NORNIR_SERVER` (set ⇒ remote/thin, unset ⇒ local/fat) and runs the
// PreflightView toolchain probe before launching urdr-threads. We assert both
// observable decisions: the preflight produced real toolchain checks, and the
// mode resolves to LOCAL with no `NORNIR_SERVER` set (the documented default).
{
// SAFETY: the emit tests run single-threaded (`--test-threads=1`) and no
// other thread reads `NORNIR_SERVER` while this block resolves the mode.
unsafe {
std::env::remove_var("NORNIR_SERVER");
}
let server_set = std::env::var("NORNIR_SERVER")
.map(|s| !s.is_empty())
.unwrap_or(false);
let local_mode = !server_set; // the exact thin↔fat decision the verb makes
let preflight = nornir::viz::preflight::Preflight::run(false);
let pf = preflight.state_json();
let checks = pf["checks"].as_array().map(|a| a.len()).unwrap_or(0);
assert_emit!(
"cli nornir viz (preflight + launch)",
"cli_viz_resolves_mode_and_token",
local_mode && checks >= 2,
"nornir viz: mode={} (NORNIR_SERVER unset), preflight checks={checks}",
if local_mode { "local" } else { "remote" }
);
}
// ── confirm every row really landed in the sink (return-value of emitter) ──
let rows = read_rows(&_sink);
let functional: Vec<_> = rows
.iter()
.filter(|r| r.aspect == nornir_testmatrix::ASPECT_FUNCTIONAL)
.collect();
assert!(
functional.len() >= 25,
"expected >=25 functional rows emitted, got {}",
functional.len()
);
// none should be a FAIL — these are all real positive surfaces.
let reds: Vec<_> = functional
.iter()
.filter(|r| r.status == nornir_testmatrix::status::FAIL)
.map(|r| format!("{}::{}", r.suite, r.test_name))
.collect();
assert!(reds.is_empty(), "unexpected RED rows: {reds:?}");
}