dwarves-gui/tests/robot_jera_wire.rs
//! **ROBOT-UI: the 🧬 Jera tab reads the job ledger OVER THE WIRE, not off a path.**
//!
//! The rule is written one line above the answer in
//! `edda/crates/nornir-wire/proto/nornir.proto:981-983`:
//!
//! > the server holds the redb lock, **SO CLIENTS GO THROUGH HERE.**
//!
//! The tab used to open `jobs.redb` at a filesystem path derived from the UI's own
//! `$HOME`. This file is the red-before-green for moving it onto `Viz.Jobs`, and it also
//! **corrects the standing diagnosis of why the file road fails**, because getting that
//! wrong would have sent the next lane after the wrong bug.
//!
//! ## What was measured, and what the received diagnosis got wrong
//!
//! The received diagnosis was: *`jobs.redb` is held open by the running server, redb
//! takes an EXCLUSIVE lock, and `JobStore::open_read_only` is called "lock-tolerant"
//! but is not — that comment is a false claim in the code.*
//!
//! Measured here, in [`the_lock_is_not_what_breaks_the_file_road`], and reproduced on
//! t14s 2026-08-25:
//!
//! | probe | result |
//! |---|---|
//! | a second plain `JobStore::open` against a live writer | `Database already open. Cannot acquire lock.` |
//! | `JobStore::open_read_only` against the SAME live writer | **Ok** — it copies the file aside and opens the copy |
//! | `open_read_only` on a ledger the reader cannot WRITE | `I/O error: Permission denied (os error 13)` |
//!
//! So `open_read_only` **is** lock-tolerant and the comment is **true**. What breaks the
//! file road is ownership, not locking: `open` routes through `redb::Database::create`,
//! so a `-rw-r--r--` ledger owned by another user fails before jera's `is_lock_error` is
//! consulted and the snapshot fallback never runs. The installed forge runs as user
//! `dwarves`; a UI a human started does not. On top of that the snapshot that makes it
//! lock-tolerant is a whole-file byte copy paid on **every** read, at a 2 s cadence, and
//! the path the UI would derive is the forge's own ledger, not the served workspace's.
//!
//! ## The tests
//!
//! 1. [`the_lock_is_not_what_breaks_the_file_road`] — the three probes above, run.
//! 2. [`red_the_file_road_cannot_read_it_green_the_wire_can`] — THE red/green. One
//! ledger, unreadable to this process the way the forge's is; the FILE road paints
//! "could not READ" and zero jobs, the WIRE road paints the rows. Same tab type, same
//! frame, one seam swapped.
//! 3. [`viz_jobs_really_travels_the_wire`] — a sentinel `job_id` no local read could
//! synthesise comes back through the shipped pane, and the client stamps the selected
//! workspace as the `nornir-workspace` header.
//! 4. [`a_forge_that_does_not_proxy_viz_is_a_NAMED_failure_never_zero_jobs`] — the state
//! this repo is in until the forge grows the `Viz` proxy: `Unimplemented` must read
//! as an outage, never as an idle forge.
//! 5. [`a_failed_refresh_keeps_the_previous_reading_and_names_the_failure`] — the two
//! properties seven panes in this repo hold.
//! 6. [`a_read_in_flight_is_named_and_does_not_blank_the_previous_reading`].
//! 7. [`an_empty_answer_is_idle_over_the_wire_and_never_the_fixture`] — "no answer" and
//! "no jobs" stay different values (`1aa8f0f`: a deleted workspace once PAINTED
//! invented jobs).
//!
//! Each check emits a `nornir-testmatrix` `functional_status` cell under
//! `--features testmatrix` (reuse edda; never a hand-rolled ledger).
use std::sync::{Arc, Mutex};
use std::time::Duration;
use dwarves_gui::jera::{JeraTab, JobsRead, LedgerJobs, WireJobs};
use facett_core::Facet;
use nornir_warehouse_embed::testkit::{spawn_mock_viz, Code, MockViz};
#[allow(unused_variables)]
fn matrix(check: &str, ok: bool, detail: &str) {
#[cfg(feature = "testmatrix")]
nornir_testmatrix::functional_status("dwarves-gui", check, ok, detail);
}
/// A jera `JobRecord` as `Viz.Jobs` serializes it. `nornir::jobs` is a re-export of
/// `jera::jobs`, so this is the SAME type the file road decodes — not a twin.
fn record(job_id: &str, kind: &str, status: &str) -> serde_json::Value {
serde_json::json!({
"job_id": job_id,
"kind": kind,
"target": "dwarves@a1b2c3d4",
"workspace": "nordisk",
"status": status,
"ts_start_micros": 1_700_000_000_000_000i64,
"ts_end_micros": null,
"elapsed_ms": null,
"detail_json": "",
"result_ref": "",
})
}
/// Render a `JeraTab` headless and return whether the given text is on screen.
fn painted(tab: &mut JeraTab, needle: &str) -> bool {
use nornir_robotui::egui_kittest::kittest::Queryable;
let cell = std::cell::RefCell::new(tab);
let mut h = nornir_robotui::egui_kittest::Harness::builder()
.with_size(egui::vec2(1400.0, 900.0))
.build_ui(|ui| Facet::ui(*cell.borrow_mut(), ui));
h.step();
h.step();
// `query_all_*`, not `query_*`: a job KIND legitimately appears twice on this surface
// (once in its row, once in the reused JobList's kind filter), and the single-node
// query PANICS on two matches — which would turn a correctly-rendered job into a test
// failure.
let hit = h.query_all_by_label_contains(needle).next().is_some();
hit
}
/// Write a real jera ledger at `root` carrying one finished job, then close it.
fn seed_ledger(root: &std::path::Path) {
let store = jera::jobs::JobStore::open(root).expect("open ledger");
store
.start(
"lens",
"change_risk @nordisk",
"nordisk",
serde_json::json!({}),
)
.finish(serde_json::json!({ "rows": 1 }), "");
drop(store);
}
/// **1 — THE LOCK IS NOT WHAT BREAKS THE FILE ROAD.**
///
/// Run the three probes the received diagnosis rested on. This test exists because the
/// diagnosis named the wrong mechanism *and* condemned a true comment: acting on it would
/// have deleted `read_ledger_jobs`' accurate `lock-tolerant` doc line and left the real
/// failure — file ownership — undiagnosed for the next lane.
///
/// RED-when-broken in BOTH directions: if `open_read_only` ever stops snapshotting past a
/// live writer, probe (b) goes red; if it ever starts coping with a read-only file, probe
/// (c) does, and the wire's justification changes.
#[test]
fn the_lock_is_not_what_breaks_the_file_road() {
use jera::jobs::{JobSelector, JobStore};
// (a) A second PLAIN open against a live writer really is refused by redb.
let d = tempfile::tempdir().unwrap();
seed_ledger(d.path());
let _writer = JobStore::open(d.path()).expect("the live writer");
let plain = JobStore::open(d.path());
let locked = plain
.as_ref()
.err()
.map(|e| format!("{e:#}"))
.unwrap_or_default();
let a_ok = plain.is_err() && locked.contains("Cannot acquire lock");
assert!(
a_ok,
"a second plain open must be refused by redb's exclusive lock: {locked}"
);
// (b) …and `open_read_only` COPES with exactly that, by snapshotting the file aside.
// This is the claim the received diagnosis said was false. It is true.
let ro = JobStore::open_read_only(d.path());
let b_ok = ro
.as_ref()
.ok()
.map(|s| s.list(&JobSelector::All).map(|v| v.len()).unwrap_or(0))
== Some(1);
assert!(
b_ok,
"`open_read_only` IS lock-tolerant — it read past a live exclusive writer: {:?}",
ro.err().map(|e| format!("{e:#}"))
);
drop(_writer);
// (c) What it does NOT cope with is a ledger this process may read but not WRITE —
// the shape the installed forge leaves behind (`-rw-r--r--`, owner `dwarves`).
let d2 = tempfile::tempdir().unwrap();
seed_ledger(d2.path());
let p = d2.path().join("jobs.redb");
let mut perms = std::fs::metadata(&p).unwrap().permissions();
std::os::unix::fs::PermissionsExt::set_mode(&mut perms, 0o444);
std::fs::set_permissions(&p, perms).unwrap();
let denied = JobStore::open_read_only(d2.path())
.err()
.map(|e| format!("{e:#}"))
.unwrap_or_default();
let c_ok = denied.contains("Permission denied");
assert!(
c_ok,
"the file road's real failure is OWNERSHIP, not the lock — expected a permission \
error from `Database::create`, got: {denied:?}"
);
let ok = a_ok && b_ok && c_ok;
matrix(
"jera_file_road_fails_on_ownership_not_the_lock",
ok,
"measured: a 2nd plain JobStore::open is refused by redb's exclusive lock, but \
open_read_only READS PAST IT by snapshotting the file aside (so its `lock-tolerant` \
doc line is TRUE); what it cannot do is open a ledger it may read but not write \
(Permission denied os error 13) — which is exactly the -rw-r--r-- the forge, \
running as user `dwarves`, leaves for a UI that does not",
);
}
/// **2 — RED: the FILE road cannot read it. GREEN: the WIRE can.**
///
/// One ledger, in the state the forge's actually is (readable bytes, not writable by this
/// reader). The FILE-road tab paints its honest "could not READ" and zero jobs; the
/// WIRE-road tab, against a server serving that ledger's rows, paints the rows. Same tab
/// type, same rendered surface, one seam swapped — which is the whole change.
#[test]
fn red_the_file_road_cannot_read_it_green_the_wire_can() {
let d = tempfile::tempdir().unwrap();
seed_ledger(d.path());
let p = d.path().join("jobs.redb");
let mut perms = std::fs::metadata(&p).unwrap().permissions();
std::os::unix::fs::PermissionsExt::set_mode(&mut perms, 0o444);
std::fs::set_permissions(&p, perms).unwrap();
// ── RED ── the road the tab used to take.
let mut file_tab = JeraTab::live(d.path().to_path_buf());
let red = file_tab.state_json();
let red_ok = red["source"] == "unreadable" && red["job_count"] == 0;
assert!(
red_ok,
"the FILE road must fail on this ledger — that is the bug being fixed: {red}"
);
// …and it fails LOUDLY, never as a blank list.
assert!(
painted(&mut file_tab, "could not READ"),
"a file road that cannot read must SAY so: {}",
red["source_headline"]
);
// Never the fixture. `1aa8f0f`: an unreadable/absent ledger once painted invented jobs.
assert!(
!painted(&mut file_tab, "cargo-build"),
"no fixture job on a failed read"
);
// ── GREEN ── the same ledger's rows, served by the server that holds it.
let srv = spawn_mock_viz(MockViz {
jobs: serde_json::json!([record("j-1", "lens", "done")]).to_string(),
..Default::default()
});
let mut wire_tab = JeraTab::wire(&srv.endpoint, "", "nordisk");
wire_tab.refresh();
let green = wire_tab.state_json();
let green_ok = green["source"] == "ledger" && green["job_count"] == 1;
assert!(
green_ok,
"the WIRE road reads the same ledger the file road could not: {green}"
);
assert_eq!(
green["error"],
serde_json::Value::Null,
"a good read clears the failure"
);
let ok = red_ok && green_ok;
matrix(
"jera_tab_reads_the_ledger_over_viz_jobs",
ok,
"red/green on ONE ledger in the state the installed forge leaves it (readable, not \
writable by the UI's user): JeraTab::live (the file road) lands `unreadable`/0 jobs \
and paints `could not READ`, while JeraTab::wire over /nornir.v1.Viz/Jobs lands \
`ledger`/1 job — same pane, same surface, the read seam swapped",
);
}
/// **3 — `Viz.Jobs` really travels the wire, and carries the selected workspace.**
///
/// The sentinel `job_id` is a value no ledger on this box holds, so seeing it in the
/// pane's rendered job tree proves the row crossed the socket. The header assertion is
/// the other half: the server picks the workspace off `nornir-workspace`, so a client
/// that forgot to stamp it would silently read whatever the server defaults to.
#[test]
fn viz_jobs_really_travels_the_wire() {
let seen: Arc<Mutex<Vec<String>>> = Arc::default();
let srv = spawn_mock_viz(MockViz {
jobs: serde_json::json!([
record("j-42", "over-wire-release", "failed"),
record("j-43", "over-wire-bench", "running"),
])
.to_string(),
seen_workspaces: Arc::clone(&seen),
..Default::default()
});
let mut tab = JeraTab::wire(&srv.endpoint, "", "nordisk");
tab.refresh();
let s = tab.state_json();
let count_ok = s["job_count"] == 2;
// The sentinel is on the RENDERED surface, not merely in a field.
let painted_ok = painted(&mut tab, "over-wire-release");
// And the honest per-status roll-up the shared JeraCentral derives came from the wire.
let status_ok = s["failed"] == 1 && s["running"] == 1;
let ws_ok =
seen.lock().unwrap().iter().all(|w| w == "nordisk") && !seen.lock().unwrap().is_empty();
assert!(
count_ok && painted_ok,
"the server's rows reach the pane's tree: {s}"
);
assert!(
status_ok,
"the wire rows keep their status through the SAME JobRecord mapper: {s}"
);
assert!(
ws_ok,
"the client stamps the selected workspace: {:?}",
seen.lock().unwrap()
);
let ok = count_ok && painted_ok && status_ok && ws_ok;
matrix(
"jera_viz_jobs_crosses_the_socket",
ok,
"a sentinel job_id no local ledger holds is rendered by the shipped pane, its \
failed/running roll-up survives the SAME jera JobRecord -> JobEntry mapper the \
file road uses, and every call carried `nornir-workspace: nordisk`",
);
}
/// **4 — a forge that does not proxy `Viz` yet is a NAMED failure, never zero jobs.**
///
/// This is the state of `main` at the time of writing: the dwarves forge proxies
/// Warehouse / Workspaces / Mímir / Telemetry / Bench / Funnel and not `Viz`, so its gRPC
/// router answers `Unimplemented` on `/nornir.v1.Viz/Jobs`. That must reach the operator
/// as an outage with a name. If it read as "0 jobs · the forge is idle" the missing proxy
/// would be invisible — the precise shape of the bug `1aa8f0f` cost a workspace to find.
#[test]
#[allow(non_snake_case)]
fn a_forge_that_does_not_proxy_viz_is_a_NAMED_failure_never_zero_jobs() {
let srv = spawn_mock_viz(MockViz {
refuse: Some((Code::Unimplemented, "unknown service nornir.v1.Viz".into())),
..Default::default()
});
let mut tab = JeraTab::wire(&srv.endpoint, "", "nordisk");
tab.refresh();
let s = tab.state_json();
let named = s["error"].as_str().unwrap_or_default().to_string();
let named_ok = named.contains("transport cannot carry it") || named.contains("Unimplemented");
let not_idle_ok = s["source"] != "idle";
assert!(
named_ok,
"an unproxied verb must be NAMED, got error={named:?} state={s}"
);
assert!(not_idle_ok, "…and must never read as an idle forge: {s}");
assert!(
painted(&mut tab, "FAILED"),
"the outage is on the rendered surface"
);
// A dead endpoint is the other named outage, and a different one.
drop(srv);
let mut dead = JeraTab::wire("http://127.0.0.1:1", "", "nordisk");
dead.refresh();
let ds = dead.state_json();
let dead_ok = ds["error"]
.as_str()
.unwrap_or_default()
.contains("unreachable")
&& ds["source"] != "idle";
assert!(
dead_ok,
"a dead forge is `unreachable`, not an idle one: {ds}"
);
let ok = named_ok && not_idle_ok && dead_ok;
matrix(
"jera_outage_is_named_never_idle",
ok,
"Unimplemented (the forge's router before it proxies nornir.v1.Viz) and a dead \
socket each surface as their own named outage in `error` with source != idle — \
an unreachable server and an empty ledger never draw the same",
);
}
/// **5 — a failed refresh keeps the PREVIOUS reading and NAMES the failure.**
///
/// The two properties seven panes in this repo already hold. A refresh that fails must
/// not blank a good job list: the operator loses real information and gains a picture
/// indistinguishable from a quiet forge.
#[test]
fn a_failed_refresh_keeps_the_previous_reading_and_names_the_failure() {
// A source that answers once, then fails forever — the shape of a server going away
// under a pane that already has a reading.
struct FlakySource {
calls: Mutex<usize>,
}
impl dwarves_gui::jera::JeraJobSource for FlakySource {
fn read(&self, _limit: usize, _kind: &str) -> JobsRead {
let mut n = self.calls.lock().unwrap();
*n += 1;
if *n == 1 {
JobsRead::Rows(
dwarves_embed::entries_from_records_json(&[record(
"j-first",
"first-good-read",
"done",
)])
.unwrap(),
)
} else {
JobsRead::Failed("nornir-server unreachable: connect refused".into())
}
}
fn describe(&self) -> String {
"flaky test source".into()
}
}
let mut tab = JeraTab::with_source(Arc::new(FlakySource {
calls: Mutex::new(0),
}));
let first = tab.state_json();
assert_eq!(first["job_count"], 1, "the good reading landed: {first}");
tab.refresh(); // …and now it fails.
let after = tab.state_json();
let kept_ok = after["job_count"] == 1;
let named_ok = after["error"]
.as_str()
.unwrap_or_default()
.contains("unreachable");
let stale_ok = after["stale"] == true;
assert!(
kept_ok,
"a failed refresh must NOT blank the previous reading: {after}"
);
assert!(named_ok, "…and must name the failure: {after}");
assert!(
stale_ok,
"…and must mark what is on screen as stale: {after}"
);
assert!(
painted(&mut tab, "PREVIOUS reading"),
"the surface says the rows are stale"
);
assert!(
painted(&mut tab, "first-good-read"),
"…and the rows are still there"
);
let ok = kept_ok && named_ok && stale_ok;
matrix(
"jera_failed_refresh_keeps_the_reading",
ok,
"a source that answers once then fails leaves the pane with its 1 job still \
rendered, `error` naming the outage, `stale=true`, and the surface saying the \
list is the PREVIOUS reading — never a blank",
);
}
/// **6 — a read IN FLIGHT is named, and does not blank the previous reading.**
///
/// The delay lives on the far side of a REAL socket (the mock holds its reply), so what
/// the frame is waiting on is what it waits on in production.
#[test]
fn a_read_in_flight_is_named_and_does_not_blank_the_previous_reading() {
let srv = spawn_mock_viz(MockViz {
jobs: serde_json::json!([record("j-slow", "populate", "done")]).to_string(),
reply_delay: Duration::from_millis(400),
..Default::default()
});
let mut tab = JeraTab::wire(&srv.endpoint, "", "nordisk");
tab.refresh(); // land a first reading (blocking).
assert_eq!(tab.state_json()["job_count"], 1);
tab.start(); // a second read, NOT waited on.
let mid = tab.state_json();
let pending_ok = mid["pending"] == true;
let kept_ok = mid["job_count"] == 1;
assert!(pending_ok, "a read in flight must SAY so: {mid}");
assert!(
kept_ok,
"…and the previous reading stays on screen while it travels: {mid}"
);
assert!(
painted(&mut tab, "previous reading"),
"the surface names the in-flight read"
);
tab.wait();
let done = tab.state_json();
assert_eq!(done["pending"], false, "…and it lands: {done}");
let ok = pending_ok && kept_ok;
matrix(
"jera_in_flight_read_is_named",
ok,
"with the mock Viz holding its reply 400 ms on the far side of a real socket, the \
pane reports pending=true and keeps its 1 previous job rendered — `still reading` \
is a third state, never `read nothing`",
);
}
/// **7 — an EMPTY answer is `idle` over the wire, and never the fixture.**
///
/// `1aa8f0f`: `read_ledger_jobs` returned `Ok(Vec::new())` for BOTH an absent and an
/// empty ledger, `jera.rs:118` fell through to `fixture_zoned_jobs()`, and a **deleted
/// workspace painted invented jobs** — a green build, a running bench, a failed release
/// that did not exist. Over the wire the client is not entitled to claim `absent` (only
/// the server knows whether the workspace exists), so the three answers are: rows, an
/// empty ledger, and a NAMED failure for "no such workspace". None of them is a fixture.
#[test]
fn an_empty_answer_is_idle_over_the_wire_and_never_the_fixture() {
let srv = spawn_mock_viz(MockViz::default()); // serves `[]`
let mut tab = JeraTab::wire(&srv.endpoint, "", "nordisk");
tab.refresh();
let s = tab.state_json();
let idle_ok = s["source"] == "idle" && s["job_count"] == 0 && s["ledger_absent"] == false;
assert!(
idle_ok,
"`[]` from a server that answered IS an idle ledger: {s}"
);
assert!(painted(&mut tab, "idle"), "…and it says idle");
let no_fixture_ok = !painted(&mut tab, "cargo-build") && !painted(&mut tab, "#1042");
assert!(
no_fixture_ok,
"an empty ledger must NEVER render the demo fixture"
);
// "no such workspace" is a NAMED failure, not an empty list — the state a wiped
// registry (`serving 0 workspace(s)`) actually produces.
let gone = spawn_mock_viz(MockViz {
refuse: Some((Code::NotFound, "workspace `nordisk` is not served".into())),
..Default::default()
});
let mut gone_tab = JeraTab::wire(&gone.endpoint, "", "nordisk");
gone_tab.refresh();
let gs = gone_tab.state_json();
let gone_ok = gs["source"] != "idle"
&& gs["error"]
.as_str()
.unwrap_or_default()
.contains("not_served");
assert!(
gone_ok,
"an unserved workspace is a named failure, not zero jobs: {gs}"
);
let ok = idle_ok && no_fixture_ok && gone_ok;
matrix(
"jera_empty_and_absent_stay_different_values",
ok,
"over Viz.Jobs: `[]` lands `idle`/0 with the demo fixture nowhere on the surface, \
while an unserved workspace lands a named `not_served` failure with source != idle \
— `no answer` and `no jobs` remain different values after the move to gRPC",
);
}
/// The FILE road survives as an explicit escape hatch and still answers its own three
/// states — kept so moving the shipped tab to the wire did not quietly delete a capability.
#[test]
fn the_file_road_still_answers_absent_idle_and_rows() {
let d = tempfile::tempdir().unwrap();
let root = d.path().to_path_buf();
let absent = JeraTab::with_source(Arc::new(LedgerJobs { root: root.clone() }));
assert_eq!(
absent.state_json()["source"],
"absent",
"no jobs.redb ⇒ absent"
);
drop(jera::jobs::JobStore::open(&root).unwrap());
let idle = JeraTab::with_source(Arc::new(LedgerJobs { root: root.clone() }));
assert_eq!(
idle.state_json()["source"],
"idle",
"an empty ledger ⇒ idle"
);
seed_ledger(&root);
let rows = JeraTab::with_source(Arc::new(LedgerJobs { root: root.clone() }));
assert_eq!(
rows.state_json()["source"],
"ledger",
"a populated ledger ⇒ ledger"
);
assert_eq!(rows.state_json()["job_count"], 1);
}
/// The shipped wire pane names its endpoint as the FORGE it was handed, and never
/// nornir's `:7878`. The suite-host-embedded-auth LAW, asserted at this pane's own seam —
/// `tests/robot_host_only.rs` folds `whence` in with every other pane and re-proves it
/// across the whole shell.
#[test]
fn the_wire_pane_dials_the_forge_and_holds_no_nornir_address() {
let tab = JeraTab::wire("http://dwarves-host.example:7900", "", "nordisk");
let whence = tab.state_json()["whence"]
.as_str()
.unwrap_or_default()
.to_string();
assert!(
whence.contains(":7900"),
"the pane names the forge: {whence}"
);
assert!(
!whence.contains("7878"),
"the UI holds no nornir address: {whence}"
);
// The type carries no second endpoint to hold one in, either.
let w = WireJobs {
endpoint: "http://dwarves-host.example:7900".into(),
token: String::new(),
workspace: "nordisk".into(),
};
assert!(!format!("{}", w.endpoint).contains("7878"));
}
/// **A JOB CLASS MUST NOT BE PUSHED OUT OF VIEW BY A NOISIER ONE** (`jerakind-1`).
///
/// # The measurement this is built on
///
/// Taken off the LIVE dwarves forge 2026-08-27 (`nornir jobs --server` against oden's
/// embedded nornir, workspace `nordisk`), comparing the server's own default 200-row window
/// with what a kind-scoped read of the same ledger returns:
///
/// ```text
/// kind in the MIXED window kind-scoped read
/// snapshot_member 57 200+
/// scip_member 47 200+
/// knowledge_member 38 200+
/// lens 26 200+
/// feed 6 84 (7.1 %)
/// workspace_populate 0 1 (invisible)
/// ```
///
/// A populate emits one job per workspace member per phase, so the three per-member kinds
/// are **142 of the 200** — 71 % of the window — and a class that ran once is not in it at
/// all. The crowding happens in the SERVER'S truncate, so no client-side filter recovers
/// it: those rows never arrived.
///
/// # What is asserted, and why it is the source and not the field
///
/// The fixture RECORDS every `kind` it was asked for, and its answer depends on that kind
/// the way the real ledger does: 200 mixed rows with zero `workspace_populate` for `""`,
/// and the one populate row for `"workspace_populate"`. So this cannot pass by the pane
/// storing a string — the read has to carry it.
///
/// **THE RED.** Before `JeraJobSource::read` took a kind, `WireJobs` passed a literal `""`
/// to `Viz.Jobs` and the pane had no kind of its own: the first assertion below (`asked`
/// contains `"workspace_populate"`) failed with `asked = [""]`, and the count stayed at 0
/// after the drive. The capability had been on the RPC since it was written — the server's
/// `JobSelector::Kind` arm — and the pane dropped it on the floor.
#[test]
fn a_quiet_job_class_is_reachable_because_the_kind_rides_the_read() {
/// The live census above, as a source. `asked` is the whole point: it is the evidence
/// that the kind reached the read rather than a field.
struct CensusJobs {
asked: Mutex<Vec<String>>,
}
impl dwarves_gui::jera::JeraJobSource for CensusJobs {
fn read(&self, limit: usize, kind: &str) -> JobsRead {
self.asked.lock().unwrap().push(kind.to_string());
// The mixed window, in the live proportions, truncated exactly as the server
// truncates: the quiet class is not in it.
let mixed: Vec<(&str, usize)> = vec![
("snapshot_member", 57),
("scip_member", 47),
("knowledge_member", 38),
("lens", 26),
("feed", 6),
("security_member", 5),
("knowledge_scan", 4),
("deepscan", 3),
("snapshot", 3),
("index_build", 3),
("scip_ingest", 2),
("security_scan", 2),
("workspace_republish", 2),
("arch_generate", 2),
];
let mut rows: Vec<serde_json::Value> = Vec::new();
let mut n = 0usize;
if kind.is_empty() {
for (k, count) in mixed {
for i in 0..count {
rows.push(record(&format!("{k}-{i}"), k, "done"));
n += 1;
}
}
} else if kind == "workspace_populate" {
// The one row the mixed window never carried.
rows.push(record("populate-0", "workspace_populate", "done"));
n = 1;
} else {
// Any other class: the scoped read returns its whole history, which is what
// makes the window a window and not a lens.
for i in 0..200 {
rows.push(record(&format!("{kind}-{i}"), kind, "done"));
}
n = 200;
}
rows.truncate(limit.max(1));
let _ = n;
JobsRead::Rows(dwarves_embed::entries_from_records_json(&rows).unwrap())
}
fn describe(&self) -> String {
"the 2026-08-27 live census, as a fixture".to_string()
}
}
let src = Arc::new(CensusJobs {
asked: Mutex::new(Vec::new()),
});
let mut tab = JeraTab::with_source(src.clone());
// ── FORE: the mixed window. Not one populate row, and the pane's own oracle says the
// read asked for every kind.
let s = tab.state_json();
assert_eq!(
s["server_kind"], "",
"the pane opens on every kind, as it always has"
);
let seen: Vec<String> = s["kinds_seen"]
.as_array()
.expect("kinds_seen is published")
.iter()
.map(|v| v.as_str().unwrap_or_default().to_string())
.collect();
assert!(
seen.contains(&"snapshot_member".to_string()),
"the picker learns the noisy classes from the mixed window: {seen:?}"
);
assert!(
!seen.contains(&"workspace_populate".to_string()),
"…and CANNOT learn the quiet one, which is exactly why the pane needs a typed \
entry beside the picker: {seen:?}"
);
let before = s["job_count"].as_u64().unwrap_or(0);
// The pane asks for `DEFAULT_LEDGER_LIMIT` (100) and the fixture's mixed window holds
// 200, so the window comes back FULL — which is the condition under which a quiet class
// is squeezed out, and the reason the assertion is `==` the limit rather than "some".
assert_eq!(
before,
u64::try_from(dwarves_embed::DEFAULT_LEDGER_LIMIT).expect("limit fits"),
"the mixed window must come back FULL, or nothing was being crowded out"
);
// ── THE DRIVE, through the robot seam the shell drives, not through the field.
tab.update_json(&serde_json::json!({ "server_kind": "workspace_populate" }).to_string());
// ── EFTER: the kind reached the READ, and the row that was invisible is on screen.
let asked = src.asked.lock().unwrap().clone();
assert!(
asked.iter().any(|k| k == "workspace_populate"),
"the read was never asked for the class — the kind is being stored and not sent. \
Asked so far: {asked:?}"
);
let s = tab.state_json();
assert_eq!(
s["server_kind"], "workspace_populate",
"the pane names its window"
);
assert_eq!(
s["job_count"], 1,
"the one populate row must be on screen. It is 0 of 200 in the mixed window, so a \
client-side filter over what arrived could never have found it."
);
assert_eq!(
s["source"], "ledger",
"a scoped read that found rows is a live ledger"
);
// ── BACK TO EVERY KIND — a window you cannot leave is a mode, not a filter.
tab.update_json(&serde_json::json!({ "server_kind": serde_json::Value::Null }).to_string());
let s = tab.state_json();
assert_eq!(
s["server_kind"], "",
"null puts the window back to every kind"
);
assert_eq!(
s["job_count"].as_u64().unwrap_or(0),
u64::try_from(dwarves_embed::DEFAULT_LEDGER_LIMIT).expect("limit fits"),
"…and the full mixed window comes back"
);
// ── NON-VACUITY: the fixture must actually DISTINGUISH the two reads, or every
// assertion above is a statement about one answer.
assert!(
src.asked.lock().unwrap().iter().any(|k| k.is_empty()),
"the fixture was never asked for the mixed window — the FORE half is untested"
);
matrix(
"jera kind window rides the read",
true,
"workspace_populate: 0 of 200 in the mixed window, 1 row through a kind-scoped read; \
the kind reaches JeraJobSource::read and not just a field",
);
}