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

nordisk/Skidbladnir

tests/testmatrix_rows.rs

//! **Test-matrix verdict rows for Skíðblaðnir's real surfaces** — this is
//! Skidbladnir's entry into nornir's constellation-wide introspection-coverage
//! gate (nornir #32). Every check asserts on the *actual* return value AND records
//! the verdict as one functional-status row.
//!
//! This follows the ordning-core / korp-collectors reference pattern. With
//! `--features testmatrix` each emit becomes a real
//! `nornir_testmatrix::functional_status` row (nornir's matrix reads them back);
//! without the feature `skidbladnir::functional_status` is a compiled-out
//! `#[inline]` no-op (no nornir dep), so this file is a plain assert-only test in
//! the default build. The `assert!` is the gate; the emit is the observation
//! (nornir's `assert_emit!` doctrine).
//!
//! HONEST STATUS — green is only for what genuinely works offline and is asserted
//! here. Surfaces that need a live host (systemd D-Bus, Redfish BMC), shell out
//! (Terraform `apply`), are unverified against a real peer (the hand-rolled
//! libvirt RPC), need a hypervisor host (KVM boot), or are declared-but-unrendered
//! (the remaining non-systemd/OpenRC/SysV init renderers) are emitted as PARTIAL rows
//! (ok=false) with an honest detail — never a fake green.

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

use skidbladnir::archive::{Archive, TarZstd};
use skidbladnir::install::{self, InstallOp};
use skidbladnir::layout::Layout;
use skidbladnir::product::ComponentSpec;
use skidbladnir::service::{self, Backend};

/// Assert on the real value AND record a GREEN matrix row under the component
/// `skidbladnir/<surface>`. Mirrors nornir's `assert_emit!` — the `assert!` gates
/// the build, the emit is the observation. Only for what genuinely works.
macro_rules! assert_emit {
    ($surface:expr, $check:expr, $ok:expr, $($detail:tt)+) => {{
        let ok: bool = $ok;
        let detail = format!($($detail)+);
        skidbladnir::functional_status(
            concat!("skidbladnir/", $surface),
            $check,
            ok,
            &detail,
        );
        assert!(ok, "{}::{} — {}", $surface, $check, detail);
    }};
}

/// Record a PARTIAL/scaffold row (ok=false) WITHOUT failing the test — the surface
/// exists but cannot be honestly greened in the offline gate (needs a live host,
/// shells out, is an unverified hand-rolled wire path, or is declared-not-rendered).
/// The row IS the observation: "present, but not green, and we say so."
macro_rules! emit_partial {
    ($surface:expr, $check:expr, $($detail:tt)+) => {{
        let detail = format!($($detail)+);
        skidbladnir::functional_status(
            concat!("skidbladnir/", $surface),
            $check,
            false,
            &detail,
        );
    }};
}

// ───────────────────────────── fixtures ─────────────────────────────

/// A tiny product archive fixture: a staging tree with the product binary at its
/// `bundle_binary` path (+ a payload file) packed via the default `TarZstd` engine.
fn product_bundle(spec: &ComponentSpec) -> (tempfile::TempDir, PathBuf) {
    let dir = tempfile::tempdir().unwrap();
    let staging = dir.path().join("staging");
    let bin = staging.join(&spec.bundle_binary);
    std::fs::create_dir_all(bin.parent().unwrap()).unwrap();
    std::fs::write(&bin, b"#!/fake product binary bytes\n").unwrap();
    std::fs::write(staging.join("README"), b"product tree\n").unwrap();
    let bundle = dir.path().join("bundle.tar.zst");
    TarZstd::new().pack(&staging, "20250505", &bundle).unwrap();
    (dir, bundle)
}

fn layout_in(tmp: &Path, version: &str) -> Layout {
    Layout {
        data_dir: tmp.join("data"),
        log_dir: tmp.join("log"),
        software_dir: tmp.join("software"),
        version: version.into(),
    }
}

// ───────────────────────────── install / apply ─────────────────────────────

#[test]
fn install_product_is_honest_and_dbus_routed() {
    let spec = ComponentSpec::holger();
    let (_guard, bundle) = product_bundle(&spec);
    let tmp = tempfile::tempdir().unwrap();
    let layout = layout_in(tmp.path(), "20250505");
    let root = tmp.path().join("root");

    let out = install::install_product(
        &spec,
        &layout,
        &install::UpgradeSource::bundle(&bundle).unwrap(),
        &TarZstd::new(),
        Backend::SystemdSystem,
        &root,
    )
    .unwrap();

    // The lifecycle is pure data: op, the three provisioned dirs, a minted identity,
    // written unit files, and the ordered [enable, start] verbs — all assertable
    // offline without running a single privileged command.
    assert_emit!(
        "install",
        "install_product_yields_enable_then_start_and_identity",
        out.op == InstallOp::Install
            && out.dirs.len() == 3
            && out.identity.is_some()
            && !out.files_written.is_empty()
            && out.verbs.len() == 2
            && out.verbs[0].contains(&"enable".to_string())
            && out.verbs[1].contains(&"start".to_string()),
        "op={:?} dirs={} identity={} files={} verbs={:?}",
        out.op,
        out.dirs.len(),
        out.identity.is_some(),
        out.files_written.len(),
        out.verbs
    );

    // The current symlink now resolves to the installed generation.
    assert_emit!(
        "install",
        "activate_points_current_at_installed_generation",
        layout.active_version().as_deref() == Some("20250505"),
        "active={:?}",
        layout.active_version()
    );

    // TRANSPORT (partial): `install::apply` routes the systemd backends' verbs
    // through the pure-Rust zbus D-Bus controller (org.freedesktop.systemd1), NOT a
    // `systemctl` subprocess. It needs a live system/session bus + privilege, so the
    // dispatch is not exercised in the offline gate — honestly partial, not green.
    emit_partial!(
        "install",
        "apply_routes_systemd_verbs_over_dbus",
        "apply() dispatches enable/start/stop over zbus org.freedesktop.systemd1; \
         needs a live bus + root, not exercised offline"
    );
}

