crates/monetize-server/tests/test_button.rs
//! **The operator's TEST BUTTON over its real wire** (owner 2026-10-11): a
//! product declares its test actions, the operator presses one, and monetize
//! makes a REAL account through the very path the sign-up page takes — the
//! name, the address, the plan, the box's room, the sold capacity, the vendor —
//! with the vendor's test payment method instead of a person at a card form.
//!
//! The private-instance half (account, order, lay, install, capture, the setup's
//! lines) is proven in `estate_order::tests`, where the estate rig lives.
use std::sync::Arc;
use monetize_pay::Currency;
use monetize_proto::test_purchase_client::TestPurchaseClient;
use monetize_proto::{self as pb};
use monetize_server::config::SignupPlan;
use monetize_server::registry::Registry;
use monetize_server::state::AppState;
use monetize_server::testing::{CallbackPay, TestServer};
use tonic::metadata::MetadataValue;
use tonic::transport::Channel;
use tonic::{Code, Request};
const BEARER: &str = "test-bearer-token-of-sufficient-length";
const OPERATOR: &str = "operator-bearer-token-of-sufficient-length";
const SECRET: &str = "mockpay-shared-secret";
const PLAN: &str = "gunnar/10gb-1m";
const GIB: u64 = 1 << 30;
/// A sink that makes the account it is asked for, unless the name is held.
#[derive(Default)]
struct Makes(std::sync::Mutex<Vec<String>>);
impl monetize_pay::SignupSink for Makes {
fn paid(&self, s: &monetize_pay::PaidSignup) -> Result<monetize_pay::SignupOutcome, monetize_pay::SignupError> {
self.0.lock().unwrap().push(s.username.clone());
Ok(monetize_pay::SignupOutcome::Created { namespace: s.username.clone(), username: s.username.clone() })
}
fn name_verdict(&self, _: &str) -> Result<monetize_pay::NameVerdict, monetize_pay::SignupError> {
Ok(monetize_pay::NameVerdict::Free)
}
}
struct Rig {
pay: CallbackPay,
product: Arc<monetize::testing::MockProduct>,
state: Arc<AppState>,
server: TestServer,
channel: Channel,
sink: Arc<Makes>,
}
impl Rig {
async fn up(livemode: Option<bool>) -> Rig {
let pay = CallbackPay::with_checkout(SECRET, "https://checkout.example/");
pay.set_livemode(livemode);
let product = Arc::new(monetize::testing::MockProduct::gunnar());
*product.test_actions.lock().unwrap() = vec![monetize_product::TestAction {
id: "hosted-10gb".into(),
label: "Test: 10 GB hosted".into(),
kind: monetize_product::TestActionKind::HostedPlan { cap_gib: 10 },
}];
let mut state = AppState::in_memory(Registry::new().sells("gunnar", pay.clone()).shared_product(product.clone()));
state.signup_plans = [(PLAN.to_string(), SignupPlan { amount_minor: 10_000, currency: Currency::Sek, cap_bytes: 10 * GIB, period_days: 30 })].into();
let sink = Arc::new(Makes::default());
state.signup_sink = sink.clone();
let state = Arc::new(state);
let server = TestServer::spawn_with_operator(state.clone(), BEARER, OPERATOR).await;
let channel = Channel::from_shared(server.endpoint()).unwrap().connect().await.expect("connect");
Rig { pay, product, state, server, channel, sink }
}
fn client(&self, bearer: &str) -> TestPurchaseClient<tonic::service::interceptor::InterceptedService<Channel, impl tonic::service::Interceptor>> {
let v: MetadataValue<_> = format!("Bearer {bearer}").parse().unwrap();
TestPurchaseClient::with_interceptor(self.channel.clone(), move |mut r: Request<()>| {
r.metadata_mut().insert("authorization", v.clone());
Ok(r)
})
}
}
fn press() -> pb::TestPurchaseStartRequest {
pb::TestPurchaseStartRequest { product: "gunnar".into(), action: "hosted-10gb".into(), ..Default::default() }
}
/// ★ **The operator's, and nobody else's.** The service bearer a product's
/// console holds is refused on every verb with PERMISSION_DENIED; no bearer
/// at all is UNAUTHENTICATED; nothing was bought either way.
#[tokio::test(flavor = "multi_thread", worker_threads = 4)]
async fn the_test_button_refuses_everyone_but_the_operator() {
let rig = Rig::up(Some(false)).await;
let mut service = rig.client(BEARER);
for refused in [
service.actions(pb::TestActionsRequest::default()).await.map(|_| ()).expect_err("actions"),
service.start(press()).await.map(|_| ()).expect_err("start"),
service.list(pb::TestPurchaseListRequest::default()).await.map(|_| ()).expect_err("list"),
service.status(pb::TestPurchaseStatusRequest { id: "tp-1".into() }).await.map(|_| ()).expect_err("status"),
] {
assert_eq!(refused.code(), Code::PermissionDenied, "{refused:?}");
}
let naked = TestPurchaseClient::new(rig.channel.clone()).start(press()).await.expect_err("no bearer");
assert_eq!(naked.code(), Code::Unauthenticated, "{naked:?}");
assert!(rig.pay.test_paid().is_empty() && rig.state.signups.list(0).unwrap().is_empty(), "nothing was bought");
rig.server.stop().await;
}
/// ★ **A key that cannot say whether it is live is refused** BEFORE anything is
/// made: no account, no checkout, no payment, no run — and the console is told
/// why on the button. (A LIVE key runs the real flow: see below.)
#[tokio::test(flavor = "multi_thread", worker_threads = 4)]
async fn a_silent_key_is_refused_before_anything_is_made() {
for mode in [None] {
let rig = Rig::up(mode).await;
let mut op = rig.client(OPERATOR);
let offers = op.actions(pb::TestActionsRequest::default()).await.unwrap().into_inner().actions;
assert_eq!(offers.len(), 1);
assert!(!offers[0].runnable && offers[0].why_not.contains("not a TEST key"), "{mode:?}: {offers:?}");
let refused = op.start(press()).await.expect_err("refused");
assert_eq!(refused.code(), Code::FailedPrecondition, "{mode:?}: {refused:?}");
assert!(refused.message().contains("never spends live money"), "{}", refused.message());
assert!(rig.pay.test_paid().is_empty() && rig.pay.signup_orders().is_empty(), "{mode:?}: the vendor was asked");
assert!(rig.state.signups.list(0).unwrap().is_empty() && rig.sink.0.lock().unwrap().is_empty(), "{mode:?}: an account was made");
assert!(rig.product.absorb_asked().is_empty(), "{mode:?}: the box was asked before the key was");
assert!(op.list(pb::TestPurchaseListRequest::default()).await.unwrap().into_inner().runs.is_empty());
rig.server.stop().await;
}
}
/// ★ **Same path as the sign-up page.** On a test key the press makes a real
/// account: the box is asked for the plan's room exactly as a signup asks
/// (the brand-new-tenant question), the plan is the one the page offers for
/// 10 GiB, the vendor is handed the very order a checkout would carry — a
/// `test-<stamp>` name, an address nobody owns — and pays it with its test
/// payment method; the paid signup is settled through the one settle path,
/// so the store reads it CREATED. The run is kept and read back with its lines.
#[tokio::test(flavor = "multi_thread", worker_threads = 4)]
async fn a_press_on_a_test_key_makes_a_real_account_through_the_signup_path() {
let rig = Rig::up(Some(false)).await;
let mut op = rig.client(OPERATOR);
let offers = op.actions(pb::TestActionsRequest { product: "gunnar".into() }).await.unwrap().into_inner().actions;
assert_eq!(offers.iter().map(|o| (o.id.as_str(), o.label.as_str(), o.kind.as_str(), o.cap_gib, o.runnable)).collect::<Vec<_>>(), [("hosted-10gb", "Test: 10 GB hosted", "hosted_plan", 10, true)]);
let run = op.start(press()).await.expect("pressed").into_inner();
assert_eq!((run.state.as_str(), run.signup_state.as_str()), ("done", "CREATED"), "{run:?}");
assert!(run.username.starts_with("test-"), "{}", run.username);
assert_eq!(run.vendor, "mockpay");
assert!(run.mailbox.is_empty(), "no mailbox configured: nobody is mailed");
// The sign-up path's own gates ran: the box was asked the new-tenant question for the plan's room.
assert_eq!(rig.product.absorb_asked().len(), 1, "the capacity gate is the signup's");
assert_eq!(rig.product.absorb_asked()[0].get("pack_bytes"), Some(&(10 * GIB)));
// The vendor was handed the order a checkout carries, and paid it with test money.
let paid = rig.pay.test_paid();
assert_eq!(paid.len(), 1);
assert_eq!((paid[0].username.as_str(), paid[0].plan.as_str(), paid[0].product.as_str()), (run.username.as_str(), PLAN, "gunnar"));
assert!(paid[0].email.ends_with("@test-purchase.invalid"), "never a real address: {}", paid[0].email);
assert_eq!(paid[0].amount.minor, 10_000, "the plan's NET price, as a checkout asks");
assert!(rig.pay.signup_orders().is_empty(), "no hosted page was minted for nobody to open");
// ★ A33-2: the test card is the only difference — the paid session is recorded as minted
// HERE (ST-7, as a buyer's checkout is), its welcome kept, and it settles on the one path.
assert!(rig.state.signups.minted("mockpay", &run.session).unwrap().is_some(), "the press's session is recorded as this box's own, as a buyer's is");
// The account exists, made by the product's sink, and the store says so.
assert_eq!(*rig.sink.0.lock().unwrap(), std::slice::from_ref(&run.username));
let rows = rig.state.signups.list(0).unwrap();
assert_eq!(rows.len(), 1);
assert!(matches!(rows[0].state, monetize_server::signup::SignupState::Created { .. }), "{:?}", rows[0].state);
// Kept, listed newest first, read back with its lines.
let listed = op.list(pb::TestPurchaseListRequest::default()).await.unwrap().into_inner().runs;
assert_eq!(listed.iter().map(|r| r.id.as_str()).collect::<Vec<_>>(), [run.id.as_str()]);
let again = op.status(pb::TestPurchaseStatusRequest { id: run.id.clone() }).await.unwrap().into_inner();
assert_eq!(again.state, "done");
assert!(again.lines.iter().any(|l| l.source == "vendor" && l.line.contains("test payment")), "{:?}", again.lines);
rig.server.stop().await;
}
/// ★ **The sign-up path's refusals are the button's**: a full box refuses the
/// press exactly as it refuses a stranger's signup — before the vendor.
#[tokio::test(flavor = "multi_thread", worker_threads = 4)]
async fn a_full_box_refuses_the_press_as_it_refuses_a_signup() {
let rig = Rig::up(Some(false)).await;
*rig.product.refuse_absorb.lock().unwrap() = Some("gunnar: the data set is full".into());
let run = rig.client(OPERATOR).start(press()).await.expect("a refused run is still a run").into_inner();
assert_eq!(run.state, "failed", "{run:?}");
assert!(run.words.to_lowercase().contains("nothing has been charged"), "the signup's own words: {}", run.words);
assert!(rig.pay.test_paid().is_empty(), "the vendor was asked for room that does not exist");
rig.server.stop().await;
}
/// ★ ST-8 (owner 2026-10-11: "test2 money = real purchase, the owner pays at the card
/// step"): **on a LIVE key the button runs the SAME real flow** — a real checkout through
/// the sign-up path, no test card, no fake settle — and hands the operator the vendor's
/// own checkout page to pay himself. The offer says so; a press without the operator's
/// "LIVE — you will be charged" is refused before anything is made.
#[tokio::test(flavor = "multi_thread", worker_threads = 4)]
async fn on_a_live_key_the_button_runs_the_real_flow_and_hands_the_operator_the_checkout() {
let rig = Rig::up(Some(true)).await;
let mut op = rig.client(OPERATOR);
let offers = op.actions(pb::TestActionsRequest::default()).await.unwrap().into_inner().actions;
assert_eq!(offers.len(), 1);
assert!(offers[0].runnable && offers[0].live && offers[0].why_not.is_empty(), "{offers:?}");
let unconfirmed = op.start(press()).await.expect_err("not confirmed");
assert_eq!(unconfirmed.code(), Code::FailedPrecondition, "{unconfirmed:?}");
assert!(unconfirmed.message().contains("LIVE — you will be charged"), "{}", unconfirmed.message());
assert!(rig.pay.signup_orders().is_empty() && rig.state.signups.list(0).unwrap().is_empty(), "nothing was started");
let run = op.start(pb::TestPurchaseStartRequest { live: true, ..press() }).await.expect("pressed, confirmed").into_inner();
assert!(run.live, "{run:?}");
assert!(run.pay_url.starts_with("https://checkout.example/"), "the vendor's own checkout page: {run:?}");
assert_eq!(run.state, "running", "waiting for the operator's payment: {run:?}");
assert!(run.words.contains("LIVE — you will be charged"), "{}", run.words);
assert!(rig.pay.test_paid().is_empty(), "no test card on a live key");
let orders = rig.pay.signup_orders();
assert_eq!(orders.len(), 1, "one real checkout");
assert_eq!((orders[0].username.as_str(), orders[0].plan.as_str()), (run.username.as_str(), PLAN));
assert!(rig.sink.0.lock().unwrap().is_empty(), "no fake settle: the account is made when the vendor says it is paid");
rig.server.stop().await;
}
/// ★ ST-8: **each offer is for ONE site, and a press sells as that site only.** A product
/// sold for two sites offers each action once per site (its estate and host); a press names
/// the site, its return page is on that site's host, its account is made on that site's
/// estate; a press naming no site (or one not declared) is refused before anything is made.
#[tokio::test(flavor = "multi_thread", worker_threads = 4)]
async fn each_site_has_its_own_button_and_never_sells_as_the_other() {
let pay = CallbackPay::with_checkout(SECRET, "https://checkout.example/");
pay.set_livemode(Some(false));
let product = Arc::new(monetize::testing::MockProduct::gunnar());
*product.test_actions.lock().unwrap() = vec![monetize_product::TestAction { id: "hosted-10gb".into(), label: "Test: 10 GB hosted".into(), kind: monetize_product::TestActionKind::HostedPlan { cap_gib: 10 } }];
let site = |s: &str| monetize_server::config::EstateConfig { product: "gunnar".into(), vendor: "mockpay".into(), owner: "ignalina".into(), site: s.into(), appliance_key: String::new(), ..Default::default() };
let reg = Registry::new().sells("gunnar", pay.clone()).shared_product(product.clone()).home("gunnar", "gunnar-rs").estate("gunnar-rs", site("gunnar.rs")).estate("test2-gunnar-rs", site("test2.gunnar.rs"));
let mut state = AppState::in_memory(reg);
state.signup_plans = [(PLAN.to_string(), SignupPlan { amount_minor: 10_000, currency: Currency::Sek, cap_bytes: 10 * GIB, period_days: 30 })].into();
let sink = Arc::new(Makes::default());
state.signup_sink = sink.clone();
let state = Arc::new(state);
let server = TestServer::spawn_with_operator(state.clone(), BEARER, OPERATOR).await;
let channel = Channel::from_shared(server.endpoint()).unwrap().connect().await.expect("connect");
let v: MetadataValue<_> = format!("Bearer {OPERATOR}").parse().unwrap();
let mut op = TestPurchaseClient::with_interceptor(channel, move |mut r: Request<()>| {
r.metadata_mut().insert("authorization", v.clone());
Ok(r)
});
let offers = op.actions(pb::TestActionsRequest::default()).await.unwrap().into_inner().actions;
let mut sites: Vec<(String, String)> = offers.iter().map(|o| (o.site.clone(), o.site_host.clone())).collect();
sites.sort();
assert_eq!(sites, [("gunnar-rs".to_string(), "gunnar.rs".to_string()), ("test2-gunnar-rs".to_string(), "test2.gunnar.rs".to_string())], "{offers:?}");
assert!(offers.iter().all(|o| o.runnable && !o.live), "{offers:?}");
let unnamed = op.start(press()).await.expect_err("which site?");
assert_eq!(unnamed.code(), Code::FailedPrecondition, "{unnamed:?}");
assert!(unnamed.message().contains("[test-site-unnamed]"), "{}", unnamed.message());
let undeclared = op.start(pb::TestPurchaseStartRequest { site: "elsewhere".into(), ..press() }).await.expect_err("not a site of gunnar");
assert_eq!(undeclared.code(), Code::FailedPrecondition, "{undeclared:?}");
assert!(pay.test_paid().is_empty() && state.signups.list(0).unwrap().is_empty(), "nothing was bought for no site");
let run = op.start(pb::TestPurchaseStartRequest { site: "test2-gunnar-rs".into(), ..press() }).await.expect("test2's button").into_inner();
assert_eq!((run.state.as_str(), run.site.as_str()), ("done", "test2-gunnar-rs"), "{run:?}");
let paid = pay.test_paid();
assert_eq!(paid.len(), 1);
assert!(paid[0].success_url.starts_with("https://test2.gunnar.rs/welcome?session="), "test2's own return page: {}", paid[0].success_url);
let rows = state.signups.list(0).unwrap();
assert_eq!(rows.len(), 1);
assert_eq!(rows[0].estate.as_deref(), Some("test2-gunnar-rs"), "made on test2, never as gunnar.rs");
server.stop().await;
}