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

nordisk/gunnar

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"
    );
}