Files
settled-reach/client/tests/test_interaction_prompt.gd
T
jpmschweitzerandClaude Opus 4.6 283518d332 refactor(ui): convert HUD status + interaction prompt to implant (#786, #788)
HUD: merge TimeDisplay into ImplantPanel (time/health/perception in one
panel), remove dead tscn nodes, move HUD to InsertOverlay for D-051 bloom.
Interaction prompt: convert from PanelContainer+StyleBoxFlat to Control
with ImplantPanel+ImplantDataRow, fix panel height for 42px minimum.
Tests updated for new scene structure and public API.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-06 12:51:58 +02:00

231 lines
8.2 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": Protocol.PROTOCOL_VERSION,
"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": Protocol.PROTOCOL_VERSION,
"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": Protocol.PROTOCOL_VERSION,
"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": Protocol.PROTOCOL_VERSION,
"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": Protocol.PROTOCOL_VERSION,
"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": Protocol.PROTOCOL_VERSION,
"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")
func test_protocol_rejects_version_mismatch() -> void:
var raw := {
"tick": 5,
"version": 2,
"entities": [],
}
var encoded = Messagepack.encode(raw)
var snapshot = Protocol.decode_snapshot(encoded.value)
assert_that(snapshot).is_null()
# -- 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)
func test_sim_bridge_test_snapshot_protocol_version() -> void:
SimBridge.reset_test_state()
var snap = SimBridge._test_snapshot()
assert_that(snap.version).is_equal(Protocol.PROTOCOL_VERSION)
# -- 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