// ───────────────────────────── rollback ─────────────────────────────

#[test]
fn rollback_product_swaps_back_a_generation() {
    let spec = ComponentSpec::holger();
    let (_guard, bundle) = product_bundle(&spec);
    let tmp = tempfile::tempdir().unwrap();
    let root = tmp.path().join("root");

    // Gen A (install) then Gen B (update) — B is active, A is retained.
    let a = layout_in(tmp.path(), "20250505");
    install::install_product(
        &spec,
        &a,
        &install::UpgradeSource::bundle(&bundle).unwrap(),
        &TarZstd::new(),
        Backend::SystemdSystem,
        &root,
    )
    .unwrap();
    let b = Layout {
        version: "20250606".into(),
        ..a.clone()
    };
    install::update_product(
        &spec,
        &b,
        &install::UpgradeSource::bundle(&bundle).unwrap(),
        &TarZstd::new(),
        Backend::SystemdSystem,
        &root,
    )
    .unwrap();
    assert_eq!(b.active_version().as_deref(), Some("20250606"));

    let out = install::rollback_product(&spec, &b, Backend::SystemdSystem, &root).unwrap();

    // Rollback finds the prior generation, swaps `current` back atomically, and
    // returns a restart (stop-then-start) — all honest data.
    assert_emit!(
        "rollback",
        "rollback_product_restores_prior_generation",
        out.op == InstallOp::Rollback
            && out.version == "20250505"
            && b.active_version().as_deref() == Some("20250505")
            && out.verbs.len() == 2
            && out.verbs[0].contains(&"stop".to_string())
            && out.verbs[1].contains(&"start".to_string()),
        "op={:?} version={} active={:?} verbs={:?}",
        out.op,
        out.version,
        b.active_version(),
        out.verbs
    );
}

// ───────────────────────────── layout ─────────────────────────────

#[test]
fn layout_previous_generation_and_activate_version() {
    let tmp = tempfile::tempdir().unwrap();
    let sw = tmp.path().join("software");
    let a = Layout {
        data_dir: tmp.path().join("data"),
        log_dir: tmp.path().join("log"),
        software_dir: sw.clone(),
        version: "20250505".into(),
    };
    // Each generation must hold real software: `prepare_product` alone leaves the
    // empty husk a failed install leaves behind, and `activate`/`previous_generation`
    // now refuse to promote one. This test is about the SWAP, so give it real gens.
    a.prepare_product("holger").unwrap();
    std::fs::write(a.product_software("holger").join("holger"), b"a").unwrap();
    a.activate().unwrap();
    let b = Layout {
        version: "20250606".into(),
        ..a.clone()
    };
    b.prepare_product("holger").unwrap();
    std::fs::write(b.product_software("holger").join("holger"), b"b").unwrap();
    b.activate().unwrap();

    assert_emit!(
        "layout",
        "previous_generation_picks_older_retained_gen",
        b.previous_generation("holger").as_deref() == Some("20250505")
            && b.previous_generation("ghost").is_none(),
        "prev(holger)={:?} prev(ghost)={:?}",
        b.previous_generation("holger"),
        b.previous_generation("ghost")
    );

    b.activate_version("20250505").unwrap();
    assert_emit!(
        "layout",
        "activate_version_swaps_current_and_guards_missing",
        b.active_version().as_deref() == Some("20250505")
            && b.activate_version("20991231").is_err()
            && b.active_version().as_deref() == Some("20250505"),
        "after swap-back active={:?}, missing-gen refused",
        b.active_version()
    );
}

// ───────────────────── deploy backends: renderers ─────────────────────

