NEO-110: encounter progress client, HUD labels, and GdUnit tests.
parent
3128f36df9
commit
d81d19b859
|
|
@ -20,6 +20,7 @@
|
|||
[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/gig_progression_client.gd" id="21_gig_prog"]
|
||||
[ext_resource type="Script" path="res://scripts/encounter_progress_client.gd" id="24_enc_prog"]
|
||||
[ext_resource type="PackedScene" path="res://assets/district/prototype_district_environment.glb" id="22_district_env"]
|
||||
[ext_resource type="Script" path="res://scripts/prototype_district_art.gd" id="23_district_art"]
|
||||
|
||||
|
|
@ -1114,6 +1115,9 @@ script = ExtResource("16_skill_prog")
|
|||
[node name="GigProgressionClient" type="Node" parent="." unique_id=2500011]
|
||||
script = ExtResource("21_gig_prog")
|
||||
|
||||
[node name="EncounterProgressClient" type="Node" parent="." unique_id=2500012]
|
||||
script = ExtResource("24_enc_prog")
|
||||
|
||||
[node name="RecipeDefinitionsClient" type="Node" parent="." unique_id=2500009]
|
||||
script = ExtResource("17_recipe_defs")
|
||||
|
||||
|
|
@ -1199,6 +1203,27 @@ theme_override_font_sizes/font_size = 22
|
|||
autowrap_mode = 3
|
||||
text = "Player HP: —"
|
||||
|
||||
[node name="EncounterProgressLabel" type="Label" parent="UICanvas/HudRoot" unique_id=9000023]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
theme_override_colors/font_color = Color(0.9, 0.82, 0.72, 1)
|
||||
theme_override_colors/font_outline_color = Color(0.08, 0.08, 0.1, 1)
|
||||
theme_override_constants/outline_size = 8
|
||||
theme_override_font_sizes/font_size = 22
|
||||
autowrap_mode = 3
|
||||
text = "Encounter:
|
||||
Loading…"
|
||||
|
||||
[node name="EncounterCompleteLabel" type="Label" parent="UICanvas/HudRoot" unique_id=9000024]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
theme_override_colors/font_color = Color(0.95, 0.9, 0.65, 1)
|
||||
theme_override_colors/font_outline_color = Color(0.08, 0.08, 0.1, 1)
|
||||
theme_override_constants/outline_size = 8
|
||||
theme_override_font_sizes/font_size = 22
|
||||
autowrap_mode = 3
|
||||
text = "Loot: —"
|
||||
|
||||
[node name="NpcStateLabel" type="Label" parent="UICanvas/HudRoot" unique_id=9000020]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
|
|
|
|||
|
|
@ -0,0 +1,105 @@
|
|||
extends Node
|
||||
|
||||
## NEO-110: prototype HTTP client for [code]GET /game/players/{id}/encounter-progress[/code] (NEO-108).
|
||||
|
||||
signal encounter_progress_received(snapshot: Dictionary)
|
||||
signal encounter_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/encounter-progress" % [_base_root(), _player_path_segment()]
|
||||
var err: Error = _http.request(url)
|
||||
if err != OK:
|
||||
var reason := "GET failed to start (%s)" % err
|
||||
push_warning("EncounterProgressClient: %s" % reason)
|
||||
_busy = false
|
||||
encounter_sync_failed.emit(reason)
|
||||
|
||||
|
||||
func encounter_row(encounter_id: String, snapshot: Dictionary = {}) -> Dictionary:
|
||||
var encounters: Variant = snapshot.get("encounters", null)
|
||||
if encounters == null or not encounters is Array:
|
||||
return {}
|
||||
for row_variant in encounters as Array:
|
||||
if not row_variant is Dictionary:
|
||||
continue
|
||||
var row: Dictionary = row_variant
|
||||
if str(row.get("encounterId", "")) == encounter_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_encounter_progress_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 encounters: Variant = root.get("encounters", null)
|
||||
if encounters == null or not encounters is Array:
|
||||
return null
|
||||
if str(root.get("playerId", "")).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("EncounterProgressClient: %s" % reason)
|
||||
encounter_sync_failed.emit(reason)
|
||||
return
|
||||
if response_code == 404:
|
||||
var reason404 := "HTTP 404 (player unknown)"
|
||||
push_warning("EncounterProgressClient: %s" % reason404)
|
||||
encounter_sync_failed.emit(reason404)
|
||||
return
|
||||
if response_code < 200 or response_code >= 300:
|
||||
var reason_code := "HTTP %s" % response_code
|
||||
push_warning("EncounterProgressClient: %s" % reason_code)
|
||||
encounter_sync_failed.emit(reason_code)
|
||||
return
|
||||
var text := body.get_string_from_utf8()
|
||||
var snapshot: Variant = parse_encounter_progress_json(text)
|
||||
if snapshot == null:
|
||||
var reason_json := "non-JSON body or schemaVersion mismatch"
|
||||
push_warning("EncounterProgressClient: %s" % reason_json)
|
||||
encounter_sync_failed.emit(reason_json)
|
||||
return
|
||||
encounter_progress_received.emit(snapshot as Dictionary)
|
||||
|
|
@ -0,0 +1 @@
|
|||
uid://bneo110encprog01
|
||||
|
|
@ -54,6 +54,8 @@ const SCRAP_METAL_BULK_ID := "scrap_metal_bulk"
|
|||
const SALVAGE_SKILL_ID := "salvage"
|
||||
const REFINE_SKILL_ID := "refine"
|
||||
const BREACH_GIG_ID := "breach"
|
||||
const PROTOTYPE_ENCOUNTER_ID := "prototype_combat_pocket"
|
||||
const PROTOTYPE_ENCOUNTER_REQUIRED_TARGETS := 3
|
||||
|
||||
## Bump on each rejection so older one-shot timers do not clear a newer message.
|
||||
var _move_reject_msg_token: int = 0
|
||||
|
|
@ -104,6 +106,8 @@ var _last_progression_snapshot: Dictionary = {}
|
|||
var _progression_error: String = ""
|
||||
var _last_gig_snapshot: Dictionary = {}
|
||||
var _gig_error: String = ""
|
||||
var _last_encounter_progress_snapshot: Dictionary = {}
|
||||
var _encounter_progress_error: String = ""
|
||||
var _gather_pre_scrap_qty: int = 0
|
||||
var _gather_pending_interactable_id: String = ""
|
||||
var _gather_awaiting_inventory_finalize: bool = false
|
||||
|
|
@ -126,6 +130,8 @@ var _dev_pulse_bootstrap_attempted: bool = false
|
|||
@onready var _cast_feedback_label: Label = $UICanvas/HudRoot/CastFeedbackLabel
|
||||
@onready var _combat_target_hp_label: Label = $UICanvas/HudRoot/CombatTargetHpLabel
|
||||
@onready var _player_combat_hp_label: Label = $UICanvas/HudRoot/PlayerCombatHpLabel
|
||||
@onready var _encounter_progress_label: Label = $UICanvas/HudRoot/EncounterProgressLabel
|
||||
@onready var _encounter_complete_label: Label = $UICanvas/HudRoot/EncounterCompleteLabel
|
||||
@onready var _npc_state_label: Label = $UICanvas/HudRoot/NpcStateLabel
|
||||
@onready var _telegraph_label: Label = $UICanvas/HudRoot/TelegraphLabel
|
||||
@onready var _cooldown_slots_label: Label = $UICanvas/HudRoot/CooldownSlotsLabel
|
||||
|
|
@ -141,6 +147,7 @@ var _skill_progression_label: Label = _economy_hud_section.get_node("Body/SkillP
|
|||
@onready var _item_defs_client: Node = $ItemDefinitionsClient
|
||||
@onready var _skill_progression_client: Node = $SkillProgressionClient
|
||||
@onready var _gig_progression_client: Node = $GigProgressionClient
|
||||
@onready var _encounter_progress_client: Node = $EncounterProgressClient
|
||||
@onready var _recipe_defs_client: Node = $RecipeDefinitionsClient
|
||||
@onready var _craft_client: Node = $CraftClient
|
||||
@onready var _target_client: Node = $TargetSelectionClient
|
||||
|
|
@ -189,6 +196,7 @@ func _ready() -> void:
|
|||
_setup_inventory_sync()
|
||||
_setup_skill_progression_sync()
|
||||
_setup_gig_progression_sync()
|
||||
_setup_encounter_progress_sync()
|
||||
_setup_gather_interact_feedback()
|
||||
_setup_craft_ui()
|
||||
|
||||
|
|
@ -475,6 +483,10 @@ func _on_item_definitions_ready(_definitions_by_id: Dictionary) -> void:
|
|||
return
|
||||
if not _last_inventory_snapshot.is_empty():
|
||||
_render_inventory_label()
|
||||
if not _encounter_progress_error.is_empty():
|
||||
return
|
||||
if not _last_encounter_progress_snapshot.is_empty():
|
||||
_render_encounter_progress_labels()
|
||||
|
||||
|
||||
func _on_inventory_received(snapshot: Dictionary) -> void:
|
||||
|
|
@ -705,6 +717,131 @@ func _request_gig_progression_refresh() -> void:
|
|||
_gig_progression_client.call("request_sync_from_server")
|
||||
|
||||
|
||||
func _setup_encounter_progress_sync() -> void:
|
||||
# NEO-110: encounter progress + loot HUD; boot hydrate + refresh after NPC defeat cast.
|
||||
_apply_authority_http_config_to_client(_encounter_progress_client)
|
||||
if _encounter_progress_client.has_signal("encounter_progress_received"):
|
||||
_encounter_progress_client.connect(
|
||||
"encounter_progress_received", Callable(self, "_on_encounter_progress_received")
|
||||
)
|
||||
if _encounter_progress_client.has_signal("encounter_sync_failed"):
|
||||
_encounter_progress_client.connect(
|
||||
"encounter_sync_failed", Callable(self, "_on_encounter_progress_sync_failed")
|
||||
)
|
||||
_render_encounter_progress_labels()
|
||||
if _encounter_progress_client.has_method("request_sync_from_server"):
|
||||
_encounter_progress_client.call("request_sync_from_server")
|
||||
|
||||
|
||||
func _on_encounter_progress_received(snapshot: Dictionary) -> void:
|
||||
_encounter_progress_error = ""
|
||||
_last_encounter_progress_snapshot = snapshot.duplicate(true)
|
||||
_render_encounter_progress_labels()
|
||||
var row: Dictionary = _encounter_row(PROTOTYPE_ENCOUNTER_ID)
|
||||
if str(row.get("state", "")) == "completed":
|
||||
_request_inventory_refresh()
|
||||
|
||||
|
||||
func _on_encounter_progress_sync_failed(reason: String) -> void:
|
||||
_encounter_progress_error = reason
|
||||
_last_encounter_progress_snapshot = {}
|
||||
_render_encounter_progress_labels()
|
||||
|
||||
|
||||
func _render_encounter_progress_labels() -> void:
|
||||
_render_encounter_progress_label()
|
||||
_render_encounter_complete_label()
|
||||
|
||||
|
||||
func _render_encounter_progress_label() -> void:
|
||||
if not is_instance_valid(_encounter_progress_label):
|
||||
return
|
||||
var header := "Encounter:"
|
||||
if not _encounter_progress_error.is_empty():
|
||||
_encounter_progress_label.text = "%s\nerror — %s" % [header, _encounter_progress_error]
|
||||
return
|
||||
if _last_encounter_progress_snapshot.is_empty():
|
||||
_encounter_progress_label.text = "%s\nLoading…" % header
|
||||
return
|
||||
var row: Dictionary = _encounter_row(PROTOTYPE_ENCOUNTER_ID)
|
||||
if row.is_empty():
|
||||
_encounter_progress_label.text = (
|
||||
"%s\n%s: — (missing row)" % [header, PROTOTYPE_ENCOUNTER_ID]
|
||||
)
|
||||
return
|
||||
var state: String = str(row.get("state", ""))
|
||||
var body: String = ""
|
||||
match state:
|
||||
"inactive":
|
||||
body = "%s: not started (0/%d)" % [
|
||||
PROTOTYPE_ENCOUNTER_ID, PROTOTYPE_ENCOUNTER_REQUIRED_TARGETS
|
||||
]
|
||||
"active":
|
||||
var defeated: Variant = row.get("defeatedTargetIds", [])
|
||||
var n: int = defeated.size() if defeated is Array else 0
|
||||
body = "%s: %d/%d" % [PROTOTYPE_ENCOUNTER_ID, n, PROTOTYPE_ENCOUNTER_REQUIRED_TARGETS]
|
||||
"completed":
|
||||
body = "%s: completed (%d/%d)" % [
|
||||
PROTOTYPE_ENCOUNTER_ID,
|
||||
PROTOTYPE_ENCOUNTER_REQUIRED_TARGETS,
|
||||
PROTOTYPE_ENCOUNTER_REQUIRED_TARGETS,
|
||||
]
|
||||
_:
|
||||
body = "%s: unknown state (%s)" % [PROTOTYPE_ENCOUNTER_ID, state]
|
||||
_encounter_progress_label.text = "%s\n%s" % [header, body]
|
||||
|
||||
|
||||
func _render_encounter_complete_label() -> void:
|
||||
if not is_instance_valid(_encounter_complete_label):
|
||||
return
|
||||
var header := "Loot:"
|
||||
if not _encounter_progress_error.is_empty():
|
||||
_encounter_complete_label.text = "%s\n—" % header
|
||||
return
|
||||
if _last_encounter_progress_snapshot.is_empty():
|
||||
_encounter_complete_label.text = "%s\n—" % header
|
||||
return
|
||||
var row: Dictionary = _encounter_row(PROTOTYPE_ENCOUNTER_ID)
|
||||
if row.is_empty() or str(row.get("state", "")) != "completed":
|
||||
_encounter_complete_label.text = "%s\n—" % header
|
||||
return
|
||||
var lines: PackedStringArray = [header]
|
||||
var completed_at: String = str(row.get("completedAt", "")).strip_edges()
|
||||
if not completed_at.is_empty():
|
||||
lines[0] = "%s (completed %s)" % [header, completed_at]
|
||||
var grants: Variant = row.get("rewardGrantSummary", [])
|
||||
if grants is Array:
|
||||
for grant_variant in grants as Array:
|
||||
if not grant_variant is Dictionary:
|
||||
continue
|
||||
var grant: Dictionary = grant_variant
|
||||
var item_id: String = str(grant.get("itemId", ""))
|
||||
var qty: int = int(grant.get("quantity", 0))
|
||||
var label: String = _item_display_name(item_id) if not item_id.is_empty() else "?"
|
||||
lines.append(" %s ×%d" % [label, qty])
|
||||
if lines.size() == 1:
|
||||
lines.append(" (no grants)")
|
||||
_encounter_complete_label.text = "\n".join(lines)
|
||||
|
||||
|
||||
func _encounter_row(encounter_id: String) -> Dictionary:
|
||||
if not is_instance_valid(_encounter_progress_client):
|
||||
return {}
|
||||
if not _encounter_progress_client.has_method("encounter_row"):
|
||||
return {}
|
||||
var row: Variant = _encounter_progress_client.call(
|
||||
"encounter_row", encounter_id, _last_encounter_progress_snapshot
|
||||
)
|
||||
return row as Dictionary
|
||||
|
||||
|
||||
func _request_encounter_progress_refresh() -> void:
|
||||
if not is_instance_valid(_encounter_progress_client):
|
||||
return
|
||||
if _encounter_progress_client.has_method("request_sync_from_server"):
|
||||
_encounter_progress_client.call("request_sync_from_server")
|
||||
|
||||
|
||||
func _render_gather_feedback_label(text: String = "Gather: —") -> void:
|
||||
if is_instance_valid(_gather_feedback_label):
|
||||
_gather_feedback_label.text = text
|
||||
|
|
@ -1005,6 +1142,7 @@ func _on_cast_result_received(accepted: bool, reason_code: String, resolution: D
|
|||
_request_npc_combat_sync()
|
||||
if bool(resolution.get("targetDefeated", false)):
|
||||
_request_gig_progression_refresh()
|
||||
_request_encounter_progress_refresh()
|
||||
return
|
||||
var rc := reason_code.strip_edges()
|
||||
if rc.is_empty():
|
||||
|
|
|
|||
|
|
@ -0,0 +1,151 @@
|
|||
extends GdUnitTestSuite
|
||||
|
||||
## NEO-110: `EncounterProgressClient` GET parse + failure signals.
|
||||
|
||||
const EncounterProgressClient := preload("res://scripts/encounter_progress_client.gd")
|
||||
|
||||
const PROTOTYPE_ENCOUNTER_ID := "prototype_combat_pocket"
|
||||
|
||||
var _progress_capture: Dictionary = {}
|
||||
|
||||
|
||||
func _capture_progress(snapshot: Dictionary) -> void:
|
||||
_progress_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 _inactive_progression_json() -> String:
|
||||
return (
|
||||
'{"schemaVersion":1,"playerId":"dev-local-1","encounters":'
|
||||
+ '[{"encounterId":"prototype_combat_pocket","state":"inactive","defeatedTargetIds":[]}]}'
|
||||
)
|
||||
|
||||
|
||||
static func _active_two_defeats_json() -> String:
|
||||
return (
|
||||
'{"schemaVersion":1,"playerId":"dev-local-1","encounters":'
|
||||
+ '[{"encounterId":"prototype_combat_pocket","state":"active",'
|
||||
+ '"defeatedTargetIds":["prototype_npc_melee","prototype_npc_ranged"]}]}'
|
||||
)
|
||||
|
||||
|
||||
static func _completed_with_grants_json() -> String:
|
||||
return (
|
||||
'{"schemaVersion":1,"playerId":"dev-local-1","encounters":'
|
||||
+ '[{"encounterId":"prototype_combat_pocket","state":"completed",'
|
||||
+ '"defeatedTargetIds":["prototype_npc_elite","prototype_npc_melee","prototype_npc_ranged"],'
|
||||
+ '"completedAt":"2026-05-31T12:00:00Z",'
|
||||
+ '"rewardGrantSummary":[{"itemId":"scrap_metal_bulk","quantity":10},'
|
||||
+ '{"itemId":"contract_handoff_token","quantity":1}]}]}'
|
||||
)
|
||||
|
||||
|
||||
func _make_client(transport: Node) -> Node:
|
||||
var c: Node = EncounterProgressClient.new()
|
||||
c.set("injected_http", transport)
|
||||
auto_free(transport)
|
||||
auto_free(c)
|
||||
add_child(c)
|
||||
return c
|
||||
|
||||
|
||||
func test_parse_inactive_encounter_row() -> void:
|
||||
# Arrange
|
||||
var json := _inactive_progression_json()
|
||||
# Act
|
||||
var snapshot: Variant = EncounterProgressClient.parse_encounter_progress_json(json)
|
||||
# Assert
|
||||
assert_that(snapshot is Dictionary).is_true()
|
||||
var c: Node = EncounterProgressClient.new()
|
||||
auto_free(c)
|
||||
var row: Dictionary = c.call(
|
||||
"encounter_row", PROTOTYPE_ENCOUNTER_ID, snapshot as Dictionary
|
||||
)
|
||||
assert_that(str(row.get("state", ""))).is_equal("inactive")
|
||||
assert_that((row.get("defeatedTargetIds", []) as Array).size()).is_equal(0)
|
||||
|
||||
|
||||
func test_parse_active_row_has_two_defeats() -> void:
|
||||
# Arrange
|
||||
var json := _active_two_defeats_json()
|
||||
# Act
|
||||
var snapshot: Variant = EncounterProgressClient.parse_encounter_progress_json(json)
|
||||
# Assert
|
||||
assert_that(snapshot is Dictionary).is_true()
|
||||
var c: Node = EncounterProgressClient.new()
|
||||
auto_free(c)
|
||||
var row: Dictionary = c.call(
|
||||
"encounter_row", PROTOTYPE_ENCOUNTER_ID, snapshot as Dictionary
|
||||
)
|
||||
assert_that(str(row.get("state", ""))).is_equal("active")
|
||||
assert_that((row.get("defeatedTargetIds", []) as Array).size()).is_equal(2)
|
||||
|
||||
|
||||
func test_parse_completed_row_has_reward_grant_summary() -> void:
|
||||
# Arrange
|
||||
var json := _completed_with_grants_json()
|
||||
# Act
|
||||
var snapshot: Variant = EncounterProgressClient.parse_encounter_progress_json(json)
|
||||
# Assert
|
||||
assert_that(snapshot is Dictionary).is_true()
|
||||
var c: Node = EncounterProgressClient.new()
|
||||
auto_free(c)
|
||||
var row: Dictionary = c.call(
|
||||
"encounter_row", PROTOTYPE_ENCOUNTER_ID, snapshot as Dictionary
|
||||
)
|
||||
assert_that(str(row.get("state", ""))).is_equal("completed")
|
||||
var grants: Variant = row.get("rewardGrantSummary", null)
|
||||
assert_that(grants is Array).is_true()
|
||||
assert_that((grants as Array).size()).is_equal(2)
|
||||
|
||||
|
||||
func test_request_sync_emits_encounter_progress_received() -> void:
|
||||
# Arrange
|
||||
_progress_capture = {}
|
||||
var transport := MockHttpTransport.new()
|
||||
transport.body_json = _inactive_progression_json()
|
||||
var c := _make_client(transport)
|
||||
c.connect("encounter_progress_received", Callable(self, "_capture_progress"))
|
||||
monitor_signals(c)
|
||||
# Act
|
||||
c.call("request_sync_from_server")
|
||||
# Assert
|
||||
await assert_signal(c).is_emitted("encounter_progress_received", any())
|
||||
assert_that(_progress_capture.get("playerId", "")).is_equal("dev-local-1")
|
||||
assert_that(transport.last_url).contains("/encounter-progress")
|
||||
|
||||
|
||||
func test_http_404_emits_encounter_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("encounter_sync_failed", "HTTP 404 (player unknown)")
|
||||
Loading…
Reference in New Issue