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

nordisk/edda

crates/storage-git-gix/parked-gix-2/coldtier-archive/tests/refs_from_archive.rs

//! `ls-remote` answered from `__gunnar_refs__` instead of from `gix-ref`, and
//! the proof that the two are the same bytes.
//!
//! # Why this is not just an output comparison
//!
//! A fidelity check that compares two outputs is **blind to a switch that does
//! not change the output** — which is the whole point of the switch. On
//! 2026-08-04 a lane's instrument measured the wrong path for an entire round
//! because its wrapper did not forward one method and the outputs matched by
//! design. So this file asserts three separate things, and no two of them can
//! fail together silently:
//!
//! 1. **The bytes match.** Same `UploadPack`, same `ObjectStore`, two different
//!    `RefStore`s — so the only variable is the ref backend.
//! 2. **The archive path actually answered.** `ArchiveRefStore::served()` is
//!    read either side of the advertisement and must have moved. A byte
//!    comparison cannot tell you which code produced the bytes; a counter can.
//! 3. **The filesystem is gone.** Every loose ref and `packed-refs` is deleted,
//!    and the archive still produces the identical advertisement while `gix-ref`
//!    now produces the *empty-repository* one. This is the same discipline
//!    `cold_tier_wired.rs` applies to packs — the honest way to ask "where did
//!    the bytes come from" is to take the other source away — and without it a
//!    green here would also be produced by a store that quietly fell back to
//!    the filesystem.

// Moved from `crates/gunnar-cli/tests/` on 2026-08-05, unchanged below this
// header. It was `#![cfg(all(feature = "server", feature = "znippy"))]` there;
// `gunnar-cli` has no `znippy` feature any more (see its manifest), and this
// crate IS the cold tier's binary, so the gate is gone rather than restated.
//
// The arm is installed by `arm()` below rather than by `main`, because a test
// binary has no `main` of ours: without it `Mode::Znippy.available()` is false
// and every assertion in this file would be measuring the `fs` arm under its
// name.

use gunnar_znippy::arm;

// The harness is `gunnar-cli`'s own, reached by path rather than copied: it is
// 787 lines and there must be exactly one of it (LAW 5). `#[path]` is what
// cargo offers for a test module that lives outside this crate's `tests/`.
#[path = "../../gunnar-cli/tests/common/mod.rs"]
mod common;

use std::path::Path;

use common::{tools_present, Harness};
use gunnar_core::{HashKind, RepoRef};

/// The 19 members of `workspace_nordisk`, the corpus every other gunnar lane
/// measures against. Overridable so the test can be pointed at one repo while
/// being written; the default is the real thing.
const REAL_MEMBERS: &[&str] = &[
    "nornir",
    "modgunn",
    "funnel-core",
    "holger",
    "facett",
    "edda",
    "skade",
    "znippy",
    "znippy-zoomies",
    "knut",
    "korp",
    "korp-collectors",
    "ordning",
    "Skidbladnir",
    "tunnr",
    "draupnir",
    "dwarves",
    "bifrost",
    "workspace_nordisk",
];

fn real_src_root() -> std::path::PathBuf {
    std::path::PathBuf::from(
        std::env::var("GUNNAR_REFS_REAL_SRC").unwrap_or_else(|_| "/home/rickard/git".to_string()),
    )
}

fn members() -> Vec<String> {
    match std::env::var("GUNNAR_REFS_MEMBERS") {
        Ok(v) if !v.trim().is_empty() => v.split(':').map(|s| s.trim().to_string()).collect(),
        _ => REAL_MEMBERS.iter().map(|s| s.to_string()).collect(),
    }
}

/// The v0 upload-pack advertisement, which is what `git ls-remote` reads.
///
/// Deliberately the **real** `gunnar-wire` entry point rather than a
/// reimplementation: the whole claim is that the wire layer cannot tell the two
/// ref backends apart, and a local copy of the emit loop would be a claim about
/// a copy.
fn advertisement(
    objects: &dyn gunnar_store::ObjectStore,
    refs: &dyn gunnar_store::RefStore,
) -> Vec<u8> {
    let upload = gunnar_wire::UploadPack::new(objects, refs);
    let mut out = Vec::new();
    upload
        .write_ref_advertisement(&mut out)
        .expect("writing the v0 ref advertisement");
    out
}

struct Arm;

impl Arm {
    fn znippy() -> Self {
        std::env::set_var(gunnar_cli::server::storage::MODE_ENV, "znippy");
        Arm
    }
}

impl Drop for Arm {
    fn drop(&mut self) {
        std::env::remove_var(gunnar_cli::server::storage::MODE_ENV);
    }
}

/// Delete every reference `gix-ref` can read: the whole loose `refs/` tree and
/// `packed-refs`. `HEAD` is left alone, because `HEAD` is not a reference
/// `gix-ref` stores under `refs/` and leaving it is what makes the fs arm
/// degrade to *unborn* rather than to *broken*.
fn take_the_filesystem_away(git_dir: &Path) {
    let refs_dir = git_dir.join("refs");
    if refs_dir.exists() {
        std::fs::remove_dir_all(&refs_dir).expect("removing the loose ref tree");
    }
    let packed = git_dir.join("packed-refs");
    if packed.exists() {
        std::fs::remove_file(&packed).expect("removing packed-refs");
    }
}

