extends GdUnitTestSuite ## NEO-73: allowed resource-node interact triggers inventory + skill refresh. const IxClient := preload("res://scripts/interaction_request_client.gd") const InventoryClient := preload("res://scripts/inventory_client.gd") const SkillProgressionClient := preload("res://scripts/skill_progression_client.gd") const Picker := preload("res://scripts/prototype_interactable_picker.gd") class MockInteractTransport: extends Node signal request_completed( result: int, response_code: int, headers: PackedStringArray, body: PackedByteArray ) var last_body: String = "" var body_json: String = '{"schemaVersion":1,"allowed":true}' var response_code: int = 200 func request( _url: String, _custom_headers: PackedStringArray = PackedStringArray(), _method: HTTPClient.Method = HTTPClient.METHOD_POST, request_data: String = "" ) -> Error: last_body = request_data var body_bytes: PackedByteArray = body_json.to_utf8_buffer() request_completed.emit( HTTPRequest.RESULT_SUCCESS, response_code, PackedStringArray(), body_bytes ) return OK class SpySyncClient: extends Node var sync_calls: int = 0 func request_sync_from_server() -> void: sync_calls += 1 class GatherRefreshHarness: extends Node var inventory_sync_calls: int = 0 var skill_sync_calls: int = 0 var pending_gather_interactable_id: String = "" var awaiting_inventory_finalize: bool = false var _inventory: SpySyncClient var _skill: SpySyncClient var _ix: Node func setup(parent: Node, ix_transport: Node) -> void: _inventory = SpySyncClient.new() _skill = SpySyncClient.new() _ix = IxClient.new() _ix.set("injected_http", ix_transport) parent.add_child(_ix) parent.add_child(_inventory) parent.add_child(_skill) _ix.connect("interaction_result_received", Callable(self, "_on_interaction_result")) func queue_resource_gather(interactable_id: String) -> void: pending_gather_interactable_id = interactable_id awaiting_inventory_finalize = false func _clear_gather_session() -> void: pending_gather_interactable_id = "" awaiting_inventory_finalize = false func _on_interaction_result( interactable_id: String, allowed: bool, _reason_code: String ) -> void: if pending_gather_interactable_id.is_empty(): return if interactable_id != pending_gather_interactable_id: if awaiting_inventory_finalize: return _clear_gather_session() return if not Picker.is_resource_node_id([], interactable_id): _clear_gather_session() return if not allowed: _clear_gather_session() return awaiting_inventory_finalize = true _inventory.request_sync_from_server() _skill.request_sync_from_server() func post_resource_alpha() -> void: queue_resource_gather("prototype_resource_node_alpha") _ix.call("post_interact_id", "prototype_resource_node_alpha") func emit_terminal_allowed() -> void: _ix.set("_current_interactable_id", "prototype_terminal") var body_bytes: PackedByteArray = '{"schemaVersion":1,"allowed":true}'.to_utf8_buffer() _ix.call("_on_request_completed", 0, 200, PackedStringArray(), body_bytes) func test_allowed_resource_interact_triggers_both_refreshes() -> void: # Arrange var transport := MockInteractTransport.new() var harness := GatherRefreshHarness.new() auto_free(transport) auto_free(harness) add_child(harness) harness.setup(self, transport) monitor_signals(harness._ix) # Act harness.post_resource_alpha() # Assert await assert_signal(harness._ix).is_emitted( "interaction_result_received", "prototype_resource_node_alpha", true, "" ) assert_that(harness._inventory.sync_calls).is_equal(1) assert_that(harness._skill.sync_calls).is_equal(1) func test_denied_resource_interact_skips_refreshes() -> void: # Arrange var transport := MockInteractTransport.new() transport.body_json = '{"schemaVersion":1,"allowed":false,"reasonCode":"node_depleted"}' var harness := GatherRefreshHarness.new() auto_free(transport) auto_free(harness) add_child(harness) harness.setup(self, transport) # Act harness.post_resource_alpha() # Assert assert_that(harness._inventory.sync_calls).is_equal(0) assert_that(harness._skill.sync_calls).is_equal(0) func test_terminal_result_clears_orphaned_gather_pending() -> void: # Arrange var transport := MockInteractTransport.new() var harness := GatherRefreshHarness.new() auto_free(transport) auto_free(harness) add_child(harness) harness.setup(self, transport) harness.queue_resource_gather("prototype_resource_node_alpha") # Act harness.emit_terminal_allowed() # Assert assert_that(harness.pending_gather_interactable_id).is_empty() assert_that(harness.awaiting_inventory_finalize).is_false() assert_that(harness._inventory.sync_calls).is_equal(0) assert_that(harness._skill.sync_calls).is_equal(0) func test_terminal_after_gather_success_keeps_awaiting_inventory() -> void: # Arrange var transport := MockInteractTransport.new() var harness := GatherRefreshHarness.new() auto_free(transport) auto_free(harness) add_child(harness) harness.setup(self, transport) # Act harness.post_resource_alpha() harness.emit_terminal_allowed() # Assert assert_that(harness.awaiting_inventory_finalize).is_true() assert_that(harness.pending_gather_interactable_id).is_equal("prototype_resource_node_alpha") assert_that(harness._inventory.sync_calls).is_equal(1) assert_that(harness._skill.sync_calls).is_equal(1)