#[test]
fn service_renderers_systemd_and_openrc_are_real() {
    let tmp = tempfile::tempdir().unwrap();
    let layout = layout_in(tmp.path(), "20250505");
    let spec = ComponentSpec::holger();

    // systemd (system scope): real unit + systemctl enable/start verbs.
    let sysd = service::renderer_for(Backend::SystemdSystem).expect("systemd renderer exists");
    let r = sysd.render(&spec.to_service_spec(&layout, Backend::SystemdSystem)).unwrap();
    assert_emit!(
        "backend-systemd",
        "systemd_renderer_emits_unit_and_verbs",
        r.files.len() == 1
            && r.files[0].contents.contains("[Service]")
            && r.files[0].contents.contains("ExecStart=")
            && r.enable.contains(&"enable".to_string())
            && r.start.contains(&"start".to_string()),
        "unit_path={} enable={:?} start={:?}",
        r.files[0].path,
        r.enable,
        r.start
    );

    // OpenRC: real `#!/sbin/openrc-run` script + rc verbs.
    let openrc = service::renderer_for(Backend::OpenRc).expect("openrc renderer exists");
    let ro = openrc.render(&spec.to_service_spec(&layout, Backend::OpenRc)).unwrap();
    assert_emit!(
        "backend-openrc",
        "openrc_renderer_emits_openrc_run_script",
        !ro.files.is_empty()
            && ro.files[0].contents.starts_with("#!/sbin/openrc-run")
            && ro.files[0].contents.contains("command=")
            && !ro.enable.is_empty()
            && !ro.start.is_empty(),
        "script_path={} enable={:?}",
        ro.files[0].path,
        ro.enable
    );

    // SysV (the universal fallback): real LSB `/etc/init.d/<name>` script with an
    // init-info header, `start-stop-daemon` verbs + the script-driven lifecycle.
    let sysv = service::renderer_for(Backend::SysV).expect("sysv renderer exists");
    let rv = sysv.render(&spec.to_service_spec(&layout, Backend::SysV)).unwrap();
    assert_emit!(
        "backend-sysv",
        "sysv_renderer_emits_lsb_initd_script",
        !rv.files.is_empty()
            && rv.files[0].contents.starts_with("#!/bin/sh")
            && rv.files[0].contents.contains("### BEGIN INIT INFO")
            && rv.files[0].contents.contains("start-stop-daemon --start")
            && !rv.enable.is_empty()
            && !rv.start.is_empty(),
        "script_path={} enable={:?}",
        rv.files[0].path,
        rv.enable
    );

    // runit: a real service DIRECTORY — `/etc/sv/<name>/run` execs under runsv, plus
    // the `ln`-into-runsvdir enable verb and `sv up` start verb.
    let runit = service::renderer_for(Backend::Runit).expect("runit renderer exists");
    let rr = runit.render(&spec.to_service_spec(&layout, Backend::Runit)).unwrap();
    let run = rr.file_ending("/run").expect("a runit run script");
    assert_emit!(
        "backend-runit",
        "runit_renderer_emits_runsv_service_dir",
        run.starts_with("#!/bin/sh")
            && run.contains("exec ")
            && rr
                .files
                .iter()
                .any(|f| f.path.ends_with("/run") && f.path.contains("/sv/"))
            && rr.enable.first().map(|s| s == "ln").unwrap_or(false)
            && rr.start.first().map(|s| s == "sv").unwrap_or(false),
        "run_path={} enable={:?} start={:?}",
        rr.files[0].path,
        rr.enable,
        rr.start
    );

    // s6-rc: a real compiled source-definition DIR — `type = longrun`, a `run` script
    // that chainloads under s6-supervise, plus the `s6-rc-bundle-update` enable verb and
    // `s6-rc … change` start verb.
    let s6rc = service::renderer_for(Backend::S6Rc).expect("s6-rc renderer exists");
    let rs = s6rc.render(&spec.to_service_spec(&layout, Backend::S6Rc)).unwrap();
    let s6run = rs.file_ending("/run").expect("an s6-rc run script");
    assert_emit!(
        "backend-s6rc",
        "s6rc_renderer_emits_source_definition_dir",
        rs.file_ending("/type")
            .map(|t| t == "longrun\n")
            .unwrap_or(false)
            && s6run.starts_with("#!/bin/sh")
            && s6run.contains("exec ")
            && rs
                .files
                .iter()
                .any(|f| f.path.ends_with("/run") && f.path.contains("/s6-rc/source/"))
            && rs
                .enable
                .first()
                .map(|s| s == "s6-rc-bundle-update")
                .unwrap_or(false)
            && rs.start.first().map(|s| s == "s6-rc").unwrap_or(false),
        "run_path={} enable={:?} start={:?}",
        s6run,
        rs.enable,
        rs.start
    );

    // OCI: a real `podman run` argv — `-d --name`, `--restart`, and the
    // `--entrypoint <exec>` … image split, plus the `podman stop` verb.
    let oci = service::renderer_for(Backend::Oci).expect("oci renderer exists");
    let ro = oci.render(&spec.to_service_spec(&layout, Backend::Oci)).unwrap();
    assert_emit!(
        "backend-oci",
        "oci_renderer_emits_podman_run_argv",
        ro.start.first().map(|s| s == "podman").unwrap_or(false)
            && ro.start.iter().any(|s| s == "run")
            && ro.start.iter().any(|s| s == "--entrypoint")
            && ro.start.iter().any(|s| s.starts_with("--restart="))
            && ro.stop.first().map(|s| s == "podman").unwrap_or(false),
        "start={:?} stop={:?}",
        ro.start,
        ro.stop
    );

    // Quadlet: a real systemd `.container` unit — `[Container] Image=`/`Exec=`, a
    // `[Install] WantedBy=` target, and the `systemctl daemon-reload` (enable) +
    // `systemctl start <name>.service` verbs the `podman-systemd` generator needs.
    let quadlet = service::renderer_for(Backend::Quadlet).expect("quadlet renderer exists");
    let rq = quadlet.render(&spec.to_service_spec(&layout, Backend::Quadlet)).unwrap();
    let cunit = rq.file_ending(".container").expect("a .container unit");
    assert_emit!(
        "backend-quadlet",
        "quadlet_renderer_emits_container_unit_and_verbs",
        cunit.contains("[Container]")
            && cunit.contains("Image=")
            && cunit.contains("Exec=")
            && cunit.contains("WantedBy=")
            && rq.enable == vec!["systemctl".to_string(), "daemon-reload".to_string()]
            && rq.start.iter().any(|s| s == "start")
            && rq
                .start
                .last()
                .map(|s| s.ends_with(".service"))
                .unwrap_or(false),
        "unit_path={} enable={:?} start={:?}",
        rq.files[0].path,
        rq.enable,
        rq.start
    );

    // Windows service (SCM): a real WiX `.wxs` service fragment (via the shared MSI
    // seam) + `sc.exe` create/start/query/stop verbs — the 11th and final backend.
    let win = service::renderer_for(Backend::WindowsService).expect("windows renderer exists");
    let rw = win.render(&spec.to_service_spec(&layout, Backend::WindowsService)).unwrap();
    let wxs = rw.file_ending(".wxs").expect("a .wxs service fragment");
    assert_emit!(
        "backend-windows",
        "windows_renderer_emits_wix_fragment_and_sc_verbs",
        wxs.contains("<ServiceInstall")
            && wxs.contains("Start=\"auto\"")
            && wxs.contains("<ServiceControl")
            && rw.enable.first().map(|s| s == "sc.exe").unwrap_or(false)
            && rw.enable.iter().any(|s| s == "create")
            && rw.start
                == vec![
                    "sc.exe".to_string(),
                    "start".to_string(),
                    "holger".to_string()
                ],
        "wxs_path={} enable={:?} start={:?}",
        rw.files[0].path,
        rw.enable,
        rw.start
    );
}

