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

nordisk/gunnar

crates/gunnar-grpc/tests/billing.rs

//! **`Billing`: gunnar enforces the tenant scope itself, and these are the
//! tests that say so.**
//!
//! # The one line this whole file exists for
//!
//! A customer must be able to renew their own subscription from the console.
//! The console (`gunnar-ui`'s BFF) holds a SERVER-WIDE admin grant on this
//! appliance, because it renders operator screens. So if the console alone
//! decided "this browser session may only touch namespace X", any routing bug
//! in the console — one namespace read off the wrong request, one cached
//! session — would be full administrative reach over every customer on the box.
//!
//! The console therefore does not decide. It presents the HUMAN's own
//! `gunnar-sshsig-v2` credential, signed with the ed25519 key `gunnar-ui`
//! enrolled on that person's own gunnar principal, and gunnar performs the
//! grant check itself with the model it already has:
//!
//! ```text
//! require(&caller, Access::namespace(&ns), GrantLevel::Admin, "Billing.MintActorTicket")
//!   -> Identity::may -> AuthTable::may -> owns_account (a principal's id IS
//!      the namespace it owns) or a Scope::Namespace grant
//! ```
//!
//! Every test below drives the real gate: the caller is produced by
//! `AuthGate::authenticate` over a real signature from a really-enrolled key,
//! so nothing here invents an identity the server would not have accepted.
//!
//! # What the double is allowed to stand in for, and what it is not
//!
//! `MemoryControlPlane` stands in for the STORE. It does not stand in for the
//! decision: its `may` is `AuthTable::may`'s shape, `owns_account` included,
//! and `tests/bootstrap.rs` asserts both directions of that so the double
//! cannot quietly allow more than the real table would. Nor does it stand in
//! for the signature: it holds a real ed25519 key and
//! [`the_ticket_a_tenant_gets_verifies_under_the_key_the_appliance_advertises`]
//! checks what it produced with `monetize_embed::verify_ticket`, which is the
//! verifier the OTHER repository runs. That test is the cross-repo contract,
//! executed rather than described.

use std::sync::Arc;

use gunnar_grpc::{
    sign_credential_for, AuthGate, AuthMode, BillingService, Caller, ControlPlane, EventBus,
    MemoryControlPlane, ServerConfig, ServiceContext, CREDENTIAL_METADATA_KEY,
};
use gunnar_proto::billing_server::Billing as _;
use gunnar_proto::{
    ApplianceIdentityRequest, EntitlementFact, EntitlementState, GrantLevel,
    MintActorTicketRequest, PrincipalKind,
};
use monetize_embed::ticket::{
    ActorTicket, MAX_LIFE_MS, PURPOSE_ORDER, PURPOSE_READ, PURPOSE_RENEW,
};
use tonic::{Code, Request};

/// **The map type `verify_ticket` compares**: `ActorTicket::caps` is a
/// `BTreeMap` because the canonical JSON that gets signed sorts keys anyway, so
/// the ordered map makes the Rust value and the signed bytes agree by
/// construction. Aliased here so a test cannot quietly introduce a second one.
type Caps = std::collections::BTreeMap<String, u64>;

const ALICE: &str = "alice";
const BETA: &str = "beta";

fn now_ms() -> i64 {
    std::time::SystemTime::now()
        .duration_since(std::time::UNIX_EPOCH)
        .expect("after 1970")
        .as_millis() as i64
}

fn mint_key() -> ssh_key::PrivateKey {
    ssh_key::PrivateKey::random(
        &mut rand::rand_core::UnwrapErr(rand::rngs::SysRng),
        ssh_key::Algorithm::Ed25519,
    )
    .expect("mint an ed25519 key")
}

struct Harness {
    plane: Arc<MemoryControlPlane>,
    ctx: ServiceContext,
}

impl Harness {
    /// Two namespaces and no grants. Every test says out loud who holds what,
    /// so a test that forgets gets a refusal rather than an accident.
    fn new() -> Harness {
        let plane = Arc::new(MemoryControlPlane::new());
        plane
            .create_namespace(ALICE, "alice's own")
            .expect("namespace alice");
        plane
            .create_namespace(BETA, "somebody else entirely")
            .expect("namespace beta");
        let control: Arc<dyn ControlPlane> = plane.clone();
        let ctx = ServiceContext::new(
            control,
            Arc::new(EventBus::new(16)),
            ServerConfig::default(),
        );
        assert_eq!(
            ctx.config.auth,
            AuthMode::Required,
            "the fixture must run the SHIPPED default; a suite that quietly ran the \
             allow-all escape hatch would prove nothing about a grant check"
        );
        Harness { plane, ctx }
    }

    /// Enrol a principal whose id IS `name`, with a freshly minted key — the
    /// shape `gunnar-ui`'s `TenantKeys` produces: one ed25519 key per console
    /// user, on that person's own gunnar principal.
    fn enrol(&self, name: &str) -> ssh_key::PrivateKey {
        let key = mint_key();
        let principal = self
            .plane
            .add_principal(name, name, PrincipalKind::User)
            .expect("principal added");
        assert_eq!(
            principal.id, name,
            "the whole scope check rests on a principal's id being the namespace it owns"
        );
        let openssh = key.public_key().to_openssh().expect("render public key");
        self.plane
            .add_key(&principal.id, &openssh, name, None, "", None)
            .expect("key registered");
        key
    }

    /// The caller the REAL gate produces for this key, over a real signature.
    fn caller_for(&self, key: &ssh_key::PrivateKey, nonce: &str) -> Caller {
        let credential = sign_credential_for(key, now_ms(), nonce, "Admin.Health").expect("sign");
        let mut request = Request::new(());
        request.metadata_mut().insert(
            CREDENTIAL_METADATA_KEY,
            credential.to_header_value().parse().expect("ascii"),
        );
        AuthGate::new(Arc::clone(&self.ctx.control), self.ctx.config.auth)
            .authenticate_bound(request.metadata(), now_ms(), Some("Admin.Health"))
            .expect("a freshly signed credential from an enrolled key must verify")
    }

    fn authed<T>(&self, caller: &Caller, message: T) -> Request<T> {
        let mut request = Request::new(message);
        request.extensions_mut().insert(caller.clone());
        request
    }

    fn billing(&self) -> BillingService {
        BillingService::new(self.ctx.clone())
    }
}

fn renew(namespace: &str) -> MintActorTicketRequest {
    MintActorTicketRequest {
        namespace: namespace.to_owned(),
        purpose: PURPOSE_RENEW.to_owned(),
        caps: no_caps(),
    }
}

