231 lines
10 KiB
GDScript
231 lines
10 KiB
GDScript
extends SceneTree
|
|
|
|
const Contracts = preload("res://domain/p9_1/contracts.gd")
|
|
const FirstSessionServiceScript = preload("res://domain/p9_1/session_service.gd")
|
|
const FirstSessionStateScript = preload("res://domain/p9_1/session_state.gd")
|
|
|
|
var failures: Array[String] = []
|
|
var successful_commands := 0
|
|
var rejected_commands := 0
|
|
|
|
|
|
func _initialize() -> void:
|
|
call_deferred("run_test")
|
|
|
|
|
|
func run_test() -> void:
|
|
_test_success_matrix()
|
|
_test_rejection_matrix()
|
|
_test_exact_guild_ids()
|
|
|
|
if failures.is_empty():
|
|
print("ABG_P9_1_FOUNDATION_ATOMICITY_OK success=%d rejected=%d exact_guild_ids=2 legacy_leaks=0" % [successful_commands, rejected_commands])
|
|
quit(0)
|
|
else:
|
|
for failure in failures:
|
|
push_error(failure)
|
|
quit(1)
|
|
|
|
|
|
func _test_success_matrix() -> void:
|
|
_success(&"reset_transient")
|
|
_success(&"open_new_session")
|
|
_success(&"toggle_recruit", ["Hearthguard"], {"screen": Contracts.SCREEN_RECRUIT_SELECTION})
|
|
_success(&"review_formation", [], _formation_fields(Contracts.SCREEN_RECRUIT_SELECTION))
|
|
_success(&"revise_formation", [], {"screen": Contracts.SCREEN_FORMATION_REVIEW})
|
|
_success(&"commit_formation", [], _formation_fields(Contracts.SCREEN_FORMATION_REVIEW))
|
|
_success(&"begin_compat_first_battle", [], {"front_choice": "Hearthguard", "rear_choice": "Farstring"})
|
|
_success(&"open_adaptation", [], {"screen": Contracts.SCREEN_RESULT_1, "first_reward_committed": true})
|
|
_success(&"open_guild_response", [], {"screen": Contracts.SCREEN_RESULT_2, "adaptation_committed": true})
|
|
_success(&"open_route_board", [], {"screen": Contracts.SCREEN_RESULT_3, "guild_committed": true})
|
|
_success(&"open_growth", [], {"screen": Contracts.SCREEN_RESULT_4, "route_committed": true, "route_reward_committed": true})
|
|
_success(&"commit_reward", [&"battle_1", 20, Contracts.SCREEN_RESULT_1], {"screen": Contracts.SCREEN_RESULT_1})
|
|
_success(&"choose_adaptation", ["EQ-002"], {"screen": Contracts.SCREEN_ADAPTATION})
|
|
_success(&"commit_adaptation", [], {"screen": Contracts.SCREEN_ADAPTATION, "adaptation_choice": "EQ-002"})
|
|
_success(&"choose_guild_response", ["retain_loan"], {"screen": Contracts.SCREEN_GUILD_RESPONSE, "coin": 20})
|
|
_success(&"commit_guild_response", [], _guild_fields("retain_loan"))
|
|
_success(&"choose_route", ["R6-01"], {"screen": Contracts.SCREEN_ROUTE_BOARD})
|
|
_success(&"review_route", [], {"screen": Contracts.SCREEN_ROUTE_BOARD, "route_choice": "R6-01"})
|
|
_success(&"cancel_route_review", [], {"screen": Contracts.SCREEN_ROUTE_REVIEW})
|
|
_success(&"commit_route", [], {"screen": Contracts.SCREEN_ROUTE_REVIEW, "route_choice": "R6-01"})
|
|
_success(&"choose_growth", ["Hearthguard"], _growth_fields(Contracts.SCREEN_GROWTH))
|
|
_success(&"review_growth", [], _growth_fields(Contracts.SCREEN_GROWTH, "Hearthguard"))
|
|
_success(&"cancel_growth_review", [], _growth_fields(Contracts.SCREEN_GROWTH_REVIEW))
|
|
_success(&"commit_growth", [], _growth_fields(Contracts.SCREEN_GROWTH_REVIEW, "Hearthguard"))
|
|
_success(&"commit_impact", [Contracts.ENCOUNTER_1, 0, &"atomicity", {"delta": 0}], _battle_fields(Contracts.SCREEN_BATTLE_1, 0, true))
|
|
_success(&"resolve_impact", [Contracts.ENCOUNTER_1, 2], _battle_fields(Contracts.SCREEN_BATTLE_1, 2, true))
|
|
_success(&"resolve_compat_impact", [Contracts.ENCOUNTER_1, 2], _battle_fields(Contracts.SCREEN_BATTLE_1, 2, false))
|
|
_success(&"complete_phase", [Contracts.ENCOUNTER_1, 0], _battle_fields(Contracts.SCREEN_BATTLE_1, 0, true))
|
|
|
|
|
|
func _test_rejection_matrix() -> void:
|
|
_reject(&"toggle_recruit", ["Hearthguard"])
|
|
_reject(&"review_formation")
|
|
_reject(&"revise_formation")
|
|
_reject(&"commit_formation")
|
|
_reject(&"begin_compat_first_battle")
|
|
_reject(&"open_adaptation")
|
|
_reject(&"open_guild_response")
|
|
_reject(&"open_route_board")
|
|
_reject(&"open_growth")
|
|
_reject(&"commit_reward", [&"battle_1", 20, Contracts.SCREEN_RESULT_1])
|
|
_reject(&"choose_adaptation", ["EQ-002"])
|
|
_reject(&"commit_adaptation")
|
|
_reject(&"choose_guild_response", ["retain_loan"])
|
|
_reject(&"commit_guild_response")
|
|
_reject(&"choose_route", ["R6-01"])
|
|
_reject(&"review_route")
|
|
_reject(&"cancel_route_review")
|
|
_reject(&"commit_route")
|
|
_reject(&"choose_growth", ["Hearthguard"])
|
|
_reject(&"review_growth")
|
|
_reject(&"cancel_growth_review")
|
|
_reject(&"commit_growth")
|
|
_reject(&"commit_impact", [Contracts.ENCOUNTER_1, 0, &"atomicity", {}])
|
|
_reject(&"resolve_impact", [Contracts.ENCOUNTER_1, 2])
|
|
_reject(&"resolve_compat_impact", [Contracts.ENCOUNTER_1, 2])
|
|
_reject(&"complete_phase", [Contracts.ENCOUNTER_1, 0])
|
|
|
|
|
|
func _test_exact_guild_ids() -> void:
|
|
var pairs: Array[Array] = [
|
|
["Hearthguard", "Wayrunner"],
|
|
["Hearthguard", "Farstring"],
|
|
["Hearthguard", "Kindhand"],
|
|
["Wayrunner", "Farstring"],
|
|
["Wayrunner", "Kindhand"],
|
|
["Farstring", "Kindhand"],
|
|
]
|
|
for pair in pairs:
|
|
for adaptation in Contracts.ADAPTATION_IDS:
|
|
for guild_id in Contracts.GUILD_RESPONSE_IDS:
|
|
var service = _service(_guild_fields_for_pair(pair, adaptation, guild_id))
|
|
var before: Dictionary = service.snapshot()
|
|
_assert_success("choose_guild_response:%s:%s:%s" % [pair_key(pair), adaptation, guild_id], service, service.callv(&"choose_guild_response", [guild_id]), before)
|
|
before = service.snapshot()
|
|
_assert_success("commit_guild_response:%s:%s:%s" % [pair_key(pair), adaptation, guild_id], service, service.callv(&"commit_guild_response", []), before)
|
|
expect(service.state.guild_response == guild_id, "%s remains the committed exact Guild ID" % guild_id)
|
|
if guild_id == "retain_loan":
|
|
expect(service.state.added_rear.is_empty(), "%s retain_loan does not add a Recruit" % pair_key(pair))
|
|
expect(service.state.field_order == pair, "%s retain_loan preserves the ordered pair" % pair_key(pair))
|
|
else:
|
|
var expected_added := _expected_guild_recruit(pair, adaptation)
|
|
expect(service.state.added_rear == expected_added, "%s add_recruit maps actual=%s expected=%s front=%s rear=%s adaptation=%s" % [pair_key(pair), service.state.added_rear, expected_added, service.state.front_choice, service.state.rear_choice, adaptation])
|
|
expect(service.state.field_order == _ordered_pair_with(expected_added, pair), "%s add_recruit orders the expanded field" % pair_key(pair))
|
|
|
|
for legacy_id in ["retain_item", "recruit_rear"]:
|
|
var service = _service(_guild_fields_for_pair(["Hearthguard", "Farstring"], "EQ-002", ""))
|
|
var before: Dictionary = service.snapshot()
|
|
var result: Dictionary = service.choose_guild_response(legacy_id)
|
|
_assert_rejected("legacy_guild_id:%s" % legacy_id, service, before, result)
|
|
expect(service.state.guild_response.is_empty(), "%s does not leak into state" % legacy_id)
|
|
|
|
|
|
func _success(method: StringName, args: Array = [], fields: Dictionary = {}) -> void:
|
|
var service = _service(fields)
|
|
var before: Dictionary = service.snapshot()
|
|
var result: Dictionary = service.callv(method, args)
|
|
_assert_success(String(method), service, result, before)
|
|
|
|
|
|
func _reject(method: StringName, args: Array = [], fields: Dictionary = {}) -> void:
|
|
var service = _service(fields)
|
|
var before: Dictionary = service.snapshot()
|
|
var result: Dictionary = service.callv(method, args)
|
|
_assert_rejected(String(method), service, before, result)
|
|
|
|
|
|
func _assert_success(label: String, service, result: Dictionary, before: Dictionary) -> void:
|
|
var after: Dictionary = service.snapshot()
|
|
expect(result.ok, "%s succeeds" % label)
|
|
expect(after.revision == before.revision + 1, "%s revision delta is exactly 1, got %d" % [label, after.revision - before.revision])
|
|
expect(result.snapshot == after, "%s returns the committed snapshot" % label)
|
|
if result.ok:
|
|
successful_commands += 1
|
|
|
|
|
|
func _assert_rejected(label: String, service, before: Dictionary, result: Dictionary) -> void:
|
|
var after: Dictionary = service.snapshot()
|
|
expect(not result.ok, "%s rejects" % label)
|
|
expect(after.revision == before.revision, "%s preserves revision" % label)
|
|
expect(after == before, "%s preserves the complete snapshot" % label)
|
|
expect(result.snapshot == before, "%s returns the unchanged snapshot" % label)
|
|
expect(result.events.is_empty(), "%s emits no event" % label)
|
|
if not result.ok:
|
|
rejected_commands += 1
|
|
|
|
|
|
func _service(fields: Dictionary = {}):
|
|
var service = FirstSessionServiceScript.new(FirstSessionStateScript.new())
|
|
for field in fields:
|
|
service.state.set(field, fields[field])
|
|
return service
|
|
|
|
|
|
func _formation_fields(screen: StringName) -> Dictionary:
|
|
var selected: Array[String] = ["Hearthguard", "Farstring"]
|
|
return {"screen": screen, "selected_recruits": selected, "front_choice": "Hearthguard", "rear_choice": "Farstring"}
|
|
|
|
|
|
func _guild_fields(guild_id: String) -> Dictionary:
|
|
return _guild_fields_for_pair(["Hearthguard", "Farstring"], "EQ-002", guild_id)
|
|
|
|
|
|
func _guild_fields_for_pair(pair: Array, adaptation: String, guild_id: String) -> Dictionary:
|
|
var selected: Array[String] = [String(pair[0]), String(pair[1])]
|
|
return {
|
|
"screen": Contracts.SCREEN_GUILD_RESPONSE,
|
|
"coin": 20,
|
|
"guild_response": guild_id,
|
|
"adaptation_choice": adaptation,
|
|
"selected_recruits": selected,
|
|
"front_choice": selected[0],
|
|
"rear_choice": selected[1],
|
|
"initial_second_id": selected[1],
|
|
"marked_id": selected[1],
|
|
"field_order": selected.duplicate(),
|
|
}
|
|
|
|
|
|
func _expected_guild_recruit(pair: Array, adaptation: String) -> String:
|
|
return {
|
|
"Hearthguard:Wayrunner:EQ-002": "Farstring",
|
|
"Hearthguard:Wayrunner:EQ-061": "Kindhand",
|
|
"Hearthguard:Farstring:EQ-002": "Kindhand",
|
|
"Hearthguard:Farstring:EQ-061": "Kindhand",
|
|
"Hearthguard:Kindhand:EQ-002": "Farstring",
|
|
"Hearthguard:Kindhand:EQ-061": "Farstring",
|
|
"Wayrunner:Farstring:EQ-002": "Kindhand",
|
|
"Wayrunner:Farstring:EQ-061": "Kindhand",
|
|
"Wayrunner:Kindhand:EQ-002": "Farstring",
|
|
"Wayrunner:Kindhand:EQ-061": "Farstring",
|
|
"Farstring:Kindhand:EQ-002": "Hearthguard",
|
|
"Farstring:Kindhand:EQ-061": "Hearthguard",
|
|
}.get("%s:%s" % [pair_key(pair), adaptation], "")
|
|
|
|
|
|
func _ordered_pair_with(added_id: String, pair: Array) -> Array[String]:
|
|
var members: Array[String] = [String(pair[0]), String(pair[1]), added_id]
|
|
var ordered: Array[String] = []
|
|
for member_id in Contracts.AUTO_FORMATION_ORDER:
|
|
if member_id in members:
|
|
ordered.append(member_id)
|
|
return ordered
|
|
|
|
|
|
func pair_key(pair: Array) -> String:
|
|
return "%s:%s" % [String(pair[0]), String(pair[1])]
|
|
|
|
|
|
func _growth_fields(screen: StringName, member_id := "") -> Dictionary:
|
|
return {"screen": screen, "front_choice": "Hearthguard", "rear_choice": "Farstring", "growth_member_id": member_id}
|
|
|
|
|
|
func _battle_fields(screen: StringName, phase: int, playing: bool) -> Dictionary:
|
|
return {"screen": screen, "front_choice": "Hearthguard", "rear_choice": "Farstring", "battle_phase": phase, "battle_playing": playing}
|
|
|
|
|
|
func expect(condition: bool, message: String) -> void:
|
|
if not condition:
|
|
failures.append("P9.1 foundation atomicity: %s" % message)
|