ref:3131e3054568aa2d70489af5877280e368635451

fix(test): binary-exact pack reassembly in walker property test (#63)

upload_pack_v2_walker_property_test flaked intermittently with {:error, :pack_checksum_mismatch} (deterministic on seed 117). extract_pack/1 reassembled the pack by running the sideband response through PktLine.decode, whose decode_one strips one trailing LF from every data packet — correct for text lines, wrong for binary sideband pack data. When a sideband chunk's last byte was 0x0A it was dropped, leaving the pack one byte short of its checksum. Whether a chunk ends in 0x0A depends on pack content, hence the flake (gitlink-to-local-commit topologies hit it often). Production is unaffected: the server frames sideband data with encode_raw / encode_sideband_frame (no LF appended), so the wire bytes are exact and real git clones succeed. The bug was only in the test's reassembly. Fix: split the response into raw length-prefixed pkt-line payloads verbatim (no LF stripping). The asserted walker-reachability invariant is unchanged. Verified across seeds 1-160 (~12k graph builds), 0 failures. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
SHA: 3131e3054568aa2d70489af5877280e368635451
Author: CI <ci@anvil.test>
Date: 2026-05-29 06:27
Parents: ed878d9
1 files changed +23 -8
Type
test/ex_git_objectstore/protocol/upload_pack_v2_walker_property_test.exs +23 −8
@@ -353,24 +353,39 @@
# when the response has no packfile section (e.g. the walker
# returned an error and only a flush was sent).
defp extract_pack(response) do
{:ok, packets, _rest} = PktLine.decode(response)
# Split into raw pkt-line payloads BINARY-EXACTLY. We must not use
# PktLine.decode here: it strips one trailing LF from every data packet
# (correct for text lines, wrong for sideband pack data), so a pack chunk
# whose last byte is 0x0A would lose it and the reassembled pack would fail
# its checksum — a content-dependent flake. The server frames sideband data
# with encode_raw (no LF added), so the bytes on the wire are exact.
frames = raw_pkt_payloads(response)
# PktLine.decode strips the trailing LF from data packets, so the
# "packfile" marker arrives without its newline.
case Enum.drop_while(packets, fn
{:data, "packfile"} -> false
_ -> true
end) do
case Enum.drop_while(frames, &(String.trim_trailing(&1, "\n") != "packfile")) do
[] ->
<<>>
[_packfile_marker | body] ->
body
|> Enum.flat_map(fn
<<1, chunk::binary>> -> [chunk]
{:data, <<1, chunk::binary>>} -> [chunk]
_ -> []
end)
|> IO.iodata_to_binary()
end
end
# Length-prefixed pkt-line split that returns payloads verbatim (no LF
# stripping). Skips flush/delim/response-end control packets.
defp raw_pkt_payloads(<<>>), do: []
defp raw_pkt_payloads(<<"0000", rest::binary>>), do: raw_pkt_payloads(rest)
defp raw_pkt_payloads(<<"0001", rest::binary>>), do: raw_pkt_payloads(rest)
defp raw_pkt_payloads(<<"0002", rest::binary>>), do: raw_pkt_payloads(rest)
defp raw_pkt_payloads(<<hex::binary-size(4), rest::binary>>) do
len = String.to_integer(hex, 16)
payload_len = len - 4
<<payload::binary-size(payload_len), tail::binary>> = rest
[payload | raw_pkt_payloads(tail)]
end
end