NEO-86: Wire gig progression client and defeat HUD refresh.
Adds GigProgressionClient, GigXpLabel in economy HUD, and event-driven GET refresh when cast accept carries targetDefeated; GdUnit coverage.pull/122/head
parent
8190614350
commit
01741fed07
|
|
@ -19,6 +19,7 @@
|
||||||
[ext_resource type="Script" path="res://scripts/craft_client.gd" id="18_craft"]
|
[ext_resource type="Script" path="res://scripts/craft_client.gd" id="18_craft"]
|
||||||
[ext_resource type="Script" path="res://scripts/craft_recipe_panel.gd" id="19_craft_panel"]
|
[ext_resource type="Script" path="res://scripts/craft_recipe_panel.gd" id="19_craft_panel"]
|
||||||
[ext_resource type="Script" path="res://scripts/prototype_economy_hud_section.gd" id="20_economy_hud"]
|
[ext_resource type="Script" path="res://scripts/prototype_economy_hud_section.gd" id="20_economy_hud"]
|
||||||
|
[ext_resource type="Script" path="res://scripts/gig_progression_client.gd" id="21_gig_prog"]
|
||||||
|
|
||||||
[sub_resource type="NavigationMesh" id="NavigationMesh_district"]
|
[sub_resource type="NavigationMesh" id="NavigationMesh_district"]
|
||||||
geometry_collision_mask = 1
|
geometry_collision_mask = 1
|
||||||
|
|
@ -1105,6 +1106,9 @@ script = ExtResource("15_inventory")
|
||||||
[node name="SkillProgressionClient" type="Node" parent="." unique_id=2500008]
|
[node name="SkillProgressionClient" type="Node" parent="." unique_id=2500008]
|
||||||
script = ExtResource("16_skill_prog")
|
script = ExtResource("16_skill_prog")
|
||||||
|
|
||||||
|
[node name="GigProgressionClient" type="Node" parent="." unique_id=2500011]
|
||||||
|
script = ExtResource("21_gig_prog")
|
||||||
|
|
||||||
[node name="RecipeDefinitionsClient" type="Node" parent="." unique_id=2500009]
|
[node name="RecipeDefinitionsClient" type="Node" parent="." unique_id=2500009]
|
||||||
script = ExtResource("17_recipe_defs")
|
script = ExtResource("17_recipe_defs")
|
||||||
|
|
||||||
|
|
@ -1269,6 +1273,18 @@ autowrap_mode = 3
|
||||||
text = "Skills:
|
text = "Skills:
|
||||||
Loading…"
|
Loading…"
|
||||||
|
|
||||||
|
[node name="GigXpLabel" type="Label" parent="UICanvas/EconomyHudSection/Body" unique_id=9000018]
|
||||||
|
custom_minimum_size = Vector2(512, 48)
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_horizontal = 3
|
||||||
|
theme_override_colors/font_color = Color(0.88, 0.78, 1, 1)
|
||||||
|
theme_override_colors/font_outline_color = Color(0.08, 0.08, 0.1, 1)
|
||||||
|
theme_override_constants/outline_size = 6
|
||||||
|
theme_override_font_sizes/font_size = 14
|
||||||
|
autowrap_mode = 3
|
||||||
|
text = "Gig XP:
|
||||||
|
Loading…"
|
||||||
|
|
||||||
[node name="CraftRecipePanel" type="Control" parent="UICanvas/EconomyHudSection/Body" unique_id=9000011]
|
[node name="CraftRecipePanel" type="Control" parent="UICanvas/EconomyHudSection/Body" unique_id=9000011]
|
||||||
custom_minimum_size = Vector2(512, 280)
|
custom_minimum_size = Vector2(512, 280)
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,105 @@
|
||||||
|
extends Node
|
||||||
|
|
||||||
|
## NEO-86: prototype HTTP client for [code]GET /game/players/{id}/gig-progression[/code] (NEO-44).
|
||||||
|
|
||||||
|
signal progression_received(snapshot: Dictionary)
|
||||||
|
signal progression_sync_failed(reason: String)
|
||||||
|
|
||||||
|
const SCHEMA_VERSION := 1
|
||||||
|
|
||||||
|
@export var base_url: String = "http://127.0.0.1:5253"
|
||||||
|
@export var dev_player_id: String = "dev-local-1"
|
||||||
|
@export var injected_http: Node = null
|
||||||
|
|
||||||
|
var _http: Node
|
||||||
|
var _busy: bool = false
|
||||||
|
|
||||||
|
|
||||||
|
func _ready() -> void:
|
||||||
|
if injected_http != null:
|
||||||
|
_http = injected_http
|
||||||
|
else:
|
||||||
|
_http = HTTPRequest.new()
|
||||||
|
add_child(_http)
|
||||||
|
if _http is HTTPRequest:
|
||||||
|
(_http as HTTPRequest).timeout = 30.0
|
||||||
|
@warning_ignore("unsafe_method_access")
|
||||||
|
_http.request_completed.connect(_on_request_completed)
|
||||||
|
|
||||||
|
|
||||||
|
func request_sync_from_server() -> void:
|
||||||
|
if _busy:
|
||||||
|
return
|
||||||
|
_busy = true
|
||||||
|
var url := "%s/game/players/%s/gig-progression" % [_base_root(), _player_path_segment()]
|
||||||
|
var err: Error = _http.request(url)
|
||||||
|
if err != OK:
|
||||||
|
var reason := "GET failed to start (%s)" % err
|
||||||
|
push_warning("GigProgressionClient: %s" % reason)
|
||||||
|
_busy = false
|
||||||
|
progression_sync_failed.emit(reason)
|
||||||
|
|
||||||
|
|
||||||
|
func gig_row(gig_id: String, snapshot: Dictionary = {}) -> Dictionary:
|
||||||
|
var gigs: Variant = snapshot.get("gigs", null)
|
||||||
|
if gigs == null or not gigs is Array:
|
||||||
|
return {}
|
||||||
|
for row_variant in gigs as Array:
|
||||||
|
if not row_variant is Dictionary:
|
||||||
|
continue
|
||||||
|
var row: Dictionary = row_variant
|
||||||
|
if str(row.get("id", "")) == gig_id:
|
||||||
|
return row
|
||||||
|
return {}
|
||||||
|
|
||||||
|
|
||||||
|
func _base_root() -> String:
|
||||||
|
return base_url.strip_edges().rstrip("/")
|
||||||
|
|
||||||
|
|
||||||
|
func _player_path_segment() -> String:
|
||||||
|
return dev_player_id.strip_edges()
|
||||||
|
|
||||||
|
|
||||||
|
static func parse_progression_json(text: String) -> Variant:
|
||||||
|
var parsed: Variant = JSON.parse_string(text)
|
||||||
|
if not parsed is Dictionary:
|
||||||
|
return null
|
||||||
|
var root: Dictionary = parsed
|
||||||
|
if int(root.get("schemaVersion", -1)) != SCHEMA_VERSION:
|
||||||
|
return null
|
||||||
|
var gigs: Variant = root.get("gigs", null)
|
||||||
|
if gigs == null or not gigs is Array:
|
||||||
|
return null
|
||||||
|
if str(root.get("mainGigId", "")).strip_edges().is_empty():
|
||||||
|
return null
|
||||||
|
return root
|
||||||
|
|
||||||
|
|
||||||
|
func _on_request_completed(
|
||||||
|
result: int, response_code: int, _headers: PackedStringArray, body: PackedByteArray
|
||||||
|
) -> void:
|
||||||
|
_busy = false
|
||||||
|
if result != HTTPRequest.RESULT_SUCCESS:
|
||||||
|
var reason := "HTTP failed (result=%s)" % result
|
||||||
|
push_warning("GigProgressionClient: %s" % reason)
|
||||||
|
progression_sync_failed.emit(reason)
|
||||||
|
return
|
||||||
|
if response_code == 404:
|
||||||
|
var reason404 := "HTTP 404 (player unknown)"
|
||||||
|
push_warning("GigProgressionClient: %s" % reason404)
|
||||||
|
progression_sync_failed.emit(reason404)
|
||||||
|
return
|
||||||
|
if response_code < 200 or response_code >= 300:
|
||||||
|
var reason_code := "HTTP %s" % response_code
|
||||||
|
push_warning("GigProgressionClient: %s" % reason_code)
|
||||||
|
progression_sync_failed.emit(reason_code)
|
||||||
|
return
|
||||||
|
var text := body.get_string_from_utf8()
|
||||||
|
var snapshot: Variant = parse_progression_json(text)
|
||||||
|
if snapshot == null:
|
||||||
|
var reason_json := "non-JSON body or schemaVersion mismatch"
|
||||||
|
push_warning("GigProgressionClient: %s" % reason_json)
|
||||||
|
progression_sync_failed.emit(reason_json)
|
||||||
|
return
|
||||||
|
progression_received.emit(snapshot as Dictionary)
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
uid://bneo86gigprog01
|
||||||
|
|
@ -47,6 +47,7 @@ const DEV_BOOTSTRAP_CAST_ABILITY_ID := "prototype_pulse"
|
||||||
const SCRAP_METAL_BULK_ID := "scrap_metal_bulk"
|
const SCRAP_METAL_BULK_ID := "scrap_metal_bulk"
|
||||||
const SALVAGE_SKILL_ID := "salvage"
|
const SALVAGE_SKILL_ID := "salvage"
|
||||||
const REFINE_SKILL_ID := "refine"
|
const REFINE_SKILL_ID := "refine"
|
||||||
|
const BREACH_GIG_ID := "breach"
|
||||||
|
|
||||||
## Bump on each rejection so older one-shot timers do not clear a newer message.
|
## Bump on each rejection so older one-shot timers do not clear a newer message.
|
||||||
var _move_reject_msg_token: int = 0
|
var _move_reject_msg_token: int = 0
|
||||||
|
|
@ -86,6 +87,8 @@ var _inventory_error: String = ""
|
||||||
var _interactables_catalog: Array = []
|
var _interactables_catalog: Array = []
|
||||||
var _last_progression_snapshot: Dictionary = {}
|
var _last_progression_snapshot: Dictionary = {}
|
||||||
var _progression_error: String = ""
|
var _progression_error: String = ""
|
||||||
|
var _last_gig_snapshot: Dictionary = {}
|
||||||
|
var _gig_error: String = ""
|
||||||
var _gather_pre_scrap_qty: int = 0
|
var _gather_pre_scrap_qty: int = 0
|
||||||
var _gather_pending_interactable_id: String = ""
|
var _gather_pending_interactable_id: String = ""
|
||||||
var _gather_awaiting_inventory_finalize: bool = false
|
var _gather_awaiting_inventory_finalize: bool = false
|
||||||
|
|
@ -113,10 +116,12 @@ var _dev_pulse_bootstrap_attempted: bool = false
|
||||||
@onready var _craft_feedback_label: Label = $UICanvas/CraftFeedbackLabel
|
@onready var _craft_feedback_label: Label = $UICanvas/CraftFeedbackLabel
|
||||||
@onready
|
@onready
|
||||||
var _skill_progression_label: Label = _economy_hud_section.get_node("Body/SkillProgressionLabel")
|
var _skill_progression_label: Label = _economy_hud_section.get_node("Body/SkillProgressionLabel")
|
||||||
|
@onready var _gig_xp_label: Label = _economy_hud_section.get_node("Body/GigXpLabel")
|
||||||
@onready var _craft_recipe_panel: Node = _economy_hud_section.get_node("Body/CraftRecipePanel")
|
@onready var _craft_recipe_panel: Node = _economy_hud_section.get_node("Body/CraftRecipePanel")
|
||||||
@onready var _inventory_client: Node = $InventoryClient
|
@onready var _inventory_client: Node = $InventoryClient
|
||||||
@onready var _item_defs_client: Node = $ItemDefinitionsClient
|
@onready var _item_defs_client: Node = $ItemDefinitionsClient
|
||||||
@onready var _skill_progression_client: Node = $SkillProgressionClient
|
@onready var _skill_progression_client: Node = $SkillProgressionClient
|
||||||
|
@onready var _gig_progression_client: Node = $GigProgressionClient
|
||||||
@onready var _recipe_defs_client: Node = $RecipeDefinitionsClient
|
@onready var _recipe_defs_client: Node = $RecipeDefinitionsClient
|
||||||
@onready var _craft_client: Node = $CraftClient
|
@onready var _craft_client: Node = $CraftClient
|
||||||
@onready var _target_client: Node = $TargetSelectionClient
|
@onready var _target_client: Node = $TargetSelectionClient
|
||||||
|
|
@ -163,6 +168,7 @@ func _ready() -> void:
|
||||||
_setup_hotbar_loadout_sync()
|
_setup_hotbar_loadout_sync()
|
||||||
_setup_inventory_sync()
|
_setup_inventory_sync()
|
||||||
_setup_skill_progression_sync()
|
_setup_skill_progression_sync()
|
||||||
|
_setup_gig_progression_sync()
|
||||||
_setup_gather_interact_feedback()
|
_setup_gather_interact_feedback()
|
||||||
_setup_craft_ui()
|
_setup_craft_ui()
|
||||||
|
|
||||||
|
|
@ -501,6 +507,22 @@ func _setup_skill_progression_sync() -> void:
|
||||||
_skill_progression_client.call("request_sync_from_server")
|
_skill_progression_client.call("request_sync_from_server")
|
||||||
|
|
||||||
|
|
||||||
|
func _setup_gig_progression_sync() -> void:
|
||||||
|
# NEO-86: breach gig XP HUD row; boot hydrate + refresh after defeat cast.
|
||||||
|
_apply_authority_http_config_to_client(_gig_progression_client)
|
||||||
|
if _gig_progression_client.has_signal("progression_received"):
|
||||||
|
_gig_progression_client.connect(
|
||||||
|
"progression_received", Callable(self, "_on_gig_progression_received")
|
||||||
|
)
|
||||||
|
if _gig_progression_client.has_signal("progression_sync_failed"):
|
||||||
|
_gig_progression_client.connect(
|
||||||
|
"progression_sync_failed", Callable(self, "_on_gig_progression_sync_failed")
|
||||||
|
)
|
||||||
|
_render_gig_xp_label()
|
||||||
|
if _gig_progression_client.has_method("request_sync_from_server"):
|
||||||
|
_gig_progression_client.call("request_sync_from_server")
|
||||||
|
|
||||||
|
|
||||||
func _setup_gather_interact_feedback() -> void:
|
func _setup_gather_interact_feedback() -> void:
|
||||||
if not is_instance_valid(_interaction_client):
|
if not is_instance_valid(_interaction_client):
|
||||||
return
|
return
|
||||||
|
|
@ -564,6 +586,53 @@ func _skill_row(skill_id: String) -> Dictionary:
|
||||||
return row as Dictionary
|
return row as Dictionary
|
||||||
|
|
||||||
|
|
||||||
|
func _on_gig_progression_received(snapshot: Dictionary) -> void:
|
||||||
|
_gig_error = ""
|
||||||
|
_last_gig_snapshot = snapshot.duplicate(true)
|
||||||
|
_render_gig_xp_label()
|
||||||
|
|
||||||
|
|
||||||
|
func _on_gig_progression_sync_failed(reason: String) -> void:
|
||||||
|
_gig_error = reason
|
||||||
|
_last_gig_snapshot = {}
|
||||||
|
_render_gig_xp_label()
|
||||||
|
|
||||||
|
|
||||||
|
func _render_gig_xp_label() -> void:
|
||||||
|
if not is_instance_valid(_gig_xp_label):
|
||||||
|
return
|
||||||
|
var header := "Gig XP:"
|
||||||
|
if not _gig_error.is_empty():
|
||||||
|
_gig_xp_label.text = "%s\nerror — %s" % [header, _gig_error]
|
||||||
|
return
|
||||||
|
if _last_gig_snapshot.is_empty():
|
||||||
|
_gig_xp_label.text = "%s\nLoading…" % header
|
||||||
|
return
|
||||||
|
var breach_row: Dictionary = _gig_row(BREACH_GIG_ID)
|
||||||
|
if breach_row.is_empty():
|
||||||
|
_gig_xp_label.text = "%s\nbreach: — (missing row)" % header
|
||||||
|
return
|
||||||
|
var level: int = int(breach_row.get("level", 0))
|
||||||
|
var xp: int = int(breach_row.get("xp", 0))
|
||||||
|
_gig_xp_label.text = "%s\nbreach: L%d · %d xp" % [header, level, xp]
|
||||||
|
|
||||||
|
|
||||||
|
func _gig_row(gig_id: String) -> Dictionary:
|
||||||
|
if not is_instance_valid(_gig_progression_client):
|
||||||
|
return {}
|
||||||
|
if not _gig_progression_client.has_method("gig_row"):
|
||||||
|
return {}
|
||||||
|
var row: Variant = _gig_progression_client.call("gig_row", gig_id, _last_gig_snapshot)
|
||||||
|
return row as Dictionary
|
||||||
|
|
||||||
|
|
||||||
|
func _request_gig_progression_refresh() -> void:
|
||||||
|
if not is_instance_valid(_gig_progression_client):
|
||||||
|
return
|
||||||
|
if _gig_progression_client.has_method("request_sync_from_server"):
|
||||||
|
_gig_progression_client.call("request_sync_from_server")
|
||||||
|
|
||||||
|
|
||||||
func _render_gather_feedback_label(text: String = "Gather: —") -> void:
|
func _render_gather_feedback_label(text: String = "Gather: —") -> void:
|
||||||
if is_instance_valid(_gather_feedback_label):
|
if is_instance_valid(_gather_feedback_label):
|
||||||
_gather_feedback_label.text = text
|
_gather_feedback_label.text = text
|
||||||
|
|
@ -859,6 +928,8 @@ func _on_cast_result_received(accepted: bool, reason_code: String, resolution: D
|
||||||
if _cooldown_client.has_method("request_sync_from_server"):
|
if _cooldown_client.has_method("request_sync_from_server"):
|
||||||
_cooldown_client.call("request_sync_from_server")
|
_cooldown_client.call("request_sync_from_server")
|
||||||
_request_combat_targets_refresh()
|
_request_combat_targets_refresh()
|
||||||
|
if bool(resolution.get("targetDefeated", false)):
|
||||||
|
_request_gig_progression_refresh()
|
||||||
return
|
return
|
||||||
var rc := reason_code.strip_edges()
|
var rc := reason_code.strip_edges()
|
||||||
if rc.is_empty():
|
if rc.is_empty():
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,103 @@
|
||||||
|
extends GdUnitTestSuite
|
||||||
|
|
||||||
|
## NEO-86: defeat cast accept triggers gig-progression GET refresh only.
|
||||||
|
|
||||||
|
const CastClient := preload("res://scripts/ability_cast_client.gd")
|
||||||
|
|
||||||
|
|
||||||
|
class MockCastTransport:
|
||||||
|
extends Node
|
||||||
|
signal request_completed(
|
||||||
|
result: int, response_code: int, headers: PackedStringArray, body: PackedByteArray
|
||||||
|
)
|
||||||
|
var body_json: String = ""
|
||||||
|
|
||||||
|
func request(
|
||||||
|
_url: String,
|
||||||
|
_custom_headers: PackedStringArray = PackedStringArray(),
|
||||||
|
_method: HTTPClient.Method = HTTPClient.METHOD_POST,
|
||||||
|
_request_data: String = ""
|
||||||
|
) -> Error:
|
||||||
|
request_completed.emit(
|
||||||
|
HTTPRequest.RESULT_SUCCESS, 200, PackedStringArray(), body_json.to_utf8_buffer()
|
||||||
|
)
|
||||||
|
return OK
|
||||||
|
|
||||||
|
|
||||||
|
class SpyGigProgressionClient:
|
||||||
|
extends Node
|
||||||
|
var sync_calls: int = 0
|
||||||
|
|
||||||
|
func request_sync_from_server() -> void:
|
||||||
|
sync_calls += 1
|
||||||
|
|
||||||
|
|
||||||
|
class GigRefreshHarness:
|
||||||
|
extends Node
|
||||||
|
var gig_sync_calls: int = 0
|
||||||
|
var _cast: Node
|
||||||
|
var _gig: SpyGigProgressionClient
|
||||||
|
|
||||||
|
func setup(parent: Node, cast_transport: Node) -> void:
|
||||||
|
_gig = SpyGigProgressionClient.new()
|
||||||
|
_cast = CastClient.new()
|
||||||
|
_cast.set("injected_http", cast_transport)
|
||||||
|
parent.add_child(_cast)
|
||||||
|
parent.add_child(_gig)
|
||||||
|
_cast.connect("cast_result_received", Callable(self, "_on_cast_result"))
|
||||||
|
|
||||||
|
func _on_cast_result(accepted: bool, _reason_code: String, resolution: Dictionary) -> void:
|
||||||
|
if not accepted:
|
||||||
|
return
|
||||||
|
if bool(resolution.get("targetDefeated", false)):
|
||||||
|
_request_gig_progression_refresh()
|
||||||
|
|
||||||
|
func _request_gig_progression_refresh() -> void:
|
||||||
|
gig_sync_calls += 1
|
||||||
|
_gig.request_sync_from_server()
|
||||||
|
|
||||||
|
func request_cast() -> void:
|
||||||
|
_cast.call("request_cast", 0, "prototype_pulse", "prototype_target_alpha")
|
||||||
|
|
||||||
|
|
||||||
|
func test_defeat_cast_triggers_gig_progression_sync() -> void:
|
||||||
|
# Arrange
|
||||||
|
var transport := MockCastTransport.new()
|
||||||
|
transport.body_json = (
|
||||||
|
'{"schemaVersion":1,"accepted":true,"combatResolution":'
|
||||||
|
+ '{"abilityId":"prototype_pulse","targetId":"prototype_target_alpha",'
|
||||||
|
+ '"damageDealt":25,"targetRemainingHp":0,"targetDefeated":true}}'
|
||||||
|
)
|
||||||
|
var harness := GigRefreshHarness.new()
|
||||||
|
auto_free(transport)
|
||||||
|
auto_free(harness)
|
||||||
|
add_child(harness)
|
||||||
|
harness.setup(self, transport)
|
||||||
|
monitor_signals(harness._cast)
|
||||||
|
# Act
|
||||||
|
harness.request_cast()
|
||||||
|
# Assert
|
||||||
|
await assert_signal(harness._cast).is_emitted("cast_result_received", true, "", any())
|
||||||
|
assert_that(harness.gig_sync_calls).is_equal(1)
|
||||||
|
assert_that(harness._gig.sync_calls).is_equal(1)
|
||||||
|
|
||||||
|
|
||||||
|
func test_non_defeat_cast_skips_gig_progression_sync() -> void:
|
||||||
|
# Arrange
|
||||||
|
var transport := MockCastTransport.new()
|
||||||
|
transport.body_json = (
|
||||||
|
'{"schemaVersion":1,"accepted":true,"combatResolution":'
|
||||||
|
+ '{"abilityId":"prototype_pulse","targetId":"prototype_target_alpha",'
|
||||||
|
+ '"damageDealt":25,"targetRemainingHp":75,"targetDefeated":false}}'
|
||||||
|
)
|
||||||
|
var harness := GigRefreshHarness.new()
|
||||||
|
auto_free(transport)
|
||||||
|
auto_free(harness)
|
||||||
|
add_child(harness)
|
||||||
|
harness.setup(self, transport)
|
||||||
|
monitor_signals(harness._cast)
|
||||||
|
# Act
|
||||||
|
harness.request_cast()
|
||||||
|
# Assert
|
||||||
|
await assert_signal(harness._cast).is_emitted("cast_result_received", true, "", any())
|
||||||
|
assert_that(harness.gig_sync_calls).is_equal(0)
|
||||||
|
|
@ -0,0 +1,94 @@
|
||||||
|
extends GdUnitTestSuite
|
||||||
|
|
||||||
|
## NEO-86: `GigProgressionClient` GET parse + failure signals.
|
||||||
|
|
||||||
|
const GigProgressionClient := preload("res://scripts/gig_progression_client.gd")
|
||||||
|
|
||||||
|
var _progression_capture: Dictionary = {}
|
||||||
|
|
||||||
|
|
||||||
|
func _capture_progression(snapshot: Dictionary) -> void:
|
||||||
|
_progression_capture = snapshot
|
||||||
|
|
||||||
|
|
||||||
|
class MockHttpTransport:
|
||||||
|
extends Node
|
||||||
|
signal request_completed(
|
||||||
|
result: int, response_code: int, headers: PackedStringArray, body: PackedByteArray
|
||||||
|
)
|
||||||
|
var last_url: String = ""
|
||||||
|
var response_code: int = 200
|
||||||
|
var body_json: String = ""
|
||||||
|
|
||||||
|
func request(
|
||||||
|
url: String,
|
||||||
|
_custom_headers: PackedStringArray = PackedStringArray(),
|
||||||
|
_method: HTTPClient.Method = HTTPClient.METHOD_GET,
|
||||||
|
_request_data: String = ""
|
||||||
|
) -> Error:
|
||||||
|
last_url = url
|
||||||
|
request_completed.emit(
|
||||||
|
HTTPRequest.RESULT_SUCCESS,
|
||||||
|
response_code,
|
||||||
|
PackedStringArray(),
|
||||||
|
body_json.to_utf8_buffer()
|
||||||
|
)
|
||||||
|
return OK
|
||||||
|
|
||||||
|
|
||||||
|
static func _breach_progression_json() -> String:
|
||||||
|
return (
|
||||||
|
'{"schemaVersion":1,"playerId":"dev-local-1","mainGigId":"breach",'
|
||||||
|
+ '"gigs":[{"id":"breach","xp":25,"level":1}]}'
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
func _make_client(transport: Node) -> Node:
|
||||||
|
var c: Node = GigProgressionClient.new()
|
||||||
|
c.set("injected_http", transport)
|
||||||
|
auto_free(transport)
|
||||||
|
auto_free(c)
|
||||||
|
add_child(c)
|
||||||
|
return c
|
||||||
|
|
||||||
|
|
||||||
|
func test_parse_progression_json_reads_breach_row() -> void:
|
||||||
|
# Arrange
|
||||||
|
var json := _breach_progression_json()
|
||||||
|
# Act
|
||||||
|
var snapshot: Variant = GigProgressionClient.parse_progression_json(json)
|
||||||
|
# Assert
|
||||||
|
assert_that(snapshot is Dictionary).is_true()
|
||||||
|
var c: Node = GigProgressionClient.new()
|
||||||
|
auto_free(c)
|
||||||
|
var row: Dictionary = c.call("gig_row", "breach", snapshot as Dictionary)
|
||||||
|
assert_that(int(row.get("xp", -1))).is_equal(25)
|
||||||
|
assert_that(int(row.get("level", -1))).is_equal(1)
|
||||||
|
|
||||||
|
|
||||||
|
func test_request_sync_emits_progression_received() -> void:
|
||||||
|
# Arrange
|
||||||
|
_progression_capture = {}
|
||||||
|
var transport := MockHttpTransport.new()
|
||||||
|
transport.body_json = _breach_progression_json()
|
||||||
|
var c := _make_client(transport)
|
||||||
|
c.connect("progression_received", Callable(self, "_capture_progression"))
|
||||||
|
monitor_signals(c)
|
||||||
|
# Act
|
||||||
|
c.call("request_sync_from_server")
|
||||||
|
# Assert
|
||||||
|
await assert_signal(c).is_emitted("progression_received", any())
|
||||||
|
assert_that(_progression_capture.get("mainGigId", "")).is_equal("breach")
|
||||||
|
assert_that(transport.last_url).contains("/gig-progression")
|
||||||
|
|
||||||
|
|
||||||
|
func test_http_404_emits_progression_sync_failed() -> void:
|
||||||
|
# Arrange
|
||||||
|
var transport := MockHttpTransport.new()
|
||||||
|
transport.response_code = 404
|
||||||
|
var c := _make_client(transport)
|
||||||
|
monitor_signals(c)
|
||||||
|
# Act
|
||||||
|
c.call("request_sync_from_server")
|
||||||
|
# Assert
|
||||||
|
await assert_signal(c).is_emitted("progression_sync_failed", "HTTP 404 (player unknown)")
|
||||||
Loading…
Reference in New Issue