crates/gunnar-monetize-guest/tests/the_kits_judge_is_the_guardians.rs
//! ★ **One guard, two callers** (lane G11, 2026-10-21): the guard before a main's data is
//! destroyed (Gunnar ca38566f, R1-R6) is asked by the operator's kit and the console through
//! `gunnar_repair::rebuild::judge` (open gunnar, money-free, with Gunnar's `TwinReview` and a
//! typed confirmation), and by every product's estate guardian through monetize-product
//! `guardian::rebuild::judge` (which Gunnar's and Holger's guardians call, linked and as signed
//! guests). This file holds the two to the same answer for every case R1-R6 reason about, so
//! the rule set is one even though the open crate cannot link the monetize one.
use gunnar_repair::rebuild as kit;
use gunnar_repair::review::{Health, TwinReview};
use monetize_product::guardian::rebuild as shared;
const STORAGE: &str = "01aa-data";
fn review(health: Health, possible: bool, head: u64) -> TwinReview {
TwinReview {
twin_addr: "10.0.0.3:50051".into(),
health: Some(health),
head_seq: head,
applied_seq: head,
recharge_possible: possible,
verdict: if possible {
"level".into()
} else {
"behind its primary".into()
},
..Default::default()
}
}
/// The shared guard's reading of Gunnar's twin review: it hears a primary when it is level
/// with one that answers (`Healthy`), and it holds its own head.
fn twin_of(t: &TwinReview) -> shared::Twin {
shared::Twin {
addr: t.twin_addr.clone(),
hears_primary: t.health == Some(Health::Healthy),
recharge_possible: t.recharge_possible,
verdict: t.verdict.clone(),
holds: Some(t.head_seq.max(t.applied_seq)),
}
}
/// The same facts, in the shared guard's types (the enums are the same JSON shape).
fn shared_of(s: &kit::Seen) -> shared::Seen {
shared::Seen {
provider: serde_json::from_value(serde_json::to_value(&s.provider).unwrap()).unwrap(),
main: serde_json::from_value(serde_json::to_value(&s.main).unwrap()).unwrap(),
main_head: s.main_head,
booted_again: s.booted_again,
installer_refused: s.installer_refused.clone(),
twin: s.twin.as_ref().map(twin_of),
data_storage: s.data_storage.clone(),
confirm: s
.confirm
.as_ref()
.map(|c| serde_json::from_value(serde_json::to_value(c).unwrap()).unwrap()),
auto: s.auto,
}
}
/// What a caller acts on: the word, the refusal code, and where it fills from / what it gives back.
fn act(v: serde_json::Value) -> (String, String, String, String) {
let s = |k: &str| v.get(k).and_then(|x| x.as_str()).unwrap_or("").to_string();
(s("do"), s("code"), s("twin"), s("storage"))
}
fn cases() -> Vec<kit::Seen> {
let mut out = Vec::new();
let providers = [
kit::Provider::Gone,
kit::Provider::Stopped,
kit::Provider::Running,
kit::Provider::Unknown("timeout".into()),
];
let mains = [
kit::Main::Silent,
kit::Main::Says { data: "ok".into() },
kit::Main::Says {
data: "fresh".into(),
},
kit::Main::Says {
data: "lost".into(),
},
kit::Main::Says {
data: "diverged".into(),
},
kit::Main::Says { data: "odd".into() },
];
let twins = [
None,
Some(review(Health::PrimarySilent, true, 100)),
Some(review(Health::Healthy, true, 100)),
Some(review(Health::Behind, false, 90)),
];
let confirms = [
None,
Some(kit::Confirm {
by: "rickard".into(),
storage: STORAGE.into(),
accept_lost: false,
}),
Some(kit::Confirm {
by: "rickard".into(),
storage: "01bb-other".into(),
accept_lost: false,
}),
Some(kit::Confirm {
by: "rickard".into(),
storage: STORAGE.into(),
accept_lost: true,
}),
];
for p in &providers {
for m in &mains {
for t in &twins {
for c in &confirms {
for (head, booted, refused, storage, auto) in [
(Some(100), false, None, STORAGE, false),
(Some(120), true, None, STORAGE, false),
(
None,
false,
Some("member 1 of 2 is missing".to_string()),
STORAGE,
true,
),
(Some(100), true, None, "", false),
] {
out.push(kit::Seen {
provider: p.clone(),
main: m.clone(),
main_head: head,
booted_again: booted,
installer_refused: refused,
twin: t.clone(),
data_storage: storage.into(),
confirm: c.clone(),
auto,
});
}
}
}
}
}
out
}
#[test]
fn the_kits_guard_and_the_guardians_guard_answer_alike() {
let all = cases();
assert!(all.len() > 1_000, "{}", all.len());
for s in &all {
let k = act(serde_json::to_value(kit::judge(s)).unwrap());
let g = act(serde_json::to_value(shared::judge(&shared_of(s))).unwrap());
assert_eq!(k, g, "{s:?}");
}
}