Files
settled-reach/client/tests/test_msgpack_boundaries.gd
T
jpmschweitzerandClaude Opus 4.6 1435a77dcc fix(client): fix MessagePack int_64 encoder dead code branch (#516)
The int_64 branch condition `-(1 << 63) <= value` overflowed in
GDScript's signed 64-bit arithmetic, making the branch unreachable.
Negative values beyond int_32 range were incorrectly encoded as
uint_64 (0xcf) instead of int_64 (0xd3). Replaced with `value < 0`.

Updated boundary tests BV-N15 and BV-N16 to expect correct int_64
header byte.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 02:14:29 +01:00

223 lines
9.1 KiB
GDScript

## Boundary value tests for MessagePack encoding: 25 positive + 16 negative boundaries.
## Validates header byte + payload for each value, encode-decode roundtrip,
## and cross-encoding decode (GDScript accepting Rust-style unsigned encodings).
## Bug #4 class prevention: format boundary encoding errors.
## Spec ref: hoshe-round3.md Section 4, workshop-outcomes.md Appendix C.
class_name TestMsgpackBoundaries
extends GdUnitTestSuite
# -- Helpers -------------------------------------------------------------------
## Assert that encoding `value` produces exactly `expected_bytes`.
func _assert_encodes_to(value: int, expected_bytes: PackedByteArray, label: String) -> void:
var result = Messagepack.encode(value)
assert_that(result.value).override_failure_message(
"%s: encode(%d) produced wrong bytes" % [label, value]
).is_equal(expected_bytes)
## Assert that encoding then decoding `value` returns the original value.
func _assert_roundtrip(value: int, label: String) -> void:
var encoded = Messagepack.encode(value)
var decoded = Messagepack.decode(encoded.value)
assert_that(decoded.value).override_failure_message(
"%s: roundtrip failed for %d" % [label, value]
).is_equal(value)
# -- Positive boundaries: encode-only -----------------------------------------
func test_encode_pos_fixint() -> void:
# BV-P01 to BV-P04: positive fixint range (0 to 127)
# Format: single byte = value itself
_assert_encodes_to(0, PackedByteArray([0x00]), "BV-P01")
_assert_encodes_to(1, PackedByteArray([0x01]), "BV-P02")
_assert_encodes_to(126, PackedByteArray([0x7e]), "BV-P03")
_assert_encodes_to(127, PackedByteArray([0x7f]), "BV-P04")
func test_encode_uint8() -> void:
# BV-P05 to BV-P08: uint 8 range (128 to 255)
# Format: 0xcc + 1 byte
# Bug #4 regression target: 128 must NOT encode as int_8 (0xd0, 0x80 = -128)
_assert_encodes_to(128, PackedByteArray([0xcc, 0x80]), "BV-P05")
_assert_encodes_to(129, PackedByteArray([0xcc, 0x81]), "BV-P06")
_assert_encodes_to(254, PackedByteArray([0xcc, 0xfe]), "BV-P07")
_assert_encodes_to(255, PackedByteArray([0xcc, 0xff]), "BV-P08")
func test_encode_int16_gdscript() -> void:
# BV-P09 to BV-P12: GDScript encodes 256-32767 as int_16 (NOT uint_16)
# Asymmetry: Rust encodes these as uint_16 (0xcd). Both are spec-valid.
# Format: 0xd1 + 2 bytes big-endian signed
_assert_encodes_to(256, PackedByteArray([0xd1, 0x01, 0x00]), "BV-P09")
_assert_encodes_to(257, PackedByteArray([0xd1, 0x01, 0x01]), "BV-P10")
_assert_encodes_to(32766, PackedByteArray([0xd1, 0x7f, 0xfe]), "BV-P11")
_assert_encodes_to(32767, PackedByteArray([0xd1, 0x7f, 0xff]), "BV-P12")
func test_encode_uint16() -> void:
# BV-P13 to BV-P16: uint 16 range (32768 to 65535)
# Format: 0xcd + 2 bytes big-endian unsigned
_assert_encodes_to(32768, PackedByteArray([0xcd, 0x80, 0x00]), "BV-P13")
_assert_encodes_to(32769, PackedByteArray([0xcd, 0x80, 0x01]), "BV-P14")
_assert_encodes_to(65534, PackedByteArray([0xcd, 0xff, 0xfe]), "BV-P15")
_assert_encodes_to(65535, PackedByteArray([0xcd, 0xff, 0xff]), "BV-P16")
func test_encode_int32_gdscript() -> void:
# BV-P17 to BV-P20: GDScript encodes 65536-2147483647 as int_32 (NOT uint_32)
# Asymmetry: Rust encodes these as uint_32 (0xce). Both are spec-valid.
# Format: 0xd2 + 4 bytes big-endian signed
_assert_encodes_to(65536, PackedByteArray([0xd2, 0x00, 0x01, 0x00, 0x00]), "BV-P17")
_assert_encodes_to(65537, PackedByteArray([0xd2, 0x00, 0x01, 0x00, 0x01]), "BV-P18")
_assert_encodes_to(2147483646, PackedByteArray([0xd2, 0x7f, 0xff, 0xff, 0xfe]), "BV-P19")
_assert_encodes_to(2147483647, PackedByteArray([0xd2, 0x7f, 0xff, 0xff, 0xff]), "BV-P20")
func test_encode_uint32() -> void:
# BV-P21 to BV-P23: uint 32 range (2^31 to 2^32-1)
# Format: 0xce + 4 bytes big-endian unsigned
_assert_encodes_to(2147483648, PackedByteArray([0xce, 0x80, 0x00, 0x00, 0x00]), "BV-P21")
_assert_encodes_to(4294967294, PackedByteArray([0xce, 0xff, 0xff, 0xff, 0xfe]), "BV-P22")
_assert_encodes_to(4294967295, PackedByteArray([0xce, 0xff, 0xff, 0xff, 0xff]), "BV-P23")
func test_encode_int64_positive() -> void:
# BV-P24 to BV-P25: positive values > uint_32 max → uint_64 (0xcf)
# Positive values correctly use uint_64 encoding for Rust interop.
_assert_encodes_to(4294967296, PackedByteArray([
0xcf, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00
]), "BV-P24")
_assert_encodes_to(9223372036854775807, PackedByteArray([
0xcf, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
]), "BV-P25")
# -- Negative boundaries: encode-only -----------------------------------------
func test_encode_neg_fixint() -> void:
# BV-N01 to BV-N03: negative fixint range (-1 to -32)
# Format: single byte, two's complement
_assert_encodes_to(-1, PackedByteArray([0xff]), "BV-N01")
_assert_encodes_to(-31, PackedByteArray([0xe1]), "BV-N02")
_assert_encodes_to(-32, PackedByteArray([0xe0]), "BV-N03")
func test_encode_int8_negative() -> void:
# BV-N04 to BV-N07: int 8 range (-33 to -128)
# Format: 0xd0 + 1 byte signed
_assert_encodes_to(-33, PackedByteArray([0xd0, 0xdf]), "BV-N04")
_assert_encodes_to(-34, PackedByteArray([0xd0, 0xde]), "BV-N05")
_assert_encodes_to(-127, PackedByteArray([0xd0, 0x81]), "BV-N06")
_assert_encodes_to(-128, PackedByteArray([0xd0, 0x80]), "BV-N07")
func test_encode_int16_negative() -> void:
# BV-N08 to BV-N11: int 16 range (-129 to -32768)
# Format: 0xd1 + 2 bytes big-endian signed
_assert_encodes_to(-129, PackedByteArray([0xd1, 0xff, 0x7f]), "BV-N08")
_assert_encodes_to(-130, PackedByteArray([0xd1, 0xff, 0x7e]), "BV-N09")
_assert_encodes_to(-32767, PackedByteArray([0xd1, 0x80, 0x01]), "BV-N10")
_assert_encodes_to(-32768, PackedByteArray([0xd1, 0x80, 0x00]), "BV-N11")
func test_encode_int32_negative() -> void:
# BV-N12 to BV-N14: int 32 range (-32769 to -2147483648)
# Format: 0xd2 + 4 bytes big-endian signed
_assert_encodes_to(-32769, PackedByteArray([0xd2, 0xff, 0xff, 0x7f, 0xff]), "BV-N12")
_assert_encodes_to(-2147483647, PackedByteArray([0xd2, 0x80, 0x00, 0x00, 0x01]), "BV-N13")
_assert_encodes_to(-2147483648, PackedByteArray([0xd2, 0x80, 0x00, 0x00, 0x00]), "BV-N14")
func test_encode_int64_negative() -> void:
# BV-N15 to BV-N16: int 64 range (< -2147483648)
# Negative values beyond int_32 now correctly encode as int_64 (0xd3).
# Fixed: encoder's int_64 branch used `-(1 << 63)` which overflowed to dead code.
_assert_encodes_to(-2147483649, PackedByteArray([
0xd3, 0xff, 0xff, 0xff, 0xff, 0x7f, 0xff, 0xff, 0xff
]), "BV-N15")
# MIN_INT64: -9223372036854775808
var min_int64: int = -9223372036854775807 - 1
_assert_encodes_to(min_int64, PackedByteArray([
0xd3, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
]), "BV-N16")
# -- Roundtrip tests -----------------------------------------------------------
func test_roundtrip_positive_boundaries() -> void:
# All 25 positive boundary values: encode -> decode -> assert equal
var values: Array[int] = [
# pos fixint
0, 1, 126, 127,
# uint 8
128, 129, 254, 255,
# int 16 (GDScript) / uint 16 (Rust)
256, 257, 32766, 32767,
# uint 16
32768, 32769, 65534, 65535,
# int 32 (GDScript) / uint 32 (Rust)
65536, 65537, 2147483646, 2147483647,
# uint 32
2147483648, 4294967294, 4294967295,
# int 64 / uint 64
4294967296, 9223372036854775807,
]
for i in values.size():
_assert_roundtrip(values[i], "BV-P%02d" % (i + 1))
func test_roundtrip_negative_boundaries() -> void:
# All 16 negative boundary values: encode -> decode -> assert equal
var min_int64: int = -9223372036854775807 - 1
var values: Array[int] = [
# neg fixint
-1, -31, -32,
# int 8
-33, -34, -127, -128,
# int 16
-129, -130, -32767, -32768,
# int 32
-32769, -2147483647, -2147483648,
# int 64 / uint 64
-2147483649, min_int64,
]
for i in values.size():
_assert_roundtrip(values[i], "BV-N%02d" % (i + 1))
# -- Encoding overlap zone: GDScript decodes Rust-style unsigned encodings -----
# Rust encodes 256-32767 as uint_16 (0xcd) and 65536-2147483647 as uint_32 (0xce).
# GDScript encodes these as int_16 (0xd1) and int_32 (0xd2) respectively.
# Both are valid MessagePack. Both decoders must accept the other side's encoding.
# These tests verify GDScript's decoder handles Rust-style unsigned encodings.
func test_decode_rust_uint16_256() -> void:
# Rust encodes 256 as uint_16: [0xcd, 0x01, 0x00]
var bytes := PackedByteArray([0xcd, 0x01, 0x00])
var result = Messagepack.decode(bytes)
assert_that(result.value).is_equal(256)
func test_decode_rust_uint16_32767() -> void:
# Rust encodes 32767 as uint_16: [0xcd, 0x7f, 0xff]
var bytes := PackedByteArray([0xcd, 0x7f, 0xff])
var result = Messagepack.decode(bytes)
assert_that(result.value).is_equal(32767)
func test_decode_rust_uint32_65536() -> void:
# Rust encodes 65536 as uint_32: [0xce, 0x00, 0x01, 0x00, 0x00]
var bytes := PackedByteArray([0xce, 0x00, 0x01, 0x00, 0x00])
var result = Messagepack.decode(bytes)
assert_that(result.value).is_equal(65536)
func test_decode_rust_uint32_2147483647() -> void:
# Rust encodes 2^31-1 as uint_32: [0xce, 0x7f, 0xff, 0xff, 0xff]
var bytes := PackedByteArray([0xce, 0x7f, 0xff, 0xff, 0xff])
var result = Messagepack.decode(bytes)
assert_that(result.value).is_equal(2147483647)