From ab7033e85a6cde11ef4cf91836ae1b26ecc6d094 Mon Sep 17 00:00:00 2001 From: VinPropane Date: Fri, 29 May 2026 20:03:47 -0400 Subject: [PATCH] NEO-99: update GdUnit tests for district visuals and prototype_npc_* targeting. Add visual catalog transform tests and refresh target selection, marker, and combat client suites for PrototypeNpcRegistry ids from NEO-91. --- client/test/ability_cast_client_test.gd | 12 +- client/test/combat_feedback_refresh_test.gd | 10 +- client/test/combat_targets_client_test.gd | 7 +- client/test/gig_feedback_refresh_test.gd | 6 +- client/test/hotbar_cast_slot_resolver_test.gd | 6 +- .../prototype_district_visual_catalog_test.gd | 25 ++++ ...totype_district_visual_catalog_test.gd.uid | 1 + .../test/prototype_target_constants_test.gd | 24 +++- client/test/prototype_target_markers_test.gd | 28 ++-- client/test/selection_event_client_test.gd | 27 ++-- .../test/target_refresh_on_locomotion_test.gd | 2 +- client/test/target_selection_client_test.gd | 123 +++++++++--------- 12 files changed, 164 insertions(+), 107 deletions(-) create mode 100644 client/test/prototype_district_visual_catalog_test.gd create mode 100644 client/test/prototype_district_visual_catalog_test.gd.uid diff --git a/client/test/ability_cast_client_test.gd b/client/test/ability_cast_client_test.gd index eeeeb84..e4dfda4 100644 --- a/client/test/ability_cast_client_test.gd +++ b/client/test/ability_cast_client_test.gd @@ -104,12 +104,12 @@ func test_request_cast_posts_target_id_string_when_set() -> void: transport.enqueue(HTTPRequest.RESULT_SUCCESS, 200, '{"schemaVersion":1,"accepted":true}') var c := _make_client(transport) # Act - var started: bool = bool(c.call("request_cast", 0, "prototype_pulse", "prototype_target_alpha")) + var started: bool = bool(c.call("request_cast", 0, "prototype_pulse", "prototype_npc_melee")) # Assert assert_that(started).is_true() var parsed: Variant = JSON.parse_string(transport.last_body) var body: Dictionary = parsed - assert_that(body.get("targetId")).is_equal("prototype_target_alpha") + assert_that(body.get("targetId")).is_equal("prototype_npc_melee") func test_request_cast_returns_false_when_http_request_fails_to_start() -> void: @@ -157,7 +157,7 @@ func test_cast_result_received_emits_false_with_reason_on_denied() -> void: got.append([accepted, reason, resolution]) ) # Act - var started: bool = bool(c.call("request_cast", 0, "prototype_pulse", "prototype_target_alpha")) + var started: bool = bool(c.call("request_cast", 0, "prototype_pulse", "prototype_npc_melee")) # Assert assert_that(started).is_true() assert_that(got.size()).is_equal(1) @@ -195,7 +195,7 @@ func test_cast_result_received_emits_combat_resolution_on_accept() -> void: 200, ( '{"schemaVersion":1,"accepted":true,"combatResolution":' - + '{"abilityId":"prototype_pulse","targetId":"prototype_target_alpha",' + + '{"abilityId":"prototype_pulse","targetId":"prototype_npc_melee",' + '"damageDealt":25,"targetRemainingHp":75,"targetDefeated":false}}' ) ) @@ -207,7 +207,7 @@ func test_cast_result_received_emits_combat_resolution_on_accept() -> void: got.append([accepted, reason, resolution]) ) # Act - var started: bool = bool(c.call("request_cast", 0, "prototype_pulse", "prototype_target_alpha")) + var started: bool = bool(c.call("request_cast", 0, "prototype_pulse", "prototype_npc_melee")) # Assert assert_that(started).is_true() assert_that(got.size()).is_equal(1) @@ -222,7 +222,7 @@ func test_parse_cast_response_json_maps_combat_resolution() -> void: # Arrange var body := ( '{"schemaVersion":1,"accepted":true,"combatResolution":' - + '{"abilityId":"prototype_pulse","targetId":"prototype_target_alpha",' + + '{"abilityId":"prototype_pulse","targetId":"prototype_npc_melee",' + '"damageDealt":25,"targetRemainingHp":75,"targetDefeated":false}}' ) # Act diff --git a/client/test/combat_feedback_refresh_test.gd b/client/test/combat_feedback_refresh_test.gd index 592bb55..1298886 100644 --- a/client/test/combat_feedback_refresh_test.gd +++ b/client/test/combat_feedback_refresh_test.gd @@ -13,7 +13,7 @@ class MockCastTransport: ) var body_json: String = ( '{"schemaVersion":1,"accepted":true,"combatResolution":' - + '{"abilityId":"prototype_pulse","targetId":"prototype_target_alpha",' + + '{"abilityId":"prototype_pulse","targetId":"prototype_npc_melee",' + '"damageDealt":25,"targetRemainingHp":75,"targetDefeated":false}}' ) @@ -76,7 +76,7 @@ class CombatRefreshHarness: return "" func request_cast() -> void: - _cast.call("request_cast", 0, "prototype_pulse", "prototype_target_alpha") + _cast.call("request_cast", 0, "prototype_pulse", "prototype_npc_melee") func test_successful_cast_triggers_combat_target_sync() -> void: @@ -106,7 +106,7 @@ func test_target_lock_change_triggers_combat_target_sync() -> void: harness.setup(self, transport) # Act harness.on_target_state_changed( - {"lockedTargetId": "prototype_target_alpha", "validity": "ok", "sequence": 1} + {"lockedTargetId": "prototype_npc_melee", "validity": "ok", "sequence": 1} ) # Assert assert_that(harness.combat_sync_calls).is_equal(1) @@ -122,11 +122,11 @@ func test_target_validity_only_update_skips_combat_target_sync() -> void: add_child(harness) harness.setup(self, transport) harness.on_target_state_changed( - {"lockedTargetId": "prototype_target_alpha", "validity": "ok", "sequence": 1} + {"lockedTargetId": "prototype_npc_melee", "validity": "ok", "sequence": 1} ) # Act harness.on_target_state_changed( - {"lockedTargetId": "prototype_target_alpha", "validity": "out_of_range", "sequence": 2} + {"lockedTargetId": "prototype_npc_melee", "validity": "out_of_range", "sequence": 2} ) # Assert assert_that(harness.combat_sync_calls).is_equal(1) diff --git a/client/test/combat_targets_client_test.gd b/client/test/combat_targets_client_test.gd index 0c2476e..5091f74 100644 --- a/client/test/combat_targets_client_test.gd +++ b/client/test/combat_targets_client_test.gd @@ -33,8 +33,9 @@ class MockHttpTransport: static func _two_target_snapshot_json() -> String: return ( '{"schemaVersion":1,"targets":[' - + '{"targetId":"prototype_target_alpha","maxHp":100,"currentHp":75,"defeated":false},' - + '{"targetId":"prototype_target_beta","maxHp":100,"currentHp":100,"defeated":false}' + + '{"targetId":"prototype_npc_melee","maxHp":100,"currentHp":75,"defeated":false},' + + '{"targetId":"prototype_npc_ranged","maxHp":80,"currentHp":80,"defeated":false},' + + '{"targetId":"prototype_npc_elite","maxHp":200,"currentHp":200,"defeated":false}' + "]}" ) @@ -86,7 +87,7 @@ func test_target_row_returns_alpha_hp_fields() -> void: c.call("request_sync_from_server") var snapshot: Dictionary = got[0] # Act - var row: Dictionary = c.call("target_row", "prototype_target_alpha", snapshot) as Dictionary + var row: Dictionary = c.call("target_row", "prototype_npc_melee", snapshot) as Dictionary # Assert assert_that(int(row.get("currentHp", 0))).is_equal(75) assert_that(int(row.get("maxHp", 0))).is_equal(100) diff --git a/client/test/gig_feedback_refresh_test.gd b/client/test/gig_feedback_refresh_test.gd index 56111b6..bc92c93 100644 --- a/client/test/gig_feedback_refresh_test.gd +++ b/client/test/gig_feedback_refresh_test.gd @@ -57,7 +57,7 @@ class GigRefreshHarness: _gig.request_sync_from_server() func request_cast() -> void: - _cast.call("request_cast", 0, "prototype_pulse", "prototype_target_alpha") + _cast.call("request_cast", 0, "prototype_pulse", "prototype_npc_melee") func test_defeat_cast_triggers_gig_progression_sync() -> void: @@ -65,7 +65,7 @@ func test_defeat_cast_triggers_gig_progression_sync() -> void: var transport := MockCastTransport.new() transport.body_json = ( '{"schemaVersion":1,"accepted":true,"combatResolution":' - + '{"abilityId":"prototype_pulse","targetId":"prototype_target_alpha",' + + '{"abilityId":"prototype_pulse","targetId":"prototype_npc_melee",' + '"damageDealt":25,"targetRemainingHp":0,"targetDefeated":true}}' ) var harness := GigRefreshHarness.new() @@ -87,7 +87,7 @@ func test_non_defeat_cast_skips_gig_progression_sync() -> void: var transport := MockCastTransport.new() transport.body_json = ( '{"schemaVersion":1,"accepted":true,"combatResolution":' - + '{"abilityId":"prototype_pulse","targetId":"prototype_target_alpha",' + + '{"abilityId":"prototype_pulse","targetId":"prototype_npc_melee",' + '"damageDealt":25,"targetRemainingHp":75,"targetDefeated":false}}' ) var harness := GigRefreshHarness.new() diff --git a/client/test/hotbar_cast_slot_resolver_test.gd b/client/test/hotbar_cast_slot_resolver_test.gd index 5174618..3c3210d 100644 --- a/client/test/hotbar_cast_slot_resolver_test.gd +++ b/client/test/hotbar_cast_slot_resolver_test.gd @@ -15,7 +15,7 @@ func test_resolve_returns_ok_with_target_from_locked_target_id() -> void: # Arrange var slots := _eight_empty_slots() slots[0] = "prototype_pulse" - var target_state := {"lockedTargetId": "prototype_target_alpha", "sequence": 3} + var target_state := {"lockedTargetId": "prototype_npc_melee", "sequence": 3} # Act var outcome: Dictionary = Resolver.resolve(0, slots, target_state) @@ -24,7 +24,7 @@ func test_resolve_returns_ok_with_target_from_locked_target_id() -> void: assert_that(bool(outcome.get(Resolver.KEY_OK, false))).is_true() assert_that(int(outcome.get(Resolver.KEY_SLOT_INDEX, -1))).is_equal(0) assert_that(outcome.get(Resolver.KEY_ABILITY_ID)).is_equal("prototype_pulse") - assert_that(outcome.get(Resolver.KEY_TARGET_ID)).is_equal("prototype_target_alpha") + assert_that(outcome.get(Resolver.KEY_TARGET_ID)).is_equal("prototype_npc_melee") func test_resolve_returns_null_target_when_no_lock() -> void: @@ -59,7 +59,7 @@ func test_resolve_denies_empty_slot_without_ability() -> void: # Arrange var slots := _eight_empty_slots() slots[3] = null - var target_state := {"lockedTargetId": "prototype_target_beta"} + var target_state := {"lockedTargetId": "prototype_npc_ranged"} # Act var outcome: Dictionary = Resolver.resolve(3, slots, target_state) diff --git a/client/test/prototype_district_visual_catalog_test.gd b/client/test/prototype_district_visual_catalog_test.gd new file mode 100644 index 0000000..3bd17e0 --- /dev/null +++ b/client/test/prototype_district_visual_catalog_test.gd @@ -0,0 +1,25 @@ +extends GdUnitTestSuite + +const Catalog := preload("res://scripts/prototype_district_visual_catalog.gd") + + +func test_instantiate_prop_clears_baked_child_transform() -> void: + # Arrange + var packed: PackedScene = Catalog.INTERACTABLE_PROPS["prototype_resource_node_alpha"] + # Act + var visual: Node3D = Catalog.instantiate_prop(packed, 0.5) + # Assert + var mesh: Node3D = visual.get_node("IX_SalvageAlpha_Mesh") as Node3D + assert_that(mesh.position).is_equal(Vector3.ZERO) + + +func test_normalize_prop_visual_grounds_mesh_feet_at_floor() -> void: + # Arrange + var packed: PackedScene = Catalog.INTERACTABLE_PROPS["prototype_resource_node_alpha"] + var visual: Node3D = packed.instantiate() as Node3D + var mesh: MeshInstance3D = visual.get_node("IX_SalvageAlpha_Mesh") as MeshInstance3D + var min_y: float = mesh.get_aabb().position.y + # Act + Catalog.normalize_prop_visual(visual, 0.5) + # Assert + assert_float(visual.position.y).is_equal_approx(-0.5 - min_y, 0.0001) diff --git a/client/test/prototype_district_visual_catalog_test.gd.uid b/client/test/prototype_district_visual_catalog_test.gd.uid new file mode 100644 index 0000000..904f8f5 --- /dev/null +++ b/client/test/prototype_district_visual_catalog_test.gd.uid @@ -0,0 +1 @@ +uid://c5m8p2n7k4w9x diff --git a/client/test/prototype_target_constants_test.gd b/client/test/prototype_target_constants_test.gd index 152d11b..acf0e75 100644 --- a/client/test/prototype_target_constants_test.gd +++ b/client/test/prototype_target_constants_test.gd @@ -1,18 +1,20 @@ extends GdUnitTestSuite -## NEO-85: prototype target display helpers for combat dummy markers and HUD copy. +## NEO-85 / NEO-91: prototype NPC display helpers for combat markers and HUD copy. const PrototypeTargetConstants := preload("res://scripts/prototype_target_constants.gd") -func test_display_short_name_maps_alpha_and_beta() -> void: +func test_display_short_name_maps_npc_archetypes() -> void: # Arrange # Act - var alpha: String = PrototypeTargetConstants.display_short_name("prototype_target_alpha") - var beta: String = PrototypeTargetConstants.display_short_name("prototype_target_beta") + var elite: String = PrototypeTargetConstants.display_short_name("prototype_npc_elite") + var melee: String = PrototypeTargetConstants.display_short_name("prototype_npc_melee") + var ranged: String = PrototypeTargetConstants.display_short_name("prototype_npc_ranged") # Assert - assert_that(alpha).is_equal("Dummy α") - assert_that(beta).is_equal("Dummy β") + assert_that(elite).is_equal("Elite") + assert_that(melee).is_equal("Melee") + assert_that(ranged).is_equal("Ranged") func test_display_short_name_falls_back_to_id() -> void: @@ -21,3 +23,13 @@ func test_display_short_name_falls_back_to_id() -> void: var unknown: String = PrototypeTargetConstants.display_short_name("unknown_target") # Assert assert_that(unknown).is_equal("unknown_target") + + +func test_ordered_ids_matches_server_tab_cycle() -> void: + # Arrange + # Act + var ids: Array[String] = PrototypeTargetConstants.ordered_ids() + # Assert + assert_that(ids).is_equal( + ["prototype_npc_elite", "prototype_npc_melee", "prototype_npc_ranged"] + ) diff --git a/client/test/prototype_target_markers_test.gd b/client/test/prototype_target_markers_test.gd index 0d734cd..d28afdf 100644 --- a/client/test/prototype_target_markers_test.gd +++ b/client/test/prototype_target_markers_test.gd @@ -4,20 +4,32 @@ extends GdUnitTestSuite const Markers := preload("res://scripts/prototype_target_markers.gd") -const ALPHA_ID := "prototype_target_alpha" +const MELEE_ID := "prototype_npc_melee" -func test_set_locked_target_brightens_body_albedo_without_emission() -> void: +func test_set_locked_target_brightens_ring_without_emission() -> void: # Arrange var markers: Node3D = Markers.new() auto_free(markers) add_child(markers) await get_tree().process_frame - var body: MeshInstance3D = markers.get_node("Marker_%s/DummyBody" % ALPHA_ID) as MeshInstance3D - var body_mat: StandardMaterial3D = body.material_override as StandardMaterial3D - var idle_color: Color = body_mat.albedo_color + var ring: MeshInstance3D = markers.get_node("Marker_%s/RadiusRing" % MELEE_ID) as MeshInstance3D + var ring_mat: StandardMaterial3D = ring.material_override as StandardMaterial3D + var idle_color: Color = ring_mat.albedo_color # Act - markers.set_locked_target(ALPHA_ID) + markers.set_locked_target(MELEE_ID) # Assert - assert_that(body_mat.albedo_color).is_not_equal(idle_color) - assert_that(body_mat.emission_enabled).is_false() + assert_that(ring_mat.albedo_color).is_not_equal(idle_color) + assert_that(ring_mat.emission_enabled).is_false() + + +func test_spawn_marker_uses_imported_prop_visual() -> void: + # Arrange + var markers: Node3D = Markers.new() + auto_free(markers) + add_child(markers) + await get_tree().process_frame + # Act + var visual: Node = markers.get_node("Marker_%s/DummyBody/PropVisual" % MELEE_ID) + # Assert + assert_that(visual).is_not_null() diff --git a/client/test/selection_event_client_test.gd b/client/test/selection_event_client_test.gd index f0e36a9..1aa120e 100644 --- a/client/test/selection_event_client_test.gd +++ b/client/test/selection_event_client_test.gd @@ -4,8 +4,9 @@ extends GdUnitTestSuite const TargetSelectionTestDouble := preload("res://test/target_selection_test_double.gd") -const ALPHA_ID := "prototype_target_alpha" -const BETA_ID := "prototype_target_beta" +const ELITE_ID := "prototype_npc_elite" +const MELEE_ID := "prototype_npc_melee" +const RANGED_ID := "prototype_npc_ranged" class MockHttpTransport: @@ -74,7 +75,7 @@ func test_tab_lock_change_emits_tab() -> void: # Arrange var transport := MockHttpTransport.new() transport.enqueue( - HTTPRequest.RESULT_SUCCESS, 200, _select_response_json(true, ALPHA_ID, "ok", 1) + HTTPRequest.RESULT_SUCCESS, 200, _select_response_json(true, MELEE_ID, "ok", 1) ) var c := _make_client(transport) var events: Array = [] @@ -86,7 +87,7 @@ func test_tab_lock_change_emits_tab() -> void: var last: Dictionary = events[0] as Dictionary assert_that(last.get("cause", "")).is_equal("tab") assert_that(last.get("previous")).is_null() - assert_that(last.get("next")).is_equal(ALPHA_ID) + assert_that(last.get("next")).is_equal(MELEE_ID) assert_that(int(last.get("sequence", -1))).is_equal(1) @@ -94,7 +95,7 @@ func test_clear_lock_change_emits_clear() -> void: # Arrange var transport := MockHttpTransport.new() transport.enqueue( - HTTPRequest.RESULT_SUCCESS, 200, _select_response_json(true, ALPHA_ID, "ok", 1) + HTTPRequest.RESULT_SUCCESS, 200, _select_response_json(true, MELEE_ID, "ok", 1) ) transport.enqueue(HTTPRequest.RESULT_SUCCESS, 200, _select_response_json(true, null, "none", 2)) var c := _make_client(transport) @@ -107,7 +108,7 @@ func test_clear_lock_change_emits_clear() -> void: assert_that(events.size()).is_equal(2) assert_that(int((events[0] as Dictionary).get("sequence", -1))).is_equal(1) assert_that((events[1] as Dictionary).get("cause", "")).is_equal("clear") - assert_that((events[1] as Dictionary).get("previous")).is_equal(ALPHA_ID) + assert_that((events[1] as Dictionary).get("previous")).is_equal(MELEE_ID) assert_that((events[1] as Dictionary).get("next")).is_null() assert_that(int((events[1] as Dictionary).get("sequence", -1))).is_equal(2) @@ -116,9 +117,9 @@ func test_get_lock_id_change_emits_server_correction() -> void: # Arrange var transport := MockHttpTransport.new() transport.enqueue( - HTTPRequest.RESULT_SUCCESS, 200, _select_response_json(true, ALPHA_ID, "ok", 1) + HTTPRequest.RESULT_SUCCESS, 200, _select_response_json(true, MELEE_ID, "ok", 1) ) - transport.enqueue(HTTPRequest.RESULT_SUCCESS, 200, _target_state_json(BETA_ID, "ok", 2)) + transport.enqueue(HTTPRequest.RESULT_SUCCESS, 200, _target_state_json(RANGED_ID, "ok", 2)) var c := _make_client(transport) var events: Array = [] c.selection_event.connect(func(ev: Dictionary) -> void: events.append(ev.duplicate())) @@ -129,8 +130,8 @@ func test_get_lock_id_change_emits_server_correction() -> void: assert_that(events.size()).is_equal(2) var last: Dictionary = events[1] as Dictionary assert_that(last.get("cause", "")).is_equal("server_correction") - assert_that(last.get("previous")).is_equal(ALPHA_ID) - assert_that(last.get("next")).is_equal(BETA_ID) + assert_that(last.get("previous")).is_equal(MELEE_ID) + assert_that(last.get("next")).is_equal(RANGED_ID) assert_that(int(last.get("sequence", -1))).is_equal(2) @@ -138,10 +139,10 @@ func test_get_validity_only_change_emits_nothing() -> void: # Arrange var transport := MockHttpTransport.new() transport.enqueue( - HTTPRequest.RESULT_SUCCESS, 200, _select_response_json(true, ALPHA_ID, "ok", 1) + HTTPRequest.RESULT_SUCCESS, 200, _select_response_json(true, MELEE_ID, "ok", 1) ) transport.enqueue( - HTTPRequest.RESULT_SUCCESS, 200, _target_state_json(ALPHA_ID, "out_of_range", 1) + HTTPRequest.RESULT_SUCCESS, 200, _target_state_json(MELEE_ID, "out_of_range", 1) ) var c := _make_client(transport) var events: Array = [] @@ -167,6 +168,6 @@ func test_denial_without_lock_id_change_emits_nothing() -> void: var events: Array = [] c.selection_event.connect(func(ev: Dictionary) -> void: events.append(ev.duplicate())) # Act - c.request_select_target_id(ALPHA_ID) + c.request_select_target_id(MELEE_ID) # Assert assert_that(events.size()).is_equal(0) diff --git a/client/test/target_refresh_on_locomotion_test.gd b/client/test/target_refresh_on_locomotion_test.gd index f4f81e6..6fdc78f 100644 --- a/client/test/target_refresh_on_locomotion_test.gd +++ b/client/test/target_refresh_on_locomotion_test.gd @@ -13,7 +13,7 @@ extends GdUnitTestSuite const PositionAuthorityTestDouble := preload("res://test/position_authority_test_double.gd") const TargetSelectionTestDouble := preload("res://test/target_selection_test_double.gd") -const ALPHA_ID := "prototype_target_alpha" +const ALPHA_ID := "prototype_npc_melee" # Mirrors the transports from the per-client suites: a queue-backed `Node` with a diff --git a/client/test/target_selection_client_test.gd b/client/test/target_selection_client_test.gd index d4e65d1..c835280 100644 --- a/client/test/target_selection_client_test.gd +++ b/client/test/target_selection_client_test.gd @@ -6,8 +6,9 @@ extends GdUnitTestSuite const TargetSelectionTestDouble := preload("res://test/target_selection_test_double.gd") -const ALPHA_ID := "prototype_target_alpha" -const BETA_ID := "prototype_target_beta" +const ELITE_ID := "prototype_npc_elite" +const MELEE_ID := "prototype_npc_melee" +const RANGED_ID := "prototype_npc_ranged" # In-process transport: not an HTTPRequest subclass (Godot 4 will not call script `request()` @@ -101,7 +102,7 @@ func test_tab_from_no_lock_selects_first_id() -> void: # Arrange var transport := MockHttpTransport.new() transport.enqueue( - HTTPRequest.RESULT_SUCCESS, 200, _select_response_json(true, ALPHA_ID, "ok", 1) + HTTPRequest.RESULT_SUCCESS, 200, _select_response_json(true, ELITE_ID, "ok", 1) ) var c := _make_client(transport) monitor_signals(c) @@ -110,49 +111,53 @@ func test_tab_from_no_lock_selects_first_id() -> void: # Assert assert_that(transport.last_url).contains("/target/select") assert_that(transport.last_method).is_equal(HTTPClient.METHOD_POST) - assert_that(transport.last_body).contains('"targetId":"%s"' % ALPHA_ID) + assert_that(transport.last_body).contains('"targetId":"%s"' % ELITE_ID) await assert_signal(c).is_emitted("target_state_changed", any()) - assert_that(c.cached_state().get("lockedTargetId")).is_equal(ALPHA_ID) + assert_that(c.cached_state().get("lockedTargetId")).is_equal(ELITE_ID) -func test_tab_from_alpha_requests_beta() -> void: +func test_tab_from_elite_requests_melee() -> void: # Arrange var transport := MockHttpTransport.new() transport.enqueue( - HTTPRequest.RESULT_SUCCESS, 200, _select_response_json(true, ALPHA_ID, "ok", 1) + HTTPRequest.RESULT_SUCCESS, 200, _select_response_json(true, ELITE_ID, "ok", 1) ) transport.enqueue( - HTTPRequest.RESULT_SUCCESS, 200, _select_response_json(true, BETA_ID, "ok", 2) + HTTPRequest.RESULT_SUCCESS, 200, _select_response_json(true, MELEE_ID, "ok", 2) ) var c := _make_client(transport) # Act c.request_tab_next() c.request_tab_next() # Assert - assert_that(transport.last_body).contains('"targetId":"%s"' % BETA_ID) - assert_that(c.cached_state().get("lockedTargetId")).is_equal(BETA_ID) + assert_that(transport.last_body).contains('"targetId":"%s"' % MELEE_ID) + assert_that(c.cached_state().get("lockedTargetId")).is_equal(MELEE_ID) -func test_tab_wraps_from_beta_to_alpha() -> void: +func test_tab_wraps_from_ranged_back_to_elite() -> void: # Arrange var transport := MockHttpTransport.new() transport.enqueue( - HTTPRequest.RESULT_SUCCESS, 200, _select_response_json(true, ALPHA_ID, "ok", 1) + HTTPRequest.RESULT_SUCCESS, 200, _select_response_json(true, ELITE_ID, "ok", 1) ) transport.enqueue( - HTTPRequest.RESULT_SUCCESS, 200, _select_response_json(true, BETA_ID, "ok", 2) + HTTPRequest.RESULT_SUCCESS, 200, _select_response_json(true, MELEE_ID, "ok", 2) ) transport.enqueue( - HTTPRequest.RESULT_SUCCESS, 200, _select_response_json(true, ALPHA_ID, "ok", 3) + HTTPRequest.RESULT_SUCCESS, 200, _select_response_json(true, RANGED_ID, "ok", 3) + ) + transport.enqueue( + HTTPRequest.RESULT_SUCCESS, 200, _select_response_json(true, ELITE_ID, "ok", 4) ) var c := _make_client(transport) # Act c.request_tab_next() c.request_tab_next() c.request_tab_next() + c.request_tab_next() # Assert - assert_that(transport.last_body).contains('"targetId":"%s"' % ALPHA_ID) - assert_that(c.cached_state().get("lockedTargetId")).is_equal(ALPHA_ID) + assert_that(transport.last_body).contains('"targetId":"%s"' % ELITE_ID) + assert_that(c.cached_state().get("lockedTargetId")).is_equal(ELITE_ID) func test_denial_reflects_authoritative_target_state() -> void: @@ -166,7 +171,7 @@ func test_denial_reflects_authoritative_target_state() -> void: ) var c := _make_client(transport) # Act - c.request_select_target_id(ALPHA_ID) + c.request_select_target_id(MELEE_ID) # Assert var state: Dictionary = c.cached_state() assert_that(bool(state.get("selectionApplied", true))).is_false() @@ -192,21 +197,21 @@ func test_authoritative_ack_while_locked_fires_refresh_get() -> void: var transport := MockHttpTransport.new() # Step 1: lock alpha via POST. transport.enqueue( - HTTPRequest.RESULT_SUCCESS, 200, _select_response_json(true, ALPHA_ID, "ok", 1) + HTTPRequest.RESULT_SUCCESS, 200, _select_response_json(true, MELEE_ID, "ok", 1) ) # Step 2: movement-triggered GET shows out_of_range (soft lock). transport.enqueue( - HTTPRequest.RESULT_SUCCESS, 200, _target_state_json(ALPHA_ID, "out_of_range", 1) + HTTPRequest.RESULT_SUCCESS, 200, _target_state_json(MELEE_ID, "out_of_range", 1) ) var c := _make_client(transport) # Act - c.request_select_target_id(ALPHA_ID) + c.request_select_target_id(MELEE_ID) var count_before_ack: int = transport.request_count c.on_authoritative_ack(Vector3.ZERO) # Assert assert_that(transport.request_count).is_equal(count_before_ack + 1) assert_that(c.cached_state().get("validity")).is_equal("out_of_range") - assert_that(c.cached_state().get("lockedTargetId")).is_equal(ALPHA_ID) + assert_that(c.cached_state().get("lockedTargetId")).is_equal(MELEE_ID) func test_authoritative_ack_without_lock_fires_nothing() -> void: @@ -223,14 +228,14 @@ func test_two_acks_within_cooldown_collapse_to_one_get() -> void: # Arrange var transport := MockHttpTransport.new() transport.enqueue( - HTTPRequest.RESULT_SUCCESS, 200, _select_response_json(true, ALPHA_ID, "ok", 1) + HTTPRequest.RESULT_SUCCESS, 200, _select_response_json(true, MELEE_ID, "ok", 1) ) transport.enqueue( - HTTPRequest.RESULT_SUCCESS, 200, _target_state_json(ALPHA_ID, "out_of_range", 1) + HTTPRequest.RESULT_SUCCESS, 200, _target_state_json(MELEE_ID, "out_of_range", 1) ) var c := _make_client(transport) # Act - c.request_select_target_id(ALPHA_ID) + c.request_select_target_id(MELEE_ID) var count_before: int = transport.request_count c.on_authoritative_ack(Vector3.ZERO) c.on_authoritative_ack(Vector3.ZERO) @@ -262,7 +267,7 @@ func test_select_post_kicks_freshness_stream_before_posting() -> void: # Arrange var transport := MockHttpTransport.new() transport.enqueue( - HTTPRequest.RESULT_SUCCESS, 200, _select_response_json(true, ALPHA_ID, "ok", 1) + HTTPRequest.RESULT_SUCCESS, 200, _select_response_json(true, MELEE_ID, "ok", 1) ) var c := _make_client(transport) var authority := FreshnessAuthorityStub.new() @@ -271,7 +276,7 @@ func test_select_post_kicks_freshness_stream_before_posting() -> void: var player := _make_player(Vector3(1.5, 0.5, -2.25)) c.set_freshness_kick(authority, player) # Act - c.request_select_target_id(ALPHA_ID) + c.request_select_target_id(MELEE_ID) # Assert # Exactly one freshness submit, matching the pre-POST capsule position. assert_that(authority.submit_count).is_equal(1) @@ -287,7 +292,7 @@ func test_select_post_includes_position_hint_when_player_wired() -> void: # `positionHint` so the server's range check can bypass any stale stored snap. var transport := MockHttpTransport.new() transport.enqueue( - HTTPRequest.RESULT_SUCCESS, 200, _select_response_json(true, BETA_ID, "ok", 1) + HTTPRequest.RESULT_SUCCESS, 200, _select_response_json(true, RANGED_ID, "ok", 1) ) var c := _make_client(transport) var authority := FreshnessAuthorityStub.new() @@ -298,12 +303,12 @@ func test_select_post_includes_position_hint_when_player_wired() -> void: var player := _make_player(Vector3(3.0, 0.5, 5.0)) c.set_freshness_kick(authority, player) # Act - c.request_select_target_id(BETA_ID) + c.request_select_target_id(RANGED_ID) # Assert var parsed: Variant = JSON.parse_string(transport.last_body) assert_that(parsed is Dictionary).is_true() var body: Dictionary = parsed - assert_that(body.get("targetId")).is_equal(BETA_ID) + assert_that(body.get("targetId")).is_equal(RANGED_ID) assert_that(body.has("positionHint")).is_true() var hint: Dictionary = body["positionHint"] assert_float(hint.get("x")).is_equal_approx(3.0, 0.0001) @@ -317,11 +322,11 @@ func test_select_post_without_freshness_wiring_is_a_noop() -> void: # still POST normally, *without* a `positionHint` (hint is opt-in per wiring). var transport := MockHttpTransport.new() transport.enqueue( - HTTPRequest.RESULT_SUCCESS, 200, _select_response_json(true, ALPHA_ID, "ok", 1) + HTTPRequest.RESULT_SUCCESS, 200, _select_response_json(true, MELEE_ID, "ok", 1) ) var c := _make_client(transport) # Act - c.request_select_target_id(ALPHA_ID) + c.request_select_target_id(MELEE_ID) # Assert assert_that(transport.last_url).contains("/target/select") # No `positionHint` key in the body when no player is wired — re-parse so the assertion @@ -329,29 +334,29 @@ func test_select_post_without_freshness_wiring_is_a_noop() -> void: var parsed: Variant = JSON.parse_string(transport.last_body) assert_that(parsed is Dictionary).is_true() assert_that((parsed as Dictionary).has("positionHint")).is_false() - assert_that(c.cached_state().get("lockedTargetId")).is_equal(ALPHA_ID) + assert_that(c.cached_state().get("lockedTargetId")).is_equal(MELEE_ID) -func test_tab_from_no_lock_skips_out_of_range_and_picks_beta() -> void: +func test_tab_from_no_lock_skips_out_of_range_and_picks_ranged() -> void: # Arrange - # Player stands next to beta (well outside alpha's ring). Without the range-aware - # tab cycle the server denies with `out_of_range` on alpha — see NEO-24 follow-up #3. + # Player stands next to ranged only (elite/melee rings are out of reach). Without the + # range-aware tab cycle the server denies on the first id in cycle order. var transport := MockHttpTransport.new() transport.enqueue( - HTTPRequest.RESULT_SUCCESS, 200, _select_response_json(true, BETA_ID, "ok", 1) + HTTPRequest.RESULT_SUCCESS, 200, _select_response_json(true, RANGED_ID, "ok", 1) ) var c := _make_client(transport) var authority := FreshnessAuthorityStub.new() auto_free(authority) add_child(authority) - # Near beta anchor (3, 3), ~1.4 m horizontal — inside 6 m. Far from alpha at (-3, -3). - var player := _make_player(Vector3(4.0, 0.5, 4.0)) + # Near ranged anchor (3, 3), inside 6 m; elite at origin and melee at (-3, -3) are out. + var player := _make_player(Vector3(7.0, 0.5, 7.0)) c.set_freshness_kick(authority, player) # Act c.request_tab_next() # Assert - assert_that(transport.last_body).contains('"targetId":"%s"' % BETA_ID) - assert_that(c.cached_state().get("lockedTargetId")).is_equal(BETA_ID) + assert_that(transport.last_body).contains('"targetId":"%s"' % RANGED_ID) + assert_that(c.cached_state().get("lockedTargetId")).is_equal(RANGED_ID) func test_tab_from_no_lock_with_nothing_in_range_falls_back_to_first_id() -> void: @@ -372,21 +377,21 @@ func test_tab_from_no_lock_with_nothing_in_range_falls_back_to_first_id() -> voi # Act c.request_tab_next() # Assert - assert_that(transport.last_body).contains('"targetId":"%s"' % ALPHA_ID) + assert_that(transport.last_body).contains('"targetId":"%s"' % ELITE_ID) assert_that(c.cached_state().get("reasonCode", "")).is_equal("out_of_range") -func test_tab_from_locked_alpha_at_origin_swaps_to_beta_when_both_in_range() -> void: +func test_tab_from_locked_elite_at_origin_swaps_to_melee_when_both_in_range() -> void: # Arrange - # With alpha (-3,-3) and beta (3,3) sharing r=6, origin is in both rings. Locked to - # alpha, Tab should request beta (normal cycle behavior; range-aware skip-current - # does not interfere when the next id is also in range). + # With elite (0,0), melee (-3,-3), and ranged (3,3) sharing r=6, origin is in elite/melee + # rings. Locked to elite, Tab should request melee (normal cycle behavior; range-aware + # skip-current does not interfere when the next id is also in range). var transport := MockHttpTransport.new() transport.enqueue( - HTTPRequest.RESULT_SUCCESS, 200, _select_response_json(true, ALPHA_ID, "ok", 1) + HTTPRequest.RESULT_SUCCESS, 200, _select_response_json(true, ELITE_ID, "ok", 1) ) transport.enqueue( - HTTPRequest.RESULT_SUCCESS, 200, _select_response_json(true, BETA_ID, "ok", 2) + HTTPRequest.RESULT_SUCCESS, 200, _select_response_json(true, MELEE_ID, "ok", 2) ) var c := _make_client(transport) var authority := FreshnessAuthorityStub.new() @@ -398,8 +403,8 @@ func test_tab_from_locked_alpha_at_origin_swaps_to_beta_when_both_in_range() -> c.request_tab_next() c.request_tab_next() # Assert - assert_that(transport.last_body).contains('"targetId":"%s"' % BETA_ID) - assert_that(c.cached_state().get("lockedTargetId")).is_equal(BETA_ID) + assert_that(transport.last_body).contains('"targetId":"%s"' % MELEE_ID) + assert_that(c.cached_state().get("lockedTargetId")).is_equal(MELEE_ID) func test_tab_when_only_current_lock_is_in_range_falls_back_to_cycle_for_denial() -> void: @@ -410,39 +415,39 @@ func test_tab_when_only_current_lock_is_in_range_falls_back_to_cycle_for_denial( # visible reaction to Tab. var transport := MockHttpTransport.new() transport.enqueue( - HTTPRequest.RESULT_SUCCESS, 200, _select_response_json(true, ALPHA_ID, "ok", 1) + HTTPRequest.RESULT_SUCCESS, 200, _select_response_json(true, MELEE_ID, "ok", 1) ) transport.enqueue( HTTPRequest.RESULT_SUCCESS, 200, - _select_response_json(false, ALPHA_ID, "ok", 1, "out_of_range") + _select_response_json(false, MELEE_ID, "ok", 1, "out_of_range") ) var c := _make_client(transport) var authority := FreshnessAuthorityStub.new() auto_free(authority) add_child(authority) - # Near alpha (-3, -3), ~1.4 m in — beta (3, 3) is ~8.5 m out. - var player := _make_player(Vector3(-2.0, 0.5, -2.0)) + # Near melee (-3, -3), inside its ring — elite at origin and ranged at (3, 3) are out. + var player := _make_player(Vector3(-8.0, 0.5, -3.0)) c.set_freshness_kick(authority, player) # Act - c.request_select_target_id(ALPHA_ID) + c.request_select_target_id(MELEE_ID) c.request_tab_next() # Assert - assert_that(transport.last_body).contains('"targetId":"%s"' % BETA_ID) + assert_that(transport.last_body).contains('"targetId":"%s"' % RANGED_ID) assert_that(c.cached_state().get("reasonCode", "")).is_equal("out_of_range") - assert_that(c.cached_state().get("lockedTargetId")).is_equal(ALPHA_ID) + assert_that(c.cached_state().get("lockedTargetId")).is_equal(MELEE_ID) func test_tab_without_player_ref_still_uses_plain_cycle_order() -> void: # Arrange # Regression: headless unit tests and any client that skips `set_freshness_kick(...)` - # must keep the pre-existing cycle-order semantics. Alpha is requested from no-lock. + # must keep the pre-existing cycle-order semantics. Elite is requested from no-lock. var transport := MockHttpTransport.new() transport.enqueue( - HTTPRequest.RESULT_SUCCESS, 200, _select_response_json(true, ALPHA_ID, "ok", 1) + HTTPRequest.RESULT_SUCCESS, 200, _select_response_json(true, ELITE_ID, "ok", 1) ) var c := _make_client(transport) # Act c.request_tab_next() # Assert - assert_that(transport.last_body).contains('"targetId":"%s"' % ALPHA_ID) + assert_that(transport.last_body).contains('"targetId":"%s"' % ELITE_ID)