crates/monetize-server/src/estate_order/tests/cap_rule.rs
//! **The cap rule** (owner 2026-10-14, after the first LIVE site lay of test2.gunnar.rs):
//!
//! * the OPERATOR's own orders — a site order, and a change of a site the operator owns — meet
//! the cloud's spend cap BEFORE the payment, refused plainly with the numbers in EUR,
//! nothing charged;
//! * a CUSTOMER's order is still taken over the cap: it is paid and waits, the operator told;
//! * in both cases the cap is checked for the whole change before ANY machine is changed (live:
//! a paid grow resized the front, and only then the cap queued the rest — a half-done grow).
use super::*;
use monetize_cloud::cap::{Ceiling, SpendCap};
const EUR: monetize_pay::Currency = monetize_pay::Currency::Eur;
/// The registry's cloud: the mock inside a real spend cap of `monthly` minor a month.
fn capped(cloud: &Arc<MockCloud>, dir: &std::path::Path, monthly: u64) -> SpendCap<Arc<MockCloud>> {
SpendCap::new(
cloud.clone(),
Ceiling::monthly(monthly, EUR),
dir.join("spend-cap.json"),
)
.unwrap()
}
/// [`site_rig`]'s operator site `gunnar-rs` (owned by `ignalina`), the cloud capped at `monthly`.
fn capped_site_rig(
name: &str,
monthly: u64,
) -> (Arc<AppState>, HoldPay, Arc<MockCloud>, Arc<AtomicU64>) {
let dir = tmp(name);
let pay = HoldPay::default();
let cloud = Arc::new(MockCloud::new());
let kit = FakeKit::new("PASS");
let site =
|vendor: &str, site: &str, owner: &str, operator_site: bool| crate::config::EstateConfig {
product: "gunnar".into(),
vendor: vendor.into(),
site: site.into(),
owner: owner.into(),
operator_site,
..Default::default()
};
let reg = Registry::new()
.sells("gunnar", pay.clone())
.cloud_capped(capped(&cloud, &dir, monthly))
.shared_product(shop_product())
.home("gunnar", "gunnar-rs")
.estate("gunnar-rs", site("holdpay", "gunnar.rs", "ignalina", true));
let (state, clock, ..) = build_in(
reg,
&dir,
EstateOrderConfig {
vendor: String::new(),
apex: String::new(),
..cfg()
},
kit,
None,
None,
);
(state, pay, cloud, clock)
}
/// [`rig`], the cloud capped at `monthly`.
fn capped_rig(name: &str, monthly: u64) -> Rig {
let dir = tmp(name);
let pay = HoldPay::default();
let cloud = Arc::new(MockCloud::new());
let kit = FakeKit::new("PASS");
let reg = Registry::new()
.sells("gunnar", pay.clone())
.cloud_capped(capped(&cloud, &dir, monthly))
.shared_product(shop_product());
let (state, clock, mails, health) = build_in(reg, &dir, cfg(), kit.clone(), None, None);
Rig {
state,
pay,
cloud,
kit,
dir,
clock,
mails,
health,
}
}
/// ★ (a) **A site order over the cloud's cap is refused BEFORE it is paid**, in EUR: the
/// default estate is 3 servers + 8 disks at 10.00 EUR = 110.00 EUR a month; the cap 50.00.
#[test]
fn a_site_order_over_the_clouds_cap_is_refused_before_anything_is_held() {
let (state, pay, cloud, _) = capped_site_rig("cap-site-order", 5_000);
let r = site::start_site(
&state,
"gunnar-rs",
"ops@example.invalid",
&EstateSizes::default(),
);
let Err(StartRefusal::NotNow(w)) = &r else {
panic!("refused plainly before the payment: {r:?}")
};
assert_eq!(w, "raise the cap first: this needs 110.00 EUR a month, the cap is 50.00 EUR a month, 0.00 EUR is committed; nothing was charged");
assert!(pay.0.requests.lock().unwrap().is_empty(), "nothing is held");
assert!(state.estates.get("gunnarrs").is_none(), "no order is kept");
assert!(cloud.lays().is_empty());
// Under a cap it fits, the same order is taken.
let (state, ..) = capped_site_rig("cap-site-order-fits", 11_000);
site::start_site(
&state,
"gunnar-rs",
"ops@example.invalid",
&EstateSizes::default(),
)
.expect("fits under the cap: taken");
}
/// ★ (b) **A customer's order over the same cap is taken**, paid, and waits in the queue —
/// the buyer never reads a cap.
#[test]
fn a_customers_order_over_the_clouds_cap_is_still_taken_and_waits() {
let r = capped_rig("cap-customer", 5_000);
start(
&r.state,
"acme",
"buyer@example.com",
"",
&EstateSizes::default(),
)
.expect("a customer is never refused for a cap");
r.pay.authorize(&handle_of(&r, "acme"));
drive(&r.state, "acme");
let run = r.state.estates.get("acme").unwrap();
assert_eq!(run.phase, phase::HELD, "{:#?}", run.history);
assert!(run.why.starts_with(ORDERS_WAIT), "{}", run.why);
assert!(r.cloud.lays().is_empty());
}
/// ★ (a) **A change of the operator's own site over the cap is refused before it is held**:
/// the site laid (110.00 EUR committed under a 120.00 cap), a bigger main adds 40.00.
#[test]
fn a_change_of_the_operators_site_over_the_cap_is_refused_before_anything_is_held() {
let (state, pay, cloud, _) = capped_site_rig("cap-site-change", 12_000);
let run = site::start_site(
&state,
"gunnar-rs",
"ops@example.invalid",
&EstateSizes::default(),
)
.unwrap();
pay.authorize(&run.handle.clone().unwrap().id);
drive(&state, "gunnarrs");
assert_eq!(
state.estates.get("gunnarrs").unwrap().phase,
phase::CAPTURED
);
cloud
.plan_prices
.lock()
.unwrap()
.insert("STARTER-16xCPU-64GB".into(), 5_000);
let held = pay.0.requests.lock().unwrap().len();
let r = change::change(&state, "gunnarrs", &bigger_main());
let Err(StartRefusal::NotNow(w)) = &r else {
panic!("refused plainly before the payment: {r:?}")
};
assert!(w.starts_with("raise the cap first: this needs 40.00 EUR a month, the cap is 120.00 EUR a month, 110.00 EUR is committed"), "{w}");
assert_eq!(
pay.0.requests.lock().unwrap().len(),
held,
"nothing more is held"
);
assert!(cloud.plan_changes().is_empty(), "no machine is touched");
assert!(
state.estates.get("gunnarrs").unwrap().change.is_none(),
"no change is opened"
);
}
/// ★ (c) **A paid change is checked against the cap as a WHOLE before any machine changes**:
/// a bigger front (+20.00) fits under the cap, front and main together (+60.00) do not — the
/// change waits with NOTHING resized (live 2026-10-14: the front was resized, then the cap
/// held the rest). A customer's change is taken and waits; the operator is told in EUR.
#[test]
fn a_paid_change_over_the_cap_waits_with_no_machine_changed() {
// The estate is laid: 110.00 EUR committed; the cap 140.00.
let r = capped_rig("cap-half-grow", 14_000);
start_for(
&r.state,
"acme",
"buyer@example.com",
"",
"alice",
&EstateSizes::default(),
)
.unwrap();
r.pay.authorize(&handle_of(&r, "acme"));
drive(&r.state, "acme");
assert_eq!(r.state.estates.get("acme").unwrap().phase, phase::CAPTURED);
{
let mut p = r.cloud.plan_prices.lock().unwrap();
p.insert("STARTER-8xCPU-32GB".into(), 3_000);
p.insert("STARTER-16xCPU-64GB".into(), 5_000);
}
let now = r.state.estates.get("acme").unwrap().paid_until_ms - 90 * DAY;
r.clock.store(now, Ordering::SeqCst);
let c = change::change(
&r.state,
"acme",
&EstateSizes {
front_plan: "STARTER-8xCPU-32GB".into(),
main_plan: "STARTER-16xCPU-64GB".into(),
..EstateSizes::default()
},
)
.expect("a customer's change is taken over the cap");
r.pay.authorize(&c.handle.as_ref().unwrap().id);
let run = at(&r, now + 60_000);
assert!(
r.cloud.plan_changes().is_empty(),
"no machine is changed while the whole change does not fit: {:?}",
r.cloud.plan_changes()
);
let open = run.change.as_ref().expect("the change waits");
assert_eq!(open.status, change::status::QUEUED, "{open:#?}");
assert!(
open.why.starts_with(ORDERS_WAIT)
&& open.why.contains("60.00 EUR a month")
&& open.why.contains("140.00 EUR a month")
&& open.why.contains("110.00 EUR is committed"),
"the operator's words, in EUR: {}",
open.why
);
assert!(open.steps.iter().all(|s| !s.done), "{:?}", open.steps);
assert_eq!(
run.quote.sizes.front_plan,
run.laid.as_ref().unwrap().machine("front").unwrap().plan,
"the front keeps its plan"
);
}
/// ★ **A waiting order's ALERT is written once**, again when its words change, and as an
/// hourly reminder — never every tick (live 2026-10-14: 242 lines in an hour).
#[test]
fn a_waiting_orders_alert_is_said_once_then_on_a_change_or_hourly() {
let reference = "estate/alert-once/2026-10-14";
let t0 = 1_900_000_000_000;
assert!(
wait_alert_due(reference, "orders wait: raise the cap: A", t0),
"the first time"
);
for tick in 1..1_800u64 {
assert!(
!wait_alert_due(
reference,
"orders wait: raise the cap: A",
t0 + tick * 2_000
),
"tick {tick}: the same words are not said again"
);
}
assert!(
wait_alert_due(
reference,
"orders wait: raise the cap: B",
t0 + 3_600_000 - 1
),
"new words are said at once"
);
assert!(!wait_alert_due(
reference,
"orders wait: raise the cap: B",
t0 + 3_600_000
));
assert!(
wait_alert_due(
reference,
"orders wait: raise the cap: B",
t0 + 2 * 3_600_000
),
"the hourly reminder"
);
forget_wait_alert(reference);
assert!(
wait_alert_due(
reference,
"orders wait: raise the cap: B",
t0 + 2 * 3_600_000 + 1
),
"a new wait after it went on is said at once"
);
}