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_lean.rs

//! ★ **The lean Holger estate** (owner 2026-10-23: "why do we have 3?"): a small Holger estate
//! costs about 20 € a month. The front is the ONE box with a public IPv4 (the estate's hub: the
//! console, the relay, the installer's and the ONE box's way in, and the boxes' way out); main and
//! the twin hold a utility address only. The smallest machines that work: a 1-core 1 GB front, a
//! 1-core 2 GB main and a 1-core 1 GB twin (the weaker box, so the installer's election makes it
//! the twin), 10 GB system disks, every disk HDD.

use super::*;

fn holger_cfg() -> EstateOrderConfig {
    EstateOrderConfig {
        product: "holger".into(),
        lifecycle: "holger".into(),
        apex: "holger.rs".into(),
        shape: crate::testing::lifecycles::holger_shape(),
        ..cfg()
    }
}

#[test]
fn holgers_mini_is_the_lean_estate_with_one_public_ipv4_on_the_front() {
    let shape = crate::testing::lifecycles::holger_shape();
    assert_eq!(
        (
            shape.front.plan.as_str(),
            shape.main.plan.as_str(),
            shape.twin.plan.as_str()
        ),
        (
            "STARTER-1xCPU-1GB",
            "STARTER-1xCPU-2GB",
            "STARTER-1xCPU-1GB"
        )
    );
    assert_eq!(
        shape.twin_floor_mib,
        Some(1024),
        "Holger names its own twin floor (Gunnar's stays 4 GiB)"
    );
    let public = |m: &MachineShape| {
        m.interfaces
            .iter()
            .filter(|i| i.kind == "public" && i.family == "IPv4")
            .count()
    };
    assert_eq!(
        (
            public(&shape.front),
            public(&shape.main),
            public(&shape.twin)
        ),
        (1, 0, 0),
        "one public IPv4, on the front"
    );
    for m in [&shape.main, &shape.twin] {
        assert!(
            m.interfaces.iter().any(|i| i.kind == "utility"),
            "{} keeps its utility address (the front's WireGuard hub reaches it there)",
            m.hostname
        );
    }
    for m in [&shape.front, &shape.main, &shape.twin] {
        assert_eq!(m.disks[0].gib, 10, "{}: a 10 GB system disk", m.hostname);
    }
    assert_eq!((shape.main.disks[1].gib, shape.twin.disks[1].gib), (20, 20));
    let mini = &crate::testing::lifecycles::holger_ladder()[0];
    assert_eq!(
        (
            mini.front_plan.as_str(),
            mini.main_plan.as_str(),
            mini.main_data_gib
        ),
        ("STARTER-1xCPU-1GB", "STARTER-1xCPU-2GB", 20),
        "Mini is the shape"
    );
    // Gunnar is unchanged.
    let g = crate::testing::lifecycles::gunnar_shape();
    assert_eq!(g.twin_floor_mib, None);
    assert_eq!(
        (public(&g.front), public(&g.main), public(&g.twin)),
        (1, 1, 1)
    );
}

#[test]
fn a_holger_quote_lays_a_1_gb_twin_and_bills_one_public_ipv4() {
    let r = rig_with("holger-lean-quote", holger_cfg(), "PASS");
    *r.cloud.ipv4_minor.lock().unwrap() = Some(351);
    let q = quote(&r.state, "acme", &EstateSizes::default()).unwrap();
    assert_eq!(
        q.twin_plan, "STARTER-1xCPU-1GB",
        "the weaker box: under main's 2 GB, so the election makes it the twin"
    );
    assert_eq!(
        q.cost_in_sale.minor,
        8 * 1_000 + 351,
        "3 servers + 5 disks at 10.00, and ONE public IPv4"
    );
    let plan =
        super::lifecycle::lay_plan_of(&holger_cfg(), "acme", &q.sizes, &q.twin_plan).unwrap();
    assert_eq!(
        plan.machines
            .iter()
            .map(|m| m.public_ipv4())
            .collect::<Vec<_>>(),
        vec![1, 0, 0]
    );
    // Gunnar's quote keeps its 4 GiB twin.
    let g = rig("gunnar-twin-floor");
    assert_eq!(
        quote(&g.state, "acme", &EstateSizes::default())
            .unwrap()
            .twin_plan,
        "STARTER-1xCPU-4GB"
    );
}