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

nordisk/nornir

tests/eager_register.rs

//! Inject-assert: **register is EAGER by default** (LAW 1).
//!
//! Registering a monitored workspace must CLONE its members *at once* — members
//! populate immediately, NOT only after a poll tick. (The warehouse BUILD that
//! followed was the v1 populate, removed with Mímir v2.) The opt-out (`lazy`)
//! still records with empty members.
//!
//! These tests drive the exact pipeline the eager register path runs across the
//! CLI / server RPC / MCP tool: `Workspace::new` (seed members from the
//! descriptor) → `upsert` → (eager) `monitor::fetch_workspace` (clone members).
//! The lazy variant stops after `upsert`.
//!
//! Real input: two fixture git crates cloned over `file://`. Real output: the
//! registry row's per-member `last_seen_sha` (populated ⇒ cloned).

use std::path::{Path, PathBuf};

use nornir::monitor;
use nornir::registry::{Mode, Registry, Workspace};

/// Make a minimal committed Rust-crate fixture repo at `dir` and return a
/// `file://` URL a pure-gix clone can fetch. Two crates so the workspace has
/// ≥2 members (LAW 1) and the cross-repo dep graph has something to chew on.
fn fixture_crate(dir: &Path, name: &str) {
    std::fs::create_dir_all(dir.join("src")).unwrap();
    std::fs::write(
        dir.join("Cargo.toml"),
        format!(
            "[package]\nname = \"{name}\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[dependencies]\n"
        ),
    )
    .unwrap();
    std::fs::write(
        dir.join("src").join("lib.rs"),
        format!("//! {name} fixture crate\npub fn {name}_answer() -> i32 {{ 42 }}\n"),
    )
    .unwrap();
    nornir::gitio::init(dir).unwrap();
    nornir::gitio::commit_all(dir, "init fixture").unwrap();
}

fn file_url(dir: &Path) -> String {
    format!("file://{}", dir.display())
}

/// Build the descriptor + registry for a monitored workspace with two
/// `file://`-cloneable members. Returns (registry, server-root, descriptor-path).
fn setup(tmp: &Path) -> (Registry, PathBuf, PathBuf) {
    // Two source crates the members clone FROM.
    let src_alpha = tmp.join("src_alpha");
    let src_beta = tmp.join("src_beta");
    fixture_crate(&src_alpha, "alpha");
    fixture_crate(&src_beta, "beta");

    // The workspace descriptor (git = file:// so a pure-gix clone works offline).
    let desc = tmp.join("nornir-workspace.toml");
    std::fs::write(
        &desc,
        format!(
            "[workspace]\nname = \"eagertest\"\n\n\
             [repos.alpha]\ngit = \"{}\"\n\n\
             [repos.beta]\ngit = \"{}\"\n",
            file_url(&src_alpha),
            file_url(&src_beta),
        ),
    )
    .unwrap();

    // The server root where <name>/{git,builds} land.
    let root = tmp.join("root");
    std::fs::create_dir_all(&root).unwrap();
    let reg = Registry::open(&root).unwrap();
    (reg, root, desc)
}

#[test]
fn register_is_eager_by_default_members_populate_at_once_no_fake_snapshot() {
    let tmp = tempfile::tempdir().unwrap();
    let (reg, root, desc) = setup(tmp.path());

    // ── The eager register path (default): record → fetch → build, inline. ──
    let ws = Workspace::new(
        "eagertest".into(),
        desc.to_string_lossy().into_owned(),
        Mode::Monitored,
        "60s".into(),
        None,
    );
    // The descriptor seeded ≥2 members, but none are cloned yet (lazy state).
    assert_eq!(ws.members.len(), 2, "descriptor seeds both members");
    assert!(
        ws.members.iter().all(|m| m.last_seen_sha.is_empty()),
        "no member is cloned at the record step — eager fetch must do it"
    );
    reg.upsert(&ws).unwrap();

    // EAGER: clone members AT ONCE (what register now runs). The warehouse build
    // that followed was the v1 populate, removed with Mímir v2.
    monitor::fetch_workspace(&reg, &root, "eagertest").expect("eager fetch clones members");

    // ── Assert members populated IMMEDIATELY (non-empty), not after a poll. ──
    let after = reg.get("eagertest").unwrap().unwrap();
    assert_eq!(after.members.len(), 2, "still two members");
    for m in &after.members {
        assert!(
            !m.last_seen_sha.is_empty(),
            "member `{}` must be cloned at once (has a HEAD sha) — register is eager",
            m.name
        );
        assert_eq!(m.sync_state, "ok", "member `{}` cloned cleanly", m.name);
    }
    // Both member checkouts physically exist under <root>/<name>/git/<member>/.
    for member in ["alpha", "beta"] {
        assert!(
            root.join("eagertest")
                .join("git")
                .join(member)
                .join(".git")
                .exists(),
            "member `{member}` checkout materialized at once"
        );
    }

    // ── No v1 build ran, so no snapshot is recorded — never a fake one. ──
    assert!(
        after.current_snapshot.trim().is_empty(),
        "no warehouse build exists until the Mímir v2 consumer loop lands"
    );
}

#[test]
fn lazy_opt_out_records_with_empty_members_no_snapshot() {
    let tmp = tempfile::tempdir().unwrap();
    let (reg, root, desc) = setup(tmp.path());

    // ── The `--lazy` / `eager:false` opt-out: record ONLY. ──
    let ws = Workspace::new(
        "eagertest".into(),
        desc.to_string_lossy().into_owned(),
        Mode::Monitored,
        "60s".into(),
        None,
    );
    reg.upsert(&ws).unwrap();
    // Lazy = stop here: NO fetch_workspace, NO republish.

    let after = reg.get("eagertest").unwrap().unwrap();
    // The descriptor still seeds the member *list*, but nothing is cloned and
    // no warehouse is built — population is deferred to the poll loop.
    assert!(
        after.members.iter().all(|m| m.last_seen_sha.is_empty()),
        "lazy register clones nothing — every member has an empty HEAD sha"
    );
    assert!(
        after.current_snapshot.trim().is_empty(),
        "lazy register builds no warehouse snapshot"
    );
    assert!(
        !root
            .join("eagertest")
            .join("git")
            .join("alpha")
            .join(".git")
            .exists(),
        "lazy register materializes no member checkout"
    );
}