/// An order for one namespace, naming [`ten_gib`] — the caps a real console
/// would put on the button the customer pressed.
fn order(namespace: &str) -> MintActorTicketRequest {
    MintActorTicketRequest {
        namespace: namespace.to_owned(),
        purpose: PURPOSE_ORDER.to_owned(),
        caps: ten_gib(),
    }
}

/// **The caps a renewal names: none.**
///
/// A function and not a constant because `verify_ticket` takes a reference to
/// the map the ORDER names, and for a renewal that map is empty on both sides —
/// the emptiness is the assertion, not an argument nobody looked at.
fn no_caps() -> Caps {
    Caps::new()
}

/// The caps an order names: one meter, at a number no other fixture in this
/// file uses, so a cap that arrived from the wrong place is visible rather
/// than coincidentally equal to the right one.
fn ten_gib() -> Caps {
    Caps::from([("pack_bytes".to_owned(), 10_737_418_240u64)])
}

/// The appliance's public key, as `ApplianceIdentity` advertises it, fetched
/// through an operator caller — which is the only caller that may.
///
/// As `monetize_embed::VerifyingKey`, the type `verify_ticket` takes: since the
/// merged line's RustCrypto bump this crate links ed25519-dalek 3 while the
/// published monetize-embed it resolves is on 2, and the target stopped
/// compiling on that mismatch. The verifier's own key type is the right one
/// to build here anyway — it is the function monetize calls.
async fn advertised_key(harness: &Harness) -> monetize_embed::VerifyingKey {
    let operator = harness.enrol("operator");
    harness.plane.grant_server("operator", GrantLevel::Admin);
    let caller = harness.caller_for(&operator, "nonce-operator-identity-1");
    let identity = harness
        .billing()
        .appliance_identity(harness.authed(&caller, ApplianceIdentityRequest {}))
        .await
        .expect("a server admin may read the appliance's public key")
        .into_inner();

    assert_eq!(identity.issuer, "gunnar");
    assert_eq!(identity.public_key_hex.len(), 64);
    let mut raw = [0u8; 32];
    for (slot, pair) in raw
        .iter_mut()
        .zip(identity.public_key_hex.as_bytes().chunks(2))
    {
        *slot = u8::from_str_radix(std::str::from_utf8(pair).expect("ascii"), 16)
            .expect("hex digit pair");
    }
    monetize_embed::VerifyingKey::from_bytes(&raw).expect("the advertised key parses")
}

// ───────────────────────────────────────────────────────────────────────────
// ★ The one that matters
// ───────────────────────────────────────────────────────────────────────────

/// ★ **A caller may not mint a ticket for a namespace they do not hold.**
///
/// This is the single most important assertion in the change. `alice` is a
/// real, enrolled, fully authenticated caller — nothing about her credential is
/// wrong — and she asks for a ticket naming `beta`. The refusal does not come
/// from the console, which is exactly the point: it comes from
/// `ServiceContext::require` asking `Identity::may` about
/// `Access::Namespace("beta")`, the same call `Repos.Delete` makes.
///
/// **And nothing is minted.** The second half is not decoration. A handler that
/// signed first and checked afterwards would satisfy a test that only looked at
/// the returned `Status`, and would have put a valid, signed, spendable ticket
/// for another tenant through whatever sits between here and the caller — a
/// log line, a trace, a buffered response. The appliance's key is asked to sign
/// exactly zero times.
/// A read for one namespace: no caps, because a read buys nothing.
fn read(namespace: &str) -> MintActorTicketRequest {
    MintActorTicketRequest {
        namespace: namespace.to_owned(),
        purpose: PURPOSE_READ.to_owned(),
        caps: no_caps(),
    }
}

/// ★ **A read ticket is minted, names no caps, and is NOT a renewal.**
///
/// The cross-repo contract for the third verb. It has a word of its own
/// because a ticket is single-use: gating a history read with a RENEWAL ticket
/// burned the one the customer would have renewed with, and made this
/// appliance vouch for a verb the caller was not performing.
#[tokio::test]
async fn a_read_ticket_is_minted_and_is_not_a_renewal() {
    let harness = Harness::new();
    let key = advertised_key(&harness).await;
    let alice = harness.enrol(ALICE);
    let caller = harness.caller_for(&alice, "nonce-alice-reads");

    let minted = harness
        .billing()
        .mint_actor_ticket(harness.authed(&caller, read(ALICE)))
        .await
        .expect("a namespace owner may mint a read ticket for their own namespace")
        .into_inner();

    let ticket = monetize_embed::verify_ticket(
        &minted.ticket,
        &key,
        "gunnar",
        ALICE,
        PURPOSE_READ,
        &no_caps(),
        now_ms(),
    )
    .expect("the ticket this appliance minted must verify as a read");
    assert!(ticket.caps.is_empty(), "a read ticket covers no amount");

    assert!(
        monetize_embed::verify_ticket(
            &minted.ticket,
            &key,
            "gunnar",
            ALICE,
            PURPOSE_RENEW,
            &no_caps(),
            now_ms(),
        )
        .is_err(),
        "a read ticket bought a period — the two verbs are not interchangeable, which is the \
         whole reason the third one exists"
    );
}

/// A read names no caps, and a request that sends some is refused by name
/// rather than quietly ignored.
#[tokio::test]
async fn a_read_that_names_an_amount_is_refused() {
    let harness = Harness::new();
    let alice = harness.enrol(ALICE);
    let caller = harness.caller_for(&alice, "nonce-alice-reads-with-caps");

    let asked = MintActorTicketRequest {
        namespace: ALICE.to_owned(),
        purpose: PURPOSE_READ.to_owned(),
        caps: ten_gib(),
    };
    let refused = harness
        .billing()
        .mint_actor_ticket(harness.authed(&caller, asked))
        .await
        .expect_err("a read that names an amount is a client that has confused two paths");

    assert_eq!(refused.code(), Code::InvalidArgument);
}

#[tokio::test]
async fn a_caller_may_not_mint_a_ticket_for_a_namespace_they_do_not_hold() {
    let harness = Harness::new();
    let alice = harness.enrol(ALICE);
    let caller = harness.caller_for(&alice, "nonce-alice-asks-for-beta");

    let before = harness.plane.actor_tickets_signed();
    let refused = harness
        .billing()
        .mint_actor_ticket(harness.authed(&caller, renew(BETA)))
        .await
        .expect_err("alice must not mint a ticket naming beta");

    assert_eq!(refused.code(), Code::PermissionDenied);
    assert_eq!(
        harness.plane.actor_tickets_signed(),
        before,
        "the appliance SIGNED something on a refused request. A ticket that exists is a \
         ticket that can leak; the grant check must run before the key is touched"
    );

    // The same caller, her own namespace: the refusal above is about the
    // namespace and not about alice, her key, or the gate being shut.
    let caller = harness.caller_for(&alice, "nonce-alice-asks-for-alice");
    harness
        .billing()
        .mint_actor_ticket(harness.authed(&caller, renew(ALICE)))
        .await
        .expect("alice holds alice");
}

