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

//! ★ **Holger's own size ladder** (lane L18, 2026-10-18): the ONE box that sells Gunnar lays a
//! Holger site too, and its order form and its ladder page offered GUNNAR's steps (Mini = a
//! 2-core front and a 4-core main with four 10 GB data disks) for Holger's three boxes. A site of
//! another product is sized on its own lifecycle's ladder, under the same step names; the shop's
//! own sites keep the shop's ladder.

use super::*;

fn shop_with_a_holger_site(name: &str) -> (Arc<crate::state::AppState>, EstateOrderConfig) {
    let dir = tmp(name);
    let reg = Registry::new()
        .sells("gunnar", HoldPay::default())
        .vendor(SitePay("sitepay-test2"))
        .cloud(Arc::new(MockCloud::new()))
        .shared_product(shop_product())
        .home("gunnar", "gunnar-rs")
        .estate(
            "gunnar-rs",
            crate::config::EstateConfig {
                product: "gunnar".into(),
                vendor: "holdpay".into(),
                site: "gunnar.rs".into(),
                ..Default::default()
            },
        )
        .estate(
            "testh-holger-rs",
            crate::config::EstateConfig {
                product: "holger".into(),
                vendor: "sitepay-test2".into(),
                site: "testh.holger.rs".into(),
                ..Default::default()
            },
        );
    let (state, ..) = build_in(
        reg,
        &dir,
        EstateOrderConfig {
            vendor: String::new(),
            apex: String::new(),
            lifecycle: "gunnar".into(),
            ..cfg()
        },
        FakeKit::new("PASS"),
        None,
        None,
    );
    let base = state.estates.cfg.clone().unwrap();
    (state, base)
}

#[test]
fn a_holger_site_is_sized_on_holgers_own_ladder_and_gunnars_is_unchanged() {
    let (state, base) = shop_with_a_holger_site("holger-ladder");
    let own = lifecycle::find("holger")
        .and_then(|l| l.ladder())
        .expect("Holger names its own ladder");
    let names = |l: &[LadderStep]| l.iter().map(|s| s.name.clone()).collect::<Vec<_>>();
    assert_eq!(
        names(&own),
        names(&default_ladder()),
        "the same step names (Mini to Gigantic)"
    );
    assert_ne!(own, default_ladder(), "never Gunnar's steps");
    // Mini is Holger's own shape: what an order that names no size is laid at.
    let shape = crate::testing::lifecycles::holger_shape();
    assert_eq!(
        (
            own[0].front_plan.as_str(),
            own[0].main_plan.as_str(),
            own[0].main_data_gib
        ),
        (
            shape.front.plan.as_str(),
            shape.main.plan.as_str(),
            shape.main.disks[1].gib
        )
    );
    assert!(
        own.windows(2)
            .all(|w| w[0].main_data_gib < w[1].main_data_gib),
        "each step holds more data: {own:?}"
    );
    assert!(
        lifecycle::find("gunnar").unwrap().ladder().is_none(),
        "Gunnar keeps the shop's ladder"
    );

    let h = terms_on(&state, &base, Some("testh-holger-rs")).expect("the Holger site's terms");
    assert_eq!(
        h.ladder, own,
        "the Holger site's terms carry Holger's ladder"
    );
    assert_eq!(
        site::sizes_of_on(&state, Some("testh-holger-rs"), "mini").unwrap(),
        own[0].sizes(),
        "a Holger site ordered at Mini is Holger's Mini"
    );
    assert_eq!(
        site::sizes_of_on(&state, Some("testh-holger-rs"), "Gigantic").unwrap(),
        own[5].sizes()
    );
    let page =
        ladder::ladder_on(&state, Some("testh-holger-rs"), "").expect("the Holger site's ladder");
    assert_eq!(
        page.steps
            .iter()
            .map(|p| p.step.clone())
            .collect::<Vec<_>>(),
        own,
        "its page shows Holger's steps"
    );

    // Gunnar: unchanged.
    let g = terms_on(&state, &base, Some("gunnar-rs")).expect("gunnar.rs's terms");
    assert_eq!(g.ladder, base.ladder);
    assert_eq!(base.ladder, default_ladder());
    assert_eq!(
        site::sizes_of_on(&state, Some("gunnar-rs"), "Mini").unwrap(),
        base.ladder[0].sizes()
    );
    assert_eq!(
        site::sizes_of(&state, "Mini").unwrap(),
        base.ladder[0].sizes()
    );
    let gp = ladder::ladder_on(&state, Some("gunnar-rs"), "").expect("gunnar.rs's ladder");
    assert_eq!(
        gp.steps.iter().map(|p| p.step.clone()).collect::<Vec<_>>(),
        base.ladder
    );
}

/// ★ Lane L18: **the shop pairs each estate as its own product** — a Holger site's run hands the
/// pairing Holger's facts (its seeds, variant, sale proof and front feed), Gunnar's sites Gunnar's.
#[test]
fn a_holger_site_is_paired_as_holger_and_gunnars_as_gunnar() {
    let (state, base) = shop_with_a_holger_site("holger-pairing-product");
    let h = terms_on(&state, &base, Some("testh-holger-rs")).unwrap();
    assert_eq!(
        pairing_facts(&h),
        crate::testing::lifecycles::holger_facts(),
        "Holger's own facts: its contract and its sale proof"
    );
    let g = terms_on(&state, &base, Some("gunnar-rs")).unwrap();
    assert_eq!(
        pairing_facts(&g),
        crate::testing::lifecycles::gunnar_facts()
    );
}

/// ★ Owner 2026-10-23: every disk of a fresh Holger estate is UpCloud's `hdd` tier, the cheapest
/// for live servers (the front's system, main's system and data, the twin's system and data), and
/// the native lay names it (no template: the medium path). Only new lays and grown disks follow
/// it; laid disks keep their tier. The guest's copy (holger-monetize-guest) is pinned to this one.
#[test]
fn every_disk_of_a_fresh_holger_shape_is_hdd() {
    // Its own lifecycles: under nextest (one process per test) no other test registered them.
    crate::testing::lifecycles::install();
    let shape = crate::testing::lifecycles::holger_shape();
    let tiers: Vec<(&str, &str)> = [&shape.front, &shape.main, &shape.twin]
        .iter()
        .flat_map(|m| m.disks.iter().map(|d| (d.volume.as_str(), d.tier.as_str())))
        .collect();
    assert_eq!(tiers.len(), 5, "{tiers:?}");
    assert!(tiers.iter().all(|(_, t)| *t == "hdd"), "{tiers:?}");
    let c = EstateOrderConfig {
        lifecycle: "holger".into(),
        shape,
        ..cfg()
    };
    let plan = lifecycle::lay_plan_of(
        &c,
        "acme",
        &EstateSizes {
            main_data_gib: 240,
            ..Default::default()
        },
        "",
    )
    .expect("laid");
    let laid: Vec<&str> = plan
        .machines
        .iter()
        .flat_map(|m| m.disks.iter().map(|d| d.tier.as_str()))
        .collect();
    assert_eq!(laid, ["hdd"; 5], "{laid:?}");
}