#[test]
fn every_declared_backend_renders_full_coverage() {
    // The coverage axis is now COMPLETE: every backend `Backend::all()` declares
    // resolves to a real renderer whose `backend()` matches its lookup key — 11/11,
    // no honest-`None` implementation debt remains.
    for b in Backend::all() {
        let r = service::renderer_for(*b);
        assert!(
            r.is_some(),
            "{} must have a renderer (full coverage)",
            b.slug()
        );
        assert_eq!(
            r.unwrap().backend(),
            *b,
            "{}: renderer reports a different backend",
            b.slug()
        );
    }
    let implemented = Backend::all()
        .iter()
        .filter(|b| service::renderer_for(**b).is_some())
        .count();
    assert_emit!(
        "backend-init-renderers",
        "renderer_coverage_is_complete",
        Backend::all().len() == 11 && implemented == 11,
        "{}/{} backends have a renderer (systemd-system, systemd-user, openrc, sysv, runit, s6-rc, dinit, kubernetes, oci, quadlet, windows-service)",
        implemented,
        Backend::all().len()
    );
}

#[test]
fn common_infra_bringup_is_wave_ordered_and_renders() {
    use skidbladnir::product::{common_infra, common_infra_waves};
    // TIER-0.5 keystone: the app-agnostic common-infra set brings up in dependency
    // WAVES via the ONE wave engine, and each infra spec renders to real k8s manifests
    // through the ONE k8s renderer (the pure renderer half; live apply is deferred).
    let waves = common_infra_waves().expect("no dependency cycle");
    let tmp = tempfile::tempdir().unwrap();
    let layout = layout_in(tmp.path(), "20250505");
    // Render the OpenBao infra spec → a StatefulSet (stateful) in its own namespace.
    let bao = common_infra()
        .into_iter()
        .find(|c| c.name == "openbao")
        .unwrap();
    let r = service::renderer_for(Backend::Kubernetes)
        .unwrap()
        .render(&bao.to_service_spec(&layout, Backend::Kubernetes)).unwrap();
    let ss = r
        .file_ending("openbao-statefulset.yaml")
        .expect("an OpenBao StatefulSet");
    assert_emit!(
        "tier0-common-infra",
        "bringup_waves_ordered_and_specs_render_k8s",
        waves
            == vec![
                vec!["cert-manager".to_string(), "ingress-nginx".to_string()],
                vec!["holger-registry".to_string(), "openbao".to_string()],
            ]
            && ss.contains("kind: \"StatefulSet\"")
            && ss.contains("namespace: \"openbao\"")
            && ss.contains("image: \"ghcr.io/openbao/openbao:2.0.3\""),
        "waves={:?} openbao_manifest_bytes={}",
        waves,
        ss.len()
    );
}

#[test]
fn ingress_nginx_renders_the_full_production_object_set() {
    use skidbladnir::infra::ingress_nginx_objects;
    // TIER-0.5 renderer depth: ingress-nginx renders its COMPLETE production object
    // graph (not an MVP single Deployment) — SA + RBAC (Cluster/Role + both bindings)
    // + ConfigMap + Deployment + LoadBalancer Service + IngressClass, all pure/offline.
    let objs = ingress_nginx_objects();
    let has = |kind: &str| {
        objs.iter()
            .any(|f| f.contents.contains(&format!("kind: {kind}\n")))
    };
    let all_kinds = [
        "ServiceAccount",
        "ConfigMap",
        "ClusterRole",
        "ClusterRoleBinding",
        "Role",
        "RoleBinding",
        "Deployment",
        "Service",
        "IngressClass",
        // admission webhook set
        "ValidatingWebhookConfiguration",
        "Job",
    ];
    let deploy = objs
        .iter()
        .find(|f| f.path.ends_with("-deployment.yaml"))
        .map(|f| f.contents.as_str())
        .unwrap_or("");
    let vwc = objs
        .iter()
        .find(|f| {
            f.path
                .ends_with("admission-validatingwebhookconfiguration.yaml")
        })
        .map(|f| f.contents.as_str())
        .unwrap_or("");
    assert_emit!(
        "tier0-ingress-nginx",
        "full_production_object_set_with_admission_renders",
        objs.len() == 18
            && all_kinds.iter().all(|k| has(k))
            && deploy.contains("serviceAccountName: ingress-nginx")
            && objs
                .iter()
                .find(|f| f.path.ends_with("ingress-nginx-service.yaml"))
                .unwrap()
                .contents
                .contains("type: LoadBalancer")
            && vwc.contains("name: ingress-nginx-controller-admission"),
        "objects={} kinds_present={}",
        objs.len(),
        all_kinds.iter().filter(|k| has(k)).count()
    );
}