// ───────────────────────────────────────────────────────────────────────────
// The happy path, and the cross-repo contract
// ───────────────────────────────────────────────────────────────────────────

/// **A tenant's own ticket parses, names them, and verifies under the key the
/// appliance advertises.**
///
/// This is the real contract test between the two repositories. The ticket is
/// minted here and checked with `monetize_embed::verify_ticket` — the function
/// monetize itself calls, taking the product, the tenant and the purpose from
/// what the ORDER says, exactly as `Purchase.Start` will. If gunnar's canonical
/// form and monetize's ever diverge, this goes red in gunnar's own test run
/// rather than in production on somebody's renewal page.
#[tokio::test]
async fn the_ticket_a_tenant_gets_verifies_under_the_key_the_appliance_advertises() {
    let harness = Harness::new();
    let key = advertised_key(&harness).await;
    let alice = harness.enrol(ALICE);
    let caller = harness.caller_for(&alice, "nonce-alice-happy-path-01");

    let minted = harness
        .billing()
        .mint_actor_ticket(harness.authed(&caller, renew(ALICE)))
        .await
        .expect("alice may mint for alice")
        .into_inner();

    let ticket = monetize_embed::verify_ticket(
        &minted.ticket,
        &key,
        "gunnar",
        ALICE,
        PURPOSE_RENEW,
        &no_caps(),
        now_ms(),
    )
    .expect("the appliance's own ticket must verify under the key it advertises");

    assert_eq!(ticket.tenant, ALICE);
    assert_eq!(ticket.purpose, PURPOSE_RENEW);
    assert_eq!(ticket.issuer, "gunnar");
    assert_eq!(ticket.product, "gunnar");
    assert_eq!(
        minted.expires_unix_ms, ticket.expires_unix_ms as u64,
        "the expiry in the envelope must be the expiry in the signed claims, or a console \
         reusing a ticket it thinks is fresh gets a refusal it cannot explain"
    );

    // ★ The other half of the enforcement, now from the VERIFIER's side: a
    // genuine ticket, signed by this very appliance, spent on another tenant.
    // Nothing about it is forged; what refuses it is that `verify_ticket`
    // compares the signed tenant against the order's.
    assert!(
        monetize_embed::verify_ticket(
            &minted.ticket,
            &key,
            "gunnar",
            BETA,
            PURPOSE_RENEW,
            &no_caps(),
            now_ms(),
        )
        .is_err(),
        "alice's genuine ticket bought beta a period"
    );
}

/// **Two tickets are two tickets**: the nonce is fresh each time, so a verifier
/// keeping a spent-nonce ring (`monetize_embed::ticket::SeenNonces`) admits the
/// second one. A constant nonce would make a customer's second renewal attempt
/// in ten minutes fail with "already spent", which is the most confusing
/// possible failure on a page whose whole job is to take money.
#[tokio::test]
async fn each_ticket_carries_its_own_nonce() {
    let harness = Harness::new();
    let alice = harness.enrol(ALICE);

    let mut nonces = std::collections::BTreeSet::new();
    for round in 0..4 {
        let caller = harness.caller_for(&alice, &format!("nonce-alice-round-{round:04}"));
        let minted = harness
            .billing()
            .mint_actor_ticket(harness.authed(&caller, renew(ALICE)))
            .await
            .expect("alice may mint for alice")
            .into_inner();
        let ticket: ActorTicket = serde_json::from_slice(&minted.ticket).expect("parses");
        assert!(
            !ticket.nonce.trim().is_empty() && ticket.nonce.len() <= 256,
            "the verifier refuses an empty or oversized nonce outright: {:?}",
            ticket.nonce
        );
        assert!(
            nonces.insert(ticket.nonce.clone()),
            "the appliance minted the same nonce twice ({}); a verifier with a spent-nonce \
             ring would refuse the second renewal a customer attempted",
            ticket.nonce
        );
    }
}

/// **A minted ticket lives well under the cap the VERIFIER enforces.**
///
/// `MAX_LIFE_MS` is ten minutes and it is monetize-embed's rule, not the
/// issuer's: `verify_ticket` refuses a longer one even when the signature is
/// perfect. A ticket is fetched inside the request that spends it, so its life
/// has to cover one round trip and a clock disagreement and nothing more —
/// minting anywhere near the cap would leave no room for either.
#[tokio::test]
async fn a_minted_ticket_lives_well_under_the_verifiers_cap() {
    let harness = Harness::new();
    let alice = harness.enrol(ALICE);
    let caller = harness.caller_for(&alice, "nonce-alice-lifetime-001");

    let minted = harness
        .billing()
        .mint_actor_ticket(harness.authed(&caller, renew(ALICE)))
        .await
        .expect("alice may mint for alice")
        .into_inner();
    let ticket: ActorTicket = serde_json::from_slice(&minted.ticket).expect("parses");

    let life = ticket.expires_unix_ms - ticket.issued_unix_ms;
    assert!(
        life > 0,
        "a ticket that expires before it is issued is refused"
    );
    assert!(
        life * 4 <= MAX_LIFE_MS,
        "a ticket lives {life} ms against a cap of {MAX_LIFE_MS} ms. Minting near the cap \
         leaves no room for the clock skew the verifier also allows"
    );
}

// ───────────────────────────────────────────────────────────────────────────
// ★ The load-bearing one: a lapsed tenant can still pay
// ───────────────────────────────────────────────────────────────────────────

