extends GdUnitTestSuite ## NEO-74: `CraftClient` POST payload + success/deny parse. const CraftClient := preload("res://scripts/craft_client.gd") var _failed_reason: String = "" var _failed_recipe_id: String = "" func _capture_failed(recipe_id: String, reason: String) -> void: _failed_recipe_id = recipe_id _failed_reason = reason 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 response_code: int = 200 var body_json: String = "" func request( url: String, _custom_headers: PackedStringArray = PackedStringArray(), _method: HTTPClient.Method = HTTPClient.METHOD_POST, request_data: String = "" ) -> Error: last_url = url last_body = request_data request_completed.emit( HTTPRequest.RESULT_SUCCESS, response_code, PackedStringArray(), body_json.to_utf8_buffer() ) return OK static func _success_craft_json() -> String: return ( '{"schemaVersion":1,"success":true,"reasonCode":null,' + '"inputsConsumed":[{"itemId":"scrap_metal_bulk","quantity":5}],' + '"outputsGranted":[{"itemId":"refined_plate_stock","quantity":1}],' + '"xpGrantSummary":{"skillId":"refine","amount":10,"sourceKind":"craft_refine"}}' ) func _make_client(transport: Node) -> Node: var c: Node = CraftClient.new() c.set("injected_http", transport) auto_free(transport) auto_free(c) add_child(c) return c func test_request_craft_posts_recipe_id_and_schema_version() -> void: # Arrange var transport := MockHttpTransport.new() transport.body_json = _success_craft_json() var c := _make_client(transport) # Act var started: bool = bool(c.call("request_craft", "refine_scrap_standard", 1)) # Assert assert_that(started).is_true() assert_that(transport.last_url).contains("/craft") var parsed: Variant = JSON.parse_string(transport.last_body) assert_that(parsed is Dictionary).is_true() var body: Dictionary = parsed as Dictionary assert_that(int(body.get("schemaVersion", -1))).is_equal(1) assert_that(str(body.get("recipeId", ""))).is_equal("refine_scrap_standard") assert_that(int(body.get("quantity", -1))).is_equal(1) func test_success_response_emits_craft_result_received() -> void: # Arrange var transport := MockHttpTransport.new() transport.body_json = _success_craft_json() var c := _make_client(transport) var got: Array = [] c.connect( "craft_result_received", func(recipe_id: String, result: Dictionary) -> void: got.append([recipe_id, result]) ) # Act c.call("request_craft", "refine_scrap_standard") # Assert assert_that(got.size()).is_equal(1) assert_that(str(got[0][0])).is_equal("refine_scrap_standard") var result: Dictionary = got[0][1] assert_that(bool(result.get("success", false))).is_true() var outputs: Variant = result.get("outputsGranted", null) assert_that(outputs is Array).is_true() assert_that((outputs as Array).size()).is_equal(1) func test_deny_response_emits_craft_result_with_reason_code() -> void: # Arrange var transport := MockHttpTransport.new() transport.body_json = ( '{"schemaVersion":1,"success":false,"reasonCode":"insufficient_materials",' + '"inputsConsumed":[],"outputsGranted":[]}' ) var c := _make_client(transport) var got: Array = [] c.connect( "craft_result_received", func(_recipe_id: String, result: Dictionary) -> void: got.append(result) ) # Act c.call("request_craft", "refine_scrap_standard") # Assert var result: Dictionary = got[0] assert_that(bool(result.get("success", true))).is_false() assert_that(str(result.get("reasonCode", ""))).is_equal("insufficient_materials") func test_http_error_emits_craft_request_failed() -> void: # Arrange _failed_reason = "" var transport := MockHttpTransport.new() transport.response_code = 404 var c := _make_client(transport) c.connect("craft_request_failed", Callable(self, "_capture_failed")) # Act c.call("request_craft", "refine_scrap_standard") # Assert assert_that(_failed_reason).contains("404")