#[test]
fn cert_manager_renders_its_full_three_component_graph() {
    use skidbladnir::infra::cert_manager_objects;
    // TIER-0.5 renderer depth (symmetric with ingress-nginx): cert-manager renders its
    // three-component production graph (controller + webhook + cainjector) with full
    // RBAC + webhook configs + CRD registrations (schemas DEFERRED), pure/offline.
    let objs = cert_manager_objects();
    let n = |slug: &str| {
        objs.iter()
            .find(|f| f.path.ends_with(&format!("cert-manager-{slug}.yaml")))
            .map(|f| f.contents.as_str())
            .unwrap_or("")
    };
    let three_deploys_run_as_their_sa = [
        ("controller", "cert-manager"),
        ("webhook", "cert-manager-webhook"),
        ("cainjector", "cert-manager-cainjector"),
    ]
    .iter()
    .all(|(c, sa)| n(&format!("{c}-deployment")).contains(&format!("serviceAccountName: {sa}")));
    let crds_deferred = [
        "certificates",
        "issuers",
        "clusterissuers",
        "orders",
        "challenges",
        "certificaterequests",
    ]
    .iter()
    .all(|p| n(&format!("crd-{p}")).contains("x-kubernetes-preserve-unknown-fields: true"));
    assert_emit!(
        "tier0-cert-manager",
        "full_three_component_graph_renders_crds_deferred",
        objs.len() == 28
            && three_deploys_run_as_their_sa
            && crds_deferred
            && n("webhook-validatingwebhookconfiguration")
                .contains("kind: ValidatingWebhookConfiguration")
            && n("webhook-mutatingwebhookconfiguration")
                .contains("kind: MutatingWebhookConfiguration"),
        "objects={} deploys_bound={} crds_deferred={}",
        objs.len(),
        three_deploys_run_as_their_sa,
        crds_deferred
    );
}

/// The ONE declared `HealthProbe` that drives the host `Lifecycle::health()` also
/// projects onto the k8s render as `readinessProbe` + `livenessProbe` (one knob,
/// two views — L5). A `ComponentSpec` with an HTTP health check, rendered for
/// `Backend::Kubernetes`, must carry an `httpGet` probe on its declared port.
#[test]
fn k8s_render_projects_health_probe_onto_readiness_and_liveness() {
    use skidbladnir::product::HealthCheck;
    use skidbladnir::service::{HealthProbe, ServiceSpec, Socket};
    // Direct neutral-spec path (Tcp): both probes render tcpSocket on the port.
    let tcp = ServiceSpec::new("web", "w", "/unused")
        .image("img:1")
        .namespace("ns")
        .socket(Socket::tcp(7070))
        .health(HealthProbe::Tcp(7070));
    let tcp_dep = service::renderer_for(Backend::Kubernetes)
        .unwrap()
        .render(&tcp).unwrap()
        .file_ending("web-deployment.yaml")
        .unwrap()
        .to_string();
    // ComponentSpec path (Http): the AIRGAP6 HealthCheck flows through to the k8s probe.
    let comp = ComponentSpec::new("api", "linux-x86_64/api", "apiuser")
        .with_health(HealthCheck::Http("/healthz".into()))
        .with_image("img:1")
        .with_port(8080);
    let comp_spec = comp.to_service_spec(
        &layout_in(Path::new("/srv"), "20250505"),
        Backend::Kubernetes,
    );
    let comp_dep = service::renderer_for(Backend::Kubernetes)
        .unwrap()
        .render(&comp_spec).unwrap()
        .file_ending("api-deployment.yaml")
        .unwrap()
        .to_string();
    assert_emit!(
        "k8s-health-probe",
        "health_probe_projects_to_readiness_and_liveness",
        tcp_dep.matches("tcpSocket:").count() == 2
            && tcp_dep.contains("port: 7070")
            && comp_dep.matches("httpGet:").count() == 2
            && comp_dep.contains("path: \"/healthz\"")
            && comp_dep.contains("port: 8080"),
        "tcp_sockets={} comp_httpget={} comp_has_path={}",
        tcp_dep.matches("tcpSocket:").count(),
        comp_dep.matches("httpGet:").count(),
        comp_dep.contains("path: \"/healthz\"")
    );
}

// ───────────────── deploy backends: KVM / Redfish / Terraform / Libvirt ─────────────────

#[test]
fn kvm_qemu_argv_is_real_boot_is_partial() {
    use skidbladnir::unfold::kvm::KvmTarget;
    let t = KvmTarget::new("skid-test", "/img/appliance.qcow2");
    let argv = t.qemu_argv();
    assert_emit!(
        "backend-kvm",
        "qemu_argv_is_q35_kvm_launch",
        argv.windows(2)
            .any(|w| w[0] == "-machine" && w[1] == "q35,accel=kvm:tcg")
            && argv.iter().any(|a| a.contains("/img/appliance.qcow2")),
        "{} argv tokens, q35/accel=kvm:tcg present",
        argv.len()
    );
    // The actual boot (and the live multi-OS matrix runner) needs a qemu/KVM host
    // and is a `#[ignore]`d heavy test — the argv is the real artifact, the spawn is
    // not exercised in the gate.
    emit_partial!(
        "backend-kvm",
        "kvm_boot_and_matrix_runner_need_host",
        "qemu_argv is the tested artifact; boot()/matrix runner need a qemu/KVM host (heavy #[ignore] live test)"
    );
}

