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/sweep_buy_flow.rs

//! ★ **The shared buy flow on Holger's site, end to end, and never a call to the ONE box by
//! name** (lane TEST-SWEEP-PRODUCTS, 2026-10-24): sign-up (free and paid), pay, welcome, grow
//! and cancel, each through the front's REAL provider (`buy::HolgerSite`, mounted by
//! `buy::relay_router`) over the shared relay, against the shared fake ONE box and fake Stripe
//! (monetize-site-buy `testkit`). The primary's buy doors are stood in on loopback exactly as
//! `holgers_site_runs_the_shared_buy_flow.rs` stands them in.
//!
//! What "never a call to `monetize.ignalina.dk`" means here, measured and not assumed: the ONE
//! box the site is fed is the fake on loopback, every shop call of the flow is in the fake's
//! book (so the flow did call a ONE box, and it was this one), every way back and every page
//! the site handed out is on Holger's own origin, and no address the flow produced or recorded
//! names the real ONE box. Addresses are `*.mock.invalid`.
//!
//! The fake's `Purchase.Cancel` closes an unpaid order and refuses a paid one (testkit); its
//! `EstateOrder.Cancel` answers UNIMPLEMENTED, so that leg proves the relay carries the cancel
//! and answers the refusal in words. A visitor's cancel stops at the door.

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

use std::collections::BTreeMap;
use std::sync::{Arc, Mutex};

use axum::http::{HeaderMap, StatusCode};
use monetize_site_buy::testkit::{
    FakeOneBox, BUNDLE, BUNDLE_BYTES, FREE_BYTES, FREE_ROW, PAID_ROW,
};
use serde_json::{json, Value};
use tower::ServiceExt as _;

const BEARER: &str = "holger-site-bearer-for-the-sweep";
const PERSON: &str = "Bearer person-token-of-x14";
const CONSOLE: &str = "console-token-for-the-sweep";
const ORIGIN: &str = "https://holger.mock.invalid";
/// The real ONE box's name: it must appear nowhere in the flow.
const THE_ONE_BOX: &str = "ignalina";

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

/// The primary's buy doors, as holger main answers them (the same stand-in as the sibling test).
async fn primary(fake: FakeOneBox) -> 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 account = {
        let fake = fake.clone();
        move |h: HeaderMap,
              axum::extract::Query(q): axum::extract::Query<BTreeMap<String, String>>| {
            let fake = fake.clone();
            async move {
                if h.get("authorization").and_then(|v| v.to_str().ok())
                    != Some(&format!("Bearer {CONSOLE}"))
                {
                    return (
                        StatusCode::UNAUTHORIZED,
                        axum::Json(json!({"error": "the console asks this"})),
                    );
                }
                let held = fake
                    .book()
                    .accounts
                    .contains_key(q.get("user").map(String::as_str).unwrap_or(""));
                (
                    StatusCode::OK,
                    axum::Json(json!({"account": if held { "no_passkey" } else { "none" }})),
                )
            }
        }
    };
    let app = axum::Router::new()
        .route("/-/buy/me", get(me))
        .route("/-/buy/account", get(account))
        .route(
            "/-/buy/ticket",
            post(|h: HeaderMap, axum::Json(b): axum::Json<Value>| 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"})));
                }
                // A fresh nonce per ticket, as holger main's: the ONE box spends each once.
                static NONCE: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
                let nonce = NONCE.fetch_add(1, std::sync::atomic::Ordering::SeqCst);
                let t = json!({"subject": "x14", "purpose": b["purpose"], "caps": b["caps"], "nonce": nonce, "by": "holger-fleet-key"});
                (StatusCode::OK, axum::Json(json!({"ticket": hex(t.to_string().as_bytes())})))
            }),
        );
    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()
}

/// The site under test: the front's relay from its feed, the fake ONE box it is fed, and every
/// answer body the site gave (scanned at the end for the real ONE box's name).
struct Site {
    app: axum::Router,
    fake: FakeOneBox,
    said: Arc<Mutex<Vec<String>>>,
}

