server/dev-narrate/src/udp_tests.rs
//! **A DEV Holger box narrates over UDP, to the installer's ear** (owner 2026-10-19: Holger's
//! test media are DEV builds, debug logs visible, exactly like Gunnar's).
//!
//! Holger's own start (this crate's [`crate::start_at`], as it runs in the box) over a real UDP
//! socket on loopback, and the installer's side as the installer runs it: Skidbladnir
//! `nordisk_narrate::receiver`, holding the private key whose public half the seed names. The
//! box's medium is a ONE-box medium: its seed starts as the metadata marker and becomes the
//! shop's seed a moment later, which is the case `boxside` alone stays OFF for.
//!
//! The release half (no sender in a release build) is read off the symbol table in
//! holger-recipe `tests/dev_narrate_symbols.rs`.
use crate as dev_narrate;
use nordisk_narrate::receiver::{self, Target};
use nordisk_narrate::{seedkey, wire};
use nordisk_pairing::bootlog::BootLog;
use std::sync::{Arc, Mutex};
use std::time::{Duration, Instant};
static LOG: BootLog = BootLog::new(1000);
fn wait_for(what: &str, budget: Duration, mut ok: impl FnMut() -> bool) {
let t0 = Instant::now();
while !ok() {
assert!(t0.elapsed() < budget, "timed out waiting for {what}");
std::thread::sleep(Duration::from_millis(50));
}
}
#[test]
fn a_dev_box_streams_its_boot_log_sealed_to_the_installer_once_its_metadata_seed_lands() {
let installer = wire::generate();
let root = std::env::temp_dir().join(format!("holger-dev-narrate-udp-{}", std::process::id()));
let _ = std::fs::remove_dir_all(&root);
std::fs::create_dir_all(root.join("pairing")).unwrap();
let seed = root.join(seedkey::SEED_REL);
// A ONE-box medium: the marker first, the shop's seed (off the metadata) a moment later.
std::fs::write(&seed, "nordisk-pairing-seed-from = \"upcloud-metadata\"\n").unwrap();
let real = format!("estate = \"test3.holger.rs\"\nkind = \"holger\"\n[installer_link]\ninstaller_pubkey = \"{}\"\nvm_private = \"broken\"\n", seedkey::b64_of_key(installer.public_key().as_bytes()));
let writer = {
let seed = seed.clone();
std::thread::spawn(move || {
std::thread::sleep(Duration::from_millis(600));
std::fs::write(&seed, real).unwrap();
})
};
LOG.push("korp-start: STAGE exec holger-mvp-metered");
LOG.push(&dev_narrate::stage_line(
"pairing",
"the seed is read and the installer's listener serves",
));
let at = dev_narrate::start_at(
&LOG,
&root,
"holger-server",
"127.0.0.1:0".parse().unwrap(),
Duration::from_secs(10),
)
.expect("the sender starts once the marker is the shop's seed");
writer.join().unwrap();
let out = root.join("main.narrate.log");
let heard = Arc::new(Mutex::new(Vec::<String>::new()));
let h = heard.clone();
let rx = receiver::start(
installer.to_bytes(),
vec![Target {
name: "main".into(),
addr: at,
out: out.clone(),
}],
Box::new(move |vm, line| h.lock().unwrap().push(format!("{vm}|{line}"))),
)
.unwrap();
wait_for("the buffered lines", Duration::from_secs(10), || {
heard
.lock()
.unwrap()
.iter()
.any(|l| l.contains("STAGE pairing"))
});
// Live: a certify step said after the hello arrives too.
LOG.push("front: certifying the twin — STAGE certify asked");
wait_for("the live line", Duration::from_secs(10), || {
heard
.lock()
.unwrap()
.iter()
.any(|l| l.contains("STAGE certify"))
});
let seen = rx.stop();
let text = std::fs::read_to_string(&out).unwrap();
assert!(
text.contains("korp-start: STAGE exec")
&& text.contains("STAGE pairing")
&& text.contains("STAGE certify"),
"{text}"
);
assert!(
text.lines().next().unwrap().contains("holger-server"),
"the stream says whose it is:\n{text}"
);
assert_eq!(seen["main"].forged, 0);
let _ = std::fs::remove_dir_all(&root);
}
#[test]
fn a_marker_that_never_becomes_a_seed_narrates_nothing_and_says_why() {
let root =
std::env::temp_dir().join(format!("holger-dev-narrate-marker-{}", std::process::id()));
let _ = std::fs::remove_dir_all(&root);
std::fs::create_dir_all(root.join("pairing")).unwrap();
std::fs::write(
root.join(seedkey::SEED_REL),
"nordisk-pairing-seed-from = \"upcloud-metadata\"\n",
)
.unwrap();
let e = dev_narrate::start_at(
&LOG,
&root,
"holger-estate-front",
"127.0.0.1:0".parse().unwrap(),
Duration::from_millis(300),
)
.unwrap_err();
assert!(e.contains("still the metadata marker"), "{e}");
let _ = std::fs::remove_dir_all(&root);
}