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/open_pin.rs

//! **F6 (2026-10-20): an adopted estate's boxes may pin their keys until they do.**
//! test3-holger-rs was adopted by "Check again" (F5) at 19:48Z: the guardian opened each box's
//! pin window for 10 minutes. Its main first called home after its restart at 20:24Z, was
//! refused, and the estate sat at "installing (paired)" for ever.
//!
//! * **Adopt opens the window until pinned** ([`monetize_guardian::UNTIL_PINNED`]): no fixed
//!   10 minutes; each box's first call pins exactly its key, and the window closes then, or at
//!   the estate's terminate.
//! * **`site open-pin <site>`** ([`OPEN_PIN`], the operator's): the window opened again for
//!   every box of the site whose key is not pinned; a pinned key is never pinned again.

use super::recheck::{answers, install_failed_with, site_of};
use super::*;
use ed25519_dalek::Signer as _;
use monetize_guardian::FleetRefusal;

const MINUTE: u64 = 60_000;

/// The box in `role` of the site calls home ONCE (a signed `Fleet.Report` presenting the key of
/// `seed`), as the fleet door admits it: its first call, then its report. `Ok(true)`: pinned.
fn calls(state: &AppState, role: &str, seed: u8) -> Result<bool, FleetRefusal> {
    let g = state.fleet.guardian().expect("the in-memory guardian");
    let key = ed25519_dalek::SigningKey::from_bytes(&[seed; 32]);
    let now = state.now();
    let c = monetize_embed::fleet::FleetCall {
        product: g
            .store
            .product_of("gunnar-rs")
            .expect("the guardian knows the site"),
        instance: "gunnar-rs".into(),
        role: role.into(),
        call: monetize_embed::fleet::call::REPORT.into(),
        body: serde_json::to_string(&running(role)).unwrap(),
        at_unix_ms: now,
    };
    let sig = key
        .sign(&monetize_embed::fleet::fleet_call_message(&c))
        .to_bytes();
    let pinned = g.first_call(&c, &sig, &key.verifying_key().to_bytes(), now)?;
    g.report(&c, &sig, now)?;
    Ok(pinned)
}

fn key_of(state: &AppState, role: &str) -> Option<[u8; 32]> {
    state
        .fleet
        .guardian()
        .unwrap()
        .store
        .key_of("gunnar-rs", role)
        .map(|k| k.to_bytes())
}

fn window(state: &AppState, role: &str) -> u64 {
    state
        .fleet
        .guardian()
        .unwrap()
        .store
        .pin_window("gunnar-rs", role)
}

/// The site's install FAILED, then adopted by "Check again" with no box having called home:
/// `paired`, waiting for its boxes.
fn adopted(name: &str) -> (Arc<AppState>, Arc<MockCloud>, Arc<AtomicU64>, Arc<Mails>) {
    let (state, cloud, kit, mails, clock) = install_failed_with(name, true);
    answers(&kit, true);
    let r = settle(&state, "gunnar-rs", RECHECK).expect("adopted");
    assert_eq!(r.phase, phase::PAIRED, "{:#?}", r.history);
    crate::estate_order::drive(&state, "gunnarrs");
    assert_eq!(
        state.estates.get("gunnarrs").unwrap().phase,
        phase::PAIRED,
        "no box called home yet"
    );
    (state, cloud, clock, mails)
}

/// ★ **An adopted estate whose main first calls home 30 minutes after the adopt** is pinned and
/// goes to ready; its other boxes may still pin theirs.
#[test]
fn an_adopted_estate_whose_main_first_reports_half_an_hour_later_is_pinned_and_ready() {
    let (state, _cloud, clock, mails) = adopted("f6-late-main");
    clock.fetch_add(30 * MINUTE, Ordering::SeqCst);
    assert_eq!(
        calls(&state, "main", 1),
        Ok(true),
        "the main's first call pins its key"
    );
    assert_eq!(
        key_of(&state, "main"),
        Some(
            ed25519_dalek::SigningKey::from_bytes(&[1; 32])
                .verifying_key()
                .to_bytes()
        )
    );
    assert_eq!(
        window(&state, "main"),
        0,
        "the pin closes the main's window"
    );
    crate::estate_order::drive(&state, "gunnarrs");
    let run = state.estates.get("gunnarrs").unwrap();
    assert_eq!(run.phase, phase::CAPTURED, "{:#?}", run.history);
    assert_eq!(site_of(&state).state, "ready");
    assert_eq!(mails.count("estate.ready"), 1);
    clock.fetch_add(24 * 60 * MINUTE, Ordering::SeqCst);
    for (role, seed) in [("front", 2), ("twin", 3)] {
        assert!(
            window(&state, role) > state.now(),
            "{role}: open until pinned"
        );
        assert_eq!(
            calls(&state, role, seed),
            Ok(true),
            "{role} pins a day later"
        );
    }
}

