crates/gunnar-front/tests/log_on_disk.rs
//! **vm1 keeps its own log on its disk** (RUNTIME LOGS gap 3, 2026-09-30).
//!
//! vm1 runs no narrator and has no data volume of its own: its whole log was
//! the installer's RAM ring (nordisk-pairing `BootLog`), so a crash — PID 1
//! gone, the machine reset — left nothing anybody could read afterwards. The
//! front now writes every line of that log to `<data root>/log/door.log`
//! (bounded, rotated to `.1`), and the next boot carries the previous boot's
//! last lines into its log, where the log door serves them.
//!
//! Its own test binary: it uses the process-wide `BOOT_LOG`, as `main` does.
use std::time::{Duration, Instant};
use gunnar_server::installer_listener::{keep_on_disk, BOOT_LOG, KEEP_REL};
#[test]
fn the_fronts_log_is_on_its_disk_within_a_second_and_the_next_boot_serves_its_last_lines() {
let root = std::env::temp_dir().join(format!("front-log-on-disk-{}", std::process::id()));
let _ = std::fs::remove_dir_all(&root);
std::fs::create_dir_all(&root).unwrap();
gunnar_server::installer_listener::capture();
let carried = keep_on_disk(&root);
assert_eq!(carried, 0, "a first boot carries nothing");
let marker = format!(
"2026-09-30T11:00:00.000000Z INFO gunnar_front::web: GET /logproof-{} 200",
std::process::id()
);
BOOT_LOG.push(&marker);
let file = root.join(KEEP_REL);
let t = Instant::now();
while !std::fs::read_to_string(&file)
.unwrap_or_default()
.contains(&marker)
{
assert!(
t.elapsed() < Duration::from_secs(5),
"the line reached {} within its second",
file.display()
);
std::thread::sleep(Duration::from_millis(100));
}
// The next boot (a fresh log over the same disk) carries it, marked.
let next = nordisk_pairing::bootlog::BootLog::new(1000);
assert!(
next.carry_previous(
&file,
nordisk_pairing::bootlog::CARRY_LINES,
nordisk_pairing::bootlog::CARRY_BYTES
) >= 1
);
let lines = next.since(0, 2000).lines;
assert!(
lines
.iter()
.any(|l| l == &format!("(previous boot) {marker}")),
"{lines:?}"
);
let _ = std::fs::remove_dir_all(&root);
}
/// **main keeps the log on the disk right after the ESP's story is taken**:
/// before stderr is captured and tracing installed, so a start-time failure
/// is on the disk too.
#[test]
fn the_front_keeps_its_log_on_its_disk_first_thing() {
let src = include_str!("../src/pid1.rs");
let main = &src[src.find("fn main()").unwrap()..];
let (capture, keep, stderr) = (
main.find("installer_listener::capture()").expect("capture"),
main.find("installer_listener::keep_on_disk(&data_root())")
.expect("main keeps its log on its disk"),
main.find("stderr_capture::start()").expect("stderr"),
);
assert!(
capture < keep && keep < stderr,
"capture, then the disk, then stderr"
);
}