crates/gunnar-wire/tests/advertise_peel_cost.rs
//! **The ref advertisement peels a tag zero times once its peel is stored.**
//!
//! The advertisement (v2 `ls-refs`, and the opening round of every clone, fetch
//! and replication) has to report each annotated tag's peeled target on a
//! `peeled:` suffix. It can get that target two ways, and the difference is the
//! whole of the finding:
//!
//! * **stored** — the ref row carries a peeled column, filled once at ingest
//! (`receive_pack::edit_for` → `RefCas::peeled` → the store), the way git's
//! `packed-refs` keeps a `^peeled` line. `advertise_from` reads it straight
//! out of the row: **zero object reads**.
//! * **re-derived** — the row has no peel (an older push, a backend without the
//! column), so `advertise_from` falls back to `graph.peel`, which reads the
//! tag object to learn its target: **one object read per tag**, the floor
//! measured after the read-halving fix.
//!
//! Both must advertise the **byte-identical** `peeled:` oid — the stored one
//! is exactly what the fallback would derive. So the guard is an op-count, not
//! a wall clock (measure, never assert): a tag written the new way costs 0
//! reads, one written the old way costs 1, and the advertised bytes are the
//! same either way. A test that only checked the bytes would pass the unstored
//! code, which is why the read count is the assertion.
mod common;
use std::io::Cursor;
use gix_hash::Kind;
use gunnar_store::git_storage_trait::{GitOps, RefUpdate};
use gunnar_wire::receive_pack::AllowAll;
use gunnar_wire::{ReceivePack, RefStatus, Session, UploadPack};
use common::{empty_pack, push_bytes, validating_ingest, write_object, Fixture};
/// A v2 `command=ls-refs` asking for `peel` over `refs/` — the exact shape
/// `git ls-remote` and every clone's opening round send.
fn ls_refs_bytes(kind: Kind) -> Vec<u8> {
let fmt = match kind {
Kind::Sha1 => "sha1",
_ => "sha256",
};
let pkt = |payload: &str| {
let mut out = format!("{:04x}", payload.len() + 4).into_bytes();
out.extend_from_slice(payload.as_bytes());
out
};
let mut req = Vec::new();
req.extend(pkt("command=ls-refs\n"));
req.extend(pkt(&format!("object-format={fmt}\n")));
req.extend_from_slice(b"0001");
req.extend(pkt("peel\n"));
req.extend(pkt("ref-prefix refs/\n"));
req.extend_from_slice(b"0000");
req
}
/// Serve one `ls-refs` against `fixture`, returning the object reads it charged
/// the store and the advertisement text. The store is opened once (untimed and
/// uncounted) by `Fixture::build`; only the advertisement is measured, so the
/// number is the re-derivation and nothing else.
fn advertise(fixture: &Fixture) -> (usize, String) {
let upload = UploadPack::new(&fixture.git, fixture.kind);
let req = ls_refs_bytes(fixture.kind);
fixture.git.reset();
let mut input = Cursor::new(req);
let mut out = Vec::new();
upload
.serve_v2_session(Session::StatelessRpc, &mut input, &mut out)
.expect("the ls-refs advertisement completes");
(
fixture.git.read_count(),
String::from_utf8(out).expect("the advertisement is text"),
)
}
/// The peeled oid the advertisement reported for `name`, from its `peeled:`
/// suffix — `None` if the ref was advertised without one.
fn advertised_peel(advertisement: &str, name: &str) -> Option<String> {
advertisement
.lines()
.find(|line| line.contains(&format!(" {name}")) || line.contains(&format!("{name} ")))
.and_then(|line| {
line.split_whitespace()
.find_map(|word| word.strip_prefix("peeled:"))
.map(str::to_owned)
})
}
/// **The read-side red→green, in one test: old-way tag = 1 read, new-way tag =
/// 0 reads, identical `peeled:` bytes.**
///
/// `Fixture::build` installs `refs/tags/v1` as an annotated tag over `merge`
/// with **no** stored peel — the old way, and the backward-compatible one: a
/// ref row that predates this fix. Advertising it must fall back to `graph.peel`
/// and cost exactly one object read (the tag body). Then the same ref is
/// re-installed **with** its peel stored, and advertising it must cost zero —
/// while reporting the same commit on the wire, to the byte.
#[test]
fn a_stored_peel_advertises_a_tag_with_zero_object_reads() {
for kind in [Kind::Sha1, Kind::Sha256] {
let fixture = Fixture::build(kind);
// ── OLD WAY: the row carries no peel (backward compat). One read. ──
let (old_reads, old_out) = advertise(&fixture);
assert_eq!(
old_reads, 1,
"{kind:?}: an unstored annotated tag must re-peel with exactly one object read \
(the floor); got {old_reads}"
);
let old_peel = advertised_peel(&old_out, "refs/tags/v1")
.expect("the tag was advertised without a peeled: line");
assert_eq!(
old_peel,
fixture.merge.to_string(),
"{kind:?}: the fallback peeled to the wrong commit"
);
// ── NEW WAY: store the peel in the row, exactly as ingest now does. ──
fixture
.git
.put_refs(&[RefUpdate::set("refs/tags/v1", fixture.tag.to_string())
.with_peeled(fixture.merge.to_string())])
.expect("re-install the tag with its peel stored");
let (new_reads, new_out) = advertise(&fixture);
assert_eq!(
new_reads, 0,
"{kind:?}: a tag whose peel is stored must advertise with zero object reads; \
got {new_reads}"
);
let new_peel = advertised_peel(&new_out, "refs/tags/v1")
.expect("the tag was advertised without a peeled: line after storing its peel");
// The whole point: 0 reads and 1 read produce the SAME wire answer.
assert_eq!(
new_peel, old_peel,
"{kind:?}: storing the peel changed the advertised peeled oid — the wire moved, \
which it must not"
);
}
}
/// A tag object body naming `target` as a commit.
fn tag_body(target: &gix_hash::oid, name: &str) -> Vec<u8> {
format!(
"object {}\ntype commit\ntag {name}\n\
tagger Gunnar Test <gunnar@example.invalid> 1700000006 +0000\n\nrelease\n",
target.to_hex()
)
.into_bytes()
}
/// Drive one non-atomic push and hand back the statuses. Mirrors
/// `force_update.rs::push` — the ordinary `git push` path, no `--atomic`.
fn push(fixture: &Fixture, commands: &[(&str, &str, &str)], pack: &[u8]) -> Vec<RefStatus> {
let receive = ReceivePack::new(&fixture.git, fixture.kind);
let input = push_bytes(commands, "report-status object-format=sha1", pack);
let mut input = Cursor::new(input);
let mut out = Vec::new();
receive
.serve(
&mut input,
&mut out,
&validating_ingest(Kind::Sha1),
&AllowAll,
)
.expect("the session completes")
.expect("a push outcome")
.statuses
}
/// **End-to-end: an annotated tag pushed the ordinary way stores its peel, so
/// the next advertisement re-derives nothing.**
///
/// This is the write half filed as the cross-repo follow-up, proven
/// through the real receive path rather than by hand-writing the ref row. A new
/// annotated tag `refs/tags/v2` over `four` is pushed (non-atomic — the default
/// `git push` shape); afterwards the row must carry `four` as its peel and the
/// advertisement must cost **zero** reads for it.
///
/// Seen RED before `edit_for` peeled: the push succeeded, the row's peel was
/// `None`, and the advertisement charged one read to re-derive it.
#[test]
fn a_pushed_annotated_tag_stores_its_peel_and_advertises_free() {
let fixture = Fixture::build(Kind::Sha1);
// A tag object over `four`, already in the store so the push only validates.
let tag = write_object(
&fixture.git,
Kind::Sha1,
gix_object::Kind::Tag,
&tag_body(&fixture.four, "v2"),
);
let zero = "0".repeat(40);
let statuses = push(
&fixture,
&[(&zero, &tag.to_string(), "refs/tags/v2")],
&empty_pack(Kind::Sha1),
);
assert_eq!(statuses[0], RefStatus::Ok, "the tag push was refused");
// The row now carries the peel, filled at ingest.
let row_peel = fixture
.git
.refs()
.expect("list refs")
.into_iter()
.find(|r| r.name == "refs/tags/v2")
.and_then(|r| r.peeled)
.map(|raw| gix_hash::ObjectId::try_from(raw.as_slice()).expect("a well-sized oid"));
assert_eq!(
row_peel,
Some(fixture.four),
"the push did not store the tag's peeled target"
);
// And so the advertisement re-derives nothing for it. `refs/tags/v1` (the
// fixture's other, unstored tag) still costs its one fallback read, so the
// whole advertisement costs exactly that one — never two.
let (reads, out) = advertise(&fixture);
assert_eq!(
advertised_peel(&out, "refs/tags/v2").as_deref(),
Some(fixture.four.to_string().as_str()),
"the pushed tag advertised the wrong peel"
);
assert_eq!(
reads, 1,
"with v2's peel stored, only v1 (unstored) should re-peel; the whole advertisement \
charged {reads} reads, so a stored peel was still being re-derived"
);
}
/// **A lightweight tag stores no peel, and that is correct.**
///
/// A ref that points straight at a commit has nothing to peel — git writes no
/// `^peeled` for it — so `edit_for` must leave the row's peel `None`, and the
/// advertisement must report no `peeled:` for it while charging no read.
#[test]
fn a_lightweight_tag_stores_no_peel() {
let fixture = Fixture::build(Kind::Sha1);
let zero = "0".repeat(40);
let statuses = push(
&fixture,
// Straight at `four`, a commit: no tag object, nothing to peel.
&[(&zero, &fixture.four.to_string(), "refs/tags/lw")],
&empty_pack(Kind::Sha1),
);
assert_eq!(
statuses[0],
RefStatus::Ok,
"the lightweight tag was refused"
);
let row = fixture
.git
.refs()
.expect("list refs")
.into_iter()
.find(|r| r.name == "refs/tags/lw")
.expect("the lightweight tag row");
assert_eq!(
row.peeled, None,
"a lightweight tag invented a peeled target it has no business having"
);
let (_reads, out) = advertise(&fixture);
assert_eq!(
advertised_peel(&out, "refs/tags/lw"),
None,
"a lightweight tag was advertised with a peeled: line"
);
}