/// The jera routing seam (feature `jera`): the `machine` module's container/VM
/// spawns route through jera (edda), the ONE job/container/VM manager, instead of raw
/// `podman`/`qemu`. A `MachineSpec` classifies into a `jera::JobPolicy` and a container
/// spec maps onto a `jera::container::ContainerSpec` → a VALID `draupnir::BootSpec`
/// (asserted daemon-free). The AIRGAP live-route is LANDED: `NetMode::Isolated` maps
/// onto jera `NetMode::None` → draupnir `--network none` (egress isolation reaches the
/// live knob); bind mounts carry too. The remaining honest gap (an arbitrary named
/// host-network; the VM kernel-less disk-boot) keeps the direct fallback for that case.
#[cfg(feature = "jera")]
#[test]
fn machine_spawn_maps_onto_a_jera_job_spec() {
    use skidbladnir::machine::{ImageRef, MachineSpec, NetMode};
    // A NAT container classifies as a jera Container job and maps to a valid spec.
    let c = MachineSpec::container("holger-c", "git.example.com/nordisk/holger:latest");
    let js = c
        .to_jera_container_spec()
        .expect("container maps to a jera ContainerSpec");
    let bootspec_ok = {
        let d = js.to_draupnir();
        d.validate().is_ok() && format!("{d:?}").contains("git.example.com/nordisk/holger:latest")
    };
    assert_emit!(
        "machine-jera",
        "container_spawn_maps_to_jera_job_spec_and_valid_draupnir_bootspec",
        c.job_policy().as_str() == "container"
            && js.image == "git.example.com/nordisk/holger:latest"
            && bootspec_ok
            && c.jera_routable(),
        "policy={} image={} routable={}",
        c.job_policy().as_str(),
        js.image,
        c.jera_routable()
    );
    // A VM disk classifies as a jera Vm job (routes through jera's vm seam) and maps
    // onto jera's VM payload (jera::vm::BootSpec) + the serde agent VmBoot carrying
    // name/disk/size — the mirror of the container spec-mapper.
    let v = MachineSpec::vm("holger-vm", "/img/base.qcow2").resources(4, 4096);
    let vm = v.to_jera_vm_spec().expect("vm maps onto a jera BootSpec");
    let payload = v
        .to_jera_vm_payload()
        .expect("vm maps onto the agent VmBoot");
    assert_emit!(
        "machine-jera",
        "vm_disk_classifies_as_jera_vm_job_and_maps_onto_the_jera_vm_payload",
        v.job_policy().as_str() == "vm"
            && v.to_jera_container_spec().is_err()
            && matches!(v.image, ImageRef::Path(_))
            && vm.disk == "/img/base.qcow2"
            && vm.mem_mb == 4096
            && vm.cores == 4
            && payload.image == "/img/base.qcow2"
            && payload.mem_mb == 4096,
        "vm disk -> JobPolicy::Vm, refused by container seam, maps onto jera BootSpec disk={} + VmBoot",
        vm.disk
    );
    // AIRGAP LIVE-ROUTE (LANDED): an airgap container now routes through jera —
    // `NetMode::Isolated` maps onto jera `NetMode::None`, threaded onward onto
    // draupnir's `HostConfig.network_mode = "none"` (a real `--network none` egress
    // isolated run). The net=None knob is reached; a bind mount likewise carries.
    let iso = MachineSpec::container("c", "alpine:3").net(NetMode::Isolated);
    let iso_js = iso
        .to_jera_container_spec()
        .expect("airgap container maps to a jera spec");
    let iso_none = format!("{:?}", iso_js.net) == "None"; // jera::container::NetMode::None
    let iso_draupnir_none = format!("{:?}", iso_js.to_draupnir()).contains("net: None");
    assert_emit!(
        "machine-jera",
        "airgap_container_routes_through_jera_with_network_none",
        iso.jera_routable() && iso_none && iso_draupnir_none && iso.jera_unsupported().is_empty(),
        "airgap routable={} jera_net={:?} draupnir_network_none={} (Skidbladnir→jera→draupnir --network none)",
        iso.jera_routable(),
        iso_js.net,
        iso_draupnir_none
    );
    // Remaining HONEST gap: an arbitrary NAMED host-network — jera's NetMode is
    // Default/None/Host/Bridge, not an arbitrary name — keeps the direct podman path.
    let named = MachineSpec::container("c", "alpine:3").net(NetMode::Named("br0".into()));
    emit_partial!(
        "machine-jera",
        "named_network_container_awaits_jera_named_net_knob",
        "named-net container gap={:?} routable={} (jera has no arbitrary named net) — direct podman fallback kept",
        named.jera_unsupported(),
        named.jera_routable()
    );
    // HONEST gap (VM): a Skidbladnir VM is a kernel-less bootloader-in-disk boot, which
    // jera/draupnir's kernel-required VM payload cannot express — so `disk-boot` is
    // always in the gap and NO vm is routable through jera today; the direct qemu path
    // stays. Airgap net + a cloud-init seed stack further gaps (tripwire flips when jera
    // grows a kernel-less disk boot + net + cloud-init).
    let airvm = MachineSpec::vm("v", "/img/base.qcow2")
        .net(NetMode::Isolated)
        .provision(skidbladnir::machine::Provision::CloudInit {
            user_data: "#cloud-config\n".into(),
            meta_data: None,
        });
    emit_partial!(
        "machine-jera",
        "vm_awaits_jera_kernelless_disk_boot_net_and_cloud_init_knobs",
        "vm gap={:?} routable={} (jera VM payload has no bootloader-in-disk/net/cloud-init) — live route deferred, direct qemu fallback kept",
        airvm.jera_vm_unsupported(),
        airvm.jera_vm_routable()
    );
}

#[test]
fn redfish_bodies_and_paths_are_real_rest_is_partial() {
    use skidbladnir::unfold::redfish::{PowerAction, power_body, reset_action_path, system_path};
    let body = power_body(PowerAction::On);
    assert_emit!(
        "backend-redfish",
        "reset_body_and_paths_are_canonical_redfish",
        body["ResetType"] == "On"
            && system_path("1") == "/redfish/v1/Systems/1"
            && reset_action_path("1") == "/redfish/v1/Systems/1/Actions/ComputerSystem.Reset",
        "power_body={} system_path={}",
        body,
        system_path("1")
    );
    // The live REST calls (feature `redfish`) drive a real BMC over HTTPS — not
    // reachable in the offline gate.
    emit_partial!(
        "backend-redfish",
        "redfish_rest_client_needs_live_bmc",
        "body/path builders are pure+tested; RedfishClient power/boot/virtual-media need a live BMC (feature `redfish`)"
    );
}