impl Site {
    async fn up() -> Site {
        let fake = FakeOneBox::spawn("holger", BEARER).await;
        // A ONE box that checks Holger's tickets: its grow doors need them (owner 2026-10-26).
        fake.verify_tickets();
        let door = primary(fake.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();
        assert!(
            !fake.url().contains(THE_ONE_BOX),
            "the site is fed the fake, on loopback: {}",
            fake.url()
        );
        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");
        Site {
            app,
            fake,
            said: Arc::new(Mutex::new(Vec::new())),
        }
    }

    async fn ask(
        &self,
        method: &str,
        uri: &str,
        auth: Option<&str>,
        body: Option<Value>,
    ) -> (StatusCode, Value) {
        let mut req = axum::http::Request::builder()
            .method(method)
            .uri(uri)
            .header("x-forwarded-for", "203.0.113.9");
        if let Some(a) = auth {
            req = req.header("authorization", a);
        }
        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();
        self.said
            .lock()
            .unwrap()
            .push(String::from_utf8_lossy(&bytes).into_owned());
        (
            status,
            serde_json::from_slice(&bytes).unwrap_or(Value::Null),
        )
    }

    /// A visitor signs up for `plan`, pays on the fake Stripe, and is welcomed on Holger's own
    /// origin; the account is made only then. Returns the sign-up session.
    async fn sign_up_pay_and_be_welcomed(
        &self,
        user: &str,
        plan: &str,
        mails_before: usize,
    ) -> String {
        let plan = format!("holger/{plan}");
        let (st, started) = self.ask("POST", "/auth/signup/start", None, Some(json!({"username": user, "email": format!("{user}@mock.invalid"), "plan": plan, "next": "/"}))).await;
        assert_eq!(st, StatusCode::OK, "{started}");
        let url = started["url"].as_str().unwrap().to_owned();
        let session = url.rsplit('/').next().unwrap().to_owned();
        let (_, pending) = self
            .ask(
                "GET",
                &format!("/auth/signup/status?session={session}"),
                None,
                None,
            )
            .await;
        assert_eq!(
            pending["state"], "pending",
            "nothing is made before the payment clears: {pending}"
        );
        assert!(
            !self.fake.book().accounts.contains_key(user),
            "no account before the payment"
        );
        assert!(self.fake.pay(&url), "the fake Stripe page is paid");
        let welcome = self
            .fake
            .book()
            .checkout(&url)
            .expect("the checkout")
            .return_url();
        assert!(
            welcome.starts_with(&format!("{ORIGIN}/welcome?session={session}")),
            "the way back from Stripe is Holger's own welcome: {welcome}"
        );
        let (st, made) = self
            .ask(
                "GET",
                &format!("/auth/signup/status?session={session}"),
                None,
                None,
            )
            .await;
        assert_eq!(st, StatusCode::OK, "{made}");
        assert_eq!(made["state"], "created", "{made}");
        assert_eq!(
            made["invite_url"],
            format!("/login?signup={session}&user={user}"),
            "the primary holds the account, no passkey yet: {made}"
        );
        assert_eq!(
            self.fake.book().mails.len(),
            mails_before + 1,
            "one sign-in mail, at account creation"
        );
        session
    }

    /// Nothing in the flow named the real ONE box, and the ONE box that WAS called is the fake.
    fn assert_nothing_went_to_the_one_box_by_name(&self) {
        let b = self.fake.book();
        assert!(!b.calls.is_empty(), "the flow called a ONE box");
        assert!(b.bearers.iter().all(|x| x == &format!("Bearer {BEARER}")), "every call carried the site's own bearer, so every call was the site's to the fake: {:?}", b.bearers);
        let mut seen: Vec<String> = b
            .urls
            .iter()
            .map(|(call, url)| format!("{call}: {url}"))
            .collect();
        seen.extend(b.checkouts.iter().flat_map(|c| {
            [
                format!("success: {}", c.success_url),
                format!("cancel: {}", c.cancel_url),
                format!("page: {}", c.url()),
            ]
        }));
        for s in &seen {
            assert!(
                !s.contains(THE_ONE_BOX),
                "a way back or a page named the real ONE box: {s}"
            );
            if let Some(url) = s.split_once(": ").map(|(_, u)| u) {
                if url.starts_with("https://holger.") || url.starts_with("https://stripe.") {
                    assert!(
                        url.starts_with(ORIGIN)
                            || url.starts_with(monetize_site_buy::testkit::STRIPE),
                        "a page off Holger's origin and off the fake Stripe: {url}"
                    );
                }
            }
        }
        for said in self.said.lock().unwrap().iter() {
            assert!(
                !said.contains(THE_ONE_BOX),
                "the site named the real ONE box in an answer: {said}"
            );
        }
    }
}

#[tokio::test(flavor = "multi_thread", worker_threads = 4)]
async fn sweep_free_and_paid_sign_ups_pay_and_are_welcomed_on_holgers_own_origin() {
    let site = Site::up().await;
    let (st, plans) = site.ask("GET", "/auth/signup/ladder", None, None).await;
    assert_eq!(st, StatusCode::OK, "{plans}");
    assert_eq!(plans["plans"][0]["id"], format!("holger/{FREE_ROW}"));
    assert_eq!(plans["plans"][1]["id"], format!("holger/{PAID_ROW}"));
    let (st, name) = site
        .ask("GET", "/auth/signup/name?u=freya", None, None)
        .await;
    assert_eq!(
        (st, name["free"].as_bool()),
        (StatusCode::OK, Some(true)),
        "{name}"
    );

    site.sign_up_pay_and_be_welcomed("freya", FREE_ROW, 0).await;
    {
        let b = site.fake.book();
        let c = &b.checkouts[0];
        assert!(
            c.setup && c.amount_minor == 0,
            "the free account is Stripe's 0 kr card check: {c:?}"
        );
        assert_eq!(b.accounts["freya"].caps["disk_bytes"], FREE_BYTES);
    }
    site.sign_up_pay_and_be_welcomed("paula", PAID_ROW, 1).await;
    {
        let b = site.fake.book();
        let c = &b.checkouts[1];
        assert!(
            !c.setup && c.amount_minor == monetize_site_buy::testkit::PAID_MINOR,
            "the paid plan is one Stripe page for its amount: {c:?}"
        );
        assert_eq!(
            b.accounts["paula"].caps["disk_bytes"],
            monetize_site_buy::testkit::PAID_BYTES
        );
        assert_eq!(b.checkouts.len(), 2, "two sign-ups, two Stripe pages");
    }
    site.assert_nothing_went_to_the_one_box_by_name();
}

#[tokio::test(flavor = "multi_thread", worker_threads = 4)]
async fn sweep_a_signed_in_person_grows_pays_and_cancels_on_holgers_site() {
    let site = Site::up().await;
    site.fake
        .add_account("x14", "x14@mock.invalid", &[("disk_bytes", 5 << 30)]);

    // Grow: the bundles window, one bought, paid, done.
    let (st, window) = site
        .ask(
            "GET",
            "/api/account/subscription/bundles",
            Some(PERSON),
            None,
        )
        .await;
    assert_eq!(st, StatusCode::OK, "{window}");
    let (st, buy) = site
        .ask(
            "POST",
            "/api/account/subscription/buy-bundle",
            Some(PERSON),
            Some(json!({"bundle": BUNDLE})),
        )
        .await;
    assert_eq!(st, StatusCode::OK, "{buy}");
    assert_eq!(
        buy["after"]["disk_bytes"].as_u64(),
        Some((5 << 30) + BUNDLE_BYTES)
    );
    assert!(site.fake.pay(buy["url"].as_str().unwrap()));
    let (_, done) = site
        .ask(
            "GET",
            &format!(
                "/api/account/subscription/status?reference={}",
                buy["reference"].as_str().unwrap()
            ),
            Some(PERSON),
            None,
        )
        .await;
    assert_eq!(
        (done["state"].as_str(), done["phase"].as_str()),
        (Some("paid"), Some("done")),
        "{done}"
    );

    // Cancel: a second order, not paid, cancelled by its buyer with the `cancel` ticket: closed,
    // its reason the customer's; the paid order cannot be cancelled here; a visitor cancels nothing.
    let (st, again) = site
        .ask(
            "POST",
            "/api/account/subscription/buy-bundle",
            Some(PERSON),
            Some(json!({"bundle": BUNDLE})),
        )
        .await;
    assert_eq!(st, StatusCode::OK, "{again}");
    let reference = again["reference"].as_str().unwrap().to_owned();
    let (st, cancelled) = site
        .ask(
            "POST",
            "/api/account/subscription/cancel",
            Some(PERSON),
            Some(json!({"reference": reference})),
        )
        .await;
    assert_eq!(st, StatusCode::OK, "{cancelled}");
    assert_eq!(
        (
            cancelled["phase"].as_str(),
            cancelled["cancellable"].as_bool()
        ),
        (Some("cancelled"), Some(false)),
        "{cancelled}"
    );
    assert_eq!(
        cancelled["reason"],
        monetize_site_buy::relay::CANCELLED_BY_CUSTOMER,
        "{cancelled}"
    );
    {
        let b = site.fake.book();
        assert!(
            b.calls.iter().any(|c| c == "Purchase.Cancel"),
            "the shop heard the cancel: {:?}",
            b.calls
        );
        let t = &b
            .tickets
            .iter()
            .rev()
            .find(|(c, _)| c == "Purchase.Cancel")
            .expect("a cancel ticket")
            .1;
        assert_eq!(
            serde_json::from_slice::<Value>(t).unwrap()["purpose"],
            "cancel",
            "the primary's cancel ticket vouches for it"
        );
    }
    let (st, refused) = site
        .ask(
            "POST",
            "/api/account/subscription/cancel",
            Some(PERSON),
            Some(json!({"reference": buy["reference"].as_str().unwrap()})),
        )
        .await;
    assert_eq!(
        st,
        StatusCode::CONFLICT,
        "a paid order is not cancelled here: {refused}"
    );
    let (st, _) = site
        .ask(
            "POST",
            "/api/account/subscription/cancel",
            None,
            Some(json!({"reference": "p-1"})),
        )
        .await;
    assert_eq!(st, StatusCode::UNAUTHORIZED, "a visitor cancels nothing");

    // A private instance's renewal stopped: the estate door; the fake's shop does not implement
    // it, and the site answers the refusal in words, never a 200 and never a panic.
    let (st, private) = site.ask("POST", "/auth/private/start", Some(PERSON), Some(json!({"company": "acme", "email": "other@mock.invalid", "front_plan": "f-mini", "main_plan": "m-mini", "main_data_gib": 40}))).await;
    assert_eq!(st, StatusCode::OK, "{private}");
    let (st, stopped) = site
        .ask(
            "POST",
            "/auth/private/cancel",
            Some(PERSON),
            Some(json!({"company": "acme"})),
        )
        .await;
    assert_eq!(st, StatusCode::BAD_GATEWAY, "{stopped}");
    assert!(
        stopped
            .get("error")
            .and_then(Value::as_str)
            .is_some_and(|w| !w.is_empty()),
        "{stopped}"
    );

    assert_eq!(
        site.fake.book().checkouts.len(),
        3,
        "two bundles and one estate: three Stripe pages, all on the fake"
    );
    site.assert_nothing_went_to_the_one_box_by_name();
}