/// ★ **A Suspended tenant can still mint a ticket, which is the only reason
/// self-service renewal works at all.**
///
/// A renewal is what a customer does when the box is full, suspended, or down.
/// A design in which the tenant must be paid-up before they can pay cannot
/// serve the case it exists for.
///
/// Today that holds because gunnar's entitlement gate is the SSH/git door and
/// ONLY the SSH/git door: `gunnar_ssh::PlanGate` is consulted by
/// `gunnar_ssh::Dispatch::authorise_service`, and the metered implementation
/// (`gunnar_server::entitlement::EntitlementSlot`) is wired in exactly twice,
/// both times through `gunnar_ssh::Control::with_plan_gate`. The gRPC control
/// plane has no equivalent — `gunnar-grpc`'s only interceptor is `AuthGate` —
/// so a Suspended namespace authenticates and is served here exactly as a Paid
/// one is.
///
/// **That is an accident of where the gate was put, and this test is what turns
/// it into a decision.** Nothing else in the tree connects "the gRPC plane has
/// no entitlement gate" to "a customer can renew". Somebody adding a plan check
/// to the gRPC plane — a reasonable-sounding thing to want — would take
/// self-service renewal away silently, and the first report would be a customer
/// who cannot pay and a box that will not let them.
///
/// The fact is seeded and then READ BACK through the very seam method the
/// box's own readers use (`ControlPlane::entitlement_get`, which is what
/// `Usage.Read` asks for a namespace's allowance), so the test cannot pass
/// because the suspension never landed.
#[tokio::test]
async fn a_lapsed_tenant_can_still_mint_a_ticket() {
    let harness = Harness::new();
    let key = advertised_key(&harness).await;
    let alice = harness.enrol(ALICE);

    harness.plane.set_entitlement_fact(EntitlementFact {
        namespace: ALICE.to_owned(),
        plan: "small".to_owned(),
        state: EntitlementState::Suspended as i32,
        paid_until_unix_ms: Some(1),
        caps: [("pack_bytes".to_owned(), 7_000_u64)].into_iter().collect(),
        source: "operator".to_owned(),
        signature: Vec::new(),
        // The replay stamp. This fixture is about the ENTITLEMENT LADDER — can
        // a lapsed tenant still mint a ticket — and not about replay, so the
        // fact is seeded unstamped, which is what a fact from before the stamp
        // existed looks like. `set_entitlement_fact` is the test double's own
        // seam and verifies nothing, so an unstamped fact is a legitimate one
        // to hand it.
        issued_unix_ms: None,
        issued_signature: Vec::new(),
    });

    // The suspension is real on this box, read through the same seam call
    // `Usage.Read` makes. Without this the test could pass on a fact that was
    // never stored, and would then be proving nothing at all.
    let held = harness
        .plane
        .entitlement_get(ALICE)
        .expect("the box can read its own facts")
        .expect("alice has one");
    assert_eq!(held.state, EntitlementState::Suspended as i32);
    assert_eq!(held.caps.get("pack_bytes"), Some(&7_000));

    // And she can still buy her way back.
    let caller = harness.caller_for(&alice, "nonce-alice-lapsed-renews-1");
    let minted = harness
        .billing()
        .mint_actor_ticket(harness.authed(&caller, renew(ALICE)))
        .await
        .expect(
            "a Suspended tenant was refused a ticket. Self-service renewal is now dead: the \
             only way back onto a plan is through an operator",
        )
        .into_inner();

    monetize_embed::verify_ticket(
        &minted.ticket,
        &key,
        "gunnar",
        ALICE,
        PURPOSE_RENEW,
        &no_caps(),
        now_ms(),
    )
    .expect("a lapsed tenant's ticket is an ordinary ticket");
}

// ───────────────────────────────────────────────────────────────────────────
// The rest of the refusals
// ───────────────────────────────────────────────────────────────────────────

/// **A key nobody enrolled gets no ticket**, and is told nothing that
/// distinguishes it from a bad signature.
#[tokio::test]
async fn an_unenrolled_caller_is_refused() {
    let harness = Harness::new();
    let stranger = mint_key();

    // The gate itself will not produce a caller for this key, which is where
    // the refusal really happens.
    let credential =
        sign_credential_for(&stranger, now_ms(), "nonce-stranger-00001", "Admin.Health")
            .expect("sign");
    let mut metadata = tonic::metadata::MetadataMap::new();
    metadata.insert(
        CREDENTIAL_METADATA_KEY,
        credential.to_header_value().parse().expect("ascii"),
    );
    assert!(
        AuthGate::new(Arc::clone(&harness.ctx.control), harness.ctx.config.auth)
            .authenticate_bound(&metadata, now_ms(), Some("Admin.Health"))
            .is_err(),
        "an unenrolled key authenticated"
    );

    // And with no verified caller attached at all — a service mounted without
    // the gate in front of it — the handler itself refuses rather than
    // assuming an admin.
    let before = harness.plane.actor_tickets_signed();
    let refused = harness
        .billing()
        .mint_actor_ticket(Request::new(renew(ALICE)))
        .await
        .expect_err("no credential, no ticket");
    assert_eq!(refused.code(), Code::Unauthenticated);
    assert_eq!(harness.plane.actor_tickets_signed(), before);

    let refused = harness
        .billing()
        .appliance_identity(Request::new(ApplianceIdentityRequest {}))
        .await
        .expect_err("no credential, no key");
    assert_eq!(refused.code(), Code::Unauthenticated);
}

/// **`ApplianceIdentity` is operator configuration, and a tenant admin is not
/// an operator.**
///
/// The key it serves is public and keeping it back protects nothing — the
/// comment in `billing.proto` says exactly that, so the gate is not mistaken
/// for protecting key material. It is server `Admin` because the one person who
/// needs the string is the one wiring this appliance to a monetize.
#[tokio::test]
async fn appliance_identity_takes_a_server_admin_and_owning_a_namespace_is_not_one() {
    let harness = Harness::new();
    let alice = harness.enrol(ALICE);
    let caller = harness.caller_for(&alice, "nonce-alice-wants-the-key");

    // alice is intrinsically admin of `alice`. That is not a server grant.
    let refused = harness
        .billing()
        .appliance_identity(harness.authed(&caller, ApplianceIdentityRequest {}))
        .await
        .expect_err("owning one namespace must not answer a server-scoped question");
    assert_eq!(refused.code(), Code::PermissionDenied);

    harness.plane.grant_server(ALICE, GrantLevel::Admin);
    let caller = harness.caller_for(&alice, "nonce-alice-is-operator-1");
    harness
        .billing()
        .appliance_identity(harness.authed(&caller, ApplianceIdentityRequest {}))
        .await
        .expect("a server admin may read it");
}

