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

nordisk/holger

holger-estate-front/tests/holgers_grow_doors_need_checked_tickets.rs

//! ★ **Holger's grow doors need a checked, unspent ticket** (owner decision 2026-10-26, item 1;
//! monetize A32-9: every site's grow doors do, there is no knob).
//!
//! The account's grow doors (the window, the grow, the status poll and the cancel) ask the ONE
//! box first whether it verifies Holger's tenant tickets (`Products.List` `tenant_renewal`), as
//! Prolong always did. A shop that does not check them would take an order whose tenant nobody
//! checked (a proto3 shop drops the ticket): there the doors answer 501 with the buyer's words
//! and no order reaches the shop. Where the shop checks them, a fresh ticket is honoured and is
//! then spent: the same ticket presented again buys nothing.
//!
//! The front's REAL provider (`buy::HolgerSite`, mounted by `buy::relay_router`) over the shared
//! relay and the shared fake ONE box (monetize-site-buy `testkit`). The primary's buy doors are
//! stood in on loopback, minting a fresh nonce per ticket, as holger main does. Addresses are
//! `*.mock.invalid`.

#![cfg(all(feature = "console", feature = "metered"))]

use std::collections::BTreeMap;
use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
use std::sync::{Arc, Mutex};

use axum::http::{HeaderMap, StatusCode};
use monetize_site_buy::relay::RENEWAL_NOT_VERIFIED;
use monetize_site_buy::testkit::{FakeOneBox, BUNDLE};
use serde_json::{json, Value};
use tower::ServiceExt as _;

const BEARER: &str = "holger-site-bearer-for-the-ticket-gate";
const PERSON: &str = "Bearer person-token-of-x14";
const CONSOLE: &str = "console-token-for-the-ticket-gate";

fn hex(b: &[u8]) -> String {
    b.iter().map(|x| format!("{x:02x}")).collect()
}

/// The primary's buy doors: `x14` signed in with [`PERSON`]; each ticket carries a fresh nonce,
/// unless `replay` is set, when it hands out the last ORDER ticket again (a ticket presented twice).
async fn primary(fake: FakeOneBox, replay: Arc<AtomicBool>) -> String {
    use axum::routing::{get, post};
    let me = {
        let fake = fake.clone();
        move |h: HeaderMap| {
            let fake = fake.clone();
            async move {
                if h.get("authorization").and_then(|v| v.to_str().ok()) != Some(PERSON) {
                    return axum::Json(json!({"who": "visitor"}));
                }
                let caps = fake
                    .book()
                    .accounts
                    .get("x14")
                    .map(|a| a.caps.clone())
                    .unwrap_or_default();
                axum::Json(
                    json!({"who": "person", "account": "x14", "tenant": "x14", "bound": true, "caps": caps}),
                )
            }
        }
    };
    let nonce = Arc::new(AtomicU64::new(0));
    let last_order = Arc::new(Mutex::new(None::<String>));
    let ticket = move |h: HeaderMap, axum::Json(b): axum::Json<Value>| {
        let (nonce, replay, last_order) = (nonce.clone(), replay.clone(), last_order.clone());
        async move {
            if h.get("authorization").and_then(|v| v.to_str().ok()) != Some(PERSON) {
                return (
                    StatusCode::UNAUTHORIZED,
                    axum::Json(json!({"error": "sign in to buy"})),
                );
            }
            let order = b["purpose"] == "order";
            if order && replay.load(Ordering::SeqCst) {
                if let Some(t) = last_order.lock().unwrap().clone() {
                    return (StatusCode::OK, axum::Json(json!({"ticket": t})));
                }
            }
            let n = nonce.fetch_add(1, Ordering::SeqCst) + 1;
            let t = hex(json!({"subject": "x14", "purpose": b["purpose"], "caps": b["caps"], "nonce": n, "by": "holger-fleet-key"}).to_string().as_bytes());
            if order {
                *last_order.lock().unwrap() = Some(t.clone());
            }
            (StatusCode::OK, axum::Json(json!({"ticket": t})))
        }
    };
    let app = axum::Router::new()
        .route("/-/buy/me", get(me))
        .route("/-/buy/ticket", post(ticket));
    let l = tokio::net::TcpListener::bind("127.0.0.1:0").await.unwrap();
    let at = l.local_addr().unwrap();
    tokio::spawn(async move { axum::serve(l, app).await });
    at.to_string()
}

struct Front {
    app: axum::Router,
    fake: FakeOneBox,
    replay: Arc<AtomicBool>,
}

