Your browser sucks, JavaScript disabled! Gunnar's console needs JavaScript and WebAssembly. Git over SSH on port 2222 does not.

nordisk/monetize

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}");
}