#[test]
fn terraform_hcl_renders_but_apply_is_shellout() {
    use skidbladnir::unfold::terraform::{TerraformTarget, render_config, render_resource};
    let t = TerraformTarget::new("fleet", "holger").count(3);
    let hcl = render_config(&t);
    // HCL RENDER is pure + verifiable → green.
    assert_emit!(
        "backend-terraform",
        "hcl_render_is_valid_nornir_component",
        hcl.contains("required_providers")
            && hcl.contains("nordisk/nornir")
            && render_resource(&t).contains("resource \"nornir_component\" \"holger\"")
            && hcl.contains("count     = 3"),
        "rendered {} bytes of HCL",
        hcl.len()
    );
    // The APPLY is a `terraform` binary shellout (feature `terraform`, std::process)
    // — NOT green: it needs the terraform CLI + a real provider/backend.
    emit_partial!(
        "backend-terraform",
        "terraform_apply_is_binary_shellout",
        "render is pure; unfold() shells `terraform init/plan/apply` (feature `terraform`) — needs the CLI, not verified in-gate"
    );
}

#[test]
fn libvirt_domain_xml_renders_but_rpc_is_unverified() {
    use skidbladnir::unfold::libvirt::LibvirtTarget;
    let t = LibvirtTarget::new("bmc-host", "skid-vm", "/img/disk.qcow2").resources(4096, 4);
    let xml = t.domain_xml();
    // The domain XML + connect URI are pure render artifacts → green.
    assert_emit!(
        "backend-libvirt",
        "domain_xml_and_uri_render",
        xml.contains("<domain")
            && xml.contains("skid-vm")
            && xml.contains("/img/disk.qcow2")
            && t.connect_uri() == "qemu+tcp://bmc-host/system",
        "{} bytes of domain XML, uri={}",
        xml.len(),
        t.connect_uri()
    );
    // The hand-rolled libvirt RPC (define/create/destroy over the raw wire protocol)
    // is UNVERIFIED against a real libvirtd — honestly partial, never green.
    emit_partial!(
        "backend-libvirt",
        "handrolled_rpc_unverified_against_libvirtd",
        "domain XML is a tested artifact; the hand-rolled DOMAIN_DEFINE_XML/create RPC wire path is unverified against a live libvirtd"
    );
}

// ───────────────────────────── PKI (rcgen X.509) ─────────────────────────────

#[cfg(feature = "pki")]
#[test]
fn pki_issues_and_verifies_real_x509() {
    use skidbladnir::pki::{MachineCa, verify_leaf};
    // Deterministic CA from a fixed seed → reproducible, still a real ed25519 key.
    let ca = MachineCa::from_seed("host14", [7u8; 32]);
    // issue_leaf returns (LeafCert, leaf_private_key_pem); the CERT PEM is on the leaf.
    let (leaf, key_pem) = ca
        .issue_leaf("holger", &["holger.host14.x14.local".to_string()])
        .unwrap();

    assert_emit!(
        "pki",
        "rcgen_issues_x509_leaf_that_verifies_under_ca",
        leaf.cert_pem.starts_with("-----BEGIN CERTIFICATE-----")
            && key_pem.contains("PRIVATE KEY")
            && !leaf.cert_der.is_empty()
            && verify_leaf(&leaf, &ca.verifying_key()),
        "leaf CN={} sans={:?} der={} bytes, verifies under CA",
        leaf.subject_cn,
        leaf.sans,
        leaf.cert_der.len()
    );

    // Tampering: a leaf must NOT verify under a DIFFERENT CA's key.
    let other = MachineCa::from_seed("host99", [9u8; 32]);
    assert_emit!(
        "pki",
        "leaf_rejected_under_wrong_ca_key",
        !verify_leaf(&leaf, &other.verifying_key()),
        "leaf issued by host14 CA is rejected under host99 CA key"
    );
}

// ───────────────────────────── secret-env hardening ─────────────────────────────

#[test]
fn secret_env_stays_out_of_the_world_readable_unit() {
    use skidbladnir::systemd::{SystemdServiceSpec, env_file, systemd_unit};
    // SECURITY: a token set via `with_secret_env` is written ONLY into the (0600)
    // env file — never inline in the world-readable `/etc/systemd/system` unit.
    // A missing env_file for a spec that carries secrets is a hard validation error.
    let spec = SystemdServiceSpec::new(
        "nornir",
        "nornir server",
        "/usr/local/bin/nornir-server",
        "nornir",
        "/home/nornir",
    )
    .with_env("NORNIR_SERVER_ADDR", "127.0.0.1:7878")
    .with_secret_env("NORNIR_SERVER_TOKEN", "s3cr3t")
    .with_env_file("/etc/nornir/nornir.env");
    let unit = systemd_unit(&spec);

    // And a secret with NO env_file is refused before anything is written.
    let leaky = SystemdServiceSpec::new("svc", "d", "/bin/svc", "svc", "/tmp")
        .with_secret_env("TOKEN", "abc");

    assert_emit!(
        "secret-env-hardening",
        "secrets_never_inline_in_world_readable_unit",
        !unit.contains("s3cr3t")
            && !unit.contains("Environment=NORNIR_SERVER_TOKEN")
            && unit.contains("EnvironmentFile=/etc/nornir/nornir.env")
            && env_file(&spec).contains("NORNIR_SERVER_TOKEN=s3cr3t")
            && spec.validate().is_ok()
            && leaky.validate().is_err(),
        "unit_bytes={} secret_in_file={} secretless_leak_rejected={}",
        unit.len(),
        env_file(&spec).contains("NORNIR_SERVER_TOKEN=s3cr3t"),
        leaky.validate().is_err()
    );
}

