extends GdUnitTestSuite ## NEO-25 / NEO-73: `InteractionRequestClient` POST bodies, result/failure signals. const IxClient := preload("res://scripts/interaction_request_client.gd") class MockHttpTransport: extends Node signal request_completed( result: int, response_code: int, headers: PackedStringArray, body: PackedByteArray ) var last_url: String = "" var last_body: String = "" var last_method: HTTPClient.Method = HTTPClient.Method.METHOD_GET 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.METHOD_GET, request_data: String = "" ) -> Error: last_url = url last_body = request_data last_method = method var body_bytes: PackedByteArray = body_json.to_utf8_buffer() request_completed.emit( HTTPRequest.RESULT_SUCCESS, response_code, PackedStringArray(), body_bytes ) return OK ## First POST defers completion one frame so a second press can queue as `_pending_interactable_id`. class HoldFirstThenAutoTransport: extends Node signal request_completed( result: int, response_code: int, headers: PackedStringArray, body: PackedByteArray ) var bodies: Array[String] = [] var _first: bool = true func request( _url: String, _custom_headers: PackedStringArray = PackedStringArray(), _method: HTTPClient.Method = HTTPClient.METHOD_GET, request_data: String = "" ) -> Error: bodies.append(request_data) var body_bytes: PackedByteArray = '{"schemaVersion":1,"allowed":true}'.to_utf8_buffer() if _first: _first = false call_deferred("_emit", body_bytes) else: request_completed.emit(HTTPRequest.RESULT_SUCCESS, 200, PackedStringArray(), body_bytes) return OK func _emit(body_bytes: PackedByteArray) -> void: request_completed.emit(HTTPRequest.RESULT_SUCCESS, 200, PackedStringArray(), body_bytes) func _make_ix(transport: Node) -> Node: var ix: Node = IxClient.new() ix.set("injected_http", transport) auto_free(transport) auto_free(ix) add_child(ix) return ix func test_post_terminal_body_contains_prototype_terminal() -> void: # Arrange var transport := MockHttpTransport.new() var ix := _make_ix(transport) # Act ix.call("_post_interact", "prototype_terminal") # Assert assert_that(transport.last_body).contains("prototype_terminal") assert_that(transport.last_url).contains("/interact") func test_post_resource_body_contains_resource_node_id() -> void: # Arrange var transport := MockHttpTransport.new() var ix := _make_ix(transport) # Act ix.call("_post_interact", "prototype_resource_node_alpha") # Assert assert_that(transport.last_body).contains("prototype_resource_node_alpha") func test_post_interact_terminal_public_entrypoint() -> void: # Arrange var transport := MockHttpTransport.new() var ix := _make_ix(transport) # Act ix.call("post_interact_terminal") # Assert assert_that(transport.last_body).contains("prototype_terminal") func test_post_interact_resource_public_entrypoint() -> void: # Arrange var transport := MockHttpTransport.new() var ix := _make_ix(transport) # Act ix.call("post_interact_resource") # Assert assert_that(transport.last_body).contains("prototype_resource_node_alpha") func test_interaction_result_emits_allow_signal() -> void: # Arrange var transport := MockHttpTransport.new() var ix := _make_ix(transport) monitor_signals(ix) # Act ix.call("post_interact_id", "prototype_resource_node_alpha") # Assert assert_signal(ix).is_emitted( "interaction_result_received", "prototype_resource_node_alpha", true, "" ) func test_interaction_result_emits_deny_signal() -> void: # Arrange var transport := MockHttpTransport.new() transport.body_json = '{"schemaVersion":1,"allowed":false,"reasonCode":"node_depleted"}' var ix := _make_ix(transport) monitor_signals(ix) # Act ix.call("post_interact_id", "prototype_urban_bulk_delta") # Assert assert_signal(ix).is_emitted( "interaction_result_received", "prototype_urban_bulk_delta", false, "node_depleted" ) func test_interaction_request_failed_emits_on_http_error() -> void: # Arrange var transport := MockHttpTransport.new() transport.response_code = 500 var ix := _make_ix(transport) monitor_signals(ix) # Act ix.call("post_interact_id", "prototype_resource_node_alpha") # Assert assert_signal(ix).is_emitted( "interaction_request_failed", "prototype_resource_node_alpha", "HTTP 500" ) func test_last_wins_second_press_before_first_completes() -> void: # Arrange var transport := HoldFirstThenAutoTransport.new() var ix := _make_ix(transport) # Act ix.call("_post_interact", "prototype_terminal") ix.call("_post_interact", "prototype_resource_node_alpha") await get_tree().process_frame await get_tree().process_frame # Assert assert_that(transport.bodies.size()).is_equal(2) assert_that(transport.bodies[0]).contains("prototype_terminal") assert_that(transport.bodies[1]).contains("prototype_resource_node_alpha")