holger-recipe/tests/cargo_install_holger.rs
//! **`cargo install holger` gives you the Holger server** (owner 2026-10-22).
//!
//! The crates.io name `holger` is `server/cli`. These checks hold the package
//! installable with plain cargo (no Build Thing, no xtask, no pre-step), keep the
//! DEFAULT install free of any monetize crate, keep every server variant row
//! reachable as `cargo install holger --features …`, and keep the README saying so.
//! If one goes red, `cargo install holger` (or the line the README gives) broke.
use std::collections::BTreeSet;
use std::path::{Path, PathBuf};
use std::process::Command;
use holger_recipe::variants::SERVER_CLI;
fn repo_root() -> PathBuf {
Path::new(env!("CARGO_MANIFEST_DIR"))
.parent()
.unwrap()
.to_path_buf()
}
fn toml_at(p: &Path) -> toml::Value {
let text = std::fs::read_to_string(p).unwrap_or_else(|e| panic!("read {}: {e}", p.display()));
toml::from_str(&text).unwrap_or_else(|e| panic!("parse {}: {e}", p.display()))
}
fn cli_manifest() -> toml::Value {
toml_at(&repo_root().join("server/cli/Cargo.toml"))
}
/// The package is `holger`, above the old crates.io `holger` 0.2.1, and its
/// `holger` and `holger-server` bins build with the default features alone.
#[test]
fn the_crates_io_name_holger_is_the_server() {
assert_eq!(
SERVER_CLI, "holger",
"the recipe's server package is the crates.io name"
);
let m = cli_manifest();
let pkg = &m["package"];
assert_eq!(pkg["name"].as_str(), Some("holger"));
// `version.workspace = true` (H1: every Holger crate is on the root's workspace version).
let version = match pkg["version"].as_str() {
Some(v) => v.to_string(),
None => {
assert_eq!(
pkg["version"]["workspace"].as_bool(),
Some(true),
"holger's version is its own or the workspace's"
);
toml_at(&repo_root().join("Cargo.toml"))["workspace"]["package"]["version"]
.as_str()
.unwrap()
.to_string()
}
};
let v: Vec<u64> = version.split('.').map(|p| p.parse().unwrap()).collect();
assert!(
v > vec![0, 2, 1],
"holger {v:?} must be above the 0.2.1 already on crates.io"
);
let bins = m["bin"].as_array().unwrap();
// Exactly ONE bin builds by default — `holger` — so a plain build links one binary.
let default_bins: Vec<&str> = bins
.iter()
.filter(|b| b.get("required-features").is_none())
.filter_map(|b| b["name"].as_str())
.collect();
assert_eq!(
default_bins,
["holger"],
"the default build must link `holger` and nothing else"
);
// `holger-server` is the same program under its old name, opt-in.
let alias = bins
.iter()
.find(|b| b["name"].as_str() == Some("holger-server"))
.expect("[[bin]] holger-server");
assert_eq!(
alias["required-features"]
.as_array()
.unwrap()
.iter()
.filter_map(|f| f.as_str())
.collect::<Vec<_>>(),
["holger-server-alias"]
);
// No build script: plain cargo builds it, nothing has to run first.
assert!(!repo_root().join("server/cli/build.rs").exists());
assert!(pkg.get("build").is_none());
}
/// Features that carry money or the shop: opt-in only, never in `default`.
/// The default is the small `server-local` build: no bench, no wasm plugins, no
/// money (owner 2026-10-22). The intake chain IS in it since 2026-10-23 (Holger G0:
/// tier 1's RON configures the steps, so a plain `cargo build` must carry them), and
/// `online` and monetize stay out.
const NEVER_DEFAULT: &[&str] = &[
"metered",
"admit-share",
"meter-guests",
"paired",
"dev-narrate",
"appliance",
"online",
"wasm-plugins",
"allow-meter",
"jobs",
"mirror",
"full",
"testmatrix",
"intel-fetch",
];
#[test]
fn the_default_features_carry_no_money() {
let m = cli_manifest();
let feats = m["features"].as_table().unwrap();
let mut seen = BTreeSet::new();
let mut q: Vec<String> = feats["default"]
.as_array()
.unwrap()
.iter()
.map(|f| f.as_str().unwrap().to_string())
.collect();
while let Some(f) = q.pop() {
if !seen.insert(f.clone()) {
continue;
}
if let Some(sub) = feats.get(&f).and_then(|v| v.as_array()) {
q.extend(
sub.iter()
.filter_map(|s| s.as_str())
.filter(|s| !s.contains('/') && !s.starts_with("dep:"))
.map(String::from),
);
}
}
for f in NEVER_DEFAULT {
assert!(
!seen.contains(*f),
"`{f}` is reached by holger's default features; it must stay opt-in"
);
}
}
/// **The small default** (owner 2026-10-22): a plain `cargo build` / `cargo install
/// holger` is the server you can start and reach over HTTPS with a token, and the raw
/// and the rust (cargo) repositories. **Plus the intake chain** (owner 2026-10-23,
/// Holger G0): `secure_update`, which carries `supply-chain`, so the tier 1 RON's
/// `steps:` work in the plain build. Everything else is opt-in.
#[test]
fn the_default_is_the_small_server() {
let m = cli_manifest();
let default: Vec<&str> = m["features"]["default"]
.as_array()
.unwrap()
.iter()
.filter_map(|f| f.as_str())
.collect();
// H3 on H2 (2026-10-26): the small core is `downstream-core`; `downstream` is the
// on-prem gateway and stays out of the default.
// B4 (A31 7.5): `systemd`, `holger install` / `holger service` through skidbladnir-systemd
// alone, is in the default (server/cli tests/systemd_flavour.rs).
// HOL-MVP-FORMATS + HOL-PRODUCTS (2026-10-30): the twelve MVP formats.
let mut want = vec!["downstream-core"];
want.extend(holger_recipe::variants::MVP_FORMATS);
// HOL-CDS: the server fetches from another Holger (`fetch`) and pushes into a diode folder
// when its RON says so (`export-push`); only the one-way `downstream` leaves `fetch` out.
want.extend([
"secure_update",
"sign",
"sync",
"fetch",
"export-push",
"systemd",
]);
assert_eq!(default, want);
}
/// **One name for the small core, and the extras are named** (H2 #3; HOL-PRODUCTS
/// 2026-10-30): the core is `downstream-core` (server-lib's `downstream`, the bearer and
/// mTLS doors); the old tier names `mini` and `embedded` are gone; `fmt-all` is the old name
/// of `all-formats`; `full` is `all-formats` + `online` + `federation`.
#[test]
fn the_core_has_one_name_and_the_extras_are_named() {
let feats = |m: &toml::Value, f: &str| -> Vec<String> {
m["features"]
.get(f)
.unwrap_or_else(|| panic!("no feature `{f}`"))
.as_array()
.unwrap()
.iter()
.filter_map(|x| x.as_str().map(String::from))
.collect()
};
let cli = cli_manifest();
assert_eq!(feats(&cli, "downstream-core"), ["server-lib/downstream"]);
assert_eq!(
feats(&cli, "downstream").first().map(String::as_str),
Some("downstream-core"),
"the gateway turns the core on"
);
assert_eq!(
feats(&cli, "mvp-server"),
["downstream-core", "fetch", "export-push", "secure_update"]
);
assert!(
!feats(&cli, "downstream").iter().any(|f| f == "fetch"),
"the downstream is one-way: no `fetch`"
);
assert_eq!(feats(&cli, "fmt-all"), ["all-formats"]);
assert_eq!(feats(&cli, "all-formats"), ["server-lib/fmt-all"]);
assert!(feats(&cli, "federation")
.iter()
.any(|f| f == "server-lib/federation"));
for gone in ["mini", "embedded"] {
assert!(
cli["features"].get(gone).is_none(),
"server/cli `{gone}` is retired"
);
}
let lib = toml_at(&repo_root().join("server/lib/Cargo.toml"));
assert_eq!(feats(&lib, "downstream"), ["auth-bearer", "auth-mtls"]);
assert!(
lib["features"].get("embedded").is_none(),
"server-lib `embedded` is retired"
);
}
/// **`online` is the pull client, not OpenPGP** (H2 #3): the CLI's `online` no longer
/// carries `maven-pgp`; `full` names it, and `jobs` is the job queue alone.
#[test]
fn online_carries_no_pgp_and_jobs_carries_no_intake_chain() {
let feats = |m: &toml::Value, f: &str| -> Vec<String> {
m["features"][f]
.as_array()
.unwrap()
.iter()
.filter_map(|x| x.as_str().map(String::from))
.collect()
};
let cli = cli_manifest();
assert!(
!feats(&cli, "online").iter().any(|f| f.contains("pgp")),
"online: {:?}",
feats(&cli, "online")
);
assert!(
feats(&cli, "full").iter().any(|f| f == "maven-pgp"),
"full names maven-pgp"
);
let lib = toml_at(&repo_root().join("server/lib/Cargo.toml"));
let jobs = feats(&lib, "jobs");
assert!(
!jobs
.iter()
.any(|f| f == "supply-chain" || f == "admission" || f == "intake"),
"server-lib jobs: {jobs:?}"
);
// H3 on H2: the queue alone; the APK re-signing key is `pull-apk`'s.
assert!(
jobs.is_empty(),
"server-lib jobs is the job queue alone: {jobs:?}"
);
assert_eq!(
feats(&lib, "pull-apk"),
["jobs", "dep:rsa", "dep:rand_core"]
);
assert!(
feats(&cli, "jobs")
.iter()
.any(|f| f == "server-lib/pull-apk"),
"the upstream rows' jobs keeps the APK pull-through"
);
}
/// The default tree stays small: measured at 290 crates on 2026-10-22 (oden, the small default),
/// 282 on 2026-10-23 before the intake chain joined the default, 316 after it, and 336 with
/// G3a's provenance verifier in the chain (builder-64, `cargo tree -p holger -e normal`,
/// distinct packages), 338 after H3 and 332 after H2's estate cut (HCUT 2026-10-26: no
/// nordisk-twin, appliance-grow or disk-snapshot). The bound leaves room for a few, not for
/// a feature that rode back into `default`.
/// 356 on main with the twelve MVP formats and WG-PSK's edges (HOL-MVP-FORMATS's merge,
/// 2026-10-30).
/// 362 on main fe3e2f75 (2026-10-31, CI-RED; 349 at 8a36e6f8 with the same siblings), every one
/// a real edge of the default server: holger-index-rows (HOL-PERF-2's intake indexes),
/// holger-job-runner and nordisk-heimdall-core with ed25519-dalek, ed25519, signature, sha2
/// 0.10, digest 0.10, block-buffer, crypto-common and generic-array (HOL-SANDBOX-TRAIT: a
/// scan worker's result is verified against its pinned key), and bzip2 with libbz2-rs-sys
/// (holger-inspect and holger-provenance read .bz2 members).
const DEFAULT_TREE_BOUND: usize = 366;
#[test]
fn the_default_tree_stays_small() {
let out = Command::new(env!("CARGO"))
.current_dir(repo_root())
.args(["tree", "-p", "holger", "-e", "normal", "--prefix", "none"])
.output()
.expect("run cargo tree");
assert!(
out.status.success(),
"cargo tree -p holger failed:\n{}",
String::from_utf8_lossy(&out.stderr)
);
let tree = String::from_utf8_lossy(&out.stdout);
let crates: BTreeSet<&str> = tree
.lines()
.map(|l| l.trim_end_matches(" (*)"))
.filter(|l| !l.is_empty())
.collect();
assert!(crates.len() > 50, "an empty tree proves nothing:\n{tree}");
assert!(
crates.len() <= DEFAULT_TREE_BOUND,
"the default `cargo install holger` links {} crates, over the bound of {DEFAULT_TREE_BOUND}: \
something rode back into `default`",
crates.len()
);
}
/// **The owner's check (a):** `cargo tree -p holger -e normal` with the default
/// features lists no crate whose name contains `monetize`.
#[test]
fn the_default_install_links_no_monetize_crate() {
let out = Command::new(env!("CARGO"))
.current_dir(repo_root())
.args(["tree", "-p", "holger", "-e", "normal", "--prefix", "none"])
.output()
.expect("run cargo tree");
assert!(
out.status.success(),
"cargo tree -p holger failed:\n{}",
String::from_utf8_lossy(&out.stderr)
);
let tree = String::from_utf8_lossy(&out.stdout);
assert!(
tree.lines().count() > 50,
"an empty tree proves nothing:\n{tree}"
);
let money: BTreeSet<&str> = tree
.lines()
.filter_map(|l| l.split_whitespace().next())
.filter(|name| name.contains("monetize"))
.collect();
assert!(
money.is_empty(),
"the default `cargo install holger` links {money:?}"
);
}
/// crates.io refuses a path dependency without a version (optional ones too), so
/// every normal and build edge reachable from `holger`, across the sibling
/// checkouts, carries one. Dev-dependencies without a version are stripped on
/// publish and are allowed.
#[test]
fn every_edge_holger_publishes_has_a_version() {
let mut seen = BTreeSet::new();
let mut missing = Vec::new();
let mut q = vec![repo_root().join("server/cli")];
while let Some(dir) = q.pop() {
let dir = dir.canonicalize().unwrap_or(dir);
if !seen.insert(dir.clone()) {
continue;
}
let m = toml_at(&dir.join("Cargo.toml"));
let name = m["package"]["name"].as_str().unwrap_or("?").to_string();
let mut tables: Vec<&toml::value::Table> = Vec::new();
for key in ["dependencies", "build-dependencies"] {
if let Some(t) = m.get(key).and_then(|t| t.as_table()) {
tables.push(t);
}
}
if let Some(targets) = m.get("target").and_then(|t| t.as_table()) {
for t in targets.values() {
for key in ["dependencies", "build-dependencies"] {
if let Some(t) = t.get(key).and_then(|t| t.as_table()) {
tables.push(t);
}
}
}
}
for t in tables {
for (key, spec) in t {
let Some(path) = spec.get("path").and_then(|p| p.as_str()) else {
continue;
};
if spec.get("version").is_none() {
missing.push(format!("{name} -> {key} (path {path})"));
}
let sub = dir.join(path);
if sub.join("Cargo.toml").is_file() {
q.push(sub);
}
}
}
}
assert!(seen.len() > 20, "the walk saw only {} crates", seen.len());
assert!(
missing.is_empty(),
"path dependencies crates.io would refuse (no version):\n{}",
missing.join("\n")
);
}
/// The install chapter (docs/v2/02-install.md) gives the first start. The products'
/// install lines are the README's one table (`rows_equal_cargo_install.rs`).
#[test]
fn the_install_chapter_gives_the_first_start() {
for readme in ["docs/v2/02-install.md"] {
let text = std::fs::read_to_string(repo_root().join(readme)).unwrap();
for must in [
"cargo install holger",
"cargo build --release",
"target/release/holger",
"http://127.0.0.1:18444/healthz",
"holger start --config",
] {
assert!(
text.contains(must),
"{readme} lacks the first start ({must:?})"
);
}
}
}
/// A plain `cargo build` / `cargo build --release` at the repo root builds the server
/// package and nothing else (`default-members`), so a fresh clone gets
/// `target/release/holger` without compiling the whole workspace.
#[test]
fn the_root_build_is_the_server_only() {
let root = toml_at(&repo_root().join("Cargo.toml"));
let dm: Vec<&str> = root["workspace"]["default-members"]
.as_array()
.expect("[workspace] default-members")
.iter()
.filter_map(|v| v.as_str())
.collect();
assert_eq!(dm, ["server/cli"]);
// What cargo itself makes of it: `workspace_default_members` is what a bare
// `cargo build` selects — the `holger` package alone.
let out = Command::new(env!("CARGO"))
.current_dir(repo_root())
.args(["metadata", "--format-version", "1", "--no-deps"])
.output()
.expect("run cargo metadata");
assert!(
out.status.success(),
"cargo metadata failed:\n{}",
String::from_utf8_lossy(&out.stderr)
);
let meta = String::from_utf8_lossy(&out.stdout);
let key = "\"workspace_default_members\":[";
let at = meta
.find(key)
.expect("cargo metadata names the default members")
+ key.len();
let list = &meta[at..at + meta[at..].find(']').unwrap()];
let members: Vec<&str> = list.split(',').filter(|m| !m.is_empty()).collect();
assert_eq!(members.len(), 1, "a bare `cargo build` selects {members:?}");
assert!(
members[0].contains("server/cli#holger@"),
"a bare `cargo build` selects {members:?}"
);
}
/// **Every crate `cargo install holger` needs can be published** (OX-VARIANTS 2026-10-28).
///
/// The README's variant table is `cargo install holger …` (crates.io), and docs/HOLGER-V2.md
/// H1 promises it from crates.io. crates.io serves only the old `holger` 0.2.1 today, and
/// `cargo publish -p holger` refuses while any path dependency of it (optional ones
/// included: every feature of the table must resolve) is `publish = false`. So: walk the
/// path dependencies of `server/cli` (normal and build, every target; dev-dependencies are
/// stripped by a publish) and require each to be publishable.
#[test]
fn every_crate_cargo_install_holger_needs_is_publishable() {
let mut seen = BTreeSet::new();
// The three products of this repository (the console's is held in holger-ui).
let mut queue = vec![
repo_root().join("server/cli"),
repo_root().join("holger-downstream"),
repo_root().join("holger-cli"),
];
let mut bad = Vec::new();
while let Some(dir) = queue.pop() {
let dir = dir
.canonicalize()
.unwrap_or_else(|e| panic!("{}: {e}", dir.display()));
if !seen.insert(dir.clone()) {
continue;
}
let m = toml_at(&dir.join("Cargo.toml"));
let name = m["package"]["name"].as_str().unwrap().to_string();
let publish = m["package"].get("publish");
let off = match publish {
Some(toml::Value::Boolean(b)) => !b,
Some(toml::Value::Array(a)) => a.is_empty(),
_ => false,
};
if off {
bad.push(format!(
"{name} ({}) is `publish = false`",
dir.strip_prefix(repo_root().canonicalize().unwrap())
.unwrap_or(&dir)
.display()
));
}
let mut tables: Vec<&toml::Value> = ["dependencies", "build-dependencies"]
.iter()
.filter_map(|t| m.get(*t))
.collect();
if let Some(targets) = m.get("target").and_then(|t| t.as_table()) {
for t in targets.values() {
tables.extend(
["dependencies", "build-dependencies"]
.iter()
.filter_map(|k| t.get(*k)),
);
}
}
for table in tables {
for (dep, spec) in table.as_table().unwrap() {
if let Some(p) = spec.get("path").and_then(|p| p.as_str()) {
assert!(
spec.get("version").is_some(),
"{name}: path dependency `{dep}` has no version, so it cannot be published"
);
queue.push(dir.join(p));
}
}
}
}
assert!(
seen.len() > 10,
"the walk found the server's crates: {seen:?}"
);
assert!(
bad.is_empty(),
"`cargo publish -p holger` is refused while these stay unpublishable:\n {}",
bad.join("\n ")
);
}
/// The crate directories of `server/cli`'s path closure (normal and build dependencies,
/// every target, optional ones included): the crates `cargo publish -p holger` needs.
fn publish_closure_dirs() -> BTreeSet<PathBuf> {
let mut seen = BTreeSet::new();
let mut queue = vec![repo_root().join("server/cli")];
while let Some(dir) = queue.pop() {
let dir = dir
.canonicalize()
.unwrap_or_else(|e| panic!("{}: {e}", dir.display()));
if !seen.insert(dir.clone()) {
continue;
}
let m = toml_at(&dir.join("Cargo.toml"));
let mut tables: Vec<&toml::Value> = ["dependencies", "build-dependencies"]
.iter()
.filter_map(|t| m.get(*t))
.collect();
if let Some(targets) = m.get("target").and_then(|t| t.as_table()) {
for t in targets.values() {
tables.extend(
["dependencies", "build-dependencies"]
.iter()
.filter_map(|k| t.get(*k)),
);
}
}
for table in tables {
for spec in table.as_table().unwrap().values() {
if let Some(p) = spec.get("path").and_then(|p| p.as_str()) {
queue.push(dir.join(p));
}
}
}
}
seen
}
fn rs_files(dir: &Path, out: &mut Vec<PathBuf>) {
for e in std::fs::read_dir(dir).unwrap_or_else(|e| panic!("{}: {e}", dir.display())) {
let p = e.unwrap().path();
if p.is_dir() {
rs_files(&p, out);
} else if p.extension().is_some_and(|x| x == "rs") {
out.push(p);
}
}
}
/// **A published crate compiles only files inside it** (PUB-HOLGER 2026-10-28).
///
/// `cargo publish` packs a crate's own directory and verifies the package alone, so an
/// `include_str!`/`include_bytes!` that climbs out of the crate (`../../../../distribution/…`)
/// builds in the repository and fails on crates.io: `cargo publish --dry-run -p
/// holger-server-lib` refused `src/sandbox/media.rs` reading
/// `distribution/scan-worker/clamd.conf`. Every library and build-script source of the
/// publish closure is checked; test code (a `tests.rs`, a file under `src/tests/`, or any
/// line after the file's first `#[cfg(test)]`) is not compiled by the verify and is left out.
#[test]
fn every_file_a_published_crate_compiles_in_is_inside_it() {
let mut bad = Vec::new();
for dir in publish_closure_dirs() {
let mut files = Vec::new();
if dir.join("src").is_dir() {
rs_files(&dir.join("src"), &mut files);
}
if dir.join("build.rs").is_file() {
files.push(dir.join("build.rs"));
}
for f in files {
let rel = f.strip_prefix(&dir).unwrap().to_path_buf();
if rel.file_name().is_some_and(|n| n == "tests.rs") || rel.starts_with("src/tests") {
continue;
}
let text = std::fs::read_to_string(&f).unwrap();
for (n, line) in text.lines().enumerate() {
if line.trim_start().starts_with("#[cfg(test)]") {
break;
}
for mac in ["include_str!(\"", "include_bytes!(\""] {
let mut rest = line;
while let Some(at) = rest.find(mac) {
let lit = &rest[at + mac.len()..];
let Some(end) = lit.find('"') else { break };
let target = f.parent().unwrap().join(&lit[..end]);
let inside = target
.canonicalize()
.map(|t| t.starts_with(&dir))
.unwrap_or(false);
if !inside {
bad.push(format!(
"{}:{}: {}",
dir.join(&rel)
.strip_prefix(repo_root().canonicalize().unwrap())
.unwrap_or(&f)
.display(),
n + 1,
&lit[..end]
));
}
rest = &lit[end..];
}
}
}
}
}
assert!(
bad.is_empty(),
"a published crate reads a file outside itself (crates.io cannot build it):\n {}",
bad.join("\n ")
);
}