crates/gunnar-wire/tests/advertisement.rs
//! The advertisement must list every argument the parser accepts.
//!
//! Object-format negotiation matters because getting it wrong is invisible: a v2 client
//! will not send `filter` unless `filter` appears in the advertised `fetch`
//! value, so a server that parses more than it advertises has support that
//! never activates, no error, no failing test, no symptom. Stock `git` 2.53
//! advertises exactly `fetch=shallow wait-for-done`, which is what makes the
//! under-advertising shape so easy to copy.
//!
//! `gunnar_wire::caps` fixes it by construction, with one table generating
//! both. This file asserts the resulting property from outside, plus asserts
//! that the assertion itself can fail.
mod common;
use gix_hash::Kind;
use gunnar_wire::caps::{self, Offered, FETCH_ARGUMENTS, LS_REFS_ARGUMENTS};
use gunnar_wire::upload_pack::{FetchRequest, LsRefsRequest, WantPolicy};
use gunnar_wire::UploadPack;
use common::{Fixture, Response};
fn advertisement(kind: Kind) -> Response {
let fixture = Fixture::build(kind);
let upload = UploadPack::new(&fixture.git, fixture.kind);
Response::parse(&upload.capability_advertisement().expect("advertise"))
}
/// Features named by `table` that do not appear in `advertised`.
fn missing_features(advertised: &str, table: &'static [caps::Argument]) -> Vec<&'static str> {
caps::features(table)
.into_iter()
.filter(|feature| !advertised.split(['=', ' ']).any(|word| word == *feature))
.collect()
}
#[test]
fn every_gated_fetch_argument_appears_in_the_fetch_capability() {
let advertised = caps::fetch_capability(Offered::default());
let missing = missing_features(&advertised, FETCH_ARGUMENTS);
assert!(
missing.is_empty(),
"the parser accepts arguments gated on {missing:?}, but the \
advertisement is {advertised:?}: that support can never activate"
);
}
#[test]
fn every_gated_ls_refs_argument_appears_in_the_ls_refs_capability() {
let advertised = caps::ls_refs_capability();
let missing = missing_features(&advertised, LS_REFS_ARGUMENTS);
assert!(missing.is_empty(), "ls-refs under-advertises {missing:?}");
}
/// **The red half.** If the check above cannot fail, it proves nothing, so run
/// it against what stock git advertises, which really is not enough for the
/// arguments gunnar parses.
#[test]
fn the_under_advertising_check_can_fail() {
let stock_git = "fetch=shallow wait-for-done";
let missing = missing_features(stock_git, FETCH_ARGUMENTS);
assert!(
missing.contains(&"filter") && missing.contains(&"ref-in-want"),
"the check did not notice that {stock_git:?} omits filter and \
ref-in-want; it would not notice gunnar omitting them either"
);
}
/// Advertising is one half; the other is that the parser really does take each
/// argument. A feature list generated from a table nothing reads would pass the
/// tests above and still be fiction.
#[test]
fn the_parser_accepts_every_argument_in_its_own_table() {
for argument in FETCH_ARGUMENTS {
let line = match argument.value {
caps::Value::None => argument.name.to_owned(),
caps::Value::Required => match argument.name {
"want" | "have" | "shallow" => {
format!("{} {}", argument.name, "0".repeat(40))
}
"deepen" | "deepen-since" => format!("{} 1", argument.name),
"deepen-not" => format!("{} refs/heads/main", argument.name),
"filter" => format!("{} blob:none", argument.name),
"want-ref" => format!("{} refs/heads/main", argument.name),
other => panic!("no sample value for the argument {other:?}"),
},
};
let parsed =
FetchRequest::parse(&[line.clone().into_bytes()], Kind::Sha1, Offered::default());
assert!(
parsed.is_ok(),
"the table names {:?} but the parser refused {line:?}: {:?}",
argument.name,
parsed.err()
);
}
for argument in LS_REFS_ARGUMENTS {
let line = match argument.value {
caps::Value::None => argument.name.to_owned(),
caps::Value::Required => format!("{} refs/heads/", argument.name),
};
assert!(
LsRefsRequest::parse(&[line.clone().into_bytes()]).is_ok(),
"the table names {:?} but the parser refused {line:?}",
argument.name
);
}
}
#[test]
fn an_argument_outside_the_table_is_refused() {
let err = FetchRequest::parse(
&[b"deepen-forever".to_vec()],
Kind::Sha1,
Offered::default(),
)
.expect_err("an unknown argument must not be accepted quietly");
assert!(
matches!(err, gunnar_wire::Error::UnknownArgument { .. }),
"unexpected error: {err}"
);
}
/// Declining a feature has to remove it from *both* sides at once.
#[test]
fn declining_filter_removes_it_from_the_advertisement_and_the_parser() {
let offered = Offered { filter: false };
let advertised = caps::fetch_capability(offered);
assert!(
!advertised.contains("filter"),
"filter was declined but is still advertised: {advertised}"
);
assert!(
FetchRequest::parse(&[b"filter blob:none".to_vec()], Kind::Sha1, offered).is_err(),
"filter was declined but the parser still accepts it"
);
// And it is genuinely a knob, not a constant that is always off.
assert!(caps::fetch_capability(Offered::default()).contains("filter"));
}
#[test]
fn the_v2_advertisement_has_the_shape_git_expects() {
let response = advertisement(Kind::Sha1);
let lines = response.lines();
assert_eq!(lines.first().map(String::as_str), Some("version 2"));
assert!(
lines.iter().any(|line| line.starts_with("agent=gunnar/")),
"no gunnar agent line: {lines:?}"
);
assert!(
lines.iter().any(|line| line == "server-option"),
"{lines:?}"
);
assert!(
lines.iter().any(|line| line == "object-format=sha1"),
"{lines:?}"
);
assert!(
lines.iter().any(|line| line.starts_with("ls-refs=")),
"{lines:?}"
);
assert!(
response
.packets
.last()
.map(gunnar_pktline::Packet::is_flush)
== Some(true),
"the advertisement is not flush-terminated"
);
}
/// The session-costing object-format bug, pinned on the type that decides it: the
/// advertisement is the repository's hash kind and nothing else.
#[test]
fn object_format_is_echoed_from_the_repositorys_actual_hash_kind() {
assert!(advertisement(Kind::Sha256).has_line("object-format=sha256"));
assert!(!advertisement(Kind::Sha256).has_line("object-format=sha1"));
assert!(advertisement(Kind::Sha1).has_line("object-format=sha1"));
assert!(!advertisement(Kind::Sha1).has_line("object-format=sha256"));
}
#[test]
fn a_client_naming_the_wrong_object_format_is_refused() {
let err = caps::check_object_format(Kind::Sha256, Some("sha1"))
.expect_err("a sha1 client on a sha256 repository must not be waved through");
assert!(
matches!(err, gunnar_wire::Error::ObjectFormatMismatch { .. }),
"unexpected error: {err}"
);
caps::check_object_format(Kind::Sha256, Some("sha256")).expect("the matching pair is fine");
caps::check_object_format(Kind::Sha256, None).expect("saying nothing is fine");
}
/// The v0 advertisement offers every ACK mode gunnar implements, and no other.
///
/// This guard used to say "no `multi_ack*` at all, gunnar is single-ACK", and
/// it was enforcing a design position that stock git disproved: offered no ACK
/// mode, git 2.53.0 falls back to single-ACK, which **cannot complete a
/// multi-round fetch over stateless-rpc** — `fetch-pack: protocol error: bad
/// band #78`, exit 128, against exit 0 from `git http-backend` on the same
/// repository (MEASURED 2026-08-05). See
/// [`caps::upload_pack_v0_capabilities`] for the mechanism.
///
/// So the rule is unchanged and only its application moved: **advertise what is
/// implemented, and nothing else.** `multi_ack_detailed` and `no-done` are
/// implemented, in `UploadPack::serve_v0_session`, and are asserted present.
/// Plain `multi_ack` is a third mode with its own semantics that nothing here
/// implements, and is asserted absent — by an exact match, because the prefix
/// test this once used would be satisfied by `multi_ack_detailed` and could
/// never fail again.
#[test]
fn the_v0_capabilities_promise_exactly_the_negotiation_gunnar_implements() {
let caps = caps::upload_pack_v0_capabilities(
Kind::Sha1,
"gunnar/test",
Offered::default(),
WantPolicy::default(),
);
assert!(
!caps.iter().any(|c| c == "multi_ack"),
"plain `multi_ack` is not implemented; advertising it would be the same \
class of lie as under-advertising fetch: {caps:?}"
);
for expected in [
// Without these two, a stateless-rpc client negotiates in single-ACK
// and a multi-round fetch over smart HTTP dies mid-pack.
"multi_ack_detailed",
"no-done",
"side-band-64k",
"ofs-delta",
"shallow",
"include-tag",
"filter",
"object-format=sha1",
] {
assert!(
caps.iter().any(|c| c == expected),
"v0 capabilities are missing {expected}: {caps:?}"
);
}
}
/// The **other** direction of the same rule, and the one v0 got wrong: a
/// capability gunnar implements and does not advertise is refused by the client
/// before the server ever hears the request, so it fails silently on both ends.
///
/// Each policy is checked against the promise it makes, because a `match` that
/// returned the same slice for every arm would satisfy any single-arm test.
#[test]
fn the_v0_capabilities_promise_exactly_the_want_policy_in_force() {
let of = |policy| {
caps::upload_pack_v0_capabilities(Kind::Sha1, "gunnar/test", Offered::default(), policy)
};
let has = |caps: &[String], name: &str| caps.iter().any(|c| c == name);
// The default, and git's `uploadpack.allowReachableSHA1InWant`. Without
// this line a `blob:none` clone over v0 cannot fetch a blob back.
let reachable = of(WantPolicy::ReachableFromRefs);
assert!(
has(&reachable, "allow-reachable-sha1-in-want"),
"the default policy serves reachable non-tip wants and says nothing: {reachable:?}"
);
assert!(!has(&reachable, "allow-tip-sha1-in-want"), "{reachable:?}");
// Strictest: promises neither, and must not, or a client sends a want that
// `check_wants` will refuse.
let tips = of(WantPolicy::AdvertisedTips);
assert!(!has(&tips, "allow-reachable-sha1-in-want"), "{tips:?}");
assert!(!has(&tips, "allow-tip-sha1-in-want"), "{tips:?}");
// `ALLOW_ANY_SHA1` is both bits set in git, so it prints both.
let any = of(WantPolicy::Any);
assert!(has(&any, "allow-reachable-sha1-in-want"), "{any:?}");
assert!(has(&any, "allow-tip-sha1-in-want"), "{any:?}");
}
#[test]
fn receive_pack_advertises_what_receive_pack_implements() {
let caps = caps::receive_pack_capabilities(Kind::Sha256, "gunnar/test");
for expected in [
"report-status",
"report-status-v2",
"delete-refs",
"side-band-64k",
"quiet",
"atomic",
"ofs-delta",
"push-options",
"object-format=sha256",
] {
assert!(
caps.iter().any(|c| c == expected),
"receive-pack does not advertise {expected}: {caps:?}"
);
}
}
#[test]
fn git_protocol_is_parsed_the_way_ssh_delivers_it() {
use gunnar_wire::ProtocolVersion::*;
assert_eq!(gunnar_wire::ProtocolVersion::from_git_protocol(None), V0);
assert_eq!(
gunnar_wire::ProtocolVersion::from_git_protocol(Some("version=2")),
V2
);
assert_eq!(
gunnar_wire::ProtocolVersion::from_git_protocol(Some("version=2:other=1")),
V2
);
// v1 is v0 plus one extra line the client tolerates the absence of.
assert_eq!(
gunnar_wire::ProtocolVersion::from_git_protocol(Some("version=1")),
V0
);
assert_eq!(
gunnar_wire::ProtocolVersion::from_git_protocol(Some("nonsense")),
V0
);
}
// ════════════════════════════════════════════════════════════════════════════
// Every advertised capability has a consumer — all three surfaces
// ════════════════════════════════════════════════════════════════════════════
//
// # What was wrong
//
// The module header above says the advertisement is fixed by construction with
// one table generating both halves. That was true of the **v2 arguments** and
// of nothing else. `upload_pack_v0_capabilities` and
// `receive_pack_capabilities` were hand-written `vec![]`s, and the v2
// capability *names* were a fourth hand-written list in `upload_pack.rs`. So
// `"push-options".to_owned()` sat in a `vec![]` that nothing in the tree had to
// agree with — advertised, parsed, and thrown away for the whole of v1, with
// `.len()` in a log line as its only consumer.
//
// An early audit stated that *"advertised but not
// honoured is not something this codebase can express"*. It was expressible in
// three of the four places, and it was expressed.
//
// # What closes it
//
// `caps::ADVERTISED` is now the only thing that decides what goes on the wire,
// for all three surfaces, and every row carries where it is honoured. These
// tests read that claim and check it against the source.
use std::path::{Path, PathBuf};
use gunnar_wire::caps::{CapValue, Capability, Honoured, ADVERTISED};
/// **The compile-time half of a [`Honoured::ByDependency`] citation.**
///
/// A capability discharged by a crate gunnar merely DEPENDS on cannot be checked
/// by reading a file: from a clone, that crate is a registry package under
/// `~/.cargo/registry`, not a path in this tree. It used to be cited as
/// `../edda/crates/gunnar-pack/src/read.rs` and the guard read that path, which
/// meant the guard passed on a machine with an edda checkout beside gunnar and
/// failed everywhere else — for the absence of a sibling, not for anything about
/// gunnar. (Both stopped being true on 2026-09-09: the sibling is a crates.io
/// dependency now, so the path resolved NOWHERE.)
///
/// The `use` below is the check, and it is stronger than the substring search it
/// replaces: if `gunnar_pack::index_thin_pack` is renamed or removed, this file
/// does not COMPILE. The table then only has to agree with it, which
/// `every_advertised_capability_names_code_that_honours_it` asserts in the
/// citation direction — a citation with no witness here is refused.
#[allow(unused_imports)]
use gunnar_pack::index_thin_pack as _witness_ofs_delta_is_resolved_by_gunnar_pack;
/// Every `(crate, item)` pair that has a `use` above. One line per citation.
const DEPENDENCY_WITNESSES: &[(&str, &str)] = &[("gunnar-pack", "index_thin_pack")];
fn workspace_root() -> PathBuf {
// `crates/gunnar-wire` → `crates` → root.
Path::new(env!("CARGO_MANIFEST_DIR"))
.ancestors()
.nth(2)
.expect("gunnar-wire lives two levels under the workspace root")
.to_path_buf()
}
/// `text` with every `//` comment removed, line comments and doc comments
/// alike, leaving string literals intact.
///
/// # Why this exists
///
/// `contains("push-options")` is satisfied by the paragraph *explaining*
/// push-options two lines above the call site. That exact shape — a
/// `contains(…)` a doc comment could satisfy — was found in this tree on
/// 2026-08-05 in a guard written to catch guards like it. So the source a
/// consumer claim is checked against is the code, and only the code.
///
/// Block comments are not stripped: nothing in this workspace uses them, and a
/// stripper that handled a construct nobody writes would be untested code
/// standing between a guard and its subject.
fn code_only(text: &str) -> String {
let mut out = String::with_capacity(text.len());
for line in text.lines() {
let bytes = line.as_bytes();
let mut in_string = false;
let mut escaped = false;
let mut end = bytes.len();
let mut i = 0;
while i < bytes.len() {
let c = bytes[i];
if escaped {
escaped = false;
} else if c == b'\\' && in_string {
escaped = true;
} else if c == b'"' {
in_string = !in_string;
} else if c == b'/' && !in_string && i + 1 < bytes.len() && bytes[i + 1] == b'/' {
end = i;
break;
}
i += 1;
}
out.push_str(&line[..end]);
out.push('\n');
}
out
}
/// Every capability on every surface, with the surface's name.
fn every_capability() -> Vec<(&'static str, &'static Capability)> {
ADVERTISED
.iter()
.flat_map(|(surface, table)| table.iter().map(move |cap| (*surface, cap)))
.collect()
}
#[test]
fn every_advertised_capability_names_code_that_honours_it() {
let root = workspace_root();
let all = every_capability();
// Not vacuous: three surfaces really are being walked, and each has rows.
assert_eq!(
ADVERTISED.len(),
3,
"a surface was added or lost: {ADVERTISED:?}"
);
assert!(
all.len() >= 25,
"only {} capabilities across all three surfaces, which is fewer than gunnar \
advertises; this test is looking at the wrong thing",
all.len()
);
let mut checked = 0usize;
let mut vacuous: Vec<&str> = Vec::new();
for (surface, cap) in &all {
match cap.honoured {
Honoured::ByDependency { krate, item } => {
// The claim is that a named ITEM exists in a named CRATE, and the
// COMPILER has already asserted it: `DEPENDENCY_WITNESSES` below
// carries a `use` of every one, so this file does not build if the
// item goes. What is left to check here is that the table and the
// witness list agree — a citation with no witness is a claim
// nothing verified, and it is the shape this arm was created to
// stop (`../edda/crates/gunnar-pack/src/read.rs`, a path that
// resolved only on a machine with a sibling checkout).
assert!(
DEPENDENCY_WITNESSES.contains(&(krate, item)),
"{surface} advertises `{}` and says {krate}::{item} honours it, and \
nothing in DEPENDENCY_WITNESSES names that pair. Add a `use` for it \
there, so the compiler is what checks the citation.",
cap.name
);
checked += 1;
}
Honoured::By { file, code } => {
let path = root.join(file);
let text = std::fs::read_to_string(&path).unwrap_or_else(|err| {
panic!(
"{surface} advertises `{}` and says it is honoured in {file}, which \
cannot be read: {err}",
cap.name
)
});
assert!(
code_only(&text).contains(code),
"{surface} advertises `{}` and claims {file} honours it with `{code}`, \
but no LINE OF CODE there contains that (comments are stripped before \
looking, so a doc comment mentioning it does not count). Either the \
consumer was deleted — in which case the capability must come out of \
the table, because advertising it is a wrong answer on the wire — or \
the code moved and the table must follow it.",
cap.name
);
checked += 1;
}
Honoured::Vacuously(reason) => {
assert!(
reason.len() > 80,
"{surface}'s `{}` is marked vacuously honoured with a {}-character \
reason. This variant is the escape hatch from the check above; it has \
to carry the argument, not a label.",
cap.name,
reason.len()
);
vacuous.push(cap.name);
}
}
}
// The escape hatch must not grow quietly. Three today: `agent` (twice over,
// once per surface — it is one constant) and `quiet` and `server-option`.
assert!(
checked >= 20,
"only {checked} capabilities were checked against real code; the rest took the \
vacuous exit: {vacuous:?}"
);
vacuous.sort_unstable();
vacuous.dedup();
assert_eq!(
vacuous,
vec!["agent", "quiet", "server-option"],
"the set of capabilities that honour themselves by doing nothing changed. Each one \
is a capability gunnar promises on the wire and has no code for; adding to this \
list is a decision, not a refactor."
);
}
/// **The red half.** The check above must be able to fail, and must not be
/// satisfiable by a comment.
#[test]
fn the_consumer_check_can_fail_and_a_comment_does_not_satisfy_it() {
// ── a doc comment is not a consumer ────────────────────────────────────
let prose = "/// `push-options` is read here and handed to the gate.\n\
let x = 1;\n";
assert!(
prose.contains(r#"push-options"#),
"the naive check would pass on this"
);
assert!(
!code_only(prose).contains("push-options"),
"a doc comment satisfied the consumer check, which is exactly the defect this \
stripper exists for"
);
// ── a trailing comment does not, either ────────────────────────────────
let trailing = "let sideband = false; // peer.has(\"side-band-64k\")\n";
assert!(!code_only(trailing).contains(r#"peer.has("side-band-64k")"#));
// ── but real code does, on the same line as a comment ──────────────────
let real = "let atomic = peer.has(\"atomic\"); // the batch flag\n";
assert!(code_only(real).contains(r#"peer.has("atomic")"#));
// ── a `//` inside a string literal is not a comment ────────────────────
let url = "let u = \"ssh://git@host\"; let a = peer.has(\"atomic\");\n";
assert!(
code_only(url).contains(r#"peer.has("atomic")"#),
"the stripper cut the line at a `//` inside a string literal and lost real code"
);
// ── and the real check really would notice a missing consumer ──────────
let root = workspace_root();
let bogus = Capability {
name: "not-a-real-capability",
value: CapValue::Bare,
offered: None,
honoured: Honoured::By {
file: "crates/gunnar-wire/src/receive_pack.rs",
code: r#"peer.has("not-a-real-capability")"#,
},
requires: None,
};
let Honoured::By { file, code } = bogus.honoured else {
unreachable!()
};
let text = std::fs::read_to_string(root.join(file)).expect("read receive_pack.rs");
assert!(
!code_only(&text).contains(code),
"the check would pass on a capability nothing reads, so it checks nothing"
);
// …and it passes on one that IS read, in the same file, so the negative
// above is not just a file that never matches anything.
assert!(code_only(&text).contains(r#"peer.has("push-options")"#));
}
/// The bytes on the wire are the table, on every surface.
///
/// Generating from a table proves nothing if a literal is written beside it.
/// Both hand-written `vec![]`s and the fourth list in `upload_pack.rs` are gone,
/// and this is what says so: the advertisement gunnar actually emits carries
/// exactly the table's names, no more and no fewer.
#[test]
fn the_wire_advertisement_carries_exactly_the_table() {
fn names(lines: &[String]) -> Vec<String> {
let mut n: Vec<String> = lines
.iter()
.map(|l| l.split_once('=').map_or(l.as_str(), |(k, _)| k).to_owned())
.collect();
n.sort();
n
}
fn table_names(table: &'static [Capability]) -> Vec<String> {
let mut n: Vec<String> = table.iter().map(|c| c.name.to_owned()).collect();
n.sort();
n
}
// `WantPolicy::Any` renders every row, so "no more and no fewer" is
// asserted against the WHOLE table rather than against a filtered view of
// it — a filtered comparison would agree with itself if the filter were
// the bug. That the policy column really withdraws rows is
// `the_v0_capabilities_promise_exactly_the_want_policy_in_force`.
let everything = WantPolicy::Any;
for kind in [Kind::Sha1, Kind::Sha256] {
assert_eq!(
names(&caps::receive_pack_capabilities(kind, "gunnar/test")),
table_names(caps::RECEIVE_PACK)
);
assert_eq!(
names(&caps::upload_pack_v0_capabilities(
kind,
"gunnar/test",
Offered::default(),
everything,
)),
table_names(caps::UPLOAD_PACK_V0)
);
// v2 is read back off the WIRE, through the real `UploadPack`, because
// that is the surface whose list lived in another file.
let fixture = Fixture::build(kind);
let upload = UploadPack::new(&fixture.git, fixture.kind);
let bytes = upload.capability_advertisement().expect("advertise");
let response = Response::parse(&bytes);
let on_wire: Vec<String> = response
.lines()
.into_iter()
.map(|l| l.trim().to_owned())
.filter(|l| l != "version 2" && !l.is_empty())
.collect();
let on_wire = names(&on_wire);
assert_eq!(
on_wire,
table_names(caps::UPLOAD_PACK_V2),
"the v2 advertisement on the wire is not the table"
);
}
// Withdrawing `filter` really removes it from the v0 wire list, so the
// `Offered` column is not decoration.
let without = caps::upload_pack_v0_capabilities(
Kind::Sha1,
"gunnar/test",
Offered { filter: false },
WantPolicy::default(),
);
assert!(!without.iter().any(|c| c == "filter"));
assert!(caps::upload_pack_v0_capabilities(
Kind::Sha1,
"gunnar/test",
Offered::default(),
WantPolicy::default()
)
.iter()
.any(|c| c == "filter"));
}
// ════════════════════════════════════════════════════════════════════════════
// The other direction: honoured, but named by nothing
// ════════════════════════════════════════════════════════════════════════════
//
// # The class, stated once
//
// `every_advertised_capability_names_code_that_honours_it` walks the table and
// asks whether each row has a consumer. That catches **a row nothing reads**.
// It cannot catch the inverse — *a behaviour the server enforces that no row
// declares* — because there is no row to walk. Nothing to look at, nothing to
// go stale, nothing to fail.
//
// That inverse has produced four wrong answers in this tree in one day:
//
// | | what was enforced | what said so |
// |---|---|---|
// | v0 incremental fetch | `thin-pack` was advertised and only the v2 parser read it | nothing |
// | v0 capability rows | three rows cited the v2 parser, so the guard watched code that could not be wrong | nothing |
// | v0 lazy fetch | `WantPolicy::ReachableFromRefs` on every request | nothing — fixed in `8f4e6e0` |
// | `--shallow-exclude=side` | the exclusion was silently discarded | nothing |
//
// The v0 lazy fetch is the sharpest of them: **not a row nothing reads, but a
// policy no row names.** A stock client refused on its own side, gunnar never
// heard the request, and no log line existed anywhere to notice.
//
// So the two guards below walk the *code* and ask the table about it, rather
// than the other way round.
/// Every `peer.has("…")` / `peer.value("…")` in `text`, in source order.
///
/// The server's capability **readers**. A name here that is not in the
/// corresponding table is a capability gunnar acts on and never offers, which
/// is the inverse drift: a client cannot know to send it, so the branch is
/// dead, and if a non-git client ever did send it the server would honour a
/// promise it never made.
fn capability_reads(text: &str) -> Vec<String> {
let mut out = Vec::new();
for opener in [r#"peer.has(""#, r#"peer.value(""#] {
let mut rest = text;
while let Some(at) = rest.find(opener) {
rest = &rest[at + opener.len()..];
if let Some(end) = rest.find('"') {
out.push(rest[..end].to_owned());
}
}
}
out.sort();
out.dedup();
out
}
/// **Every capability the server READS is one the server ADVERTISES.**
///
/// Seen red: this test was written against the tree at `c74053c` and failed
/// naming `side-band` — `upload_pack.rs` read `peer.has("side-band-64k") ||
/// peer.has("side-band")` while `UPLOAD_PACK_V0` offered only the 64k form.
/// Nothing in the tree ever sent the plain one, so the branch was dead; had a
/// non-git client sent it, gunnar would have opened a sideband and framed it in
/// 65515-byte packets at a peer that asked for 1000-byte ones. A capability
/// gunnar cannot keep is not a capability gunnar may read.
#[test]
fn every_capability_the_server_reads_is_one_it_advertises() {
let root = workspace_root();
let surfaces: [(&str, &str, &[Capability]); 2] = [
(
"upload-pack v0",
"crates/gunnar-wire/src/upload_pack.rs",
caps::UPLOAD_PACK_V0,
),
(
"receive-pack",
"crates/gunnar-wire/src/receive_pack.rs",
caps::RECEIVE_PACK,
),
];
for (surface, file, table) in surfaces {
let text = std::fs::read_to_string(root.join(file)).expect("read the surface's source");
let reads = capability_reads(&code_only(&text));
// Not vacuous: a scanner that found nothing would pass this loop
// silently, which is the shape of a guard that can never be red.
assert!(
reads.len() >= 4,
"{surface}: only {} capability reads were found in {file} ({reads:?}). The scanner \
is looking at the wrong thing, and an empty list satisfies every assertion below",
reads.len()
);
for name in &reads {
assert!(
table.iter().any(|cap| cap.name == name),
"{surface} READS the capability `{name}` in {file}, and does not ADVERTISE it. \
A client is never told it may send `{name}`, so the branch is dead code at \
best; at worst gunnar honours a promise it never made, to a peer that guessed. \
Either add the row — with a `Honoured` and, if it depends on a policy, a \
`requires` — or stop reading it."
);
}
}
}
/// **The red half.** The scanner must find real names and reject a fabricated
/// one, or the loop above is a walk over an empty list.
#[test]
fn the_capability_read_scanner_can_fail_and_ignores_comments() {
// It finds what is really there.
let root = workspace_root();
let upload = std::fs::read_to_string(root.join("crates/gunnar-wire/src/upload_pack.rs"))
.expect("read upload_pack.rs");
let reads = capability_reads(&code_only(&upload));
for expected in ["ofs-delta", "thin-pack", "side-band-64k", "object-format"] {
assert!(
reads.contains(&expected.to_string()),
"the scanner missed `{expected}`, which upload-pack demonstrably reads: {reads:?}"
);
}
// A name only a comment mentions is not a read, so the guard cannot be
// satisfied — or tripped — by prose.
let prose = "// peer.has(\"invented-capability\")\nlet x = 1;\n";
assert!(
capability_reads(&code_only(prose)).is_empty(),
"a commented-out read counted as a read"
);
// …and a real one on the same line as a comment IS found, so the stripper
// is not simply eating everything.
let real = "let q = peer.has(\"invented-capability\"); // why\n";
assert_eq!(
capability_reads(&code_only(real)),
vec!["invented-capability".to_string()],
"the scanner cannot see a read at all"
);
// And the assertion the loop makes really would reject it.
assert!(
!caps::UPLOAD_PACK_V0
.iter()
.any(|cap| cap.name == "invented-capability"),
"the table would accept a capability nothing advertises, so the loop checks nothing"
);
}
/// The `pub <name>:` fields of the struct that `declaration` opens, read out of
/// `text`.
///
/// Deliberately naive and deliberately anchored: it takes the lines from the
/// declaration to the first `}` at column zero. Nothing in this workspace nests
/// a braced item inside a struct body, and a parser that handled a construct
/// nobody writes would be untested code standing between a guard and its
/// subject — the same argument `code_only` makes about block comments.
fn struct_fields(text: &str, declaration: &str) -> Vec<String> {
let body = text
.split_once(declaration)
.unwrap_or_else(|| panic!("no {declaration:?} in this file"))
.1;
let body = body.split_once("\n}").map_or(body, |(head, _)| head);
let mut out: Vec<String> = Vec::new();
for line in code_only(body).lines() {
let line = line.trim();
let Some(rest) = line.strip_prefix("pub ") else {
continue;
};
let Some((name, _)) = rest.split_once(':') else {
continue;
};
let name = name.trim();
if !name.is_empty() && name.chars().all(|c| c.is_ascii_lowercase() || c == '_') {
out.push(name.to_owned());
}
}
out.sort();
out.dedup();
out
}
/// Judge one [`caps::Policy`]'s verdict, or say what is wrong with it.
///
/// A function rather than three arms inlined in the loop below, because the
/// loop cannot reach every arm: no field of any `Config` is
/// [`caps::Declared::Invisible`] today, so that arm would be a branch of a
/// guard that has never once executed — a blind third of the check, in a test
/// written to find blind checks. `the_policy_census_can_fail` calls this
/// directly with a fabricated `Policy` and exercises all three.
fn check_verdict(
root: &Path,
surface: &str,
policy: &caps::Policy,
) -> std::result::Result<(), String> {
let field = policy.field;
match policy.declared {
caps::Declared::As(name) => {
if !ADVERTISED
.iter()
.any(|(_, table)| table.iter().any(|cap| cap.name == name))
{
return Err(format!(
"{surface}'s `{field}` claims a client learns it from the capability \
`{name}`, and no surface advertises that. The claim is the whole value of \
the entry."
));
}
}
caps::Declared::Refuses { seen_by, reason } => {
if seen_by.is_empty() {
return Err(format!(
"{surface}'s `{field}` says it refuses and names no test that saw the refusal"
));
}
for citation in seen_by {
// Rejoined because the constants are written across two source
// lines and carry the continuation's indentation.
let citation: String = citation.split_whitespace().collect::<Vec<_>>().join("");
let Some((file, test)) = citation.split_once("::") else {
return Err(format!("{citation:?} is not <file>::<test fn>"));
};
let text = std::fs::read_to_string(root.join(file)).map_err(|err| {
format!("{surface}'s `{field}` cites {file}, which cannot be read: {err}")
})?;
if !code_only(&text).contains(&format!("fn {test}(")) {
return Err(format!(
"{surface}'s `{field}` says the peer sees the refusal and cites \
{file}::{test}, which is not a test there. A citation that does not \
resolve is the claim without the evidence."
));
}
}
if reason.len() <= 120 {
return Err(format!(
"{surface}'s `{field}` explains its refusal in {} characters; this arm is a \
claim about what a peer sees and has to carry it",
reason.len()
));
}
}
caps::Declared::RefusesUncovered(reason) => {
// An ADMISSION, so what it must carry is different from what `Refuses`
// carries: no citation (there is nothing to cite — that IS the arm), a
// longer argument than the escape hatch, and the way out named. An
// admission that does not say how it stops being one becomes furniture.
if reason.len() <= 200 {
return Err(format!(
"{surface}'s `{field}` admits the refusal is uncovered in {} characters. \
This arm has to say what the peer sees AND what would cover it.",
reason.len()
));
}
let joined: String = reason.split_whitespace().collect::<Vec<_>>().join(" ");
if !joined.contains("THE WAY OUT") {
return Err(format!(
"{surface}'s `{field}` admits it is uncovered and does not name THE WAY \
OUT. An admission with no route back is how a gap becomes permanent."
));
}
}
caps::Declared::Invisible(reason) => {
if reason.len() <= 80 {
return Err(format!(
"{surface}'s `{field}` is marked invisible with a {}-character reason; that \
is the escape hatch and it has to cost the argument",
reason.len()
));
}
}
}
Ok(())
}
/// **Every serving-side policy is declared somewhere — on the wire, or in a
/// refusal a test has watched arrive.**
///
/// This is the guard for the class, rather than for one member of it. The
/// question it makes unanswerable-by-omission is *"gunnar enforces this; how
/// does a client find out?"*, and the three legal answers are the arms of
/// [`caps::Declared`].
///
/// It works by reading the `Config` structs **out of the source** rather than
/// from a list beside them, so a new knob fails this test the moment it is
/// added — which is the only version of this guard that stays true. A
/// hand-written second list of field names is the exact shape (`push-options`
/// in a `vec![]`) that put four wrong answers on the wire.
///
/// Seen red four ways ; 2026-08-05, each naming its own subject:
///
/// * `pub stray_knob: bool` added to `upload_pack::Config` → *"upload-pack
/// enforces `stray_knob` … and nothing in `SERVER_POLICIES` says how a client
/// finds out"*;
/// * an entry left in `SERVER_POLICIES` after its field went → *"`SERVER_POLICIES`
/// classifies `a_removed_knob` for what is on offer, which is not a field of
/// `pub struct Offered {`"*;
/// * `Declared::As("allow-reachable-sha1-in-wants")`, one letter wrong →
/// *"claims a client learns it from the capability
/// `allow-reachable-sha1-in-wants`, and no surface advertises that"*;
/// * a `Declared::Refuses` citation with a typo in the test name → *"cites
/// …`::a_traversal_budget_of_one_refuses_a_clone_rather_than_truncating_itx`,
/// which is not a test there"*.
#[test]
fn every_serving_policy_is_declared_somewhere() {
let root = workspace_root();
// Not vacuous: three surfaces, and each really has fields.
assert_eq!(
caps::SERVER_POLICIES.len(),
3,
"a policy surface was added or lost: {:?}",
caps::SERVER_POLICIES
);
let mut classified = 0usize;
for surface in caps::SERVER_POLICIES {
let text = std::fs::read_to_string(root.join(surface.file))
.unwrap_or_else(|err| panic!("{} cannot be read: {err}", surface.file));
let in_source = struct_fields(&text, surface.declaration);
assert!(
!in_source.is_empty(),
"{}: `{}` in {} parsed to no fields at all, so every assertion below is about an \
empty set",
surface.surface,
surface.declaration,
surface.file
);
let mut listed: Vec<String> = surface.fields.iter().map(|p| p.field.to_owned()).collect();
listed.sort();
for field in &in_source {
assert!(
listed.contains(field),
"{} enforces `{field}` (a field of `{}` in {}) and nothing in \
`SERVER_POLICIES` says how a client finds out. That is the shape of the \
`WantPolicy` defect: a policy the server applies to every request and no \
capability names, so a stock client refuses on its OWN side and the server \
never hears the request. Classify it — `Declared::As` if a peer must know \
before it asks, `Declared::Refuses` with a test that watched the refusal \
arrive, or `Declared::Invisible` with the argument.",
surface.surface,
surface.declaration,
surface.file
);
}
for field in &listed {
assert!(
in_source.contains(field),
"`SERVER_POLICIES` classifies `{field}` for {}, which is not a field of `{}` \
in {} ({in_source:?}). A verdict on a knob that no longer exists reads as \
coverage and is not.",
surface.surface,
surface.declaration,
surface.file
);
}
for policy in surface.fields {
if let Err(complaint) = check_verdict(&root, surface.surface, policy) {
panic!("{complaint}");
}
classified += 1;
}
}
assert!(
classified >= 8,
"only {classified} policy fields were classified across all surfaces, which is fewer \
than gunnar has; this test is looking at the wrong structs"
);
}
/// **The red half.** The field reader has to see real fields and has to notice
/// one that is missing, or the test above is a loop over nothing.
#[test]
fn the_policy_census_can_fail() {
let root = workspace_root();
let upload = std::fs::read_to_string(root.join("crates/gunnar-wire/src/upload_pack.rs"))
.expect("read upload_pack.rs");
// It reads the real struct.
let fields = struct_fields(&upload, "pub struct Config {");
for expected in ["agent", "budget", "limits", "offered", "want_policy"] {
assert!(
fields.contains(&expected.to_string()),
"the field reader missed `{expected}`, which upload-pack's Config demonstrably \
has: {fields:?}"
);
}
// …and stops at the struct, rather than running on into the file. `Config`
// is followed by `impl Default` and then by `WantPolicy`, so a reader that
// did not stop would sweep up half the module.
assert!(
!fields.contains(&"wants".to_string()) && fields.len() < 10,
"the field reader ran past the end of the struct: {fields:?}"
);
// A field the census does not classify is genuinely caught.
let invented = "pub struct Fake {\n /// doc\n pub stray_knob: bool,\n}\n";
assert_eq!(
struct_fields(invented, "pub struct Fake {"),
vec!["stray_knob".to_string()],
"the reader cannot see a new field, so it can never report one"
);
assert!(
!caps::SERVER_POLICIES
.iter()
.flat_map(|s| s.fields)
.any(|p| p.field == "stray_knob"),
"the census would accept an unclassified field, so it checks nothing"
);
// A doc comment naming a field is not a field.
let commented = "pub struct Fake {\n // pub ghost_knob: bool,\n pub real: bool,\n}\n";
assert_eq!(
struct_fields(commented, "pub struct Fake {"),
vec!["real".to_string()],
"a commented-out field counted as policy"
);
// ── all three verdict arms, including the one no real field uses ───────
//
// `Declared::Invisible` is not on any `Config` field today, so the census
// loop never enters that arm. A branch of a guard that has never executed
// is a guard that has never been tested, so it is exercised here instead of
// being taken on trust.
let judge = |declared| {
check_verdict(
&root,
"a fixture",
&caps::Policy {
field: "f",
declared,
},
)
};
assert!(
judge(caps::Declared::Invisible("too short to be an argument")).is_err(),
"a one-line `Invisible` reason was accepted; the escape hatch costs nothing"
);
assert!(
judge(caps::Declared::Invisible(
"this is a reason long enough to be an argument rather than a label, which is the \
entire bar the variant sets, and it is set at eighty characters"
))
.is_ok(),
"a real argument was rejected, so the `Invisible` arm refuses everything and says \
nothing about length"
);
assert!(
judge(caps::Declared::As("not-a-capability-anyone-advertises")).is_err(),
"the `As` arm accepted a capability no table carries"
);
assert!(
judge(caps::Declared::As("filter")).is_ok(),
"the `As` arm rejected a capability gunnar demonstrably advertises, so its failure above \
was not about the name"
);
assert!(
judge(caps::Declared::Refuses {
seen_by: &[],
reason: "x"
})
.is_err(),
"the `Refuses` arm accepted a claim with no test behind it"
);
}
/// **The v0 WIRE carries exactly the table, plus the one documented exception.**
///
/// `the_wire_advertisement_carries_exactly_the_table` reads the v2
/// advertisement off the wire and the v0 one out of
/// `caps::upload_pack_v0_capabilities`. That asymmetry is the hole: the v0
/// capability string is assembled in `write_ref_advertisement`, which takes the
/// table's list and then **appends to it** —
///
/// ```text
/// extra.push(format!("symref=HEAD:{symref}"));
/// ```
///
/// — so `symref` has always ridden on the v0 wire while being in no table, seen
/// by no `Honoured`, and counted by no guard. It is not a wrong answer: git's
/// client reads it, gunnar computes it from the repository's real `HEAD`, and
/// `the_v0_advertisement_names_heads_branch` covers the value. It is an
/// *unwatched* one, and the next literal appended beside it would be too.
///
/// So this reads the actual first ref line and holds the whole capability set
/// to the table with `symref` named as the single exception. A second
/// off-table literal fails here.
///
/// Seen red both ways ; 2026-08-05: with `extra.push("no-done")` added to
/// `write_ref_advertisement`, this reports *"the v0 wire carries `no-done`,
/// which no row of `UPLOAD_PACK_V0` names"*; with the `symref` exception
/// misspelt so it stops matching, it reports the same sentence about `symref` —
/// which is what says the exception is load bearing rather than decorative.
#[test]
fn the_v0_wire_carries_exactly_the_table_plus_symref() {
use gunnar_wire::upload_pack::Config;
for kind in [Kind::Sha1, Kind::Sha256] {
let fixture = Fixture::build(kind);
// `WantPolicy::Any` for the same reason the v2 arm uses it: every
// policy-gated row renders, so "no more and no fewer" is asserted
// against the whole table rather than against a filtered view that
// would agree with itself if the filter were the bug.
let upload = UploadPack::new(&fixture.git, fixture.kind).with_config(Config {
want_policy: WantPolicy::Any,
..Config::default()
});
let mut bytes = Vec::new();
upload
.write_ref_advertisement(&mut bytes)
.expect("the v0 advertisement");
let text = String::from_utf8_lossy(&bytes).into_owned();
// The capability string is everything after the first NUL, up to the
// end of that pkt-line.
let nul = text.find('\0').unwrap_or_else(|| {
panic!(
"the v0 advertisement has no NUL, so it carries no capabilities at all: {text:?}"
)
});
let rest = &text[nul + 1..];
let line = &rest[..rest.find('\n').unwrap_or(rest.len())];
let on_wire: Vec<&str> = line.split(' ').filter(|c| !c.is_empty()).collect();
assert!(
on_wire.len() >= 10,
"only {} capabilities on the v0 wire ({on_wire:?}); this test is reading the wrong \
bytes and every assertion below is about a short list",
on_wire.len()
);
for entry in &on_wire {
let name = entry.split_once('=').map_or(*entry, |(k, _)| k);
// `symref` is the one capability whose value is a fact about the
// repository rather than about the server, so it cannot be a
// static row. It is named here, once, deliberately.
if name == "symref" {
continue;
}
assert!(
caps::UPLOAD_PACK_V0.iter().any(|cap| cap.name == name),
"the v0 wire carries `{name}`, which no row of `UPLOAD_PACK_V0` names. A \
capability appended beside the table is a promise with no `Honoured`, no \
`requires` and no guard — which is how `push-options` was advertised, parsed \
and discarded for a whole release. Wire: {on_wire:?}"
);
}
// …and nothing the table promises went missing on the way out.
for cap in caps::UPLOAD_PACK_V0 {
assert!(
on_wire
.iter()
.any(|entry| entry.split_once('=').map_or(*entry, |(k, _)| k) == cap.name),
"`{}` is in `UPLOAD_PACK_V0` and is not on the v0 wire: {on_wire:?}",
cap.name
);
}
// The exception is real and not a spelling nobody emits.
assert!(
on_wire.iter().any(|c| c.starts_with("symref=HEAD:")),
"the `symref` exception above is excusing something that is not there: {on_wire:?}"
);
}
}