crates/monetize-server/src/estate_order/tests/custody_step.rs
//! **F9 (2026-10-21): the ONE box takes a box's update step from the custody's copy of the
//! installer material.** test3-holger-rs's r6 front never calls `SoldEstate.TakeMaterial`, so
//! its primary keeps no installer material and its update to r10 fails on the twin: "its live
//! door did not answer: no installer material is kept in holger's primary … monetize-backup
//! yet". The front cannot be updated while the update itself needs that material — a fix in the
//! front alone is a circle.
//!
//! * **A paired estate whose front never took its material still updates every box**: the ONE
//! box takes the main's and the twin's steps itself, over the installer link with the
//! custody's material (live where it can, the keep medium where the BASE moved); the front is
//! handed only its own step.
//! * **A front that says its primary keeps no material** hands the box to the ONE box instead
//! of stopping the update.
//! * Only for an estate whose every box is PAIRED and that the custody still holds: never one
//! being deleted or gone, never one the front took its material for.
use super::paired_update::{call, runs_older, take_as};
use super::*;
const ARCHIVE: &[u8] = b"live-app!";
fn sha256(b: &[u8]) -> String {
use sha2::Digest as _;
sha2::Sha256::digest(b)
.iter()
.map(|x| format!("{x:02x}"))
.collect()
}
/// A Holger release r10 whose live app is [`ARCHIVE`] (its sha256 and length as published).
fn holger_r10() -> EstateOrderConfig {
let mut c = EstateOrderConfig {
lifecycle: "holger".into(),
..cfg()
};
c.media = [
("front", "front"),
("holger-front", "holger-front"),
("holger", "holger"),
("holger-keep", "holger-keep"),
("holger-front-keep", "holger-front-keep"),
]
.into_iter()
.map(|(k, v)| (k.to_string(), format!("r10-{v}")))
.collect();
for (kind, pin) in [("holger", "a"), ("holger-front", "f")] {
c.live_apps.insert(
kind.into(),
update::LiveRecord {
base: "b".repeat(64),
app: pin.repeat(64),
app_len: ARCHIVE.len() as u64,
sig: "5".repeat(128),
..Default::default()
},
);
c.media_objects.insert(
format!("{kind}{}", update::LIVE_SUFFIX),
monetize_cloud::lay::MediaObject {
url: format!("https://objsto.example/release/{kind}/r10.live-app.znippy"),
sha256: sha256(ARCHIVE),
bytes: ARCHIVE.len() as u64,
},
);
}
c
}
/// A Holger estate paired by its installer's PASS, every box on r6, its installer material in
/// the custody — and its front has never taken it.
fn paired(name: &str) -> (Rig, monetize::Signer) {
paired_with(name, holger_r10())
}
fn paired_with(name: &str, release: EstateOrderConfig) -> (Rig, monetize::Signer) {
let r = rig_with(name, release, "PASS");
r.kit.silent.store(true, Ordering::SeqCst);
r.kit.bundles.store(true, Ordering::SeqCst);
start_for(
&r.state,
"acme",
"buyer@example.org",
"",
"alice",
&EstateSizes::default(),
)
.unwrap();
r.pay.authorize(&handle_of(&r, "acme"));
crate::estate_order::drive(&r.state, "acme");
let run = r.state.estates.get("acme").unwrap();
assert_eq!(run.phase, phase::PAIRED, "no box reported yet");
assert_eq!(
run.material_taken_at_ms, 0,
"its front never took its material"
);
assert!(
update::custody_holds(&r.state, "acme"),
"the custody holds the installer material its pairing handed back"
);
runs_older(&r.state, "acme");
let key = client_of(&r, "acme");
// Its front pulls updates (test3's r6 front does): the update is not opened front first.
assert!(!take_as(&r.state, &r.clock, &key, "acme").open);
// The twin runs the release's BASE (a live update); the main's BASE moved (its keep medium).
let box_on = |base: &str| update::LiveRunning {
base: base.repeat(64),
app: "0".repeat(64),
slot: "a".into(),
live_update: true,
};
r.kit.lives.lock().unwrap().extend([
("twin".to_string(), box_on("b")),
("main".to_string(), box_on("9")),
]);
(r, key)
}
fn tweak() -> update::CustodyTweak {
update::CustodyTweak {
fetch: Some(Box::new(|url: &str| {
if url.contains("/holger/") {
Ok(ARCHIVE.to_vec())
} else {
Err(format!("no object at {url}"))
}
})),
no_wait: true,
}
}
fn report_as(
r: &Rig,
key: &monetize::Signer,
role: &str,
outcome: &update::Outcome,
words: &str,
) -> Result<String, sold::SoldRefusal> {
let carries = update::report_carries(outcome, words);
let (at, sig) = call(
&r.clock,
key,
"acme",
update::CALL_UPDATE_REPORT,
&[role],
0,
&carries,
);
update::report(&r.state, "acme", role, &carries, at, &sig)
}
/// ★ **A paired estate whose front never took its material still updates all three boxes to
/// the current release**: the ONE box takes the twin (live) and the main (keep) from the
/// custody's material; the front, handed only its own step, updates itself.
#[test]
fn a_paired_estate_whose_front_never_took_its_material_still_updates_every_box() {
let (r, key) = paired("f9-never-took");
let keep_before = r.cloud.keep_booted.lock().unwrap().len();
let u =
update::start(&r.state, "acme", "", "operator").expect("a paired estate takes the update");
assert_eq!(u.release, "r10-holger", "{}", u.line());
// The front is handed no box while the ONE box takes the twin.
let t = take_as(&r.state, &r.clock, &key, "acme");
assert!(t.open && t.role.is_empty(), "{t:?}");
assert!(t.words.contains("custody"), "{}", t.words);
let said = update::custody_step_with(&r.state, "acme", &tweak());
assert_eq!(
r.kit.staged.lock().unwrap().as_slice(),
&[("twin".to_string(), "a".repeat(64))],
"{said}"
);
let run = r.state.estates.get("acme").unwrap();
let steps = &run.update.as_ref().expect("still open").steps;
assert_eq!(
(
steps[0].role.as_str(),
steps[0].state.as_str(),
steps[0].how.as_str()
),
("twin", update::step::DONE, "live"),
"{said}"
);
assert!(
take_as(&r.state, &r.clock, &key, "acme").role.is_empty(),
"the main is the ONE box's too"
);
let said = update::custody_step_with(&r.state, "acme", &tweak());
assert_eq!(
r.cloud.keep_booted.lock().unwrap().len(),
keep_before + 1,
"{said}"
);
assert_eq!(
r.cloud.keep_booted.lock().unwrap().last().unwrap().1,
"r10-holger-keep",
"the main, from its keep medium, its data kept"
);
// The front's own step is the front's.
let t = take_as(&r.state, &r.clock, &key, "acme");
assert_eq!(t.role, "front", "{t:?}");
report_as(&r, &key, "front", &update::Outcome::Done("live".into()), "")
.expect("the front reports itself");
let run = r.state.estates.get("acme").unwrap();
assert!(run.update.is_none(), "closed");
assert_eq!(
run.updates.last().unwrap().state,
update::state::DONE,
"{}",
run.updates.last().unwrap().line()
);
assert!(
["front", "main", "twin"].iter().all(|b| run
.release_of
.get(*b)
.is_some_and(|x| x.starts_with("r10-"))),
"{:?}",
run.release_of
);
assert_eq!(run.phase, phase::PAIRED, "an update is no delivery");
assert!(
r.state.estates.custody.as_ref().unwrap().holds("acme"),
"the custody keeps its copy until the estate is delivered"
);
}
/// ★ **A front that says its primary keeps no material hands the box to the ONE box** — the
/// update goes on instead of stopping at the twin.
#[test]
fn a_front_whose_primary_keeps_no_material_hands_the_box_to_the_one_box() {
let (r, key) = paired("f9-said-none");
// Its front took the material once (a primary laid again since lost it).
{
let _pen = r.state.estates.pen("acme");
let mut run = r.state.estates.get("acme").unwrap();
run.material_taken_at_ms = 1;
save(&r.state, &run).unwrap();
}
update::start(&r.state, "acme", "", "operator").unwrap();
assert_eq!(
take_as(&r.state, &r.clock, &key, "acme").role,
"twin",
"a front that took its material takes the twin"
);
let said = report_as(&r, &key, "twin", &update::Outcome::Failed, "its live door did not answer: no installer material is kept in holger's primary 10.44.0.2:50051 repository monetize-backup yet").expect("taken");
assert!(said.contains("the ONE box takes the twin"), "{said}");
let run = r.state.estates.get("acme").unwrap();
assert_eq!(
run.update.as_ref().map(|u| u.state.as_str()),
Some(update::state::RUNNING),
"the update did not stop"
);
assert!(take_as(&r.state, &r.clock, &key, "acme").role.is_empty());
update::custody_step_with(&r.state, "acme", &tweak());
assert_eq!(
take_as(&r.state, &r.clock, &key, "acme").role,
"main",
"the front takes the main again: its words named only the twin"
);
}
/// **Never for an estate being deleted or gone, nor without the custody's copy.**
#[test]
fn only_a_paired_or_delivered_estate_the_custody_holds_is_driven() {
let (r, _key) = paired("f9-never-gone");
update::start(&r.state, "acme", "", "operator").unwrap();
let mut run = r.state.estates.get("acme").unwrap();
let roles = vec!["twin".to_string(), "main".to_string()];
assert_eq!(
update::custody_drives(&run, &roles, true).as_deref(),
Some("twin")
);
assert_eq!(
update::custody_drives(&run, &roles, false),
None,
"no custody copy"
);
for p in [
phase::DELETING,
phase::TERMINATED,
phase::INSTALLING,
phase::LAYING,
phase::FAILED,
] {
run.phase = p.into();
assert_eq!(update::custody_drives(&run, &roles, true), None, "{p}");
}
run.phase = phase::CAPTURED.into();
assert_eq!(
update::custody_drives(&run, &roles, true).as_deref(),
Some("twin"),
"a delivered estate whose custody still holds it"
);
run.material_taken_at_ms = 1;
assert_eq!(
update::custody_drives(&run, &roles, true),
None,
"its front took its material: the front drives"
);
}
/// ★ **A box with no live door is keep-updated from the custody's material** (c1, 2026-10-21:
/// r6 boxes run no live door): live on or off, the ONE box keep-boots the twin and the main
/// from the release's keep medium and the update goes on to the front.
#[test]
fn a_box_with_no_live_door_is_keep_updated_from_the_custodys_material() {
for live in [true, false] {
let (r, key) = paired(if live {
"f9-no-door-live"
} else {
"f9-no-door-off"
});
r.kit.lives.lock().unwrap().clear();
if !live {
update::set_live(&r.state, "acme", false, "operator").unwrap();
}
let keep_before = r.cloud.keep_booted.lock().unwrap().len();
update::start(&r.state, "acme", "", "operator").unwrap();
for role in ["twin", "main"] {
assert!(
take_as(&r.state, &r.clock, &key, "acme").role.is_empty(),
"the {role} is the ONE box's"
);
let said = update::custody_step_with(&r.state, "acme", &tweak());
let run = r.state.estates.get("acme").unwrap();
let s = run
.update
.as_ref()
.expect("still open")
.steps
.iter()
.find(|s| s.role == role)
.unwrap()
.clone();
assert_eq!(
(s.state.as_str(), s.how.as_str()),
(update::step::DONE, "keep"),
"live {live}, {role}: {said} / {}",
s.words
);
}
assert_eq!(
r.cloud.keep_booted.lock().unwrap().len(),
keep_before + 2,
"twin and main from the keep medium"
);
assert!(
r.kit.staged.lock().unwrap().is_empty(),
"nothing staged live"
);
assert_eq!(take_as(&r.state, &r.clock, &key, "acme").role, "front");
}
}
// ── lane B5: what the release declares rides the ONE box's stage ────────────────────────
const BUNDLE: &[u8] = b"korp-release-v1\nholger.ron 5\n(two)\n";
fn hex(b: &[u8]) -> String {
b.iter().map(|x| format!("{x:02x}")).collect()
}
/// r10 with a release declaration and the release's env on Holger's live record.
fn holger_r10_declared() -> EstateOrderConfig {
let mut c = holger_r10();
let r = c.live_apps.get_mut("holger").unwrap();
r.release = hex(BUNDLE);
r.release_sig = "7".repeat(128);
r.env = vec!["korp.env.HOLGER_DATA=/data".into()];
c
}
/// ★ **The ONE box's live stage carries what the release declares** (lane B5): the release
/// declaration (bundle and signature, from the live record) and the release's env reach the
/// box with the app; a record that declares neither stages neither.
#[test]
fn the_one_boxs_live_stage_carries_the_release_declaration_and_env() {
let (r, _key) = paired_with("b5-declared", holger_r10_declared());
update::start(&r.state, "acme", "", "operator").unwrap();
let said = update::custody_step_with(&r.state, "acme", &tweak());
let stages = r.kit.stages.lock().unwrap().clone();
assert_eq!(stages.len(), 1, "{said}");
assert_eq!(
stages[0].release,
Some((BUNDLE.to_vec(), vec![0x77; 64])),
"{said}"
);
assert_eq!(
stages[0].env,
Some(vec!["korp.env.HOLGER_DATA=/data".to_string()])
);
let (r, _key) = paired("b5-undeclared");
update::start(&r.state, "acme", "", "operator").unwrap();
update::custody_step_with(&r.state, "acme", &tweak());
let stages = r.kit.stages.lock().unwrap().clone();
assert_eq!(
(stages[0].release.as_ref(), stages[0].env.as_ref()),
(None, None),
"an older record declares nothing"
);
}
/// ★ **A box that refuses a stage because only a keep boot lays the release** (its env moved,
/// its declaration cannot be carried, or it takes none live) is keep-booted, never failed; a
/// declaration that does not hold stays a failure.
#[test]
fn a_box_refusing_a_moved_env_is_keep_booted_and_a_bad_declaration_fails() {
let (r, _key) = paired_with("b5-env-moved", holger_r10_declared());
let keep_before = r.cloud.keep_booted.lock().unwrap().len();
update::start(&r.state, "acme", "", "operator").unwrap();
*r.kit.refuse_stage.lock().unwrap() = Some(
"REFUSED [live-env-moved] the release changes korp.env.* (HOLGER_DATA: /srv → /data)"
.into(),
);
let said = update::custody_step_with(&r.state, "acme", &tweak());
assert_eq!(
r.cloud.keep_booted.lock().unwrap().len(),
keep_before + 1,
"{said}"
);
let run = r.state.estates.get("acme").unwrap();
let s = &run.update.as_ref().expect("still open").steps[0];
assert_eq!(
(s.role.as_str(), s.state.as_str(), s.how.as_str()),
("twin", update::step::DONE, "keep"),
"{said}"
);
assert!(s.words.contains("[live-env-moved]"), "{}", s.words);
let (r, _key) = paired_with("b5-bad-declaration", holger_r10_declared());
update::start(&r.state, "acme", "", "operator").unwrap();
*r.kit.refuse_stage.lock().unwrap() =
Some("REFUSED [release-sig] the release declaration's signature does not hold".into());
let said = update::custody_step_with(&r.state, "acme", &tweak());
let run = r.state.estates.get("acme").unwrap();
let failed = run
.update
.as_ref()
.map(|u| u.steps[0].state.clone())
.or_else(|| run.updates.last().map(|u| u.steps[0].state.clone()));
assert_eq!(failed.as_deref(), Some(update::step::FAILED), "{said}");
}