/// **A purpose this appliance does not mint is refused at the mint**, rather
/// than being signed and refused at the till.
///
/// `verify_ticket` compares the purpose, so a ticket minted for an invented
/// verb would be rejected on the other side anyway — but rejected as a
/// signature failure the customer cannot act on, from a box that had already
/// signed something it should not have.
#[tokio::test]
async fn a_purpose_this_appliance_does_not_mint_is_refused() {
    let harness = Harness::new();
    let alice = harness.enrol(ALICE);
    let caller = harness.caller_for(&alice, "nonce-alice-invents-verb");

    let before = harness.plane.actor_tickets_signed();
    let refused = harness
        .billing()
        .mint_actor_ticket(harness.authed(
            &caller,
            MintActorTicketRequest {
                namespace: ALICE.to_owned(),
                // A verb no version of this appliance mints. It was `"order"`
                // until orders became real, which is precisely the reason the
                // purpose travels as a WORD: a build that predates a verb
                // refuses it instead of signing something whose meaning it does
                // not know.
                purpose: "refund".to_owned(),
                caps: no_caps(),
            },
        ))
        .await
        .expect_err("this build mints two verbs and `refund` is neither");
    assert_eq!(refused.code(), Code::InvalidArgument);
    assert_eq!(harness.plane.actor_tickets_signed(), before);

    // An EMPTY purpose is the default and means renew: a console that never
    // learned the field still works, and the ticket still says the word.
    let caller = harness.caller_for(&alice, "nonce-alice-omits-purpose");
    let minted = harness
        .billing()
        .mint_actor_ticket(harness.authed(
            &caller,
            MintActorTicketRequest {
                namespace: ALICE.to_owned(),
                purpose: String::new(),
                caps: no_caps(),
            },
        ))
        .await
        .expect("an omitted purpose is renewal, the purpose that predates the field")
        .into_inner();
    let ticket: ActorTicket = serde_json::from_slice(&minted.ticket).expect("parses");
    assert_eq!(ticket.purpose, PURPOSE_RENEW);
}

// ───────────────────────────────────────────────────────────────────────────
// Cancel: the tenant's own unpaid order, or a refund — monetize's verb
// ───────────────────────────────────────────────────────────────────────────

/// monetize's word for the ticket `Purchase.Cancel` and `Purchase.Abandon`
/// spend (monetize `feat/thin-ui-2026-10-04`, `monetize_embed::PURPOSE_CANCEL`).
/// The literal: the published `monetize-embed` this tree resolves predates the
/// constant.
const CANCEL: &str = "cancel";

fn cancel(namespace: &str) -> MintActorTicketRequest {
    MintActorTicketRequest {
        namespace: namespace.to_owned(),
        purpose: CANCEL.to_owned(),
        caps: no_caps(),
    }
}

/// ★ **A cancel ticket is minted for the tenant's own namespace, names no caps,
/// and is no other verb's ticket** (2026-10-04: monetize scopes Status/Abandon
/// by tenant and actor ticket and adds Cancel; a person cancelling their OWN
/// unpaid order needs no more than ordering it took — the account admin's
/// `Admin` on the namespace, the line every purpose here shares).
#[tokio::test]
async fn a_cancel_ticket_is_minted_names_no_caps_and_is_no_other_verbs() {
    let harness = Harness::new();
    let key = advertised_key(&harness).await;
    let alice = harness.enrol(ALICE);
    let caller = harness.caller_for(&alice, "nonce-alice-cancels");

    let minted = harness
        .billing()
        .mint_actor_ticket(harness.authed(&caller, cancel(ALICE)))
        .await
        .expect("the account admin may mint a cancel ticket for their own namespace")
        .into_inner();
    let ticket = monetize_embed::verify_ticket(
        &minted.ticket,
        &key,
        "gunnar",
        ALICE,
        CANCEL,
        &no_caps(),
        now_ms(),
    )
    .expect("the ticket this appliance minted must verify as a cancel");
    assert!(ticket.caps.is_empty(), "a cancel ticket covers no amount");
    for other in [PURPOSE_RENEW, PURPOSE_READ] {
        assert!(
            monetize_embed::verify_ticket(
                &minted.ticket,
                &key,
                "gunnar",
                ALICE,
                other,
                &no_caps(),
                now_ms()
            )
            .is_err(),
            "a cancel ticket was spendable as {other:?}"
        );
    }
}

/// A cancel names no caps (refused by name, nothing signed), and is refused
/// for a namespace the caller does not hold (nothing signed).
#[tokio::test]
async fn a_cancel_naming_an_amount_or_another_namespace_is_refused_and_nothing_is_signed() {
    let harness = Harness::new();
    let alice = harness.enrol(ALICE);
    let before = harness.plane.actor_tickets_signed();

    let caller = harness.caller_for(&alice, "nonce-alice-cancels-caps");
    let refused = harness
        .billing()
        .mint_actor_ticket(harness.authed(
            &caller,
            MintActorTicketRequest {
                namespace: ALICE.to_owned(),
                purpose: CANCEL.to_owned(),
                caps: ten_gib(),
            },
        ))
        .await
        .expect_err("a cancel buys nothing, so it names no amount");
    assert_eq!(refused.code(), Code::InvalidArgument, "{refused:?}");

    let caller = harness.caller_for(&alice, "nonce-alice-cancels-beta");
    let refused = harness
        .billing()
        .mint_actor_ticket(harness.authed(&caller, cancel(BETA)))
        .await
        .expect_err("alice must not mint a cancel ticket naming beta");
    assert_eq!(refused.code(), Code::PermissionDenied, "{refused:?}");
    assert_eq!(
        harness.plane.actor_tickets_signed(),
        before,
        "a refused cancel was signed"
    );
}

// ───────────────────────────────────────────────────────────────────────────
// Orders: the same gate, and an AMOUNT the appliance has to vouch for
// ───────────────────────────────────────────────────────────────────────────

/// **An order ticket verifies for the caps it names, and for no others.**
///
/// The cross-repo contract test for the order half, mirroring
/// [`the_ticket_a_tenant_gets_verifies_under_the_key_the_appliance_advertises`]
/// for renewal: minted by the real handler, checked with
/// `monetize_embed::verify_ticket`, which is the function monetize itself
/// calls on `Purchase.Start`.
///
/// ★ The second half is the one that matters. The ticket is genuine, signed by
/// this very appliance, for the right tenant, product and verb — and the order
/// it is presented with asks for a hundred times the room. Without the caps in
/// the signed message the appliance would be vouching for "this human may buy"
/// and whatever held the ticket would choose how much: a blank cheque, signed
/// here.
#[tokio::test]
async fn an_order_ticket_verifies_for_the_caps_it_names_and_not_for_others() {
    let harness = Harness::new();
    let key = advertised_key(&harness).await;
    let alice = harness.enrol(ALICE);
    let caller = harness.caller_for(&alice, "nonce-alice-orders-room-1");

    let minted = harness
        .billing()
        .mint_actor_ticket(harness.authed(&caller, order(ALICE)))
        .await
        .expect("alice may order for alice")
        .into_inner();

    let ticket = monetize_embed::verify_ticket(
        &minted.ticket,
        &key,
        "gunnar",
        ALICE,
        PURPOSE_ORDER,
        &ten_gib(),
        now_ms(),
    )
    .expect("the appliance's own order ticket must verify under the key it advertises");

    assert_eq!(ticket.tenant, ALICE);
    assert_eq!(ticket.purpose, PURPOSE_ORDER);
    assert_eq!(
        ticket.caps,
        ten_gib(),
        "the caps the console asked for must be the caps the appliance signed, meter for \
         meter — a ticket that covers something else is a ticket for another order"
    );
    assert_eq!(
        minted.expires_unix_ms, ticket.expires_unix_ms as u64,
        "the expiry in the envelope must be the expiry in the signed claims"
    );

    // ★ The same genuine ticket, against an order a hundred times the size.
    let hundredfold = Caps::from([("pack_bytes".to_owned(), 1_073_741_824_000u64)]);
    assert!(
        monetize_embed::verify_ticket(
            &minted.ticket,
            &key,
            "gunnar",
            ALICE,
            PURPOSE_ORDER,
            &hundredfold,
            now_ms(),
        )
        .is_err(),
        "alice's genuine ticket for 10 GiB bought a hundred times that. The appliance's \
         signature is now a blank cheque"
    );

    // And still not another tenant's, which is the renewal rule unchanged.
    assert!(
        monetize_embed::verify_ticket(
            &minted.ticket,
            &key,
            "gunnar",
            BETA,
            PURPOSE_ORDER,
            &ten_gib(),
            now_ms(),
        )
        .is_err(),
        "alice's genuine order ticket bought beta room"
    );
}