async fn front() -> Front {
    let fake = FakeOneBox::spawn("holger", BEARER).await;
    fake.add_account("x14", "x14@mock.invalid", &[("disk_bytes", 5 << 30)]);
    let replay = Arc::new(AtomicBool::new(false));
    let door = primary(fake.clone(), replay.clone()).await;
    let feed: BTreeMap<String, String> = [
        (
            holger_estate_front::buy::FEED_MONETIZE_BEARER,
            BEARER.to_string(),
        ),
        (
            holger_estate_front::buy::FEED_CONSOLE_PRODUCT,
            "holger".into(),
        ),
        (holger_estate_front::buy::FEED_FLEET, fake.url()),
        (
            holger_estate_front::buy::FEED_PASSKEY_RP,
            "holger.mock.invalid".into(),
        ),
    ]
    .into_iter()
    .map(|(k, v)| (k.to_string(), v))
    .collect();
    let shop = holger_estate_front::buy::shop_feed_of(&feed)
        .expect("the feed sells here")
        .expect("a usable feed");
    let app = holger_estate_front::buy::relay_router(&shop, &door, Some(CONSOLE.into()))
        .expect("the relay");
    Front { app, fake, replay }
}

impl Front {
    async fn ask(&self, method: &str, uri: &str, body: Option<Value>) -> (StatusCode, Value) {
        let req = axum::http::Request::builder()
            .method(method)
            .uri(uri)
            .header("x-forwarded-for", "203.0.113.9")
            .header("authorization", PERSON);
        let req = match body {
            Some(b) => req
                .header("content-type", "application/json")
                .body(axum::body::Body::from(b.to_string()))
                .unwrap(),
            None => req.body(axum::body::Body::empty()).unwrap(),
        };
        let res = self.app.clone().oneshot(req).await.unwrap();
        let status = res.status();
        let bytes = axum::body::to_bytes(res.into_body(), 1 << 20)
            .await
            .unwrap();
        (
            status,
            serde_json::from_slice(&bytes).unwrap_or(Value::Null),
        )
    }
}

/// ★ RED before the owner's decision: on a ONE box that does not check Holger's tickets, every
/// grow door ran (the window answered 200 and the grow minted a checkout). Now each answers 501
/// with the buyer's words, and no `Purchase` call reaches the shop.
#[tokio::test(flavor = "multi_thread", worker_threads = 4)]
async fn a_holger_grow_door_refuses_where_the_one_box_checks_no_ticket() {
    let f = front().await;
    f.fake.checks_no_tickets();
    for (method, uri, body) in [
        ("GET", "/api/account/subscription/bundles", None),
        (
            "POST",
            "/api/account/subscription/buy-bundle",
            Some(json!({"bundle": BUNDLE})),
        ),
        (
            "GET",
            "/api/account/subscription/status?reference=p-1",
            None,
        ),
        (
            "POST",
            "/api/account/subscription/cancel",
            Some(json!({"reference": "p-1"})),
        ),
    ] {
        let (st, body) = f.ask(method, uri, body).await;
        assert_eq!(st, StatusCode::NOT_IMPLEMENTED, "{method} {uri}: {body}");
        assert_eq!(
            body["error"], RENEWAL_NOT_VERIFIED,
            "{method} {uri}: the buyer's words: {body}"
        );
    }
    let b = f.fake.book();
    assert!(
        b.calls.iter().all(|c| !c.starts_with("Purchase.")),
        "no order reached a shop that checks no ticket: {:?}",
        b.calls
    );
    assert!(b.checkouts.is_empty(), "no Stripe page");
}

/// ★ On a ONE box that checks them, a fresh ticket grows the account; the same ticket presented
/// again is spent, and buys nothing more.
#[tokio::test(flavor = "multi_thread", worker_threads = 4)]
async fn a_checked_unspent_ticket_grows_holgers_account_and_is_then_spent() {
    let f = front().await;
    f.fake.verify_tickets();
    let (st, window) = f
        .ask("GET", "/api/account/subscription/bundles", None)
        .await;
    assert_eq!(st, StatusCode::OK, "{window}");
    let (st, buy) = f
        .ask(
            "POST",
            "/api/account/subscription/buy-bundle",
            Some(json!({"bundle": BUNDLE})),
        )
        .await;
    assert_eq!(st, StatusCode::OK, "a fresh ticket is honoured: {buy}");
    assert_eq!(
        f.fake.book().checkouts_of("bundle").len(),
        1,
        "one Stripe page"
    );
    assert!(f.fake.pay(buy["url"].as_str().unwrap()));
    let (_, done) = f
        .ask(
            "GET",
            &format!(
                "/api/account/subscription/status?reference={}",
                buy["reference"].as_str().unwrap()
            ),
            None,
        )
        .await;
    assert_eq!(
        (done["state"].as_str(), done["phase"].as_str()),
        (Some("paid"), Some("done")),
        "{done}"
    );

    f.replay.store(true, Ordering::SeqCst);
    let (st, again) = f
        .ask(
            "POST",
            "/api/account/subscription/buy-bundle",
            Some(json!({"bundle": BUNDLE})),
        )
        .await;
    assert!(
        !st.is_success(),
        "a spent ticket buys nothing: {st} {again}"
    );
    assert_eq!(
        f.fake.book().checkouts_of("bundle").len(),
        1,
        "still one Stripe page"
    );
    let spent = f
        .fake
        .book()
        .tickets
        .iter()
        .filter(|(c, _)| c == "Purchase.Start")
        .count();
    assert_eq!(
        spent, 2,
        "the shop saw both: it honoured the first and refused the second"
    );
}