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

nordisk/gunnar

crates/gunnar-front/tests/one_front_pull.rs

//! **RED-when-broken: the front runs under ONE monetize** (owner 2026-10-10 13:35Z, final:
//! exactly ONE monetize at monetize.ignalina.dk; NO estate runs or embeds one).
//!
//! What this gate holds, K3 v3 (2026-10-11):
//! - the shipped front LINKS no monetize — not the server, not its plugin host, not the
//!   dial-in door — in ANY feature set ([dependencies], [build-dependencies], every feature);
//! - there is no embedded monetize SOURCE: no `src/monetize.rs` serve thread on loopback
//!   :50061, no `src/self_register.rs` (the LEGACY lane monetize DIALS), no
//!   `src/ledger_backup.rs` (the embedded ledger pushed off vm1);
//! - what STAYS is the pull channel only: F4's `recharge.rs` — the front calls the ONE
//!   monetize IN (`SoldEstate.Recharge/Upgrade/TakeMaterial`) over `monetize-proto` and signs
//!   with `monetize::sign`; those two crates are the WIRE of the pull, not a monetize.
//!
//! The parity of the thin client's words with the shop's is held by
//! `tests/shop_pairing_contract.rs` (dev-dependencies only: nothing of it ships).

use std::path::{Path, PathBuf};

const MANIFEST: &str = include_str!("../Cargo.toml");

/// The crates that ARE a per-estate monetize, or only exist to feed or dial one.
/// `monetize` (sign/verify) and `monetize-proto` (the wire) are NOT here: they are
/// how an estate calls the ONE monetize and verifies its signed orders.
const EMBEDS_A_MONETIZE: &[&str] = &[
    "monetize-server",
    "monetize-front",
    "monetize-product-wasm",
    "plugin-door-wire",
];

fn names_in(table: &toml::Table, key: &str) -> Vec<String> {
    table
        .get(key)
        .and_then(|d| d.as_table())
        .map(|d| d.keys().cloned().collect())
        .unwrap_or_default()
}

#[test]
fn the_front_ships_no_embedded_monetize_in_any_build() {
    let m: toml::Table = toml::from_str(MANIFEST).expect("the front's Cargo.toml");
    let deps: Vec<String> = ["dependencies", "build-dependencies"]
        .iter()
        .flat_map(|k| names_in(&m, k))
        .collect();
    let linked: Vec<&String> = deps
        .iter()
        .filter(|d| EMBEDS_A_MONETIZE.contains(&d.as_str()))
        .collect();
    assert!(
        linked.is_empty(),
        "the front links an embedded monetize: {linked:?}"
    );
    let features = m
        .get("features")
        .and_then(|f| f.as_table())
        .cloned()
        .unwrap_or_default();
    for (name, on) in &features {
        let on: Vec<&str> = on
            .as_array()
            .map(|a| a.iter().filter_map(|v| v.as_str()).collect())
            .unwrap_or_default();
        let named: Vec<&&str> = on
            .iter()
            .filter(|f| {
                EMBEDS_A_MONETIZE
                    .iter()
                    .any(|c| f.trim_start_matches("dep:").split('/').next() == Some(c))
            })
            .collect();
        assert!(
            named.is_empty(),
            "feature `{name}` turns an embedded monetize on: {named:?}"
        );
        assert!(!on.iter().any(|f| f.contains("custody-ring")), "feature `{name}` turns the SHOP's custody on — that is the ONE box's, never an estate's: {on:?}");
    }
}

fn src_files() -> Vec<PathBuf> {
    fn walk(dir: &Path, out: &mut Vec<PathBuf>) {
        for e in std::fs::read_dir(dir).expect("src/ reads").flatten() {
            let p = e.path();
            if p.is_dir() {
                walk(&p, out);
            } else if p.extension().is_some_and(|x| x == "rs") {
                out.push(p);
            }
        }
    }
    let mut out = Vec::new();
    walk(&Path::new(env!("CARGO_MANIFEST_DIR")).join("src"), &mut out);
    out
}

#[test]
fn no_monetize_server_socket_and_no_self_register_path_are_built_here() {
    let files = src_files();
    for gone in ["monetize.rs", "self_register.rs", "ledger_backup.rs"] {
        let still: Vec<&PathBuf> = files
            .iter()
            .filter(|p| p.file_name().is_some_and(|n| n == gone))
            .collect();
        assert!(
            still.is_empty(),
            "src/{gone} is an embedded monetize's (ONE monetize): {still:?}"
        );
    }
    for p in &files {
        let text = std::fs::read_to_string(p).expect("a source file reads");
        for said in ["monetize_server::cli", "self_register", ":50061", "50061"] {
            assert!(
                !text.contains(said),
                "{} still says {said:?}: an embedded monetize path (ONE monetize)",
                p.display()
            );
        }
    }
}

/// **The pull channel stays.** F4: a sold estate's front re-admits its own lost box — the
/// front CALLS the ONE monetize in; the shop never calls into an estate (G1). The metered
/// front keeps the wire (`monetize-proto`), the signature (`monetize`) and the ONE pump
/// (`gunnar-repair`); `src/recharge.rs` is F4's and survives K3.
#[test]
fn f4_recharge_and_the_pull_wire_survive_the_monetize_removal() {
    let m: toml::Table = toml::from_str(MANIFEST).expect("the front's Cargo.toml");
    let metered: Vec<String> = m["features"]["metered"]
        .as_array()
        .expect("a metered feature")
        .iter()
        .filter_map(|v| v.as_str().map(String::from))
        .collect();
    for kept in [
        "dep:monetize-proto",
        "dep:monetize",
        "dep:gunnar-repair",
        "nordisk-pairing/installer-wireguard",
    ] {
        assert!(
            metered.iter().any(|f| f == kept),
            "the paid front lost the pull channel's {kept}: {metered:?}"
        );
    }
    assert!(
        Path::new(env!("CARGO_MANIFEST_DIR"))
            .join("src/recharge.rs")
            .is_file(),
        "F4's recharge.rs is gone — the sold front can no longer re-admit its boxes"
    );
    assert!(!metered.iter().any(|f| f.starts_with("dep:monetize-server")), "the paid front links monetize-server: the SoldEstate client is the thin `shop_door`, not the server crate");
}