Files
settled-reach/client/tests/test_protocol.gd
T
jpmschweitzerandClaude Opus 4.6 0f75c3194d feat(client): add LocalBridge framing tests and diagonal fixtures
12 new tests covering framing roundtrips, cross-layer Protocol+framing
integration, and diagonal wire mapping. 4 new diagonal movement
fixtures generated from Rust for D-030 Layer 1 cross-language
verification. 33/33 tests pass.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-11 20:59:46 +01:00

190 lines
6.5 KiB
GDScript

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