Files
settled-reach/client/tests/test_interaction_prompt.gd
T
jpmschweitzerandClaude Sonnet 4.6 d72fcc7847 fix(protocol): drop PROTOCOL_VERSION lockstep — D-192 (#875)
Removes the version-mismatch guard from Protocol.decode_snapshot() and the
PROTOCOL_VERSION constant from the client (server side done in #874).

Core changes:
- protocol.gd: remove const PROTOCOL_VERSION, remove version mismatch guard,
  remove "version" from return dict, add gauntlet_mode/room_id decode
- sim_bridge.gd: remove handshake version check; relax handshake guard to
  require only a valid Dictionary (server no longer sends protocol_version);
  emit handshake_complete(0) for API compat
- loading_screen.gd: drop "· protocol N" suffix from version label
- test_harness.gd: replace Protocol.PROTOCOL_VERSION with literal 23

Test updates (21 files): replace "version": Protocol.PROTOCOL_VERSION with
"version": 23 in all snapshot bytes dicts; remove snapshot.version == N
assertions; remove version-rejection tests (test_rejects_version_6,
test_decode_snapshot_rejects_missing_version, test_decode_snapshot_rejects_old_version,
test_protocol_rejects_version_mismatch, test_sim_bridge_test_snapshot_uses_current_protocol_version).

Also includes: #872 bookmark_catalog carry-forward regression test, and
#873 merge-path flow tests (test_merge_path_flows_sprint37.gd).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-21 17:29:49 +02:00

214 lines
7.6 KiB
GDScript

class_name TestInteractionPrompt
extends GdUnitTestSuite
# Tests for ticket #405: Interaction prompt system.
# Covers protocol v4 decode, GameState storage, SimBridge test mode,
# InteractionPrompt UI, and input encoding.
# -- Protocol: v4 nearby_interactions decoding --
func test_protocol_decode_v4_with_nearby_interactions() -> void:
var raw := {
"tick": 10,
"version": 23,
"entities": [
{"entity_id": 1, "x": 5.0, "y": 5.0, "z": 0, "kind": "Player",
"visibility": "Forward", "relationship": "Unknown", "observation": "Visible"},
],
"nearby_interactions": [{
"entity_id": 2,
"entity_type": "Npc",
"distance": 1,
"verbs": [
{"kind": "Talk", "label": "Talk", "priority": 1, "available": true},
{"kind": "ExamineNpc", "label": "Observe", "priority": 2, "available": true},
],
}],
}
var encoded = Messagepack.encode(raw)
assert_that(encoded.status == null).is_true()
var snapshot = Protocol.decode_snapshot(encoded.value)
assert_that(snapshot).is_not_null()
assert_that(snapshot.nearby_interactions.size()).is_equal(1)
var ni = snapshot.nearby_interactions[0]
assert_that(ni.entity_id).is_equal(2)
assert_that(ni.entity_type).is_equal("Npc")
assert_that(ni.distance).is_equal(1)
assert_that(ni.verbs.size()).is_equal(2)
assert_that(ni.verbs[0].kind).is_equal("Talk")
assert_that(ni.verbs[0].label).is_equal("Talk")
assert_that(ni.verbs[0].priority).is_equal(1)
assert_that(ni.verbs[0].available).is_true()
assert_that(ni.verbs[1].kind).is_equal("ExamineNpc")
func test_protocol_decode_v4_no_nearby_interactions() -> void:
var raw := {
"tick": 5,
"version": 23,
"entities": [],
}
var encoded = Messagepack.encode(raw)
var snapshot = Protocol.decode_snapshot(encoded.value)
assert_that(snapshot).is_not_null()
assert_that(snapshot.nearby_interactions.size()).is_equal(0)
func test_protocol_decode_empty_nearby_interactions() -> void:
var raw := {
"tick": 1,
"version": 23,
"entities": [],
"nearby_interactions": [],
}
var encoded = Messagepack.encode(raw)
var snapshot = Protocol.decode_snapshot(encoded.value)
assert_that(snapshot.nearby_interactions.size()).is_equal(0)
func test_protocol_decode_interaction_missing_verbs() -> void:
var raw := {
"tick": 1,
"version": 23,
"entities": [],
"nearby_interactions": [{"entity_id": 2}],
}
var encoded = Messagepack.encode(raw)
var snapshot = Protocol.decode_snapshot(encoded.value)
assert_that(snapshot.nearby_interactions.size()).is_equal(0)
func test_protocol_decode_interaction_empty_verbs() -> void:
var raw := {
"tick": 1,
"version": 23,
"entities": [],
"nearby_interactions": [{"entity_id": 2, "entity_type": "Npc", "distance": 1, "verbs": []}],
}
var encoded = Messagepack.encode(raw)
var snapshot = Protocol.decode_snapshot(encoded.value)
assert_that(snapshot.nearby_interactions.size()).is_equal(0)
func test_protocol_decode_v4_entity_relationship() -> void:
var raw := {
"tick": 1,
"version": 23,
"entities": [
{"entity_id": 1, "x": 5.0, "y": 5.0, "z": 0, "kind": "Npc",
"visibility": "Forward", "relationship": "Friendly", "observation": "Visible"},
],
}
var encoded = Messagepack.encode(raw)
var snapshot = Protocol.decode_snapshot(encoded.value)
assert_that(snapshot.entities[0].relationship).is_equal("Friendly")
# -- GameState: nearby_interactions storage --
func test_game_state_stores_nearby_interactions() -> void:
var ni := [{"entity_id": 2, "entity_type": "Npc", "distance": 1,
"verbs": [{"kind": "Talk", "label": "Talk", "priority": 1, "available": true}]}]
GameState.apply_snapshot({"tick": 1, "entities": [], "nearby_interactions": ni})
assert_that(GameState.nearby_interactions.size()).is_equal(1)
assert_that(GameState.nearby_interactions[0].entity_id).is_equal(2)
GameState.nearby_interactions = []
func test_game_state_clears_nearby_interactions_when_absent() -> void:
var ni := [{"entity_id": 2, "entity_type": "Npc", "distance": 1,
"verbs": [{"kind": "Talk", "label": "Talk", "priority": 1, "available": true}]}]
GameState.apply_snapshot({"tick": 1, "entities": [], "nearby_interactions": ni})
assert_that(GameState.nearby_interactions.size()).is_equal(1)
GameState.apply_snapshot({"tick": 2, "entities": []})
assert_that(GameState.nearby_interactions.size()).is_equal(0)
# -- SimBridge test mode: nearby_interactions generation --
func test_sim_bridge_test_snapshot_interaction_when_near_npc() -> void:
SimBridge.reset_test_state()
SimBridge._test_player_pos = Vector2i(11, 9)
var snap = SimBridge._test_snapshot()
assert_that(snap.has("nearby_interactions")).is_true()
assert_that(snap.nearby_interactions.size()).is_equal(1)
assert_that(snap.nearby_interactions[0].entity_id).is_equal(2)
assert_that(snap.nearby_interactions[0].verbs.size()).is_greater(0)
func test_sim_bridge_test_snapshot_no_interaction_when_far() -> void:
SimBridge.reset_test_state()
SimBridge._test_player_pos = Vector2i(10, 10)
var snap = SimBridge._test_snapshot()
assert_that(snap.nearby_interactions.size()).is_equal(0)
func test_sim_bridge_test_snapshot_interaction_at_range_2() -> void:
SimBridge.reset_test_state()
SimBridge._test_player_pos = Vector2i(10, 9)
var snap = SimBridge._test_snapshot()
assert_that(snap.nearby_interactions.size()).is_equal(1)
# -- InteractionPrompt UI --
func test_prompt_get_selected_verb_returns_first_kind() -> void:
GameState.nearby_interactions = [{
"entity_id": 2, "entity_type": "Npc", "distance": 1,
"verbs": [
{"kind": "Talk", "label": "Talk", "priority": 1, "available": true},
{"kind": "ExamineNpc", "label": "Observe", "priority": 2, "available": true},
],
}]
var prompt = _make_prompt()
assert_that(prompt.get_selected_verb()).is_equal("Talk")
prompt.queue_free()
GameState.nearby_interactions = []
func test_prompt_get_selected_verb_empty_when_no_interactions() -> void:
GameState.nearby_interactions = []
var prompt = _make_prompt()
assert_that(prompt.get_selected_verb()).is_equal("")
prompt.queue_free()
func test_prompt_get_target_negative_when_no_interactions() -> void:
GameState.nearby_interactions = []
var prompt = _make_prompt()
assert_that(prompt.get_interaction_target()).is_equal(-1)
prompt.queue_free()
func test_prompt_hidden_initially() -> void:
GameState.nearby_interactions = []
var prompt = _make_prompt()
assert_that(prompt._is_showing).is_false()
prompt.queue_free()
# -- Input encoding: Interact --
func test_interact_encodes_as_struct_variant() -> void:
# Interact is always a struct variant: {"Interact": {"target_entity_id": null, "verb": null}}
var inputs: Array = [{"tick": 100, "action_name": "Interact"}]
var bytes := Protocol.encode_player_inputs(inputs)
var raw = Messagepack.decode(bytes)
assert_that(raw.status == null).is_true()
assert_that(raw.value[0]["action"] is Dictionary).is_true()
assert_that(raw.value[0]["action"].has("Interact")).is_true()
var interact_data: Dictionary = raw.value[0]["action"]["Interact"]
assert_that(interact_data.has("target_entity_id")).is_true()
assert_that(interact_data.has("verb")).is_true()
func test_interact_with_data_encodes_as_data_variant() -> void:
var inputs: Array = [{
"tick": 100,
"action_name": "Interact",
"action_data": {"target_entity_id": 2, "verb": "Talk"},
}]
var bytes := Protocol.encode_player_inputs(inputs)
var raw = Messagepack.decode(bytes)
assert_that(raw.status == null).is_true()
assert_that(raw.value[0]["action"] is Dictionary).is_true()
assert_that(raw.value[0]["action"].has("Interact")).is_true()
# -- Helpers --
func _make_prompt() -> Control:
var PromptScript = load("res://ui/interaction_prompt.gd")
var ctrl = Control.new()
ctrl.set_script(PromptScript)
add_child(ctrl) # triggers _ready() which creates ImplantPanel + ImplantDataRow
return ctrl