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/clock_both_dialects.rs

//! **A Gunnar front answers an old box and a new box** (lane L10, 2026-10-21).
//!
//! Gunnar's estates move from its own gunnar-clock onto edda estate-clock over
//! one release, front first: for a while the front runs this build and its
//! boxes still run the release before, which asks in `GUNNAR-TIME/1`. This is
//! the front as the medium's PID 1 starts it (`gunnar_front::clock::start`,
//! the offer it hands the installer), on a loopback "utility" address, asked
//! by:
//!
//! * the OLD box: gunnar-clock's own `query`, verbatim
//!   (`clock_both_dialects/gunnar_clock_peer.rs`), pinning the offered key;
//! * the NEW box: estate-clock's `peer::query`, as gunnar-server asks.
//!
//! Its own test binary: it sets `GUNNAR_CLOCK_TRUST_SYSTEM` (the kit's Gunnar
//! spelling, read as the alias of `ESTATE_CLOCK_TRUST_SYSTEM`) for the process,
//! so the front signs at once without asking the internet for the time.

#[allow(dead_code)]
#[path = "clock_both_dialects/gunnar_clock_peer.rs"]
mod gunnar_clock;

use std::net::{Ipv4Addr, SocketAddr};
use std::time::{Duration, Instant};

use estate_clock::config::DEFAULT_PEER_PORT;
use estate_clock::peer::{self, Dialect};

/// Ask until the front's listener is up (it binds on its own thread).
fn asked<T>(what: &str, mut ask: impl FnMut() -> Result<T, String>) -> T {
    let t0 = Instant::now();
    loop {
        match ask() {
            Ok(v) => return v,
            Err(why)
                if t0.elapsed() < Duration::from_secs(20)
                    && why.contains("did not accept a connection") =>
            {
                std::thread::sleep(Duration::from_millis(100))
            }
            Err(why) => panic!("{what}: {why}"),
        }
    }
}

#[test]
fn a_gunnar_front_answers_an_old_gunnar_time_box_and_a_new_box() {
    // Set before any thread of this test binary reads the environment (one test).
    std::env::remove_var("ESTATE_CLOCK");
    std::env::remove_var("GUNNAR_CLOCK");
    std::env::remove_var("ESTATE_CLOCK_TRUST_SYSTEM");
    std::env::set_var("GUNNAR_CLOCK_TRUST_SYSTEM", "1");
    let root = tempfile::tempdir().unwrap();
    // A loopback address of this process's own, so a parallel run does not meet it.
    let pid = std::process::id();
    let utility = Ipv4Addr::new(127, 77, (pid >> 8) as u8, (pid as u8).max(2));
    let offer = gunnar_front::clock::start(root.path(), Some(utility), "")
        .expect("the front's clock starts");
    assert_eq!(
        offer.peer.as_deref(),
        Some(format!("peer://{utility}:{DEFAULT_PEER_PORT}").as_str())
    );
    let rows = offer.rows();
    assert_eq!(rows[gunnar_front::clock::OFFER_CLOCK_KEY], offer.key_line);
    let addr = SocketAddr::from((utility, DEFAULT_PEER_PORT));

    // The OLD box, gunnar-clock verbatim: GUNNAR-TIME/1, gunnar-time-v1@gunnar.rs.
    let old_key = gunnar_clock::PeerKey::parse(&offer.key_line).unwrap();
    let old = asked("the old GUNNAR-TIME/1 box", || {
        gunnar_clock::query(addr, &old_key)
    });
    assert!(
        old.offset_ms.abs() < 5_000,
        "the old box read the front's time: {old:?}"
    );

    // The NEW box, as gunnar-server asks (ESTATE-TIME/1 first).
    let key = peer::PeerKey::parse(&offer.key_line).unwrap();
    let new = asked("the new box", || peer::query(addr, &key));
    assert!(
        new.offset_ms.abs() < 5_000,
        "the new box read the front's time: {new:?}"
    );

    // Each dialect alone is answered in itself.
    for d in Dialect::ALL {
        let s = asked(&format!("{d:?}"), || peer::query_in(d, addr, &key));
        assert!(s.offset_ms.abs() < 5_000, "{d:?}: {s:?}");
    }

    // The front's offer is what a Gunnar box parses as its clock.
    let c = estate_clock::config::Config::parse(
        offer.peer.as_deref(),
        Some(&offer.key_line),
        estate_clock::config::Role::Appliance,
    )
    .unwrap();
    assert!(
        matches!(
            c,
            estate_clock::config::Config::Sources {
                peer_key: Some(_),
                ..
            }
        ),
        "{c:?}"
    );
}