neon-sprawl/client/test/faction_standing_hud_test.gd

261 lines
7.6 KiB
GDScript

extends GdUnitTestSuite
## NEO-142: faction standing label + faction_gate_blocked accept deny HUD tests.
const QuestHudController := preload("res://scripts/quest_hud_controller.gd")
const QuestDefinitionsClient := preload("res://scripts/quest_definitions_client.gd")
const FactionStandingClient := preload("res://scripts/faction_standing_client.gd")
const GRID_CONTRACT_QUEST_ID := "prototype_quest_grid_contract"
class MockHttpTransport:
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_GET,
_request_data: String = ""
) -> Error:
request_completed.emit(
HTTPRequest.RESULT_SUCCESS, 200, PackedStringArray(), body_json.to_utf8_buffer()
)
return OK
class MockFactionSyncClient:
extends Node
var sync_count: int = 0
func request_sync_from_server() -> void:
sync_count += 1
func display_name_for(faction_id: String) -> String:
return FactionStandingClient.display_name_for(faction_id)
static func _post_operator_chain_standing_snapshot() -> Dictionary:
return {
"schemaVersion": 1,
"playerId": "dev-local-1",
"factions":
[
{"id": FactionStandingClient.GRID_OPERATORS_ID, "standing": 15},
{"id": FactionStandingClient.RUST_COLLECTIVE_ID, "standing": 0},
],
}
static func _grid_contract_defs_json() -> String:
return (
'{"schemaVersion":1,"quests":['
+ '{"id":"prototype_quest_grid_contract","displayName":"Grid Contract",'
+ '"prerequisiteQuestIds":["prototype_quest_operator_chain"],'
+ '"factionGateRules":[{"factionId":"prototype_faction_grid_operators","minStanding":15}],'
+ '"steps":[]}]}'
)
func test_faction_standing_label_paints_grid_operators_standing() -> void:
# Arrange
var controller: Node = QuestHudController.new()
var standing_label := Label.new()
auto_free(controller)
auto_free(standing_label)
add_child(controller)
controller.set("_faction_standing_label", standing_label)
# Act
controller.call("_on_faction_standing_received", _post_operator_chain_standing_snapshot())
# Assert
assert_str(standing_label.text).contains("Faction standing:")
assert_str(standing_label.text).contains("Grid Operators: 15")
assert_str(standing_label.text).contains("Rust Collective: 0")
func test_faction_standing_sync_error_shows_error_line() -> void:
# Arrange
var controller: Node = QuestHudController.new()
var standing_label := Label.new()
auto_free(controller)
auto_free(standing_label)
add_child(controller)
controller.set("_faction_standing_label", standing_label)
# Act
controller.call("_on_faction_standing_sync_failed", "HTTP 503")
# Assert
assert_str(standing_label.text).contains("error — HTTP 503")
assert_str(standing_label.text).contains("Grid Operators: 0")
func test_faction_gate_blocked_deny_uses_readable_gate_copy() -> void:
# Arrange
var defs_transport := MockHttpTransport.new()
defs_transport.body_json = _grid_contract_defs_json()
var defs_client: Node = QuestDefinitionsClient.new()
defs_client.set("injected_http", defs_transport)
auto_free(defs_transport)
auto_free(defs_client)
add_child(defs_client)
defs_client.call("request_sync_from_server")
var controller: Node = QuestHudController.new()
var accept_label := Label.new()
auto_free(controller)
auto_free(accept_label)
add_child(controller)
controller.set("_defs_client", defs_client)
controller.set("_accept_label", accept_label)
controller.call(
"_on_faction_standing_received",
{"schemaVersion": 1, "playerId": "dev-local-1", "factions": []}
)
# Act
var deny_text: String = str(
controller.call("_format_faction_gate_blocked_deny", GRID_CONTRACT_QUEST_ID)
)
# Assert
assert_str(deny_text).contains("Grid Operators standing 15 required (have 0)")
func test_faction_gate_blocked_deny_falls_back_without_gate_rules() -> void:
# Arrange
var controller: Node = QuestHudController.new()
auto_free(controller)
add_child(controller)
# Act
var deny_text: String = str(
controller.call("_format_faction_gate_blocked_deny", GRID_CONTRACT_QUEST_ID)
)
# Assert
assert_str(deny_text).contains("faction standing too low")
assert_str(deny_text).contains("faction_gate_blocked")
func test_accept_result_received_paints_readable_faction_gate_blocked() -> void:
# Arrange
var defs_transport := MockHttpTransport.new()
defs_transport.body_json = _grid_contract_defs_json()
var defs_client: Node = QuestDefinitionsClient.new()
defs_client.set("injected_http", defs_transport)
auto_free(defs_transport)
auto_free(defs_client)
add_child(defs_client)
defs_client.call("request_sync_from_server")
var controller: Node = QuestHudController.new()
var accept_label := Label.new()
var progress_client := Node.new()
auto_free(controller)
auto_free(accept_label)
auto_free(progress_client)
add_child(controller)
controller.set("_defs_client", defs_client)
controller.set("_accept_label", accept_label)
controller.set("_progress_client", progress_client)
controller.call(
"_on_faction_standing_received",
{"schemaVersion": 1, "playerId": "dev-local-1", "factions": []}
)
# Act
controller.call(
"_on_accept_result_received",
GRID_CONTRACT_QUEST_ID,
{"schemaVersion": 1, "accepted": false, "reasonCode": "faction_gate_blocked"}
)
# Assert
assert_str(accept_label.text).contains("Grid Operators standing 15 required (have 0)")
func test_completion_transition_requests_faction_standing_refresh() -> void:
# Arrange
var controller: Node = QuestHudController.new()
var faction_client := MockFactionSyncClient.new()
auto_free(controller)
auto_free(faction_client)
add_child(controller)
add_child(faction_client)
controller.set("_faction_standing_client", faction_client)
var active_snapshot := {
"schemaVersion": 1,
"playerId": "dev-local-1",
"quests":
[
{
"questId": "prototype_quest_operator_chain",
"status": "active",
"currentStepIndex": 3,
"objectiveCounters": {},
}
],
}
var completed_snapshot := {
"schemaVersion": 1,
"playerId": "dev-local-1",
"quests":
[
{
"questId": "prototype_quest_operator_chain",
"status": "completed",
"currentStepIndex": 3,
"objectiveCounters": {},
"completedAt": "2026-06-07T14:00:00Z",
"completionRewardSummary":
{
"itemGrants": [],
"skillXpGrants": [],
"reputationGrants":
[{"factionId": FactionStandingClient.GRID_OPERATORS_ID, "amount": 15}],
},
}
],
}
# Act
controller.call("_on_progress_received", active_snapshot)
controller.call("_on_progress_received", completed_snapshot)
# Assert
assert_int(faction_client.sync_count).is_equal(1)
func test_setup_wires_faction_standing_hud_dictionary() -> void:
# Arrange
var controller: Node = QuestHudController.new()
var progress_label := Label.new()
var accept_label := Label.new()
var standing_label := Label.new()
var progress := Node.new()
var defs := Node.new()
var faction_client := MockFactionSyncClient.new()
auto_free(controller)
auto_free(progress_label)
auto_free(accept_label)
auto_free(standing_label)
auto_free(progress)
auto_free(defs)
auto_free(faction_client)
add_child(controller)
# Act
controller.call(
"setup",
progress,
defs,
progress_label,
accept_label,
Callable(self, "_noop_http_config"),
null,
null,
null,
null,
{"client": faction_client, "label": standing_label}
)
# Assert
assert_object(controller.get("_faction_standing_client")).is_same(faction_client)
assert_object(controller.get("_faction_standing_label")).is_same(standing_label)
func _noop_http_config(_client: Node) -> void:
pass