crates/monetize-shop-pairing/src/sale_check/holger_tests.rs
//! **The sale check of a sold Holger estate** (2026-10-17): holger's console is holger-ui's
//! THIN server — the bundle (`/login`, loading `/login.js`) plus a pass-through to holger's
//! own doors — so the check reads holger's `/-/auth/passkey/config`, its
//! `POST /-/auth/passkey/register/start`, and its front door's roster `/-/releases`.
use super::*;
#[allow(unused_imports)]
use crate::test_fixtures::*;
const HOST: &str = "acme.holger.rs";
const INVITE: &str = "0f1e2d3c4b5a69788796a5b4c3d2e1f00f1e2d3c4b5a69788796a5b4c3d2e1f0";
const BEARER: &str = "b7e1a0c9d8f7e6d5c4b3a29180f7e6d5c4b3a29180f7e6d5c4b3a29180f7e6d5";
/// The thin console in front of holger, faked: the bundle's login page and holger's doors.
struct Thin {
repos: Vec<&'static str>,
spent: bool,
asked: std::sync::Mutex<Vec<(String, String, Option<String>)>>,
}
fn thin(repos: Vec<&'static str>) -> Thin {
Thin { repos, spent: false, asked: Default::default() }
}
impl Front for Thin {
fn get(&self, path_and_query: &str, bearer: Option<&str>) -> Result<(u16, String), String> {
self.asked.lock().unwrap().push(("GET".into(), path_and_query.to_string(), bearer.map(str::to_string)));
Ok(match path_and_query {
holger_console::LOGIN_PATH => (200, "<html><title>Holger — sign in</title><script src=\"/login.js\"></script></html>".into()),
holger_console::CONFIG_PATH => (200, serde_json::json!({"enabled": true, "rp_id": HOST, "bootstrap_spent": self.spent}).to_string()),
holger_console::REPOS_PATH => (200, serde_json::json!({"repositories": self.repos.iter().map(|n| serde_json::json!({"name": n, "format": "rust", "packages": 0})).collect::<Vec<_>>(), "releases": []}).to_string()),
// The thin console has no BFF any more.
"/auth/status" | "/api/repositories" | "/api/repos" => (404, "not found".into()),
_ => (404, "not found".into()),
})
}
fn post(&self, path: &str, body: &str, bearer: Option<&str>) -> Result<(u16, String), String> {
self.asked.lock().unwrap().push(("POST".into(), path.to_string(), bearer.map(str::to_string)));
let ask: serde_json::Value = serde_json::from_str(body).map_err(|e| e.to_string())?;
Ok(match path {
holger_console::REGISTER_START_PATH if ask["invite"] == INVITE && ask["display"] == "alice" => (200, r#"{"publicKey":{"challenge":"aG9sZ2Vy"}}"#.into()),
holger_console::REGISTER_START_PATH => (401, "this invite is not valid any more; ask for a new one".into()),
_ => (404, "not found".into()),
})
}
}
#[test]
fn a_holger_front_is_proven_by_holgers_own_doors() {
let front = thin(vec!["crates", "bundles"]);
let proven = judge_for(&holger(), &front, HOST, "alice", BEARER, INVITE).expect("a serving holger front is proven");
assert!(proven.contains("2 (crates, bundles)") && proven.contains(HOST), "{proven}");
let asked = front.asked.lock().unwrap().clone();
let doors: Vec<(&str, &str)> = asked.iter().map(|(m, p, _)| (m.as_str(), p.as_str())).collect();
assert_eq!(doors, [("GET", "/login"), ("GET", "/-/auth/passkey/config"), ("POST", "/-/auth/passkey/register/start"), ("GET", "/-/releases")]);
assert!(asked[..3].iter().all(|(_, _, b)| b.is_none()), "the public doors are asked as the buyer: no bearer");
assert_eq!(asked[3].2.as_deref(), Some(BEARER), "the roster with the console's bearer");
assert!(!proven.contains(INVITE) && !proven.contains(BEARER));
}
#[test]
fn a_holger_front_that_half_serves_is_not_proven() {
let why = judge_for(&holger(), &thin(vec![]), HOST, "alice", BEARER, INVITE).unwrap_err();
assert!(why.contains("/repositories lists nothing") && why.contains(HOST), "{why}");
let spent = Thin { spent: true, ..thin(vec!["crates"]) };
assert!(judge_for(&holger(), &spent, HOST, "alice", BEARER, INVITE).unwrap_err().contains("spent"));
let why = judge_for(&holger(), &thin(vec!["crates"]), HOST, "alice", BEARER, "not-the-invite-0000").unwrap_err();
assert!(why.contains("HTTP 401") && why.contains("register/start"), "{why}");
// A Gunnar sale against a thin holger console fails: there is no /auth/status there.
assert!(judge(&thin(vec!["crates"]), HOST, "alice", BEARER, INVITE).is_err());
}
/// ★ **Holger's first-admin door** (owner 2026-10-22, `site first-link`).
#[test]
fn holgers_first_admin_door_is_open_spent_or_closed() {
use monetize_server::estate_order::FirstDoor;
assert_eq!(first_door_for(&holger(), &thin(vec!["crates"]), HOST, "alice", INVITE), Ok(FirstDoor::Open));
let spent = Thin { spent: true, ..thin(vec!["crates"]) };
assert_eq!(first_door_for(&holger(), &spent, HOST, "alice", INVITE), Ok(FirstDoor::Spent));
match first_door_for(&holger(), &thin(vec!["crates"]), HOST, "alice", "not-the-invite-0000") {
Ok(FirstDoor::Closed(why)) => assert!(why.contains("HTTP 401") && !why.contains("not-the-invite"), "{why}"),
other => panic!("{other:?}"),
}
}