Your browser sucks, JavaScript disabled! Gunnar's console needs JavaScript and WebAssembly. Git over SSH on port 2222 does not.

nordisk/monetize

crates/monetize-server/tests/server_iron.rs

//! **The open-source deployment: this box's own iron, no price list, and no
//! payment vendor linked at all.** `Purchase.Start` over gRPC lands
//! `Reserved / Ensured(OnHand) / Captured / EntitlementPushed` with zero vendor
//! calls — there is no vendor to call — the pool refuses the sale it cannot
//! cover naming the shortfall, `Purchase.Quote` prices it at nothing and says
//! whether the box can serve it, and the fleet's cloud block shows the box
//! itself under `provenance: server` with nothing orphaned.
//!
//! Measure, never assert: every claim has its red twin here; every assertion is
//! on applied
//! output — ledger rows off the wire, the pool's own sold count, the product's
//! pushed facts — never a round-trip.

use std::sync::Arc;

use monetize::testing::{MockProduct, NOW};
use monetize_cloud::cap::CannotSpend;
use monetize_cloud::server::{Server, ServerConfig};
use monetize_cloud::{Resource, ServerId};
use monetize_pay::Market;
use monetize_proto::ledger_client::LedgerClient;
use monetize_proto::purchase_client::PurchaseClient;
use monetize_proto::query_ledger_request::By;
use monetize_proto::{self as pb, PurchaseState};
use monetize_server::config::Config;
use monetize_server::registry::Registry;
use monetize_server::services::usage::{cloud_inventory_pb, EstateIron};
use monetize_server::state::{AppState, TermsStore};
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";
const GIB: u64 = 1 << 30;

struct Rig {
    product: Arc<MockProduct>,
    iron: Arc<Server>,
    state: Arc<AppState>,
    #[allow(dead_code)]
    server: TestServer,
    channel: Channel,
}

