crates/monetize-server/src/estate_order/tests/operator_terminate.rs
//! **The operator terminates a SOLD private estate** (owner 2026-10-22): `monetize site
//! terminate polka --confirm polka` over the operator door was refused "a private Gunnar is
//! bought, read and changed by its gunnar.rs account" (Unauthenticated). The operator of the
//! ONE box may end a customer's private estate too, by the SAME path as the buyer's own:
//!
//! * the operator bearer (no site's own bearer beside it) needs no account ticket;
//! * the name is still typed back, and a protected site is still refused;
//! * no backup is offered to the operator (the wire carries no backup word, A32-27);
//! * the run's history says "terminated by the operator";
//! * the money rules are unchanged: nothing is captured and nothing is refunded.
use super::*;
use crate::services::estate_order::EstateOrderService;
use monetize_proto as pb;
use monetize_proto::estate_order_server::EstateOrder as _;
fn as_operator<T>(r: T) -> tonic::Request<T> {
let mut req = tonic::Request::new(r);
req.extensions_mut().insert(crate::auth::Operator);
req
}
fn ask(confirm: &str) -> pb::EstateOrderTerminateRequest {
pb::EstateOrderTerminateRequest {
company: "acme".into(),
actor_ticket: Vec::new(),
confirm: confirm.into(),
}
}
/// ★ **The operator ends a sold estate** with no account ticket: every machine and disk given
/// back, the run terminated, its history saying the operator did it, and no money moved.
#[test]
fn the_operator_terminates_a_sold_estate_by_the_buyers_path() {
let r = delivered_acme("op-term-sold");
assert!(
!r.state.estates.get("acme").unwrap().site_order,
"a customer's sold estate"
);
assert!(r.cloud.live_under("estate/acme") > 0);
let (captures, refunds) = (r.pay.captures(), r.pay.refunds());
let svc = EstateOrderService(r.state.clone());
let rt = tokio::runtime::Builder::new_multi_thread()
.enable_all()
.build()
.unwrap();
// No backup word: the operator is not offered one, the estate is ended without it.
let t = rt
.block_on(svc.terminate(as_operator(ask("acme"))))
.expect("the operator terminates a sold estate")
.into_inner();
assert!(!t.done && t.words.contains("being ended now"), "{t:?}");
crate::estate_order::drive(&r.state, "acme");
assert_eq!(
r.cloud.live_under("estate/acme"),
0,
"every machine and disk is given back"
);
let run = r.state.estates.get("acme").unwrap();
assert_eq!(run.phase, phase::TERMINATED, "{:#?}", run.history);
assert!(
run.history
.iter()
.any(|t| t.phase == phase::TERMINATING && t.note.contains("by the operator")),
"{:#?}",
run.history
);
assert!(
run.history
.iter()
.any(|t| t.phase == phase::TERMINATED && t.note == "terminated by the operator"),
"{:#?}",
run.history
);
assert!(
!run.history
.iter()
.any(|t| t.note.contains("by its account") || t.note.contains("the account chose")),
"{:#?}",
run.history
);
assert_eq!(
(r.pay.captures(), r.pay.refunds()),
(captures, refunds),
"no money moved: nothing captured, nothing refunded"
);
}
/// ★ **Without the name typed back nothing ends**, for the operator too; and nobody without
/// the operator marker (or a site's own bearer beside it) is heard without the account.
#[test]
fn the_operator_is_refused_without_the_name_typed_back_and_others_without_the_account() {
let r = delivered_acme("op-term-refused");
let before = r.cloud.live_under("estate/acme");
let svc = EstateOrderService(r.state.clone());
let rt = tokio::runtime::Builder::new_multi_thread()
.enable_all()
.build()
.unwrap();
for typed in ["", "acne", "polka"] {
let e = rt
.block_on(svc.terminate(as_operator(ask(typed))))
.unwrap_err();
assert_eq!(e.code(), tonic::Code::InvalidArgument, "{typed:?}: {e:?}");
}
let plain = rt
.block_on(svc.terminate(tonic::Request::new(ask("acme"))))
.unwrap_err();
assert_eq!(plain.code(), tonic::Code::Unauthenticated, "{plain:?}");
let mut a_site = as_operator(ask("acme"));
a_site
.extensions_mut()
.insert(crate::auth::CallerEstate("gunnar-rs".into()));
assert_eq!(
rt.block_on(svc.terminate(a_site)).unwrap_err().code(),
tonic::Code::Unauthenticated
);
assert_eq!(
r.cloud.live_under("estate/acme"),
before,
"every machine and disk stands"
);
assert_eq!(r.state.estates.get("acme").unwrap().phase, phase::CAPTURED);
}
/// ★ **A protected site stays refused** for the operator (gunnar.rs).
#[test]
fn a_protected_site_stays_refused_for_the_operator() {
let (state, ..) = operator_rig("op-term-protected", cfg());
let svc = EstateOrderService(state.clone());
let rt = tokio::runtime::Builder::new_multi_thread()
.enable_all()
.build()
.unwrap();
for name in ["gunnar.rs", "gunnar-rs"] {
let req = pb::EstateOrderTerminateRequest {
company: name.into(),
actor_ticket: Vec::new(),
confirm: name.into(),
};
let e = rt.block_on(svc.terminate(as_operator(req))).unwrap_err();
assert_eq!(e.code(), tonic::Code::FailedPrecondition, "{name}: {e:?}");
assert!(e.message().contains("protected"), "{e:?}");
}
}