/// ★ **A caller may not mint an ORDER ticket for a namespace they do not
/// hold, and nothing is signed.**
///
/// The same assertion as
/// [`a_caller_may_not_mint_a_ticket_for_a_namespace_they_do_not_hold`], driven
/// down the order path, because the grant check must not have been weakened for
/// orders on the way — and an order is the request with an amount attached, so
/// a hole here is worth more than a hole in renewal.
///
/// The signature counter is the second half and not decoration: a handler that
/// signed first and checked afterwards would satisfy a test that only read the
/// `Status`, having already put a valid, spendable order ticket for another
/// tenant through whatever sits between here and the caller.
#[tokio::test]
async fn a_caller_may_not_mint_an_order_ticket_for_a_namespace_they_do_not_hold() {
    let harness = Harness::new();
    let alice = harness.enrol(ALICE);
    let caller = harness.caller_for(&alice, "nonce-alice-orders-for-beta");

    let before = harness.plane.actor_tickets_signed();
    let refused = harness
        .billing()
        .mint_actor_ticket(harness.authed(&caller, order(BETA)))
        .await
        .expect_err("alice must not mint an order ticket naming beta");

    assert_eq!(refused.code(), Code::PermissionDenied);
    assert_eq!(
        harness.plane.actor_tickets_signed(),
        before,
        "the appliance SIGNED an order for a namespace the caller does not hold. The grant \
         check must run before the key is touched, on the order path exactly as on renewal"
    );

    // Her own namespace, same request shape: the refusal above is about the
    // namespace and not about alice or about orders being shut.
    let caller = harness.caller_for(&alice, "nonce-alice-orders-for-self");
    harness
        .billing()
        .mint_actor_ticket(harness.authed(&caller, order(ALICE)))
        .await
        .expect("alice holds alice");
}

/// **An order with no caps is refused by name**, rather than minted as a
/// ticket that covers nothing.
///
/// An order names an AMOUNT. A ticket for `order` that covers no meters is a
/// client that has confused the two paths — it wanted `renew` — and the remedy
/// is to say which path it wanted, not to sign something monetize would then
/// refuse as a signature failure the customer cannot act on.
#[tokio::test]
async fn an_order_with_no_caps_is_refused() {
    let harness = Harness::new();
    let alice = harness.enrol(ALICE);
    let caller = harness.caller_for(&alice, "nonce-alice-empty-order-1");

    let before = harness.plane.actor_tickets_signed();
    let refused = harness
        .billing()
        .mint_actor_ticket(harness.authed(
            &caller,
            MintActorTicketRequest {
                namespace: ALICE.to_owned(),
                purpose: PURPOSE_ORDER.to_owned(),
                caps: no_caps(),
            },
        ))
        .await
        .expect_err("an order that names no amount is not an order");
    assert_eq!(refused.code(), Code::InvalidArgument);
    assert!(
        refused.message().contains("caps"),
        "the refusal must name the field the client got wrong: {refused:?}"
    );
    assert_eq!(
        harness.plane.actor_tickets_signed(),
        before,
        "an order covering nothing was signed anyway"
    );
}

/// **A renewal carrying caps is refused by name**, and it is refused HERE
/// rather than at the till.
///
/// `verify_ticket` refuses a renewal ticket that carries caps — a renewal sells
/// another period of what is already held, and the caps come off the stored
/// fact — so minting one would produce a ticket that cannot be spent. The
/// customer would see a signature failure on the page whose whole job is to
/// take money, from a box that had already signed something it should not have.
#[tokio::test]
async fn a_renewal_carrying_caps_is_refused() {
    let harness = Harness::new();
    let alice = harness.enrol(ALICE);
    let caller = harness.caller_for(&alice, "nonce-alice-renew-w-caps");

    let before = harness.plane.actor_tickets_signed();
    let refused = harness
        .billing()
        .mint_actor_ticket(harness.authed(
            &caller,
            MintActorTicketRequest {
                namespace: ALICE.to_owned(),
                purpose: PURPOSE_RENEW.to_owned(),
                caps: ten_gib(),
            },
        ))
        .await
        .expect_err("a renewal names no caps");
    assert_eq!(refused.code(), Code::InvalidArgument);
    assert!(
        refused.message().contains("caps"),
        "the refusal must name the field the client got wrong: {refused:?}"
    );
    assert_eq!(harness.plane.actor_tickets_signed(), before);

    // And the same for the EMPTY purpose, which means renew: a console that
    // predates the purpose field but learned about caps must not slip an
    // amount past the rule by omitting the word.
    let caller = harness.caller_for(&alice, "nonce-alice-omit-purp-caps");
    let refused = harness
        .billing()
        .mint_actor_ticket(harness.authed(
            &caller,
            MintActorTicketRequest {
                namespace: ALICE.to_owned(),
                purpose: String::new(),
                caps: ten_gib(),
            },
        ))
        .await
        .expect_err("an omitted purpose is renewal, and a renewal names no caps");
    assert_eq!(refused.code(), Code::InvalidArgument);
    assert_eq!(harness.plane.actor_tickets_signed(), before);
}