// ───────────────────────────── nornir_spec drop-in ─────────────────────────────

// ───────────────── IMPLANTED lifecycle-surface emits: red-when-broken ─────────────────

/// RED-WHEN-BROKEN (the law): the lifecycle SURFACES emit `functional_status`
/// straight from their runtime path — not just from this test's re-derivation. Drive
/// three surfaces into their genuine failure mode and assert the IMPLANTED emit the
/// production code left behind reads as RED with real detail. Because the emit is only
/// compiled when `--features testmatrix` is on (and only then is the read-back buffer
/// present), this proof is gated on that feature; the default build compiles the emits
/// out entirely, so a green tree here means "the surfaces are wired, and broken input
/// makes them go red" — the exact tripwire nornir's matrix ingests.
#[cfg(feature = "testmatrix")]
#[test]
fn lifecycle_surfaces_emit_red_when_broken() {
    use skidbladnir::archive::{Archive, TarZstd, load_manifest, manifest_path};
    use skidbladnir::fetch::verify_on_receipt;
    use skidbladnir::orchestrate::start_waves;
    use skidbladnir::product::Product;
    use skidbladnir::systemd::{Scope, SystemdServiceSpec, install_systemd_scoped};

    // Clear any rows parallel tests already parked in the process-global buffer so
    // the drain below sees a clean slate; we still filter by surface+check to be safe.
    let _ = skidbladnir::drain_functional_rows();

    // 1) FETCH — tamper the sidecar manifest so verify-on-receipt fails; the surface
    //    must return not-ok AND have emitted a RED `verify_on_receipt` row.
    let dir = tempfile::tempdir().unwrap();
    let staging = dir.path().join("stage");
    std::fs::create_dir_all(staging.join("linux-x86_64")).unwrap();
    std::fs::write(staging.join("linux-x86_64/holger"), b"holger-bytes").unwrap();
    let bundle = dir.path().join("lake.tar.zst");
    let eng = TarZstd::new();
    eng.pack(&staging, "20250505", &bundle).unwrap();
    let mut m = load_manifest(&bundle).unwrap();
    m.entries[0].sha256 = "ff".repeat(32); // corrupt the recorded digest
    std::fs::write(manifest_path(&bundle), serde_json::to_vec(&m).unwrap()).unwrap();
    let report = verify_on_receipt(&bundle, &eng, None, None).unwrap();
    assert!(!report.is_ok(), "tampered bundle must fail the gate");

    // 2) START — a dependency cycle is an install that never converges; start_waves
    //    must Err AND have emitted a RED `start_waves_ordered` row.
    let cycle = [
        Product {
            name: "a".into(),
            depends_on: vec!["b".into()],
            user: "a".into(),
            ports: vec![],
        },
        Product {
            name: "b".into(),
            depends_on: vec!["a".into()],
            user: "b".into(),
            ports: vec![],
        },
    ];
    assert!(start_waves(&cycle).is_err(), "a cycle cannot be ordered");

    // 3) SYSTEMD — a spec that carries a secret but no private env-file would leak it
    //    into the world-readable unit; install refuses it BEFORE any privilege (so this
    //    is reachable without root) AND emits a RED `install_unit` row.
    let leaky = SystemdServiceSpec::new("svc", "d", "/bin/svc", "svc", "/tmp")
        .with_secret_env("TOKEN", "s3cr3t");
    assert!(
        install_systemd_scoped(&leaky, Scope::System).is_err(),
        "a secret with no env-file must be refused"
    );

    // Now read back the rows the SURFACES themselves left behind and assert each went RED.
    let rows = skidbladnir::drain_functional_rows();
    let red = |suite: &str, check: &str| -> String {
        let hits: Vec<_> = rows
            .iter()
            .filter(|r| r.suite == suite && r.test_name == check && r.status == "fail")
            .collect();
        assert_eq!(
            hits.len(),
            1,
            "exactly one RED `{suite}::{check}` row (got {}): {:?}",
            hits.len(),
            rows.iter()
                .map(|r| (&r.suite, &r.test_name, &r.status))
                .collect::<Vec<_>>()
        );
        assert!(!hits[0].message.is_empty(), "RED row carries real detail");
        hits[0].message.clone()
    };

    let fetch_detail = red("skidbladnir/fetch", "verify_on_receipt");
    assert!(
        fetch_detail.contains("bad_entries=") || fetch_detail.contains("manifest_root_ok="),
        "fetch RED names the failing check: {fetch_detail}"
    );
    let start_detail = red("skidbladnir/start", "start_waves_ordered");
    assert!(
        start_detail.contains("cycle"),
        "start RED names the cycle: {start_detail}"
    );
    let systemd_detail = red("skidbladnir/systemd", "install_unit");
    assert!(
        systemd_detail.contains("refused"),
        "systemd RED says refused: {systemd_detail}"
    );
}

#[test]
fn nornir_spec_dropin_renders_a_real_unit() {
    use skidbladnir::systemd::{nornir_spec, systemd_unit};
    let spec = nornir_spec("/opt/nornir/nornir-server", "0.0.0.0:8443");
    let unit = systemd_unit(&spec);
    assert_emit!(
        "nornir-spec-dropin",
        "nornir_spec_renders_installable_unit",
        unit.contains("[Unit]")
            && unit.contains("[Service]")
            && unit.contains("ExecStart=")
            && unit.contains("/opt/nornir/nornir-server"),
        "rendered {} bytes; unit_file={}",
        unit.len(),
        spec.unit_file_name()
    );
}