crates/gunnar-pktline/tests/encode.rs
//! Encoder tests, **literal bytes only**.
//!
//! Every assertion here compares against a hand-written byte string, never
//! against the output of this crate's own decoder. A codec checked solely by
//! round-tripping agrees with itself by construction: shift the length by four
//! in both halves and every round-trip still passes while no git in the world
//! can read a byte of it. The literals below are the only thing in the crate
//! that is anchored to the protocol rather than to the implementation.
use gunnar_pktline::*;
fn enc(f: impl FnOnce(&mut Vec<u8>) -> Result<()>) -> Vec<u8> {
let mut out = Vec::new();
f(&mut out).expect("encode failed");
out
}
#[test]
fn write_data_emits_length_including_the_header() {
assert_eq!(enc(|o| write_data(o, b"hi")), b"0006hi".to_vec());
assert_eq!(enc(|o| write_data(o, b"a")), b"0005a".to_vec());
assert_eq!(enc(|o| write_data(o, b"abcdef")), b"000aabcdef".to_vec());
}
#[test]
fn empty_data_packet_is_0004_and_is_not_a_flush() {
// 0000 and 0004 are different packets. Collapsing them loses section
// boundaries in protocol v2.
assert_eq!(enc(|o| write_data(o, b"")), b"0004".to_vec());
assert_ne!(enc(|o| write_data(o, b"")), enc(write_flush));
}
#[test]
fn control_packets_are_exact_four_byte_literals() {
assert_eq!(enc(write_flush), b"0000".to_vec());
assert_eq!(enc(write_delim), b"0001".to_vec());
assert_eq!(enc(write_response_end), b"0002".to_vec());
}
#[test]
fn a_real_ref_advertisement_line_is_byte_exact() {
// 40 hex + space + 15 + newline = 57 payload, +4 header = 61 = 0x3d.
let line = b"0000000000000000000000000000000000000000 refs/heads/main\n";
assert_eq!(line.len(), 57);
let got = enc(|o| write_data(o, line));
assert_eq!(
got,
b"003d0000000000000000000000000000000000000000 refs/heads/main\n".to_vec()
);
assert_eq!(got.len(), 61);
}
#[test]
fn write_data_never_touches_the_payload() {
// Binary is legal on the wire; an encoder that "helpfully" newline-terminates
// would corrupt every packfile byte it framed.
let payload = [0x00u8, 0xff, 0x0a, 0x50, 0x41, 0x43, 0x4b];
let mut expected = b"000b".to_vec();
expected.extend_from_slice(&payload);
assert_eq!(enc(|o| write_data(o, &payload)), expected);
}
#[test]
fn write_text_appends_exactly_one_newline_when_absent() {
assert_eq!(enc(|o| write_text(o, b"hi")), b"0007hi\n".to_vec());
assert_eq!(enc(|o| write_text(o, b"")), b"0005\n".to_vec());
}
#[test]
fn write_text_is_idempotent_on_an_already_terminated_line() {
assert_eq!(enc(|o| write_text(o, b"hi\n")), b"0007hi\n".to_vec());
assert_eq!(
enc(|o| write_text(o, b"hi\n")),
enc(|o| write_text(o, b"hi"))
);
// A payload that legitimately ends in a blank line keeps both newlines.
assert_eq!(enc(|o| write_text(o, b"hi\n\n")), b"0008hi\n\n".to_vec());
}
#[test]
fn length_header_is_lowercase_and_zero_padded() {
// 167 payload bytes → 171 total → 0x00ab. Catches both a `{:x}` without the
// zero pad and an uppercase `{:X}`; either desyncs a peer.
let got = enc(|o| write_data(o, &[b'x'; 167]));
assert_eq!(&got[..4], b"00ab");
assert_eq!(got.len(), 171);
}
#[test]
fn max_payload_is_exactly_the_one_that_fits() {
let got = enc(|o| write_data(o, &vec![b'x'; MAX_PAYLOAD]));
assert_eq!(&got[..4], b"fff0");
assert_eq!(got.len(), MAX_LINE);
assert_eq!(MAX_LINE, 65520);
}
#[test]
fn one_byte_over_max_payload_is_rejected_not_truncated() {
let mut out = Vec::new();
let err = write_data(&mut out, &vec![b'x'; MAX_PAYLOAD + 1]).unwrap_err();
assert!(
matches!(err, Error::TooLong(n) if n == MAX_PAYLOAD + 1),
"{err:?}"
);
// Nothing may be emitted for a rejected packet. A half-written frame is
// worse than no frame.
assert!(out.is_empty());
}
#[test]
fn write_text_counts_the_newline_it_is_about_to_add() {
// MAX_PAYLOAD - 1 bytes plus the appended newline is exactly the limit.
let ok = enc(|o| write_text(o, &vec![b'x'; MAX_PAYLOAD - 1]));
assert_eq!(&ok[..4], b"fff0");
assert_eq!(ok.len(), MAX_LINE);
// MAX_PAYLOAD bytes of text would become MAX_PAYLOAD + 1 once terminated.
let mut out = Vec::new();
let err = write_text(&mut out, &vec![b'x'; MAX_PAYLOAD]).unwrap_err();
assert!(
matches!(err, Error::TooLong(n) if n == MAX_PAYLOAD + 1),
"{err:?}"
);
assert!(out.is_empty());
}
#[test]
fn a_whole_v2_command_section_is_byte_exact() {
// The shape gunnar's upload-pack actually writes: command, delim, args, flush.
let got = enc(|o| {
write_text(o, b"command=ls-refs")?;
write_delim(o)?;
write_text(o, b"peel")?;
write_text(o, b"symrefs")?;
write_flush(o)
});
assert_eq!(
got,
b"0014command=ls-refs\n00010009peel\n000csymrefs\n0000".to_vec()
);
}
#[test]
fn write_packet_matches_the_dedicated_writers() {
assert_eq!(
enc(|o| write_packet(o, &Packet::Data(b"hi".to_vec()))),
b"0006hi".to_vec()
);
assert_eq!(enc(|o| write_packet(o, &Packet::Flush)), b"0000".to_vec());
assert_eq!(enc(|o| write_packet(o, &Packet::Delim)), b"0001".to_vec());
assert_eq!(
enc(|o| write_packet(o, &Packet::ResponseEnd)),
b"0002".to_vec()
);
}
#[test]
fn constants_are_the_protocol_numbers() {
// A renumber here would be silently absorbed by every other test in the
// suite, since they all read the constants back.
assert_eq!(MAX_PAYLOAD, 65516);
assert_eq!(BAND_PACK, 1);
assert_eq!(BAND_PROGRESS, 2);
assert_eq!(BAND_ERROR, 3);
assert_eq!(SIDEBAND_CHUNK, 65515);
}