/// ★ **`open-pin` opens a closed window again**: the adopt's window closed (an adopt made before
/// F6: 10 minutes), the operator opens it, and the main's late first call pins.
#[test]
fn open_pin_reopens_a_closed_window() {
    let (state, _cloud, clock, _mails) = adopted("f6-reopen");
    let g = state.fleet.guardian().unwrap();
    for role in ["front", "main", "twin"] {
        g.store
            .open_pin("gunnar-rs", role, state.now() + 10 * MINUTE)
            .unwrap();
    }
    clock.fetch_add(36 * MINUTE, Ordering::SeqCst);
    assert_eq!(
        calls(&state, "main", 1),
        Err(FleetRefusal::Unauthenticated),
        "the window closed"
    );
    let r = settle(&state, "gunnar-rs", OPEN_PIN).expect("the operator's open-pin is taken");
    assert_eq!(r.phase, phase::PAIRED, "open-pin changes no phase");
    let said = &r.history.last().unwrap().note;
    assert!(
        said.contains("front") && said.contains("main") && said.contains("twin"),
        "{said}"
    );
    clock.fetch_add(60 * MINUTE, Ordering::SeqCst);
    assert_eq!(
        calls(&state, "main", 1),
        Ok(true),
        "pinned in the window opened again"
    );
    crate::estate_order::drive(&state, "gunnarrs");
    assert_eq!(
        state.estates.get("gunnarrs").unwrap().phase,
        phase::CAPTURED
    );
}

/// ★ **A pinned key is never pinned again by `open-pin`**: the main pinned its key; open-pin
/// opens no window for it, and a box presenting another key is refused; the key stays.
#[test]
fn open_pin_never_repins_a_pinned_box() {
    let (state, _cloud, clock, _mails) = adopted("f6-no-repin");
    clock.fetch_add(MINUTE, Ordering::SeqCst);
    assert_eq!(calls(&state, "main", 1), Ok(true));
    let pinned = key_of(&state, "main");
    let r = settle(&state, "gunnar-rs", OPEN_PIN).expect("taken");
    let said = &r.history.last().unwrap().note;
    assert!(
        said.contains("main") && said.contains("already pinned"),
        "{said}"
    );
    assert_eq!(window(&state, "main"), 0, "no window for a pinned box");
    clock.fetch_add(MINUTE, Ordering::SeqCst);
    assert_eq!(
        calls(&state, "main", 9),
        Err(FleetRefusal::Unauthenticated),
        "another key is refused"
    );
    assert_eq!(key_of(&state, "main"), pinned, "the pinned key stays");
    assert_eq!(
        calls(&state, "main", 1),
        Ok(false),
        "the pinned box is heard"
    );
}

/// ★ **The window closes at terminate**: an estate ended with boxes not pinned leaves no window.
#[test]
fn the_windows_close_at_terminate() {
    let (state, _cloud, _kit, _mails, _clock) = install_failed_with("f6-terminate", true);
    assert!(
        matches!(settle(&state, "gunnar-rs", OPEN_PIN), Err(StartRefusal::NotNow(w)) if w.contains("does not know")),
        "nothing to pin before the guardian knows the site"
    );
    // The site known to the guardian (an install PASSed once), its boxes not pinned.
    state
        .fleet
        .guardian()
        .unwrap()
        .store
        .declare("gunnar-rs", "gunnar")
        .unwrap();
    settle(&state, "gunnar-rs", OPEN_PIN).expect("taken");
    assert!(window(&state, "main") > state.now());
    let t = terminate::terminate(&state, None, "gunnar-rs", "gunnar-rs")
        .expect("a FAILED install is terminated");
    assert!(!t.done && t.words.contains("being ended now"), "{t:?}");
    crate::estate_order::drive(&state, "gunnarrs");
    for role in ["front", "main", "twin"] {
        assert_eq!(window(&state, role), 0, "{role}: no window after terminate");
    }
}