87 lines
3.2 KiB
GDScript
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)
|