/// One repository, all three assertions.
///
/// Returns `(refs_advertised, bytes)` so the caller can report the corpus totals
/// and, more importantly, so an empty corpus cannot masquerade as a pass.
fn check_one(src: &Path, name: &str) -> Option<(usize, usize)> {
    let hash = HashKind::Sha1;
    let _arm = Arm::znippy();
    let harness = Harness::start("team", "project", hash);

    // Build the corpus by pushing into a bare repo rather than cloning one:
    // a clone would put refs on the server that the server never negotiated,
    // and `refs/remotes/*` would come along for the ride.
    let work = harness.dir.path().join("mirror");
    let clone = harness.git(
        harness.dir.path(),
        &[
            "clone",
            "--bare",
            "--quiet",
            &src.display().to_string(),
            &work.display().to_string(),
        ],
    );
    if !clone.ok {
        eprintln!("skipping {name}: could not mirror-clone it");
        return None;
    }
    let push = harness.git(
        &work,
        &["push", "--mirror", &harness.url("team", "project")],
    );
    if !push.ok {
        eprintln!("skipping {name}: mirror push failed: {}", push.stderr);
        return None;
    }

    // `gunnar gc` is what seals the consolidated pack AND `__gunnar_refs__`;
    // a push alone does not write the section at all.
    gunnar_cli::server::admin::gc(
        &harness.stores,
        gunnar_cli::cli::GcArgs {
            path: Some("team/project".into()),
            ..Default::default()
        },
    )
    .expect("gunnar gc with the znippy cold tier");

    let repo = RepoRef::new("team", "project").expect("a legal repo path");
    let opened = harness
        .stores
        .catalog()
        .open(&repo)
        .expect("the repository");
    let store = harness
        .stores
        .repo_store(&opened)
        .expect("the repository is open");
    let git_dir = harness.git_dir("team", "project");
    let archive = gunnar_cli::server::storage::sealed_archive_path(&git_dir);
    assert!(archive.exists(), "{name}: gc did not seal an archive");

    let objects = store.objects();

    // ── 1. the bytes ─────────────────────────────────────────────────────────
    //
    // **The reference side is the STORE's ref log, not `gix-ref`.** It used to
    // be `store.refs()`, and that comparison is now vacuous: a znippy-git
    // repository's references live in the store, so the filesystem ref store is
    // empty and `advertisement` over it returns the empty-repository answer —
    // an all-zeros oid and `capabilities^{}`. Comparing the archive against
    // that would pass only if the archive were empty too, which is exactly the
    // hollow shape this file's own header warns about.
    let live_refs = gunnar_cli::server::storage::write_refs(&git_dir, store.hash_kind())
        .expect("the znippy-git ref log");
    let from_gix = advertisement(&objects, live_refs.store.as_ref());
    let archived = gunnar_coldtier::refstore::ArchiveRefStore::open(&archive)
        .expect("reading __gunnar_refs__")
        .unwrap_or_else(|| panic!("{name}: the sealed archive carries no __gunnar_refs__ section"));

    let before = archived.served();
    let from_archive = advertisement(&objects, &archived);
    let after = archived.served();

    assert_eq!(
        from_gix,
        from_archive,
        "{name}: the advertisement from the archive differs from the live ref log.\n  live: \
         {:?}\n  archive: {:?}",
        String::from_utf8_lossy(&from_gix),
        String::from_utf8_lossy(&from_archive)
    );

    // ── 2. the archive path actually answered ────────────────────────────────
    assert!(
        after > before,
        "{name}: the advertisement matched but the archive store was never called, so this \
         comparison is measuring gix-ref against itself"
    );

    // A namespace with no refs would make every comparison above trivially
    // true. Refuse it rather than report a pass.
    let n = archived.len();
    assert!(
        n > 0,
        "{name}: the sealed namespace is empty; the comparison would pass on anything"
    );

    // ── 3. no filesystem ─────────────────────────────────────────────────────
    take_the_filesystem_away(&git_dir);

    let archived2 = gunnar_coldtier::refstore::ArchiveRefStore::open(&archive)
        .expect("re-reading __gunnar_refs__ with no refs on disk")
        .expect("the section is still there");
    let from_archive_bare = advertisement(&objects, &archived2);
    assert_eq!(
        from_gix, from_archive_bare,
        "{name}: with every ref file deleted the archive stopped producing the same \
         advertisement, so it had been reading the filesystem"
    );

    // The red that makes the line above mean something: with the ref files gone
    // `gix-ref` must NOT still produce the advertisement. If it does, this test
    // is measuring a cache and says so instead of reporting a win.
    let from_gix_bare = advertisement(&objects, &store.refs());
    assert_ne!(
        from_gix, from_gix_bare,
        "{name}: gix-ref produced the full advertisement with no ref files on disk, so deleting \
         them did not take the filesystem away and assertion 3 proves nothing"
    );

    Some((n, from_gix.len()))
}

/// The whole claim, over the 19 real repositories.
#[test]
fn the_advertisement_from_the_archive_is_byte_identical_on_the_real_corpus() {
    arm();
    if !tools_present() {
        eprintln!("skipping: git and ssh are required");
        return;
    }
    let root = real_src_root();
    let mut checked = 0usize;
    let mut refs_total = 0usize;

    for name in members() {
        let src = root.join(&name);
        if !src.join(".git").exists() && !src.join("HEAD").exists() {
            eprintln!("skipping {name}: not a git checkout at {}", src.display());
            continue;
        }
        if let Some((n, _bytes)) = check_one(&src, &name) {
            checked += 1;
            refs_total += n;
            eprintln!("  {name}: {n} refs, advertisement byte-identical");
        }
    }

    // The fixture trap, eight lanes deep: a corpus that silently did not get
    // built passes every assertion above by never running one.
    assert!(
        checked >= 12,
        "only {checked} of the 19 members were checked; the corpus did not get built, and a \
         corpus that did not get built cannot have proved anything"
    );
    assert!(
        refs_total > 400,
        "only {refs_total} refs across {checked} repositories; too few"
    );
    eprintln!("byte-identical advertisement on {checked} repositories, {refs_total} refs total");
}