extends SceneTree const DraftService = preload("res://entry/initial_company_draft_service.gd") const ContractService = preload("res://entry/guild_contract_service.gd") const FormationService = preload("res://entry/formation_risk_service.gd") const DRAFT_PATH := "user://dev4_foundation_draft.json" const COMPANY_PATH := "user://dev4_foundation_company.json" const SEED := "1032547698badcfeefcdab8967452301" const CONTRACT_OP := "10101010202020203030303040404040" const HIRE_OP_1 := "11112222333344445555666677778888" const HIRE_OP_2 := "88887777666655554444333322221111" const ORDER_OP := "abcdefabcdefabcdefabcdefabcdefab" const ORDER_OP_2 := "1234567890abcdef1234567890abcdef" const READY_OP := "fedcbafedcbafedcbafedcbafedcbafe" var failures: Array[String] = [] func _initialize() -> void: call_deferred("run_test") func run_test() -> void: _cleanup() var company := _create_contract_company() if not company.is_empty(): verify_precondition(company) company = _hire_opening_pair(company) if not company.is_empty(): verify_foundation(company) verify_order_and_readiness() verify_restart_and_recovery() _cleanup() if failures.is_empty(): print("ABG_DEV4_FOUNDATION_OK schema=v2_in_place roster=4 auto_order=deterministic orientation=rear_left_front_right front=0 preview=swap,reset save=atomic,idempotent readiness=atomic,idempotent risk=steady,pressured,elite rewards=20,27,34 knockout=returns_next_stage battle_receipts=0 resources=24,line4 recovery=candidate,backup") quit(0) else: for failure in failures: push_error(failure) quit(1) func verify_precondition(company: Dictionary) -> void: var before_hash := String(company.company_hash) var result: Dictionary = _formation_service().begin_formation() var after: Dictionary = _draft_service().load_company() expect(not result.ok and result.code == "FORMATION_OPENING_HIRES_REQUIRED", "formation is unavailable before both opening hires") expect(after.ok and String(after.company.company_hash) == before_hash and not after.company.active_contract.has("formation"), "rejected early entry changes no Company state") func verify_foundation(company: Dictionary) -> void: var preserved := { "company_id": String(company.company_id), "recruit_hashes": _recruit_hashes(company), "coin": int(company.active_contract.coin), "board_hash": String(company.active_contract.market_board.board_hash), "hire_receipts": Array(company.active_contract.hire_receipts).duplicate(true), "company_revision": int(company.company_revision), } var first: Dictionary = _formation_service().begin_formation() expect(first.ok and first.code == "formation_created", "eligible Company creates one in-place formation foundation") if not first.ok: return var formation: Dictionary = first.formation expect(int(first.company.company_version) == 2 and int(first.company.company_revision) == preserved.company_revision, "formation extends schema v2 without falsifying economic revision") expect(String(first.company.company_id) == preserved.company_id and _recruit_hashes(first.company) == preserved.recruit_hashes, "foundation preserves all four Recruit identities and hashes") expect(int(first.company.active_contract.coin) == 24, "foundation preserves exact 24 Coin") expect(String(first.company.active_contract.market_board.board_hash) == String(preserved.board_hash), "foundation preserves the Market board hash") expect(_hash_array(first.company.active_contract.hire_receipts) == _hash_array(preserved.hire_receipts), "foundation preserves both hire receipts byte-for-byte") expect(formation.base_front_to_rear_ids.size() == 4 and formation.base_front_to_rear_ids == formation.draft_front_to_rear_ids, "automatic order contains all four members exactly once") expect(formation.orientation == "rear_left_front_right" and int(formation.front_position) == 0 and int(formation.relations[0].position) == 0, "stored line semantics fix rear-left and exposed front-right position zero") expect(bool(formation.relations[0].first_threat_target) and not bool(formation.relations[3].first_threat_target), "first known threat targets the exposed front member") expect(formation.risk_options.size() == 3, "foundation projects exactly three risk options") var tiers: Array[String] = [] var rewards: Array[int] = [] for risk in range(3): var option: Dictionary = formation.risk_options[risk] tiers.append(String(option.tier_id)) rewards.append(int(option.reward_coin)) expect(String(option.risk_option_id) == String(first.company.active_contract.market_board.battle_offers[risk].offer_id), "risk %d retains the stable Market offer ID" % risk) expect(String(option.knockout_rule_id) == "returns_next_stage" and int(option.known_threat.target_position) == 0, "risk %d names next-stage return and its front threat" % risk) expect(tiers == ["steady", "pressured", "elite"] and rewards == [20, 27, 34], "risk tiers expose distinct exact first-round rewards") var again: Dictionary = _formation_service().begin_formation() expect(again.ok and again.code == "formation_loaded" and String(again.formation.formation_hash) == String(formation.formation_hash), "restart does not regenerate the formation or risk options") func verify_order_and_readiness() -> void: var service := _formation_service() var loaded: Dictionary = service.begin_formation() if not loaded.ok: return var base: Array = Array(loaded.formation.base_front_to_rear_ids).duplicate() var swapped := base.duplicate() var temporary = swapped[0] swapped[0] = swapped[3] swapped[3] = temporary var before_hash := String(loaded.company.company_hash) var preview: Dictionary = service.preview_order(swapped) expect(preview.ok and preview.front_to_rear_ids == swapped and bool(preview.relations[0].first_threat_target), "swap preview moves the exposed target relation without committing") expect(String(_draft_service().load_company().company.company_hash) == before_hash, "preview changes no persisted state") var reset: Dictionary = service.reset_order() expect(reset.ok and reset.front_to_rear_ids == base, "Reset restores deterministic automatic order") expect_rejected_unchanged(func(): return service.preview_order([base[0], base[1], base[2]]), "FORMATION_COUNT_INVALID", "short order") expect_rejected_unchanged(func(): return service.preview_order([base[0], base[0], base[2], base[3]]), "FORMATION_RECRUIT_DUPLICATE", "duplicate Recruit") expect_rejected_unchanged(func(): return service.preview_order([base[0], base[1], base[2], "00000000000000000000000000000000"]), "FORMATION_RECRUIT_UNKNOWN", "unknown Recruit") expect_rejected_unchanged(func(): return service.save_order("bad", swapped), "FORMATION_OPERATION_ID_INVALID", "malformed order operation") var saved: Dictionary = service.save_order(ORDER_OP, swapped) expect(saved.ok and saved.code == "order_saved" and saved.company.active_contract.formation.saved_front_to_rear_ids == swapped, "Save Order atomically persists the exact swapped order") if not saved.ok: return expect(int(saved.company.active_contract.formation.order_revision) == 1 and saved.company.active_contract.formation.order_receipts.size() == 1, "first save creates one order revision and receipt") var duplicate: Dictionary = service.save_order(ORDER_OP, swapped) var recovered_retry: Dictionary = service.save_order(ORDER_OP_2, swapped) expect(duplicate.ok and duplicate.code == "order_already_saved" and String(duplicate.receipt.receipt_hash) == String(saved.receipt.receipt_hash), "duplicate order operation returns the first receipt") expect(recovered_retry.ok and recovered_retry.code == "order_already_saved" and String(recovered_retry.receipt.receipt_hash) == String(saved.receipt.receipt_hash), "new operation retry resolves the already-saved order intent") var conflicting := base.duplicate() expect_rejected_unchanged(func(): return service.save_order(ORDER_OP, conflicting), "FORMATION_OPERATION_CONFLICT", "reused order operation with changed intent") expect_rejected_unchanged(func(): return service.accept_readiness(READY_OP, "0".repeat(64)), "READINESS_ORDER_STALE", "stale readiness order") var order_hash := String(saved.receipt.order_hash) var ready: Dictionary = service.accept_readiness(READY_OP, order_hash) expect(ready.ok and ready.code == "readiness_accepted", "readiness accepts the frozen saved order once") if not ready.ok: return expect(ready.snapshot.front_to_rear_ids == swapped and ready.snapshot.risk_option_ids.size() == 3 and ready.company.active_contract.formation.status == "ready", "prepared snapshot freezes members, order and three risk IDs") expect(not ready.company.active_contract.has("battle_receipts") and not ready.company.active_contract.has("battle_checkpoint"), "DEV-4 foundation creates no battle receipt or simulation checkpoint") var ready_retry: Dictionary = service.accept_readiness(READY_OP, order_hash) expect(ready_retry.ok and ready_retry.code == "readiness_already_accepted" and String(ready_retry.receipt.receipt_hash) == String(ready.receipt.receipt_hash), "duplicate readiness returns the original receipt") func verify_restart_and_recovery() -> void: var expected: Dictionary = _draft_service().load_company() if not expected.ok: expect(false, "final foundation state loads before recovery checks") return var expected_hash := String(expected.company.company_hash) var expected_formation_hash := String(expected.company.active_contract.formation.formation_hash) _write(COMPANY_PATH, "{broken primary") var backup: Dictionary = _formation_service().load_company() expect(backup.ok and backup.code == "recovered_backup", "valid backup repairs a corrupt foundation primary") expect(String(backup.company.active_contract.formation.formation_hash) == expected_formation_hash, "backup recovery preserves formation, readiness and risk state") var recovered_company: Dictionary = backup.company.duplicate(true) _remove_company_files() _write("%s.candidate" % COMPANY_PATH, JSON.stringify(_canonical(recovered_company))) var candidate: Dictionary = _formation_service().load_company() expect(candidate.ok and candidate.code == "recovered_candidate", "verified interrupted candidate promotes on restart") expect(String(candidate.company.company_hash) == expected_hash and String(candidate.company.active_contract.formation.formation_hash) == expected_formation_hash, "candidate recovery is byte-stable at the DEV-4 foundation boundary") func _create_contract_company() -> Dictionary: var draft: Dictionary = _draft_service().begin_or_load(SEED) if not draft.ok: expect(false, "fixture creates Initial Company draft") return {} var selected: Dictionary = _draft_service().set_selection([String(draft.draft.offers[0].offer_id), String(draft.draft.offers[2].offer_id)]) if not selected.ok: expect(false, "fixture selects opening pair") return {} var committed: Dictionary = _draft_service().commit_company() if not committed.ok: expect(false, "fixture commits opening Company") return {} var created: Dictionary = _contract_service().create_contract(CONTRACT_OP, "CTR-R1-01", "standard") expect(created.ok, "fixture creates first Contract") return created.company if created.ok else {} func _hire_opening_pair(company: Dictionary) -> Dictionary: var offers: Array = company.active_contract.market_board.recruit_offers var first: Dictionary = _contract_service().hire_recruit(HIRE_OP_1, String(offers[0].source_offer_id), 14) var second: Dictionary = _contract_service().hire_recruit(HIRE_OP_2, String(offers[1].source_offer_id), 14) if first.ok else {"ok": false} expect(first.ok and second.ok, "fixture completes two independent opening hires") return second.company if second.ok else {} func expect_rejected_unchanged(call: Callable, code: String, label: String) -> void: var before: Dictionary = _draft_service().load_company() var result: Dictionary = call.call() var after: Dictionary = _draft_service().load_company() expect(before.ok and after.ok and not result.ok and result.code == code and String(after.company.company_hash) == String(before.company.company_hash), "%s rejects with %s and changes nothing" % [label, code]) func _draft_service() -> RefCounted: var service := DraftService.new() service.path = DRAFT_PATH service.company_path = COMPANY_PATH return service func _contract_service() -> RefCounted: var service := ContractService.new() service.company_path = COMPANY_PATH return service func _formation_service() -> RefCounted: var service := FormationService.new() service.company_path = COMPANY_PATH return service func _recruit_hashes(company: Dictionary) -> Array[String]: var hashes: Array[String] = [] for recruit in company.owned_recruits: hashes.append(String(recruit.recruit_hash)) return hashes func _hash_array(value: Array) -> String: return JSON.stringify(_canonical(value)).sha256_text() func _cleanup() -> void: for path in [DRAFT_PATH, "%s.bak" % DRAFT_PATH, "%s.candidate" % DRAFT_PATH, "%s.bak.candidate" % DRAFT_PATH, COMPANY_PATH, "%s.bak" % COMPANY_PATH, "%s.candidate" % COMPANY_PATH, "%s.bak.candidate" % COMPANY_PATH, "%s.corrupt" % COMPANY_PATH]: if FileAccess.file_exists(path): DirAccess.remove_absolute(path) func _remove_company_files() -> void: for path in [COMPANY_PATH, "%s.bak" % COMPANY_PATH, "%s.candidate" % COMPANY_PATH, "%s.bak.candidate" % COMPANY_PATH, "%s.corrupt" % COMPANY_PATH]: if FileAccess.file_exists(path): DirAccess.remove_absolute(path) func _write(path: String, content: String) -> void: var file := FileAccess.open(path, FileAccess.WRITE) if file == null: expect(false, "fixture writes %s" % path) return file.store_string(content) file.close() func _canonical(value): if value is float and is_equal_approx(value, round(value)): return int(round(value)) if value is Dictionary: var result := {} var keys: Array = value.keys() keys.sort_custom(func(left, right): return String(left) < String(right)) for key in keys: result[key] = _canonical(value[key]) return result if value is Array: var result: Array = [] for item in value: result.append(_canonical(item)) return result return value func expect(condition: bool, message: String) -> void: if not condition: failures.append("DEV-4 foundation: %s" % message)