## 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)