/// An empty namespace is `INVALID_ARGUMENT` and not a grant question.
///
/// `owns_account` is a string equality, and the double and the real table both
/// guard it with `!namespace.is_empty()` — but a request that reached the grant
/// check with an empty scope would be one refactor away from asking "does
/// anybody own ''", and the answer to that must never be able to become yes.
#[tokio::test]
async fn an_empty_namespace_is_refused_before_the_grant_check() {
    let harness = Harness::new();
    let alice = harness.enrol(ALICE);
    let caller = harness.caller_for(&alice, "nonce-alice-empty-ns-01");

    let refused = harness
        .billing()
        .mint_actor_ticket(harness.authed(&caller, renew("")))
        .await
        .expect_err("an unnamed tenant is not a tenant");
    assert_eq!(refused.code(), Code::InvalidArgument);
}

/// **A grant is a grant**: a principal with an explicit `Admin` grant on a
/// namespace they do not own mints for it, exactly as the owner does.
///
/// The scope check is `Identity::may`, and `owns_account` is one of its two
/// answers. This pins the other, so "gunnar enforces the tenant scope" cannot
/// quietly narrow to "gunnar compares two strings" — an organisation's
/// administrator is a real case, and a renewal path that only ever worked for
/// a principal whose id happened to equal the namespace would fail it.
#[tokio::test]
async fn an_explicit_namespace_admin_may_mint_for_a_namespace_they_do_not_own() {
    let harness = Harness::new();
    let key = advertised_key(&harness).await;
    let admin = harness.enrol("org-admin");
    harness
        .plane
        .grant("org-admin", BETA, "", GrantLevel::Admin)
        .expect("granted");

    let caller = harness.caller_for(&admin, "nonce-orgadmin-mints-01");
    let minted = harness
        .billing()
        .mint_actor_ticket(harness.authed(&caller, renew(BETA)))
        .await
        .expect("an Admin grant on beta is what this RPC asks for")
        .into_inner();

    let ticket = monetize_embed::verify_ticket(
        &minted.ticket,
        &key,
        "gunnar",
        BETA,
        PURPOSE_RENEW,
        &no_caps(),
        now_ms(),
    )
    .expect("verifies");
    assert_eq!(ticket.tenant, BETA);
}

/// ★ **The known exclusion, pinned rather than left to be discovered.**
///
/// `Write` on a namespace is NOT enough. That is a ruling and not an oversight:
/// see the handler's doc comment for the whole of it, including the case it
/// knowingly excludes — an organisation member who holds only `Write` and who
/// may well be the person who actually pays the bill.
///
/// The test exists so that widening it later is a deliberate act with a red
/// test attached, and so that nobody reads `Admin` in the handler as a typo for
/// the level the neighbouring RPCs take.
#[tokio::test]
async fn a_write_grant_is_deliberately_not_enough() {
    let harness = Harness::new();
    let writer = harness.enrol("contributor");
    harness
        .plane
        .grant("contributor", BETA, "", GrantLevel::Write)
        .expect("granted");

    let caller = harness.caller_for(&writer, "nonce-writer-tries-01");
    let before = harness.plane.actor_tickets_signed();
    let refused = harness
        .billing()
        .mint_actor_ticket(harness.authed(&caller, renew(BETA)))
        .await
        .expect_err(
            "Write on a namespace minted a ticket. If that is now intended, this test is the \
             place to say so out loud — the handler's doc comment records the ruling",
        );
    assert_eq!(refused.code(), Code::PermissionDenied);
    assert_eq!(harness.plane.actor_tickets_signed(), before);
}

/// **A red-first instrument, used the way `tests/auth.rs` uses it.**
///
/// `force_may(Some(false))` must make the happy path go red. Without this, a
/// handler that had been hard-wired to allow would satisfy every allow test in
/// this file just as well as a correct one — and the allow tests are what give
/// the refusal tests their meaning.
#[tokio::test]
async fn the_grant_check_is_really_consulted() {
    let harness = Harness::new();
    let alice = harness.enrol(ALICE);
    let caller = harness.caller_for(&alice, "nonce-alice-forced-deny-1");

    harness.plane.force_may(Some(false));
    let refused = harness
        .billing()
        .mint_actor_ticket(harness.authed(&caller, renew(ALICE)))
        .await
        .expect_err("with `may` forced to false nothing may be minted");
    assert_eq!(refused.code(), Code::PermissionDenied);

    harness.plane.force_may(None);
    let caller = harness.caller_for(&alice, "nonce-alice-forced-restored");
    harness
        .billing()
        .mint_actor_ticket(harness.authed(&caller, renew(ALICE)))
        .await
        .expect("the instrument is not sticky");
}

// ───────────────────────────────────────────────────────────────────────────
// A private Gunnar's owner, at its seller (private ownership (c), 2026-10-07)
// ───────────────────────────────────────────────────────────────────────────

/// monetize's word for the ticket a private Gunnar's console presents at the
/// SELLER's monetize (`EstateOwner.Ladder` / `ChangePlan`):
/// `monetize_embed::ticket::PURPOSE_ESTATE`. The literal: the published
/// `monetize-embed` this tree resolves predates the constant.
const ESTATE: &str = "estate";

fn estate(namespace: &str) -> MintActorTicketRequest {
    MintActorTicketRequest {
        namespace: namespace.to_owned(),
        purpose: ESTATE.to_owned(),
        caps: no_caps(),
    }
}

/// ★ **The estate's own gunnar vouches for its owner at the seller**: an
/// `estate` ticket is minted for the namespace the caller is `Admin` of — on
/// a sold estate the order's account, whose principal id IS its namespace —
/// names no caps, verifies under the key this appliance advertises (the one
/// the seller recorded at install) as an `estate` ticket for that tenant, and
/// is no other verb's ticket.
#[tokio::test]
async fn an_estate_ticket_is_minted_for_the_owners_own_namespace_and_is_no_other_verbs() {
    let harness = Harness::new();
    let key = advertised_key(&harness).await;
    let alice = harness.enrol(ALICE);
    let caller = harness.caller_for(&alice, "nonce-alice-at-the-seller");
    let minted = harness
        .billing()
        .mint_actor_ticket(harness.authed(&caller, estate(ALICE)))
        .await
        .expect("the owner may mint an estate ticket for their own namespace")
        .into_inner();
    let ticket = monetize_embed::verify_ticket(
        &minted.ticket,
        &key,
        "gunnar",
        ALICE,
        ESTATE,
        &no_caps(),
        now_ms(),
    )
    .expect("the ticket this appliance minted must verify as an estate ticket");
    assert!(
        ticket.caps.is_empty(),
        "an estate ticket covers no amount: the seller prices a plan change itself"
    );
    for other in [PURPOSE_RENEW, PURPOSE_READ, PURPOSE_ORDER] {
        assert!(
            monetize_embed::verify_ticket(
                &minted.ticket,
                &key,
                "gunnar",
                ALICE,
                other,
                &no_caps(),
                now_ms()
            )
            .is_err(),
            "an estate ticket was spendable as {other:?}"
        );
    }
    assert!(
        monetize_embed::verify_ticket(
            &minted.ticket,
            &key,
            "gunnar",
            BETA,
            ESTATE,
            &no_caps(),
            now_ms()
        )
        .is_err(),
        "an estate ticket for alice must not speak for beta"
    );
}

