crates/estate-clock/tests/gunnar_time_compat.rs
//! **GUNNAR-TIME/1 across the move** (lane G3b, 2026-10-21). gunnar.rs and
//! test2 update the front first, then the twin, then the main: for a while an
//! old Gunnar front (gunnar-clock) serves new boxes (estate-clock), and a new
//! front serves old boxes. Both sides here are real: the old side is
//! gunnar-clock's `peer.rs` verbatim (`gunnar_time_compat/gunnar_clock_peer.rs`),
//! the new side is this crate.
#[allow(dead_code)]
#[path = "gunnar_time_compat/gunnar_clock_peer.rs"]
mod gunnar_clock;
use std::net::TcpListener;
use estate_clock::peer::{self, Dialect};
const NOW_MS: i64 = 1_789_394_521_628;
fn key() -> (ssh_key::PrivateKey, String) {
let k = peer::generate_key().unwrap();
let line = k.public_key().to_openssh().unwrap();
(k, line)
}
/// An old Gunnar front: gunnar-clock's own `serve` on loopback.
fn old_front(signer: ssh_key::PrivateKey, vouched: Result<(), String>) -> std::net::SocketAddr {
let listener = TcpListener::bind("127.0.0.1:0").unwrap();
let addr = listener.local_addr().unwrap();
std::thread::spawn(move || gunnar_clock::serve(listener, &signer, &move || vouched.clone(), &|_, _| {}));
addr
}
/// A new front: this crate's `serve` on loopback.
fn new_front(signer: ssh_key::PrivateKey, vouched: Result<(), String>) -> std::net::SocketAddr {
let listener = TcpListener::bind("127.0.0.1:0").unwrap();
let addr = listener.local_addr().unwrap();
std::thread::spawn(move || peer::serve(listener, &signer, &move || vouched.clone(), &|_, _| {}));
addr
}
#[test]
fn an_old_gunnar_front_with_a_new_box() {
let (signer, line) = key();
let addr = old_front(signer, Ok(()));
let sample = peer::query(addr, &peer::PeerKey::parse(&line).unwrap()).expect("a new box follows an old Gunnar front");
assert_eq!(sample.source, format!("peer://{addr}"));
assert!(sample.offset_ms.abs() < 1_000, "{sample:?}");
}
#[test]
fn a_new_front_with_an_old_gunnar_box() {
let (signer, line) = key();
let addr = new_front(signer, Ok(()));
let sample = gunnar_clock::query(addr, &gunnar_clock::PeerKey::parse(&line).unwrap()).expect("an old Gunnar box follows a new front");
assert_eq!(sample.source, format!("peer://{addr}"));
assert!(sample.offset_ms.abs() < 1_000, "{sample:?}");
}
#[test]
fn a_new_front_with_a_new_box_still_speaks_estate_time() {
let (signer, line) = key();
let addr = new_front(signer, Ok(()));
let key = peer::PeerKey::parse(&line).unwrap();
assert!(peer::query_in(Dialect::Estate, addr, &key).is_ok());
assert!(peer::query(addr, &key).is_ok());
}
#[test]
fn signed_by_the_old_front_verified_by_the_new_box() {
let (signer, line) = key();
let nonce = peer::fresh_nonce();
let reply = gunnar_clock::answer(peer::request_line_in(Dialect::Gunnar, &nonce).as_bytes(), &signer, NOW_MS, Ok(()));
let key = peer::PeerKey::parse(&line).unwrap();
assert_eq!(peer::verify_reply_in(Dialect::Gunnar, &reply, &key, &nonce), Ok(NOW_MS));
// Not as estate time: the namespace is Gunnar's.
assert!(peer::verify_reply_in(Dialect::Estate, &reply, &key, &nonce).is_err());
}
#[test]
fn signed_by_the_new_front_verified_by_the_old_box() {
let (signer, line) = key();
let nonce = gunnar_clock::fresh_nonce();
let reply = peer::answer(gunnar_clock::request_line(&nonce).as_bytes(), &signer, NOW_MS, Ok(()));
let key = gunnar_clock::PeerKey::parse(&line).unwrap();
assert_eq!(gunnar_clock::verify_reply(&reply, &key, &nonce), Ok(NOW_MS));
// The exact bytes the old box asked about: Gunnar's message, Gunnar's namespace.
let text = String::from_utf8(reply).unwrap();
assert!(text.starts_with(&format!("GUNNAR-TIME/1 {nonce} {NOW_MS}\n-----BEGIN SSH SIGNATURE-----")), "{text}");
}
#[test]
fn the_new_front_refuses_an_old_box_in_words_it_reads() {
let (signer, line) = key();
let addr = new_front(signer, Err("this front's own clock has not synchronised".into()));
let refusal = gunnar_clock::query(addr, &gunnar_clock::PeerKey::parse(&line).unwrap()).expect_err("unvouched");
assert!(refusal.contains("the peer refused to sign: this front's own clock has not synchronised"), "{refusal}");
}
#[test]
fn an_old_front_that_refuses_is_said_to_the_new_box_not_swallowed() {
let (signer, line) = key();
let addr = old_front(signer, Err("this front's own clock has not synchronised".into()));
let refusal = peer::query(addr, &peer::PeerKey::parse(&line).unwrap()).expect_err("unvouched");
assert!(refusal.contains("has not synchronised"), "{refusal}");
}
#[test]
fn an_old_front_signs_with_another_key_and_the_new_box_refuses_it() {
let (signer, _) = key();
let (_, other_line) = key();
let addr = old_front(signer, Ok(()));
let refusal = peer::query(addr, &peer::PeerKey::parse(&other_line).unwrap()).expect_err("wrong key");
assert!(refusal.contains("not the configured peer key"), "{refusal}");
}