crates/monetize-server/tests/a_free_product_sells_nothing.rs
//! **A product its block says is served FREE sells nothing** (wiring tracer B 2026-10-08). A sold estate's own
//! monetize is fed `plugin.gunnar.free = true` (its `[products.gunnar] free = true`) and no price list: its owner
//! buys at the shop that sold it. An order or a renewal that reached `Purchase.Start` there was priced at nothing
//! and GRANTED as Paid with no vendor — room for nothing on an estate that sells none. It is refused by name.
use std::sync::Arc;
use monetize::testing::{purchase, Answer, MockCloud, MockPay, NOW};
use monetize::transaction::Outcome;
use monetize_proto::purchase_client::PurchaseClient;
use monetize_proto::{self as pb};
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";
fn ask(renew: bool, gib: u64) -> pb::StartPurchaseRequest {
pb::StartPurchaseRequest {
product: "gunnar".into(),
tenant: "alice".into(),
caps: if renew { Default::default() } else { [("pack_bytes".to_string(), gib << 30)].into() },
vendor: "mock".into(),
payer: String::new(),
starts_unix_ms: if renew { 0 } else { NOW },
market: 0,
renew,
actor_ticket: Vec::new(),
bundle: String::new(),
users: None,
}
}
#[tokio::test]
async fn an_order_or_a_renewal_of_a_free_product_is_refused_and_grants_nothing() {
let cloud = MockCloud::new();
cloud.answer_all(Answer::Provisioned);
let mut state = AppState::in_memory(Registry::new().pulled_product(TestProduct::gunnar(), "gunnar.rs").sells("gunnar", MockPay::new()).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);
// alice holds a period (bought before the block said free), so a renewal has something to extend.
let out = state.transaction(state.registry.products().next().unwrap(), monetize_pay::Vendor::of(&MockPay::new())).run(&purchase("alice")).expect("the first period sells");
assert!(matches!(out, Outcome::Paid { .. }), "{out:?}");
// The block says free: a sold estate's own monetize.
state.config.write().unwrap().products.insert("gunnar".into(), monetize_server::config::ProductConfig { free: true, ..Default::default() });
let server = TestServer::spawn(state.clone(), BEARER).await;
let channel = Channel::from_shared(server.endpoint()).unwrap().connect().await.expect("connect");
let start = |req: pb::StartPurchaseRequest| {
let channel = channel.clone();
async move {
let mut r = Request::new(req);
r.metadata_mut().insert("authorization", MetadataValue::try_from(format!("Bearer {BEARER}")).unwrap());
PurchaseClient::new(channel).start(r).await.map(|r| r.into_inner())
}
};
for (what, req) in [("an order for 100 GiB", ask(false, 100)), ("a renewal", ask(true, 0))] {
match start(req).await {
Ok(granted) => panic!("{what} of a FREE product was answered {:?} ({}): it sells nothing", pb::PurchaseState::try_from(granted.state), granted.reference),
Err(s) => {
assert_eq!(s.code(), tonic::Code::FailedPrecondition, "{what}: {s:?}");
assert!(s.message().contains("nothing is sold here"), "{what}: {}", s.message());
}
}
}
server.stop().await;
}