impl Rig {
    /// No vendor at all — `vendors []` — no price list, and the box has
    /// `pool_gib` GiB for tenants, walled by the product saying it can serve
    /// `servable_gib`, with 4 000 millicores declared as what the box has.
    async fn up(pool_gib: u64, servable_gib: u64) -> Rig {
        let product = Arc::new(MockProduct::gunnar());
        product.can_serve(servable_gib * GIB, 300 * GIB);
        let product_dyn: Arc<dyn monetize_product::Product> = product.clone();
        let mut cfg = ServerConfig::new(product_dyn.clone(), pool_gib);
        cfg.ceilings = [("cpu_millicores".to_string(), 4000u64)].into();
        let iron = Arc::new(Server::new(cfg).unwrap());
        let registry = Registry::new().shared_product(product_dyn).cloud(CannotSpend::new(iron.clone()));
        assert!(registry.vendor_names().is_empty(), "the claim of this file: no payment vendor is linked");
        let mut state = AppState::in_memory(registry);
        // THE OPEN-SOURCE TERMS: no list at all. Every order is free.
        state.terms = std::sync::RwLock::new(TermsStore { hosts: [("gunnar".to_string(), "this-box".to_string())].into(), ..TermsStore::default() });
        let state = Arc::new(state);
        let server = TestServer::spawn(state.clone(), BEARER).await;
        let channel = Channel::from_shared(server.endpoint()).unwrap().connect().await.unwrap();
        Rig { product, iron, state, 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
    }

    /// An order for `gib` GiB of pack_bytes, naming `vendor` (which at price
    /// 0 is ignored).
    async fn start(&self, tenant: &str, gib: u64, vendor: &str) -> Result<pb::StartPurchaseResponse, tonic::Status> {
        PurchaseClient::new(self.channel.clone())
            .start(self.authed(pb::StartPurchaseRequest {
                product: "gunnar".into(),
                tenant: tenant.into(),
                caps: [("pack_bytes".to_string(), gib * GIB)].into(),
                vendor: vendor.into(),
                payer: String::new(),
                starts_unix_ms: NOW,
                renew: false,
                actor_ticket: Vec::new(),
                bundle: String::new(),
                market: pb::Market::Se as i32,
                users: None,
            }))
            .await
            .map(|r| r.into_inner())
    }

    async fn quote(&self, tenant: &str, caps: &[(&str, u64)]) -> pb::QuoteResponse {
        PurchaseClient::new(self.channel.clone())
            .quote(self.authed(pb::QuoteRequest {
                product: "gunnar".into(),
                tenant: tenant.into(),
                caps: caps.iter().map(|(k, v)| (k.to_string(), *v)).collect(),
                market: pb::Market::Se as i32,
                renew: false,
                starts_unix_ms: NOW,
                actor_ticket: Vec::new(),
                bundle: String::new(),
            }))
            .await
            .expect("a quote is answered")
            .into_inner()
    }

    async fn rows(&self, reference: &str) -> Vec<pb::LedgerRow> {
        LedgerClient::new(self.channel.clone())
            .query(self.authed(pb::QueryLedgerRequest { by: Some(By::Reference(reference.into())) }))
            .await
            .unwrap()
            .into_inner()
            .rows
    }
}

/// **The whole thing, over the wire, with nobody to pay.** Four rows, the
/// grant `OnHand`, the handle `none`, the fact's source `payment:none:`, the
/// pool's sold count moved, the product told — and the vendor field of the
/// request ignored, because at price 0 there is nobody it could name.
#[tokio::test]
async fn a_free_order_on_this_box_is_sold_over_the_wire_with_no_vendor_linked() {
    let rig = Rig::up(100, 200).await;
    let r = rig.start("alice", 10, "").await.unwrap();
    assert_eq!(r.state, PurchaseState::Paid as i32, "{r:?}");
    assert_eq!((r.handle_vendor.as_str(), r.handle_id.as_str()), ("none", r.reference.as_str()));
    assert!(r.redirect_url.is_empty(), "nowhere to send a customer who owes nothing");
    let fact = r.fact.expect("paid carries the fact");
    assert_eq!(fact.source, format!("payment:none:{}", r.reference));
    assert_eq!(fact.plan, r.reference, "the fact names the order");
    assert_eq!(fact.caps["pack_bytes"], 10 * GIB);

    let rows = rig.rows(&r.reference).await;
    let kinds: Vec<&str> = rows.iter().map(|r| r.kind.as_str()).collect();
    assert_eq!(kinds, ["Reserved", "Ensured", "Captured", "EntitlementPushed"]);
    assert!(rows[1].json.contains("\"OnHand\""), "on hand, never bought: {}", rows[1].json);
    assert!(!rows[1].json.contains("Provisioned"), "{}", rows[1].json);
    assert!(rows[2].json.contains("nothing to draw"), "{}", rows[2].json);
    assert_eq!((rig.iron.sold_gib(), rig.iron.free_gib()), (10, 90), "the pool moved by exactly the grant");
    assert_eq!(rig.product.pushed().len(), 1, "the product was told");
    assert!(rig.state.ledger.unfinished().unwrap().is_empty());

    // A vendor NAME on a free purchase is ignored, not looked up: the same
    // request naming a vendor that does not exist still sells.
    let r2 = rig.start("bob", 10, "klarna").await.unwrap();
    assert_eq!(r2.state, PurchaseState::Paid as i32, "{r2:?}");
    assert_eq!(rig.iron.sold_gib(), 20);

    // Twin: put a PRICE LIST on the deployment and the same order, with no
    // vendor linked, is refused by name — nobody can take 220 SEK — and
    // nothing moves. The list is per market: 22.00 SEK per GiB-month in SE.
    rig.state.terms.write().unwrap().prices = [("gunnar".to_string(), [(Market::Se, monetize::testing::price_list())].into())].into();
    let err = rig.start("mira", 10, "klarna").await.expect_err("no vendor can take 220 SEK");
    assert_eq!(err.code(), tonic::Code::NotFound, "{err}");
    assert!(err.message().contains("220.00 SEK") && err.message().contains("serving: []"), "{err}");
    let err = rig.start("mira", 10, "").await.expect_err("nor with no vendor named");
    assert!(err.message().contains("[products.gunnar] names no `vendor`") && err.message().contains("serving: []"), "{err}");
    assert_eq!(rig.iron.sold_gib(), 20, "a refused order grants nothing");
    assert_eq!(rig.product.pushed().len(), 2);
    assert!(rig.state.ledger.attempt(&format!("gunnar/mira/2026-09-03+pack_bytes={}", 10 * GIB)).unwrap().is_none(), "and writes nothing");
    // The quote says the price and still answers: the box CAN serve it.
    let q = rig.quote("mira", &[("pack_bytes", 10 * GIB)]).await;
    assert_eq!((q.price.as_ref().unwrap().minor, q.can_serve), (22_000, true), "{q:?}");
}

/// **Refuse twin: the box refuses what it cannot cover, over the wire, naming
/// the shortfall — and nothing is granted.** Declared pool first; then the
/// product's own measurement, which the declaration cannot talk over.
#[tokio::test]
async fn refuse_twin_the_pool_and_the_products_measurement_both_refuse_over_the_wire() {
    let rig = Rig::up(15, 200).await;
    assert_eq!(rig.start("alice", 10, "").await.unwrap().state, PurchaseState::Paid as i32);
    // THE QUOTE REFUSES FIRST, by name, before the order is placed: the same
    // sentence `ensure` would refuse with, and nothing written.
    let q = rig.quote("bob", &[("pack_bytes", 10 * GIB)]).await;
    assert!(!q.can_serve, "{q:?}");
    assert!(q.reason.contains("10 GiB already granted") && q.reason.contains("short 5 GiB"), "{}", q.reason);
    assert_eq!(q.price.as_ref().unwrap().minor, 0, "free, and still refused: the box is full");
    let err = rig.start("bob", 10, "").await.expect_err("the order is refused where the quote was");
    assert_eq!(err.code(), tonic::Code::FailedPrecondition, "{err}");
    assert!(err.message().contains("short 5 GiB"), "{err}");
    assert!(rig.state.ledger.attempt(&q.reference).unwrap().is_none(), "nothing reserved, nothing to destroy");
    assert_eq!(rig.iron.sold_gib(), 10, "the refused sale granted nothing");
    assert_eq!(rig.product.pushed().len(), 1);

    let rig = Rig::up(100, 4).await;
    let err = rig.start("alice", 10, "").await.expect_err("the disk has 4 GiB");
    assert!(err.message().contains("gunnar measured") && err.message().contains("4.0 GiB more"), "{err}");
    assert_eq!(rig.iron.sold_gib(), 0);

    // What no tenant owns: the box declares 4 000 millicores, so a product-scope
    // cap of 8 000 is refused by name — and the declared number is the one
    // named, because this mock product measures no cores.
    let rig = Rig::up(100, 200).await;
    let q = rig.quote("alice", &[("pack_bytes", 10 * GIB), ("cpu_millicores", 8000)]).await;
    assert!(!q.can_serve && q.reason.contains("cpu_millicores = 8000 is past what this box has (4000)"), "{q:?}");
    let q = rig.quote("alice", &[("pack_bytes", 10 * GIB), ("cpu_millicores", 4000)]).await;
    assert!(q.can_serve, "4000 exactly is not past it: {q:?}");
}

/// **The fleet's cloud block shows the box itself**: one `dataset` row,
/// `provenance: server`, sized by the product's total, priced at zero, attached
/// to the product — and NOT an orphan, which is what it was before
/// `Reconciliation::own` existed. The OnHand grant is neither matched nor
/// missing, as ever.
#[tokio::test]
async fn the_cloud_block_shows_the_box_as_its_own_iron_and_never_as_an_orphan() {
    let rig = Rig::up(100, 200).await;
    rig.start("alice", 10, "").await.unwrap();
    let cloud = rig.state.registry.transaction_slot().expect("a cloud a sale spends at");
    assert_eq!(cloud.name(), "server");
    assert_eq!(cloud.spend_ceiling(), None, "cannot spend, so no ceiling");
    let c = cloud_inventory_pb("gunnar", cloud, &rig.state.ledger, &EstateIron::default());
    assert!(c.available, "{}", c.error);
    assert_eq!(c.owned.len(), 1, "{c:?}");
    let o = &c.owned[0];
    assert_eq!((o.provider.as_str(), o.id.as_str(), o.kind.as_str(), o.provenance.as_str()), ("server", "dataset:gunnar", "dataset", "server"));
    assert_eq!((o.gib, o.attached_to.as_str(), o.reference.as_str(), o.tenant.as_str()), (300, "gunnar", "", ""));
    assert_eq!((o.priced, o.monthly_minor), (true, 0), "zero is a price, not an unpriced row");
    assert!(c.orphans.is_empty() && c.missing.is_empty(), "{c:?}");
    assert_eq!((c.total_bytes, c.monthly_minor, c.foreign_count), (300 * GIB, 0, 0));
    // Another product's view of the same box does not list gunnar's data set.
    let other = cloud_inventory_pb("holger", cloud, &rig.state.ledger, &EstateIron::default());
    assert!(other.owned.is_empty() && other.orphans.is_empty(), "{other:?}");
}

/// **`Purchase.Quote` on this box: free, costs nothing, the iron named, the
/// reference the order would run under — and NOTHING written.** Then the order
/// for MORE: a tenant on 10 GiB quoted at 100 is quoted the 90 they lack, and
/// the box, which has 100 declared and 10 granted, says no by 0 — exactly the
/// boundary — while 95 fits.
#[tokio::test]
async fn a_quote_on_this_box_is_free_names_the_iron_and_writes_nothing() {
    let rig = Rig::up(100, 200).await;
    let q = rig.quote("alice", &[("pack_bytes", 10 * GIB)]).await;
    assert_eq!(q.reference, format!("gunnar/alice/2026-09-03+pack_bytes={}", 10 * GIB));
    assert_eq!((q.price.as_ref().unwrap().minor, q.cost.as_ref().unwrap().minor), (0, 0), "no list, and the box's own iron costs nothing");
    assert_eq!(q.price.as_ref().unwrap().currency, pb::Currency::Sek as i32, "the market's currency, even at zero");
    assert!(q.current.is_empty() && q.delta["pack_bytes"] == 10 * GIB && q.caps["pack_bytes"] == 10 * GIB);
    assert!(matches!(&q.resources[0].kind, Some(pb::resource::Kind::Disk(d)) if d.gib == 10 && d.server == "this-box"), "{:?}", q.resources);
    assert!(q.can_serve && q.reason.is_empty(), "{q:?}");
    assert_eq!(q.period, "month");
    assert_eq!(rig.state.ledger.step_count().unwrap(), 0, "a quote writes nothing");
    assert_eq!(rig.iron.sold_gib(), 0, "and grants nothing");

    // Sold, then quoted for more: the delta, and the box's boundary.
    rig.start("alice", 10, "").await.unwrap();
    let q = rig.quote("alice", &[("pack_bytes", 100 * GIB)]).await;
    assert_eq!((q.current["pack_bytes"], q.delta["pack_bytes"]), (10 * GIB, 90 * GIB));
    assert!(q.can_serve, "90 more on a box with 90 left fits exactly: {q:?}");
    let q = rig.quote("alice", &[("pack_bytes", 101 * GIB)]).await;
    assert!(!q.can_serve && q.reason.contains("short 1 GiB"), "one GiB past the box: {q:?}");
    // A downgrade and a no-op are not quotes at all.
    let err = PurchaseClient::new(rig.channel.clone())
        .quote(rig.authed(pb::QuoteRequest { product: "gunnar".into(), tenant: "alice".into(), caps: [("pack_bytes".to_string(), GIB)].into(), market: 0, starts_unix_ms: NOW, renew: false, actor_ticket: Vec::new(), bundle: String::new() }))
        .await
        .expect_err("a downgrade");
    assert_eq!(err.code(), tonic::Code::FailedPrecondition);
    assert!(err.message().contains("SHRINK"), "{err}");
    // A cap on a meter the product does not declare is refused by name.
    let err = PurchaseClient::new(rig.channel.clone())
        .quote(rig.authed(pb::QuoteRequest { product: "gunnar".into(), tenant: "alice".into(), caps: [("moon_phase".to_string(), 1u64)].into(), market: 0, starts_unix_ms: NOW, renew: false, actor_ticket: Vec::new(), bundle: String::new() }))
        .await
        .expect_err("an undeclared meter");
    assert_eq!(err.code(), tonic::Code::InvalidArgument);
    assert!(err.message().contains("moon_phase"), "{err}");
}

/// **The registry builds `[clouds.server]` with no `cap`, and STILL refuses
/// an enabled `upcloud` without one.** The exemption is by name, explicit, and
/// enforced by `CannotSpend`; every other cloud stays behind the ceiling.
#[test]
fn the_registry_builds_server_uncapped_and_still_refuses_upcloud_without_a_cap() {
    let dir = std::env::temp_dir().join(format!("monetize-server-iron-registry-{}", std::process::id()));
    let _ = std::fs::remove_dir_all(&dir);
    let base = format!("data_dir = {:?}\n[products.holger]\nendpoint = \"127.0.0.1:1\"\n", dir.display());
    // The open build links no product plugin (owner ruling 2026-09-22), so
    // the holger block builds only through a registered factory — here the
    // Holger-shaped TEST product's (v2.0 P2 12.0).
    let mut linked = monetize_server::registry::Linked::open();
    linked.product(monetize_server::testing::products::HOLGER_FACTORY);
    let reg = Registry::from_config_with(&Config::from_toml(&format!("{base}[clouds.server]\nenabled = true\npool_gib = 100\n")).unwrap(), &linked).unwrap();
    assert_eq!(reg.cloud_names(), vec!["server"]);
    let cloud = reg.transaction_slot().expect("a cloud a sale spends at");
    assert_eq!(cloud.spend_ceiling(), None);
    // Applied output: a grant through the registry's cloud lands in the data
    // dir, OnHand, and the file says so.
    let res = Resource::Disk { server: ServerId("this-box".into()), gib: 10 };
    assert!(matches!(monetize_cloud::ask::ensure(&cloud, "holger/alice/2026-09", &res, None).unwrap(), monetize_cloud::Ensured::OnHand { pool_free_after: 90 }));
    let state = std::fs::read_to_string(dir.join("server-pool.json")).expect("the registry gives a deployment a home for its grants");
    assert!(state.contains("\"kind\": \"on_hand\"") && !state.contains("provisioned"), "{state}");
    assert!(monetize_server::registry::Linked::open().find_cloud("server").unwrap().config_envs.is_empty(), "no credential env for the box");

    // Refuse twins, each naming what is missing.
    let err = Registry::from_config_with(&Config::from_toml(&format!("{base}[clouds.server]\nenabled = true\n")).unwrap(), &linked).err().map(|e| e.to_string()).expect("no pool_gib");
    assert!(err.contains("[clouds.server]") && err.contains("no `pool_gib`") && err.contains("0 is a valid answer"), "{err}");
    // An unlinked product is SKIPPED by name at start (release-3 3ed6028: a
    // self-registering product arrives after monetize serves), never the end of it.
    let reg = Registry::from_config_with(&Config::from_toml(&format!("{base}[clouds.server]\nenabled = true\npool_gib = 1\nproduct = \"idun\"\n")).unwrap(), &linked).expect("the server starts");
    assert!(reg.cloud_names().is_empty());
    let said: Vec<String> = reg.vendor_start_refusals().iter().map(|r| r.to_string()).collect();
    assert!(said.iter().any(|l| l.contains("cloud-product-not-linked-at-start") && l.contains("\"idun\"") && l.contains("[products.idun]")), "{said:?}");
    let err = Registry::from_config(&Config::from_toml(&format!("data_dir = {:?}\n[clouds.server]\nenabled = true\npool_gib = 1\n", dir.display())).unwrap())
        .err()
        .map(|e| e.to_string())
        .expect("no product at all");
    assert!(err.contains("none is linked"), "{err}");

    // THE exemption does not leak: upcloud enabled with no cap is refused on the
    // cap, whether or not the provider is compiled into this build.
    let err = Registry::from_config_with(&Config::from_toml(&format!("{base}[clouds.upcloud]\nenabled = true\n")).unwrap(), &linked).err().map(|e| e.to_string()).expect("no ceiling must refuse");
    assert!(err.contains("[clouds.upcloud]") && err.contains("no `cap`"), "{err}");
    // And the sandbox, the other spender, likewise.
    let err = Registry::from_config_with(&Config::from_toml(&format!("{base}[clouds.sandbox]\nenabled = true\n")).unwrap(), &linked).err().map(|e| e.to_string()).expect("no ceiling must refuse");
    assert!(err.contains("[clouds.sandbox]") && err.contains("no `cap`"), "{err}");
}