holger-estate-front/tests/holgers_site_runs_the_shared_buy_flow.rs
//! ★ **Holger's site runs the shared buy flow** (owner 2026-10-23: "make integration tests for
//! shared lib"): the front's REAL provider (`buy::HolgerSite`, mounted by `buy::relay_router`
//! from the front's feed) over the shared relay, against the shared fake ONE box and fake
//! Stripe (monetize-site-buy `testkit`). The primary's buy doors (`/-/buy/me`, `/-/buy/ticket`,
//! `/-/buy/account`) are stood in on loopback with the shapes holger main answers
//! (server/lib `room::buy_me`, `buy_ticket`, `signup_account`).
//!
//! One visitor flow (a paid plan, welcomed on the site) and one signed-in flow (grow the
//! account, then a private instance with no e-mail). Addresses are `*.mock.invalid`.
#![cfg(all(feature = "console", feature = "metered"))]
use std::collections::BTreeMap;
use axum::http::{HeaderMap, StatusCode};
use monetize_site_buy::testkit::{self, FakeOneBox, What, BUNDLE, BUNDLE_BYTES, PAID_ROW};
use serde_json::{json, Value};
use tower::ServiceExt as _;
const BEARER: &str = "holger-site-bearer-for-tests";
const PERSON: &str = "Bearer person-token-of-x14";
const CONSOLE: &str = "console-token-for-tests";
const ORIGIN: &str = "https://holger.mock.invalid";
fn hex(b: &[u8]) -> String {
b.iter().map(|x| format!("{x:02x}")).collect()
}
/// The primary's buy doors, as holger main answers them: `x14` signed in with [`PERSON`],
/// its caps the fake ONE box's; its tickets its own JSON (the fleet key's signature stood in);
/// a paid sign-up's account held once the ONE box made it.
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 front's relay, from its feed as monetize-shop-pairing feeds an operator site.
async fn front() -> (axum::Router, FakeOneBox) {
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();
let shop = holger_estate_front::buy::shop_feed_of(&feed)
.expect("the feed sells here")
.expect("a usable feed");
(
holger_estate_front::buy::relay_router(&shop, &door, Some(CONSOLE.into()))
.expect("the relay"),
fake,
)
}
async fn ask(
app: &axum::Router,
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 = 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),
)
}
#[tokio::test(flavor = "multi_thread", worker_threads = 4)]
async fn a_visitor_buys_a_plan_on_holgers_site_and_is_welcomed_there() {
let (app, fake) = front().await;
let (st, plans) = ask(&app, "GET", "/auth/signup/ladder", None, None).await;
assert_eq!(st, StatusCode::OK, "{plans}");
let plan = format!("holger/{PAID_ROW}");
assert_eq!(plans["plans"][1]["id"], plan);
let (st, started) = ask(&app, "POST", "/auth/signup/start", None, Some(json!({"username": "newbie", "email": "newbie@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 b = fake.book();
assert_eq!(b.checkouts.len(), 1, "one Stripe page");
assert_eq!(
b.checkouts[0].success_url,
format!("{ORIGIN}/welcome?session={{session}}&next=%2F"),
"the way back is Holger's own welcome"
);
assert!(
b.bearers.iter().all(|x| x == &format!("Bearer {BEARER}")),
"every call carries the site's own bearer"
);
}
let (_, pending) = ask(
&app,
"GET",
&format!("/auth/signup/status?session={session}"),
None,
None,
)
.await;
assert_eq!(pending["state"], "pending");
assert!(fake.pay(&url));
let (st, made) = ask(
&app,
"GET",
&format!("/auth/signup/status?session={session}"),
None,
None,
)
.await;
assert_eq!(st, StatusCode::OK, "{made}");
assert_eq!(
made["invite_url"],
format!("/login?signup={session}&user=newbie"),
"the primary holds the account, no passkey yet: {made}"
);
assert_eq!(
fake.book().mails.len(),
1,
"one sign-in mail, at account creation"
);
}
#[tokio::test(flavor = "multi_thread", worker_threads = 4)]
async fn a_signed_in_person_grows_the_account_and_buys_an_instance_on_holgers_site() {
let (app, fake) = front().await;
fake.add_account("x14", "x14@mock.invalid", &[("disk_bytes", 5 << 30)]);
let (st, window) = ask(
&app,
"GET",
"/api/account/subscription/bundles",
Some(PERSON),
None,
)
.await;
assert_eq!(st, StatusCode::OK, "{window}");
let (st, buy) = ask(
&app,
"POST",
"/api/account/subscription/buy-bundle",
Some(PERSON),
Some(json!({"bundle": BUNDLE})),
)
.await;
assert_eq!(st, StatusCode::OK, "{buy}");
assert_eq!(
buy["before"]["disk_bytes"].as_u64(),
Some(5 << 30),
"the held caps are the primary's word"
);
assert_eq!(
buy["after"]["disk_bytes"].as_u64(),
Some((5 << 30) + BUNDLE_BYTES)
);
let order = fake
.book()
.tickets
.iter()
.find(|(c, _)| c == "Purchase.Start")
.unwrap()
.1
.clone();
let order: Value = serde_json::from_slice(&order).unwrap();
assert_eq!(
(
order["purpose"].as_str(),
order["caps"]["disk_bytes"].as_u64()
),
(Some("order"), Some((5 << 30) + BUNDLE_BYTES)),
"the primary's order ticket covers the exact target"
);
assert!(fake.pay(buy["url"].as_str().unwrap()));
let (_, done) = ask(
&app,
"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}"
);
let (st, private) = ask(&app, "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 b = fake.book();
let c = b.checkouts_of("estate")[0].clone();
let What::Estate {
account,
email,
new_account,
..
} = &c.what
else {
panic!("an estate order")
};
assert_eq!(
(account.as_str(), email.as_str(), *new_account),
("x14", "", false),
"signed in: the account's own order, no e-mail"
);
let estate = &b
.tickets
.iter()
.find(|(x, _)| x == "EstateOrder.Start")
.unwrap()
.1;
assert_eq!(
serde_json::from_slice::<Value>(estate).unwrap()["purpose"],
"estate"
);
assert_eq!(b.checkouts.len(), 2, "two buys, two Stripe pages");
}
let (st, _) = ask(&app, "GET", "/api/account/subscription/bundles", None, None).await;
assert_eq!(st, StatusCode::UNAUTHORIZED, "a visitor grows nothing");
let _ = testkit::STRIPE;
}