Files
aetherbound-guild/runtime/tests/fg4/contract2_save_v11_test.gd
T

87 lines
3.2 KiB
GDScript

extends SceneTree
const Battle = preload("res://fg4/contract2_battle_service.gd")
const Contract = preload("res://fg4/contract2_service.gd")
const Fixture = preload("res://tests/fg4/contract1_fixture.gd")
const SaveV11 = preload("res://fg4/save_v11_service.gd")
var failures: Array[String] = []
func _initialize() -> void:
call_deferred("run_test")
func run_test() -> void:
var result := Fixture.new().region_result_snapshot()
var recovery := boss_recovery_snapshot()
var closed := Fixture.new().region_closed_snapshot()
verify_roundtrip("result", result, 201)
verify_roundtrip("recovery", recovery, 202)
verify_roundtrip("closed", closed, 203)
verify_tamper_and_rollback(closed)
if failures.is_empty():
print("ABG_FG4_CONTRACT2_SAVE_V11_OK states=region_result,contract2_boss_recovery,region_closed roundtrip=3 checksum=true rollback=true offline_coin,credit,xp,renown,healing,battle_ticks=0")
quit(0)
else:
for failure in failures:
push_error(failure)
quit(1)
func boss_recovery_snapshot() -> Dictionary:
var snapshot_value := Fixture.new().contract2_boss_muster_snapshot()
for recruit_id in snapshot_value.current_run.company:
snapshot_value.current_run.recruit_records[recruit_id].current_hp = 1
var service := Contract.new(snapshot_value)
if not service.valid:
return {}
var prepared := service.commit_contract2_boss_plan(
"fg4-save-contract2-boss-prepare",
service.state.revision,
String(service.state.current_run.boss_muster.muster_hash),
"safe_promotion"
)
if not prepared.ok:
return {}
var battle := Battle.new(service.state.snapshot()).run_to_terminal("safe_promotion")
if not battle.ok or battle.snapshot.outcome != "defeat":
return {}
var settled := service.settle_contract2_boss(
"fg4-save-contract2-boss-settle",
service.state.revision,
battle.snapshot
)
return service.state.snapshot() if settled.ok else {}
func verify_roundtrip(label: String, snapshot_value: Dictionary, sequence: int) -> void:
expect(not snapshot_value.is_empty(), "%s fixture exists" % label)
if snapshot_value.is_empty():
return
var encoded := SaveV11.new().encode_snapshot(snapshot_value, sequence)
expect(encoded.ok, "%s Save-v11 encodes" % label)
if not encoded.ok:
return
var decoded := SaveV11.new().decode_text(String(encoded.text), sequence)
expect(decoded.ok and decoded.snapshot == snapshot_value, "%s Save-v11 roundtrips byte-equivalent" % label)
expect(decoded.offline_delta == SaveV11.OFFLINE_ZERO, "%s load invents no offline value" % label)
func verify_tamper_and_rollback(closed: Dictionary) -> void:
if closed.is_empty():
return
var saves := SaveV11.new()
var encoded := saves.encode_snapshot(closed, 210)
var tampered = JSON.parse_string(String(encoded.text))
tampered.state.guild_profile.persistent_renown += 1
var rejected := saves.decode_text(JSON.stringify(tampered), 210)
expect(not rejected.ok and rejected.code == "checksum_mismatch", "closed-state tamper rejects")
var rollback := saves.decode_text(String(encoded.text), 211)
expect(not rollback.ok and rollback.code == "rollback_rejected", "closed-state rollback rejects")
func expect(condition: bool, message: String) -> void:
if not condition:
failures.append("FG-4 Contract-2 Save v11: %s" % message)