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

nordisk/nornir

tests/support/ephemeral_server.rs

//! **One ephemeral `nornir-server`, for any test that needs a live wire.**
//!
//! Three test files had grown their own byte-identical `spawn_server` —
//! `data_submit_rpcs.rs`, `viz_ops_buttons.rs`, `server_scip_ingest.rs` — and every
//! one of them is `#![cfg(all(feature = "viz", feature = "server"))]`. That is not a
//! tidiness complaint, it is *why a bug lived here*: nothing in this repo ever stood
//! a server up in the configuration the fat forge actually ships
//! (`--no-default-features --features server`), so a service gated on the GUI feature
//! could go missing from the daemon and every server test stayed green. This module
//! is the shared body, and it is feature-neutral on purpose: it needs only
//! `wire-client`, which `server` already turns on.
//!
//! The workspace is registered through the redb registry BEFORE the server boots (the
//! handle is dropped so the server can take the single-writer lock), with a *path*
//! member so the fetch loop skips cloning and the workspace is served immediately.
//! The warehouse itself is created lazily — so a non-zero read-back can only have come
//! from a write this test made over the wire.

#![allow(dead_code)]

use std::io::Write as _;
use std::net::TcpListener;
use std::path::PathBuf;
use std::process::{Child, Command};
use std::time::{Duration, Instant};

pub struct Server {
    child: Child,
    root: PathBuf,
    pub endpoint: String,
    pub token: String,
    pub workspace: String,
}

impl Drop for Server {
    fn drop(&mut self) {
        let _ = self.child.kill();
        let _ = self.child.wait();
        let _ = std::fs::remove_dir_all(&self.root);
    }
}

fn free_port() -> u16 {
    TcpListener::bind("127.0.0.1:0")
        .unwrap()
        .local_addr()
        .unwrap()
        .port()
}

/// Spawn a server serving ONE workspace named `ws`, over a warehouse that starts
/// EMPTY. `tag` only keeps parallel spawns from sharing a root/HOME.
pub fn spawn(ws: &str, tag: &str) -> Server {
    let token = format!("{tag}-ephemeral-token-0123456789");
    let port = free_port();
    let endpoint = format!("http://127.0.0.1:{port}");

    static N: std::sync::atomic::AtomicU32 = std::sync::atomic::AtomicU32::new(0);
    let uniq = N.fetch_add(1, std::sync::atomic::Ordering::SeqCst);
    let root = std::env::temp_dir().join(format!("nornir-{tag}-{}-{uniq}", std::process::id()));
    std::fs::create_dir_all(&root).unwrap();
    let server_home = root.join("home");
    let registry_root = server_home.join(".nornir").join("workspaces");
    std::fs::create_dir_all(&registry_root).unwrap();

    let desc = root.join("ws.toml");
    let mut f = std::fs::File::create(&desc).unwrap();
    writeln!(f, "[workspace]\nname = \"{ws}\"\n").unwrap();
    writeln!(
        f,
        "[repos.\"{ws}-core\"]\npath = \"{}\"\n",
        env!("CARGO_MANIFEST_DIR")
    )
    .unwrap();
    f.flush().unwrap();
    drop(f);

    {
        use nornir::registry::{Mode, Registry, Workspace};
        let reg = Registry::open(&registry_root).expect("open registry");
        reg.upsert(&Workspace::new(
            ws.to_string(),
            desc.to_string_lossy().into_owned(),
            Mode::Monitored,
            "3600s".to_string(),
            None,
        ))
        .expect("register workspace");
    } // dropped → releases registry.redb before the server boots

    let child = Command::new(env!("CARGO_BIN_EXE_nornir-server"))
        .env("NORNIR_SERVER_ADDR", format!("127.0.0.1:{port}"))
        .env("NORNIR_SERVER_TOKEN", &token)
        .env("HOME", &server_home)
        .stdout(std::process::Stdio::null())
        .stderr(std::process::Stdio::null())
        .spawn()
        .expect("spawn nornir-server");

    let srv = Server {
        child,
        root,
        endpoint: endpoint.clone(),
        token: token.clone(),
        workspace: ws.to_string(),
    };

    let deadline = Instant::now() + Duration::from_secs(45);
    loop {
        if let Ok(names) = nornir::viz::remote::list_workspaces(&endpoint, &token) {
            if names.iter().any(|s| s == ws) {
                break;
            }
        }
        assert!(
            Instant::now() < deadline,
            "server never served workspace `{ws}`"
        );
        std::thread::sleep(Duration::from_millis(300));
    }
    srv
}