package marmot // QUIC varint codec tests (RFC 9000 §16, Appendix A). // // The critical test in this file is TestDecodeVarint8ByteAbove2p31 — an // 8-byte-encoded value above 2^31 must survive decoding as uint64 without // silent truncation. This would regress if anyone typed the API as uint // or int, since Moxie's uint/int are 32-bit. import "testing" // roundTrip encodes v, decodes it back, and checks size class + value. func roundTrip(t *testing.T, v uint64, wantN int) { var buf [8]byte n, err := EncodeVarint(v, buf[:]) if err != nil { t.Fatalf("EncodeVarint(%d): %s", int64(v), err.Error()) } if n != wantN { t.Errorf("EncodeVarint(%d): wrote %d bytes, want %d", int64(v), n, wantN) } got, gotN, err := DecodeVarint(buf[:n]) if err != nil { t.Fatalf("DecodeVarint(%d bytes): %s", n, err.Error()) } if gotN != n { t.Errorf("DecodeVarint(%d): consumed %d bytes, want %d", int64(v), gotN, n) } if got != v { t.Errorf("DecodeVarint round-trip: got %d, want %d", int64(got), int64(v)) } } func TestVarintSizeClasses(t *testing.T) { // 1-byte class: 0..63 roundTrip(t, 0, 1) roundTrip(t, 63, 1) // 2-byte class: 64..16383 roundTrip(t, 64, 2) roundTrip(t, 16383, 2) // 4-byte class: 16384..1073741823 (2^30-1) roundTrip(t, 16384, 4) roundTrip(t, 1073741823, 4) // 8-byte class: 1073741824..VarintMax roundTrip(t, 1073741824, 8) } // TestDecodeVarint8ByteAbove2p31 is the regression guard for the whole // uint64-in-the-API rule. An 8-byte varint encoding a value > 2^31 must // decode to the correct uint64. If someone refactored this to return uint // (32-bit in Moxie), the value would silently truncate to its low 32 bits // and every caller would get wrong data with no error. func TestDecodeVarint8ByteAbove2p31(t *testing.T) { // 2^31 exactly (first value that overflows signed int32). v := uint64(0x80000000) roundTrip(t, v, 8) // 2^31 + 1 (first value that overflows unsigned int32 if signed is used). roundTrip(t, uint64(0x80000001), 8) // 2^32 (first value above uint32 range — critical test). roundTrip(t, uint64(0x100000000), 8) // 2^40 — solidly in the upper range. roundTrip(t, uint64(0x10000000000), 8) // VarintMax (2^62-1) — largest legal QUIC varint. roundTrip(t, VarintMax, 8) } func TestEncodeVarintOverflow(t *testing.T) { // Values above VarintMax must be rejected. var buf [8]byte _, err := EncodeVarint(VarintMax+1, buf[:]) if err == nil { t.Error("EncodeVarint(VarintMax+1) should error") } _, err = EncodeVarint(uint64(0xffffffffffffffff), buf[:]) if err == nil { t.Error("EncodeVarint(max uint64) should error") } } func TestEncodeVarintBufferTooSmall(t *testing.T) { // 8-byte value into 4-byte buffer. var small [4]byte _, err := EncodeVarint(uint64(0x100000000), small[:]) if err == nil { t.Error("EncodeVarint into small buffer should error") } // 4-byte value into 2-byte buffer. _, err = EncodeVarint(16384, small[:1]) if err == nil { t.Error("EncodeVarint into 1-byte buffer should error") } } func TestDecodeVarintTruncated(t *testing.T) { // Advertise 8-byte class but supply 3 bytes. _, _, err := DecodeVarint([]byte{0xc0, 0x00, 0x00}) if err == nil { t.Error("DecodeVarint(truncated 8-byte) should error") } // Advertise 4-byte class but supply 2 bytes. _, _, err = DecodeVarint([]byte{0x80, 0x00}) if err == nil { t.Error("DecodeVarint(truncated 4-byte) should error") } // Advertise 2-byte class but supply 1 byte. _, _, err = DecodeVarint([]byte{0x40}) if err == nil { t.Error("DecodeVarint(truncated 2-byte) should error") } // Empty. _, _, err = DecodeVarint(nil) if err == nil { t.Error("DecodeVarint(nil) should error") } } // readQuicVec narrows uint64 to int at the call site. A varint length above // int32 range must be rejected with an error, not silently truncated. func TestReadQuicVecRejectsOversizedLength(t *testing.T) { // Build a QUIC varint encoding 2^32 (well above int32 max). var buf [8]byte n, _ := EncodeVarint(uint64(0x100000000), buf[:]) _, _, err := readQuicVec(buf[:n]) if err == nil { t.Error("readQuicVec(length=2^32) should error — cannot fit in int32") } }