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

nordisk/monetize

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:?}"),
    }
}