/// An estate ticket names no caps, and is refused for a namespace the caller
/// does not hold — nothing is signed either way.
#[tokio::test]
async fn an_estate_ticket_naming_an_amount_or_another_namespace_is_refused_and_nothing_is_signed() {
    let harness = Harness::new();
    let alice = harness.enrol(ALICE);
    let before = harness.plane.actor_tickets_signed();
    let caller = harness.caller_for(&alice, "nonce-alice-estate-caps");
    let refused = harness
        .billing()
        .mint_actor_ticket(harness.authed(
            &caller,
            MintActorTicketRequest {
                namespace: ALICE.to_owned(),
                purpose: ESTATE.to_owned(),
                caps: ten_gib(),
            },
        ))
        .await
        .expect_err("an estate ticket names no amount");
    assert_eq!(refused.code(), Code::InvalidArgument, "{refused:?}");
    let caller = harness.caller_for(&alice, "nonce-alice-estate-beta");
    let refused = harness
        .billing()
        .mint_actor_ticket(harness.authed(&caller, estate(BETA)))
        .await
        .expect_err("alice must not mint an estate ticket naming beta");
    assert_eq!(refused.code(), Code::PermissionDenied, "{refused:?}");
    assert_eq!(
        harness.plane.actor_tickets_signed(),
        before,
        "a refused estate ticket was signed"
    );
}

// ───────────────────────────────────────────────────────────────────────────
// Members never buy (owner, 2026-10-07: a customer is an ACCOUNT that pays)
// ───────────────────────────────────────────────────────────────────────────

/// `name`, enrolled by `account` the way "Add users" enrols a member: a NEW
/// principal bound to the account, no namespace of its own, `Read` on the
/// account's — with one key, the shape a console's per-person key has.
fn member(harness: &Harness, account: &str, name: &str) -> ssh_key::PrivateKey {
    harness
        .plane
        .enroll_employee(
            account,
            &gunnar_grpc::EmployeeAsk {
                id: name.to_owned(),
                name: name.to_owned(),
                ..Default::default()
            },
        )
        .expect("the account enrols its member");
    let key = mint_key();
    harness
        .plane
        .add_key(
            name,
            &key.public_key().to_openssh().expect("openssh"),
            name,
            None,
            "",
            None,
        )
        .expect("the member's key");
    key
}

/// ★ **A member of an account is never minted a ticket — for its own name or
/// the account's — and is told why in plain words.**
///
/// The walkthrough of 2026-10-07 measured the hole: `ben`, an employee `acme`
/// enrolled, pressed "Grow your account", and this appliance vouched for an
/// order for tenant `ben` — `owns_account("ben", "ben")` is a string equality,
/// and no namespace `ben` exists. He paid, and bought room for nothing. A
/// member uses the account's room; more of it is the account admin's to buy.
#[tokio::test]
async fn a_member_of_an_account_is_refused_a_ticket_by_name() {
    let harness = Harness::new();
    let alice = harness.enrol(ALICE);
    let ben = member(&harness, ALICE, "ben");

    let before = harness.plane.actor_tickets_signed();
    for (nonce, asked) in [
        ("nonce-ben-orders-for-ben", order("ben")),
        ("nonce-ben-renews-ben", renew("ben")),
        ("nonce-ben-reads-ben", read("ben")),
        ("nonce-ben-orders-for-alice", order(ALICE)),
    ] {
        let caller = harness.caller_for(&ben, nonce);
        let refused = harness
            .billing()
            .mint_actor_ticket(harness.authed(&caller, asked))
            .await
            .expect_err("a member buys nothing: the account's admin does");
        assert_eq!(
            refused.code(),
            Code::PermissionDenied,
            "{nonce}: {refused:?}"
        );
        let words = refused.message();
        assert!(
            words.contains("ben is a member of alice") && words.contains("Ask the admin of alice"),
            "{nonce}: the refusal names the account and whom to ask: {words}"
        );
    }
    assert_eq!(
        harness.plane.actor_tickets_signed(),
        before,
        "nothing was signed for a member"
    );

    // The account's own admin still buys for the account.
    let caller = harness.caller_for(&alice, "nonce-alice-still-orders");
    harness
        .billing()
        .mint_actor_ticket(harness.authed(&caller, order(ALICE)))
        .await
        .expect("the account admin orders room for the account");
}

/// ★ **No ticket for a name that has no namespace.** Room is bought FOR a
/// namespace; a ticket for one this box does not hold would sell room that
/// lands nowhere — the walkthrough's "your room is ready" for a namespace
/// that does not exist. Refused by name, after the grant check (so it tells a
/// stranger nothing), for the name's own principal and for a server admin.
#[tokio::test]
async fn a_name_with_no_namespace_is_refused_a_ticket() {
    let harness = Harness::new();
    // A principal whose namespace was never made: `owns_account` holds by
    // string equality, so the grant check alone would let it through.
    let ghost = harness.enrol("ghost");
    let operator = harness.enrol("operator");
    harness.plane.grant_server("operator", GrantLevel::Admin);

    let before = harness.plane.actor_tickets_signed();
    for (key, nonce, asked) in [
        (&ghost, "nonce-ghost-orders", order("ghost")),
        (&ghost, "nonce-ghost-renews", renew("ghost")),
        (
            &operator,
            "nonce-operator-orders-for-nobody",
            order("nobody"),
        ),
    ] {
        let caller = harness.caller_for(key, nonce);
        let refused = harness
            .billing()
            .mint_actor_ticket(harness.authed(&caller, asked))
            .await
            .expect_err("room is bought for a namespace that exists");
        assert_eq!(refused.code(), Code::NotFound, "{nonce}: {refused:?}");
        assert!(
            refused.message().contains("there is no namespace"),
            "{nonce}: the refusal says why: {}",
            refused.message()
        );
    }
    assert_eq!(
        harness.plane.actor_tickets_signed(),
        before,
        "nothing was signed"
    );

    // A stranger asking about a name it does not hold learns nothing: the
    // grant check answers first, the same way for a name that exists and one
    // that does not.
    let caller = harness.caller_for(&ghost, "nonce-ghost-asks-for-nobody");
    let refused = harness
        .billing()
        .mint_actor_ticket(harness.authed(&caller, order("nobody")))
        .await
        .expect_err("not ghost's to ask");
    assert_eq!(refused.code(), Code::PermissionDenied);
}