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

nordisk/holger

server/cli/tests/systemd_flavour.rs

//! **Holger's `systemd` flavour links only the systemd lifecycle** (lane B4, A31 §7.5).
//!
//! `holger install …` and `holger service …` register Holger with systemd. They
//! came from the whole `skidbladnir` crate (77k lines: k8s, vmsuite, korpprov,
//! machine, msi, promote, pack …), none of which a container or an appliance
//! uses. Now they come from `skidbladnir-systemd` alone, behind the `systemd`
//! feature, which the plain `cargo install holger` (the exe flavour) keeps on and
//! the container and appliance builds leave off.

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

fn section<'a>(text: &'a str, name: &str) -> Vec<&'a str> {
    let mut out = Vec::new();
    let mut inside = false;
    for l in text.lines() {
        let t = l.trim();
        if t.starts_with('[') {
            inside = t == name;
            continue;
        }
        if inside && !t.starts_with('#') && !t.is_empty() {
            out.push(t);
        }
    }
    out
}

#[test]
fn the_cli_names_the_lifecycle_crate_behind_a_default_systemd_feature() {
    let deps = section(MANIFEST, "[dependencies]");
    assert!(
        !deps.iter().any(|l| l.starts_with("skidbladnir =")),
        "the whole skidbladnir crate is still a dependency"
    );
    let edge = deps
        .iter()
        .find(|l| l.starts_with("skidbladnir-systemd ="))
        .expect("skidbladnir-systemd is a dependency");
    assert!(edge.contains("optional = true"), "{edge}");
    let features = section(MANIFEST, "[features]");
    let systemd = features
        .iter()
        .find(|l| l.starts_with("systemd ="))
        .expect("a `systemd` feature");
    assert!(systemd.contains("dep:skidbladnir-systemd"), "{systemd}");
    let default = features
        .iter()
        .find(|l| l.starts_with("default ="))
        .expect("default");
    assert!(
        default.contains("\"systemd\""),
        "`cargo install holger` keeps `holger install`: {default}"
    );
}

#[test]
fn the_install_and_service_verbs_are_there_exactly_with_the_feature() {
    let out = std::process::Command::new(env!("CARGO_BIN_EXE_holger"))
        .arg("--help")
        .output()
        .expect("holger --help");
    let help = String::from_utf8_lossy(&out.stdout);
    for verb in ["install", "service"] {
        let listed = help
            .lines()
            .any(|l| l.trim_start().starts_with(&format!("{verb} ")) || l.trim() == verb);
        assert_eq!(
            listed,
            cfg!(feature = "systemd"),
            "`{verb}` in --help with systemd={}:\n{help}",
            cfg!(feature = "systemd")
        );
    }
}

/// The crates of `holger` built with `features` (and no default), by name.
fn tree(features: &str) -> Vec<String> {
    let cargo = std::env::var("CARGO").unwrap_or_else(|_| "cargo".into());
    let out = std::process::Command::new(cargo)
        .args([
            "tree",
            "--locked",
            "--offline",
            "-p",
            "holger",
            "-e",
            "normal",
            "--prefix",
            "none",
            "--no-default-features",
            "--features",
            features,
        ])
        .current_dir(env!("CARGO_MANIFEST_DIR"))
        .output()
        .expect("cargo tree");
    assert!(
        out.status.success(),
        "cargo tree: {}",
        String::from_utf8_lossy(&out.stderr)
    );
    let mut names: Vec<String> = String::from_utf8_lossy(&out.stdout)
        .lines()
        .filter_map(|l| l.split_whitespace().next().map(str::to_string))
        .collect();
    names.sort();
    names.dedup();
    names
}

/// **The container image carries no Skidbladnir** (lane OX-HOLFLAV, A31 §7.3): the
/// Kubernetes flavour is the default without `systemd` (`cargo xtask k8s image`,
/// `K8S_FEATURES`), and its tree holds no crate of the Skidbladnir repository
/// (`skidbladnir`, `skidbladnir-systemd`, the `nordisk-*` estate crates,
/// `plugin-door-wire`) and no D-Bus. The exe flavour (the default) has exactly the
/// lifecycle crate and zbus more. `holger-handler-skidbladnir` is not Skidbladnir: it is
/// holger's own reader of that bundle FORMAT (a `ron_handler`, beside the `fmt-*`
/// formats), with znippy's path classifier.
#[test]
fn the_container_flavour_links_no_skidbladnir() {
    let features = section(MANIFEST, "[features]");
    let default = features
        .iter()
        .find(|l| l.starts_with("default ="))
        .expect("default");
    let all: Vec<&str> = default
        .trim_start_matches("default = [")
        .trim_end_matches(']')
        .split(',')
        .map(|f| f.trim().trim_matches('"'))
        .collect();
    let container: Vec<&str> = all.iter().copied().filter(|f| *f != "systemd").collect();
    // `nordisk-heimdall-*` is Heimdall's job API (lane HOL-SANDBOX-TRAIT), not a Skidbladnir crate.
    let forbidden = |n: &str| {
        n == "skidbladnir"
            || n.starts_with("skidbladnir-")
            || (n.starts_with("nordisk-") && !n.starts_with("nordisk-heimdall-"))
            || n == "plugin-door-wire"
            || n.starts_with("zbus")
    };
    let k8s = tree(&container.join(","));
    let found: Vec<&String> = k8s.iter().filter(|n| forbidden(n)).collect();
    assert!(
        found.is_empty(),
        "the container flavour ({}) links {found:?}",
        container.join(",")
    );
    let exe = tree(&all.join(","));
    let extra: Vec<&String> = exe.iter().filter(|n| forbidden(n)).collect();
    assert!(
        extra.iter().any(|n| *n == "skidbladnir-systemd") && extra.iter().any(|n| *n == "zbus"),
        "the exe flavour has the lifecycle: {extra:?}"
    );
    assert!(
        extra
            .iter()
            .all(|n| *n == "skidbladnir-systemd" || n.starts_with("zbus")),
        "the exe flavour links more of Skidbladnir than the lifecycle: {extra:?}"
    );
}