## D-030 Layer 1: Cross-language MessagePack tests ## Validates that Protocol.gd correctly decodes fixtures generated by Rust (rmp_serde). ## Fixtures generated by: cargo test --test gen_fixtures -- --ignored class_name TestProtocol extends GdUnitTestSuite const FIXTURE_DIR = "res://tests/fixtures/msgpack/" func _load_fixture(name: String) -> PackedByteArray: var path = FIXTURE_DIR + name + ".msgpack" var file = FileAccess.open(path, FileAccess.READ) assert_that(file).is_not_null() return file.get_buffer(file.get_length()) # -- Snapshot decoding ---------------------------------------------------------- func test_decode_snapshot_one_npc() -> void: var bytes = _load_fixture("snapshot_one_npc") var snapshot = Protocol.decode_snapshot(bytes) assert_that(snapshot).is_not_null() assert_that(snapshot.tick).is_equal(42) assert_that(snapshot.entities.size()).is_equal(1) var entity = snapshot.entities[0] assert_that(entity.entity_id).is_equal(1) assert_float(entity.x).is_equal_approx(10.0, 0.001) assert_float(entity.y).is_equal_approx(20.0, 0.001) assert_that(entity.z).is_equal(0) assert_that(entity.kind.variant).is_equal("Npc") assert_that(entity.kind.data).is_null() func test_decode_snapshot_empty() -> void: var bytes = _load_fixture("snapshot_empty") var snapshot = Protocol.decode_snapshot(bytes) assert_that(snapshot).is_not_null() assert_that(snapshot.tick).is_equal(0) assert_that(snapshot.entities.size()).is_equal(0) func test_decode_snapshot_multi_entity() -> void: var bytes = _load_fixture("snapshot_multi_entity") var snapshot = Protocol.decode_snapshot(bytes) assert_that(snapshot).is_not_null() assert_that(snapshot.tick).is_equal(999) assert_that(snapshot.entities.size()).is_equal(3) # NPC at (5, 10, 0) var npc = snapshot.entities[0] assert_that(npc.entity_id).is_equal(1) assert_float(npc.x).is_equal_approx(5.0, 0.001) assert_float(npc.y).is_equal_approx(10.0, 0.001) assert_that(npc.z).is_equal(0) assert_that(npc.kind.variant).is_equal("Npc") # Object at (15.5, 3, 1) var obj = snapshot.entities[1] assert_that(obj.entity_id).is_equal(2) assert_float(obj.x).is_equal_approx(15.5, 0.001) assert_float(obj.y).is_equal_approx(3.0, 0.001) assert_that(obj.z).is_equal(1) assert_that(obj.kind.variant).is_equal("Object") # Terrain at (0, 0, -1) var terrain = snapshot.entities[2] assert_that(terrain.entity_id).is_equal(3) assert_float(terrain.x).is_equal_approx(0.0, 0.001) assert_float(terrain.y).is_equal_approx(0.0, 0.001) assert_that(terrain.z).is_equal(-1) assert_that(terrain.kind.variant).is_equal("Terrain") # -- PlayerInput decoding ------------------------------------------------------- func test_decode_input_move_north() -> void: var bytes = _load_fixture("input_move_north") var input = Protocol.decode_player_input(bytes) assert_that(input).is_not_null() assert_that(input.tick).is_equal(100) assert_that(input.action.variant).is_equal("MoveNorth") assert_that(input.action.data).is_null() func test_decode_input_perception_mode() -> void: var bytes = _load_fixture("input_perception_mode") var input = Protocol.decode_player_input(bytes) assert_that(input).is_not_null() assert_that(input.tick).is_equal(200) assert_that(input.action.variant).is_equal("UsePerceptionMode") assert_that(input.action.data).is_equal("thermal") # -- PlayerInput encoding ------------------------------------------------------- func test_encode_decode_roundtrip_unit_variant() -> void: var bytes = Protocol.encode_player_input(50, "MoveEast") assert_that(bytes.size()).is_greater(0) var decoded = Protocol.decode_player_input(bytes) assert_that(decoded).is_not_null() assert_that(decoded.tick).is_equal(50) assert_that(decoded.action.variant).is_equal("MoveEast") assert_that(decoded.action.data).is_null() func test_encode_decode_roundtrip_data_variant() -> void: var bytes = Protocol.encode_player_input(75, "UsePerceptionMode", "infrared") assert_that(bytes.size()).is_greater(0) var decoded = Protocol.decode_player_input(bytes) assert_that(decoded).is_not_null() assert_that(decoded.tick).is_equal(75) assert_that(decoded.action.variant).is_equal("UsePerceptionMode") assert_that(decoded.action.data).is_equal("infrared") # -- Cross-language roundtrip: GDScript encode matches Rust decode --------------- func test_gdscript_encode_matches_rust_fixture() -> void: # Encode the same MoveNorth input as the Rust fixture var bytes = Protocol.encode_player_input(100, "MoveNorth") # Decode and verify the content matches the Rust fixture var rust_bytes = _load_fixture("input_move_north") var from_gd = Protocol.decode_player_input(bytes) var from_rust = Protocol.decode_player_input(rust_bytes) assert_that(from_gd.tick).is_equal(from_rust.tick) assert_that(from_gd.action.variant).is_equal(from_rust.action.variant) assert_that(from_gd.action.data).is_equal(from_rust.action.data) # -- Negative tests: malformed/truncated input ----------------------------------- func test_decode_snapshot_truncated_bytes() -> void: var truncated := PackedByteArray([0x82, 0xa4]) # Incomplete msgpack map var result = Protocol.decode_snapshot(truncated) assert_that(result).is_null() func test_decode_snapshot_wrong_type() -> void: # Encode an array instead of a map — should fail validation var encoded = Messagepack.encode([1, 2, 3]) var result = Protocol.decode_snapshot(encoded.value) assert_that(result).is_null() func test_decode_snapshot_missing_fields() -> void: # Map with wrong keys var encoded = Messagepack.encode({"foo": "bar"}) var result = Protocol.decode_snapshot(encoded.value) assert_that(result).is_null() func test_decode_player_input_empty_bytes() -> void: var result = Protocol.decode_player_input(PackedByteArray()) assert_that(result).is_null() func test_encode_produces_nonempty_bytes() -> void: var bytes = Protocol.encode_player_input(1, "MoveNorth") assert_that(bytes.size()).is_greater(0) # -- Diagonal movement fixtures (D-030 Layer 1 cross-language) ----------------- func test_decode_diagonal_fixtures() -> void: # All 4 diagonal fixtures: clockwise NE, SE, SW, NW var diagonals: Array = [ ["input_move_northeast", "MoveNortheast"], ["input_move_southeast", "MoveSoutheast"], ["input_move_southwest", "MoveSouthwest"], ["input_move_northwest", "MoveNorthwest"], ] for pair in diagonals: var bytes = _load_fixture(pair[0]) var input: Variant = Protocol.decode_player_input(bytes) assert_that(input).is_not_null() assert_that(input.tick).is_equal(100) assert_that(input.action.variant).is_equal(pair[1]) assert_that(input.action.data).is_null()