Artifact 9abee30a2cb85afa53619a0413a50e39aed609e3029b706216901c934ca5dbee:
- File
src/container_test.zig
— part of check-in
[22d23eae1c]
at
2026-08-05 22:47:36
on branch sub-manifest
— citations: re-key 29 more, mostly worked-example appendix cites
Second pass over the leftovers after the bulk re-key. 29 of 107 survived review; 10 were refuted and 68 returned unresolved, which is the right shape for leftovers -- the easy ones were already gone.
Most are Appendix A worked-example citations, now visible to the checker for the first time: the round-1/round-2 request and reply bodies, their igot and gimme runs, and the SHA3 gate response. An example transcript is often the better citation for a claim about concrete wire bytes than the prose that governs them.
The refusals stay instructive. One proposal was rejected because the directional half of its claim lives in §3.2.1 Round Message Layout rather than §6.1, so it was mis-sectioned rather than mis-lined; another because its range stopped mid-sentence, dropping the words the claim rested on.
Baseline 135 to 106. test 3495/3495, fmt. Issue #18. (user: dmestas, size: 6948) [annotate] [blame] [check-ins using]
//! Public-surface unit tests for the §4.1 zlib container codec //! (`container.zig`). The oracle is draft-fossil-sync-protocol-00 §4.1; every //! expected value is transcribed from the spec text and its RFC1950 references, //! never captured from a running Fossil. const std = @import("std"); const container = @import("container.zig"); /// A generous decode capacity for the round-trip tests; the codec's `max_output` /// bound is exercised directly by the "over max_output" test below. Sync passes /// its `reply_body_cap` here in production; the codec itself is cap-agnostic. const cap: usize = 1 << 24; test "container §4.1 r18: the container is BE32 length then a zlib stream (worked example)" { const encoded = try container.encode(std.testing.allocator, "abc", .default); defer std.testing.allocator.free(encoded); // BE32 uncompressed length (r18). try std.testing.expectEqualSlices(u8, &[_]u8{ 0, 0, 0, 3 }, encoded[0..4]); // RFC1950 §2.2 MUST: CM (low nibble of CMF) is 8 (deflate). try std.testing.expectEqual(@as(u8, 8), encoded[4] & 0x0f); // RFC1950 §2.2 MUST: (CMF*256 + FLG) % 31 == 0 (FCHECK). const cmf_flg = @as(u16, encoded[4]) * 256 + @as(u16, encoded[5]); try std.testing.expectEqual(@as(u16, 0), cmf_flg % 31); // FDICT must be 0 — no preset dictionary. try std.testing.expectEqual(@as(u8, 0), encoded[5] & 0x20); // RFC1950 §9 Adler-32 of "abc": s1=1+97+98+99=295=0x0127, // s2=0+98+196+295=589=0x024D, stored MSB-first as the trailer. try std.testing.expectEqualSlices(u8, &[_]u8{ 0x02, 0x4D, 0x01, 0x27 }, encoded[encoded.len - 4 ..]); } test "container §4.1 r19: the ordinary single-input path uses the zlib default level" { const encoded = try container.encode(std.testing.allocator, "hello fossil sync", .default); defer std.testing.allocator.free(encoded); const decoded = try container.decode(std.testing.allocator, encoded, cap); defer std.testing.allocator.free(decoded); try std.testing.expectEqualStrings("hello fossil sync", decoded); } test "container §4.1 r20: an inline-login prepend compresses the concatenation at level 9" { const login = "login foo bar baz\n"; const body = "gimme 0123456789012345678901234567890123456789\n"; const combined = login ++ body; const encoded = try container.encode(std.testing.allocator, combined, .best); defer std.testing.allocator.free(encoded); try std.testing.expectEqual(@as(u32, combined.len), std.mem.readInt(u32, encoded[0..4], .big)); const decoded = try container.decode(std.testing.allocator, encoded, cap); defer std.testing.allocator.free(decoded); try std.testing.expectEqualStrings(combined, decoded); } test "container §4.1 r21: decoding does not depend on the compression level" { const input = "the quick brown fox jumps over the lazy dog"; const enc_default = try container.encode(std.testing.allocator, input, .default); defer std.testing.allocator.free(enc_default); const enc_best = try container.encode(std.testing.allocator, input, .best); defer std.testing.allocator.free(enc_best); const dec_default = try container.decode(std.testing.allocator, enc_default, cap); defer std.testing.allocator.free(dec_default); const dec_best = try container.decode(std.testing.allocator, enc_best, cap); defer std.testing.allocator.free(dec_best); try std.testing.expectEqualStrings(input, dec_default); try std.testing.expectEqualStrings(input, dec_best); } test "container §4.1: the empty input round-trips" { const encoded = try container.encode(std.testing.allocator, "", .default); defer std.testing.allocator.free(encoded); try std.testing.expectEqualSlices(u8, &[_]u8{ 0, 0, 0, 0 }, encoded[0..4]); const decoded = try container.decode(std.testing.allocator, encoded, cap); defer std.testing.allocator.free(decoded); try std.testing.expectEqual(@as(usize, 0), decoded.len); } test "container §4.1: a truncated container header is rejected" { try std.testing.expectError(error.TruncatedHeader, container.decode(std.testing.allocator, "\x00\x00", cap)); } test "container §4.1: a decode that fits within declared capacity is accepted" { // §4.1 L905-909, L1210-1211: the length field is an output-capacity // value, not an exact length — a decode that succeeds within capacity is // accepted. Declared 99, decoded 11 <= 99, so the shorter stream is // accepted as-is and the actual bytes are returned. const encoded = try container.encode(std.testing.allocator, "mismatch me", .default); defer std.testing.allocator.free(encoded); var lied: [64]u8 = undefined; @memcpy(lied[0..encoded.len], encoded); std.mem.writeInt(u32, lied[0..4], 99, .big); // declares 99 (capacity), stream carries 11 const decoded = try container.decode(std.testing.allocator, lied[0..encoded.len], cap); defer std.testing.allocator.free(decoded); try std.testing.expectEqualStrings("mismatch me", decoded); } test "container §4.1: output exceeding declared capacity is rejected" { // §4.1 L901-910: reject when the decoder cannot fit its output within the // declared capacity — declared 5, actual 11 > 5. const encoded = try container.encode(std.testing.allocator, "mismatch me", .default); defer std.testing.allocator.free(encoded); var lied: [64]u8 = undefined; @memcpy(lied[0..encoded.len], encoded); std.mem.writeInt(u32, lied[0..4], 5, .big); // declares capacity 5, stream carries 11 try std.testing.expectError(error.OutputTooLarge, container.decode(std.testing.allocator, lied[0..encoded.len], cap)); } test "container §4.1: a declared length over max_output is rejected" { const encoded = try container.encode(std.testing.allocator, "capped", .default); defer std.testing.allocator.free(encoded); try std.testing.expectError(error.OutputTooLarge, container.decode(std.testing.allocator, encoded, 2)); } test "container §4.1: garbage after the length prefix is rejected" { const garbage = [_]u8{ 0, 0, 0, 5, 0xff, 0xff, 0xff, 0xff, 0xff }; try std.testing.expectError(error.InvalidStream, container.decode(std.testing.allocator, &garbage, cap)); } fn encodeNoLeak(gpa: std.mem.Allocator) !void { const out = try container.encode(gpa, "allocation failure probe", .default); gpa.free(out); } test "container §4.1: encode survives allocation failure" { try std.testing.checkAllAllocationFailures(std.testing.allocator, encodeNoLeak, .{}); } fn decodeNoLeak(gpa: std.mem.Allocator) !void { const encoded = try container.encode(gpa, "allocation failure probe", .default); defer gpa.free(encoded); const out = try container.decode(gpa, encoded, cap); gpa.free(out); } test "container §4.1: decode survives allocation failure" { try std.testing.checkAllAllocationFailures(std.testing.allocator, decodeNoLeak, .{}); }