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