crates/monetize-server/tests/granted_words.rs
//! **The sentence a customer reads when the answer is YES.**
//!
//! `Refusal::customer_words` has given every REFUSAL a customer sentence since
//! 2026-09-14, and `QuoteResponse.words` carries it. There was no equivalent for
//! a grant, and there has to be, because of a policy the console cannot express
//! for itself:
//!
//! > **A purchase never buys cloud disk.** It raises the tenant's cap at once
//! > and is served from the free room; it only QUEUES an evaluation. Growth
//! > fires at 80 % used and sizes back to 65 %.
//!
//! So the customer must be told the raise has ALREADY happened, and must not be
//! led to expect a provisioning step that will never be reported to them.
//! gunnar-ui may not write that sentence — its standing rule is no price, no
//! currency, no size, no menu, and monetize's strings rendered verbatim — so it
//! lives in monetize's voice (`monetize::transaction::words`) and travels on
//! `StartPurchaseResponse.words`, the same way the refusal's does on the quote.
//!
//! What this file proves is the part no unit test on the string can: that the
//! sentence actually reaches the WIRE, on every path that ends in a grant and
//! on none that does not.
use std::sync::Arc;
use monetize::testing::{purchase, Answer, MockCloud, MockPay, NOW};
use monetize::transaction::{words, Outcome};
use monetize_proto::purchase_client::PurchaseClient;
use monetize_proto::{self as pb, PurchaseState};
use monetize_server::registry::Registry;
use monetize_server::state::AppState;
use monetize_server::testing::estate::FakeEstate;
use monetize_server::testing::products::TestProduct;
use monetize_server::testing::TestServer;
use tonic::metadata::MetadataValue;
use tonic::transport::Channel;
use tonic::Request;
const BEARER: &str = "test-bearer-token-of-sufficient-length";
struct Rig {
server: TestServer,
channel: Channel,
}
impl Rig {
/// alice bought a period at [`NOW`] and holds 10 GiB — so an order for 100
/// is a raise and a renewal has a period to extend.
async fn up() -> Rig {
Rig::up_with(|_| {}).await
}
/// The same, with the vendor configured by `tune` before it is registered —
/// which is how a PENDING attempt is rehearsed (a vendor that mints a link
/// and holds nothing until the customer uses it).
async fn up_with(tune: impl FnOnce(&MockPay)) -> Rig {
let cloud = MockCloud::new();
cloud.answer_all(Answer::Provisioned);
let vendor = MockPay::new();
tune(&vendor);
let mut state = AppState::in_memory(Registry::new().pulled_product(TestProduct::gunnar(), "gunnar.rs").sells("gunnar", vendor).cloud(cloud));
FakeEstate::declare(&state, "gunnar", "gunnar.rs").buys_iron(&["pack_bytes"]);
*state.terms.write().unwrap() = monetize_server::state::TermsStore::fixture();
state.clock = Box::new(|| NOW);
let state = Arc::new(state);
let out = state
.transaction(state.registry.products().next().unwrap(), monetize_pay::Vendor::of(&MockPay::new()))
.run(&purchase("alice"))
.expect("the first period must sell");
assert!(matches!(out, Outcome::Paid { .. }), "{out:?}");
let server = TestServer::spawn(state.clone(), BEARER).await;
let channel = Channel::from_shared(server.endpoint()).unwrap().connect().await.expect("connect");
Rig { server, channel }
}
fn authed<T>(&self, req: T) -> Request<T> {
let mut r = Request::new(req);
r.metadata_mut().insert("authorization", MetadataValue::try_from(format!("Bearer {BEARER}")).unwrap());
r
}
async fn start(&self, req: pb::StartPurchaseRequest) -> Result<pb::StartPurchaseResponse, tonic::Status> {
PurchaseClient::new(self.channel.clone()).start(self.authed(req)).await.map(|r| r.into_inner())
}
fn order(gib: u64) -> pb::StartPurchaseRequest {
pb::StartPurchaseRequest {
product: "gunnar".into(),
tenant: "alice".into(),
caps: [("pack_bytes".to_string(), gib << 30)].into(),
vendor: "mock".into(),
payer: String::new(),
starts_unix_ms: NOW,
market: 0,
renew: false,
actor_ticket: Vec::new(),
bundle: String::new(),
users: None,
}
}
fn renewal() -> pb::StartPurchaseRequest {
pb::StartPurchaseRequest {
product: "gunnar".into(),
tenant: "alice".into(),
caps: Default::default(),
vendor: "mock".into(),
payer: String::new(),
starts_unix_ms: 0,
market: 0,
renew: true,
actor_ticket: Vec::new(),
bundle: String::new(),
users: None,
}
}
}
/// ★ **A GRANTED ORDER SAYS THE ROOM IS THERE NOW.**
///
/// The sentence is monetize's, byte for byte — a console that composed its own
/// would be inventing the one thing it cannot know: that the cap moved before
/// the response was written and that nothing is being provisioned on the
/// customer's behalf.
#[tokio::test]
async fn a_granted_order_carries_monetizes_own_sentence() {
let rig = Rig::up().await;
let out = rig.start(Rig::order(100)).await.expect("the order must sell");
assert_eq!(out.state, PurchaseState::Paid as i32, "{}", out.reason);
assert_eq!(out.words, words::ORDER_GRANTED, "the granted sentence must reach the wire verbatim");
rig.server.stop().await;
}
/// **A granted RENEWAL says the subscription is extended and claims no new
/// room.** A renewal sells an empty delta; a sentence that told the customer
/// their limit had gone up would be a plain untruth about their money.
#[tokio::test]
async fn a_granted_renewal_claims_no_new_room() {
let rig = Rig::up().await;
let out = rig.start(Rig::renewal()).await.expect("the renewal must sell");
assert_eq!(out.state, PurchaseState::Paid as i32, "{}", out.reason);
assert_eq!(out.words, words::RENEWAL_GRANTED);
assert_ne!(out.words, words::ORDER_GRANTED);
rig.server.stop().await;
}
/// ★ **THE RETRY PATH CARRIES IT TOO** — and it is the one an implementation
/// forgets.
///
/// An order already filled answers PAID from `already_filled` without running
/// a transaction at all, so it is a second place the response is built. A
/// customer whose first request timed out after the money moved is exactly the
/// customer who most needs to be told the room is already theirs.
#[tokio::test]
async fn an_order_already_filled_still_tells_the_customer_the_room_is_theirs() {
let rig = Rig::up().await;
let first = rig.start(Rig::order(100)).await.expect("the order must sell");
assert_eq!(first.state, PurchaseState::Paid as i32, "{}", first.reason);
let again = rig.start(Rig::order(100)).await.expect("a retry answers the attempt that filled it");
assert_eq!(again.reference, first.reference, "the same attempt, not a second sale");
assert_eq!(again.words, words::ORDER_GRANTED, "the retry is a grant and must say so");
rig.server.stop().await;
}
/// **An attempt that is not a grant carries no granted sentence.**
///
/// The vendor mints a link and holds nothing until the customer uses it, so the
/// answer is PENDING: nothing has been raised, nothing is theirs yet, and a
/// sentence saying otherwise would be a lie told at the worst moment. Empty,
/// and the console falls back to `reason` exactly as before.
#[tokio::test]
async fn a_pending_attempt_carries_no_granted_sentence() {
let rig = Rig::up_with(|pay| pay.awaits_the_customer()).await;
let out = rig.start(Rig::order(100)).await.expect("the order reserves");
assert_eq!(out.state, PurchaseState::Pending as i32, "{}", out.reason);
assert!(out.words.is_empty(), "a pending attempt granted nothing: {:?}", out.words);
rig.server.stop().await;
}