crates/monetize-site-buy/tests/ladder_sells.rs
//! ★ **A kept private ladder is the shop's word that it sells private instances** (LADDER-2
//! follow-up, gunnar-ui `a_refused_ticket_is_403_in_plain_words_through_this_consoles_router`,
//! red on gunnar-ui main 37f2601 and flaky since). The private ladder page answers from the kept
//! ladder (monetize 5cddbbfd) without asking `EstateOrder.Sells`, so the sells word was never
//! kept; a box that then refused the next call (PermissionDenied on the buyer's ticket) left
//! the private doors saying "A private Gunnar is not sold here." (404) instead of the buyer's
//! sentence (403). A ladder the box answered for a new estate is its word that it sells them.
use std::net::SocketAddr;
use std::sync::Arc;
use std::time::Duration;
use axum::http::StatusCode;
use monetize_site_buy::testkit::{FakeOneBox, Shape, TestSite};
use monetize_site_buy::{rules, Relay};
use serde_json::Value;
use tower::ServiceExt;
const BEARER: &str = "site-bearer-for-ladder-sells";
async fn ask(router: &axum::Router, method: &str, path: &str, who: Option<&str>, body: Option<Value>) -> (StatusCode, Value) {
let mut req = axum::http::Request::builder().method(method).uri(path).header("x-forwarded-for", "203.0.113.4").header("content-type", "application/json");
if let Some(u) = who {
req = req.header("cookie", format!("gunnar_session={u}"));
}
let req = req.body(axum::body::Body::from(body.map(|b| b.to_string()).unwrap_or_default())).unwrap();
let res = router.clone().oneshot(req).await.unwrap();
let st = res.status();
let bytes = axum::body::to_bytes(res.into_body(), 1 << 20).await.unwrap();
(st, serde_json::from_slice(&bytes).unwrap_or(Value::Null))
}
#[tokio::test(flavor = "multi_thread", worker_threads = 4)]
async fn a_kept_private_ladder_keeps_the_shops_word_that_it_sells_instances() {
let fake = FakeOneBox::spawn("gunnar", BEARER).await;
fake.add_account("alice", "alice@mock.invalid", &[]);
fake.add_estate("acme", "alice", "Mini");
let relay = Arc::new(Relay::new(TestSite::over(Shape::Gunnar, &fake), fake.link()));
let router = monetize_site_buy::router(relay.clone());
// The doors are built: the kept ladders are read on their own clock. Wait for the private one.
for _ in 0..100 {
if relay.shop.caches().ladders().kept(monetize_site_buy::ladders::PRIVATE).is_some() {
break;
}
tokio::time::sleep(Duration::from_millis(50)).await;
}
assert!(relay.shop.caches().ladders().kept(monetize_site_buy::ladders::PRIVATE).is_some(), "the private ladder is kept");
let (st, _) = ask(&router, "GET", "/auth/private/ladder", None, None).await;
assert_eq!(st, StatusCode::OK, "the kept ladder answers");
// From now on the box refuses the site's word for who asks.
fake.fail(Some((tonic::Code::PermissionDenied, "REFUSED [estate-unnamed] the ticket names no estate this monetize holds a key for")));
let (st, body) = ask(&router, "POST", "/auth/private/start", Some("alice"), Some(serde_json::json!({"company": "beta", "front_plan": "f-mini", "main_plan": "m-mini", "main_data_gib": 40}))).await;
assert_eq!((st, body["error"].as_str()), (StatusCode::FORBIDDEN, Some(rules::WHO_NOT_CONFIRMED)), "the buyer's sentence, never \"not sold here\": {body}");
let _ = SocketAddr::from(([127, 0, 0, 1], 0));
}