137 lines
6.5 KiB
GDScript
137 lines
6.5 KiB
GDScript
extends SceneTree
|
|
|
|
const Content = preload("res://p10_2/content_registry.gd")
|
|
|
|
var failures: Array[String] = []
|
|
var rejected_cases := 0
|
|
|
|
|
|
func _initialize() -> void:
|
|
call_deferred("run_test")
|
|
|
|
|
|
func run_test() -> void:
|
|
var first := Content.snapshot()
|
|
var second := Content.snapshot()
|
|
var validation := Content.validate(first)
|
|
expect(validation.ok, "canonical P10.2 registry validates: %s" % str(validation.errors))
|
|
expect(first == second, "catalog loading and enrichment are byte-identical")
|
|
expect(String(first.catalog_hash).length() == 64, "catalog has a canonical SHA-256")
|
|
verify_counts_and_identity(first)
|
|
verify_route_and_market_authority(first)
|
|
verify_negative_boundaries(first)
|
|
if failures.is_empty():
|
|
print("ABG_P10_2_CONTENT_REGISTRY_OK region=1 contract=1 equipment=8 enemies=3 routes=9 markets=3 localized=22 source=exact formulas=coin,capacity,offer_level negative_cases=%d" % rejected_cases)
|
|
quit(0)
|
|
else:
|
|
for failure in failures:
|
|
push_error(failure)
|
|
quit(1)
|
|
|
|
|
|
func verify_counts_and_identity(content: Dictionary) -> void:
|
|
expect(content.region.id == "RG-02", "Region identity is exact")
|
|
expect(content.contract.id == "CTR-R2-01" and content.contract.region_id == "RG-02", "Contract and Region reference are exact")
|
|
expect(content.contract.slice_start_market == 2 and content.contract.slice_end_market == 5, "mid-Contract boundary is exact")
|
|
expect(not content.contract.boss_executable, "Boss execution remains frozen")
|
|
expect(content.equipment.size() == 8, "EQ-049..EQ-056 are exposed")
|
|
expect(content.enemies.size() == 3, "EN-R2-08..EN-R2-10 are exposed")
|
|
expect(content.routes.size() == 9 and content.markets.size() == 3, "nine routes and three Markets are exposed")
|
|
expect(content.names_zh.size() == 22, "every bounded content identity has zh_CN authority")
|
|
expect(content.source == Content.SOURCE_AUTHORITY, "four source blobs are bound exactly")
|
|
|
|
var ids := {}
|
|
for singleton in [content.region, content.contract]:
|
|
verify_row(singleton, ids)
|
|
for family in [content.equipment, content.enemies, content.routes]:
|
|
for row in family:
|
|
verify_row(row, ids)
|
|
expect(ids.size() == 22, "all runtime identities are unique")
|
|
|
|
|
|
func verify_route_and_market_authority(content: Dictionary) -> void:
|
|
var routes := Content.rows_by_id(content, "routes")
|
|
var markets := Content.markets_by_index(content)
|
|
expect(markets.keys().size() == 3, "Market index lookup is complete")
|
|
var seen_new_enemies := {"EN-R2-08": 0, "EN-R2-09": 0, "EN-R2-10": 0}
|
|
for market_index in [2, 3, 4]:
|
|
var market: Dictionary = markets[market_index]
|
|
expect(market.round_index == market_index, "Market %d binds the same Round" % market_index)
|
|
expect(market.cleared_rounds_before == market_index - 1, "Market %d clear lineage is exact" % market_index)
|
|
expect(market.line_capacity == [4, 5, 5][market_index - 2], "Market %d capacity follows authority" % market_index)
|
|
expect(market.offer_level == [1, 1, 2][market_index - 2], "Market %d offer level follows authority" % market_index)
|
|
expect(market.route_ids.size() == 3 and market.required_equipment_ids.size() == 2, "Market %d owns exact board rows" % market_index)
|
|
for risk in range(3):
|
|
var route: Dictionary = routes[market.route_ids[risk]]
|
|
expect(route.market_index == market_index and route.round_index == market_index and route.risk == risk, "Market %d risk %d identity is exact" % [market_index, risk])
|
|
expect(route.reward_coin == 18 + 2 * market_index + 2 + 7 * risk, "Market %d risk %d reward formula is exact" % [market_index, risk])
|
|
expect(route.wave_order.map(func(wave): return wave.enemy_id) == route.enemy_ids, "Market %d risk %d wave order binds composition" % [market_index, risk])
|
|
for enemy_id in route.enemy_ids:
|
|
if seen_new_enemies.has(enemy_id):
|
|
seen_new_enemies[enemy_id] += 1
|
|
for enemy_id in seen_new_enemies:
|
|
expect(seen_new_enemies[enemy_id] >= 2, "%s appears in at least two route decisions" % enemy_id)
|
|
|
|
|
|
func verify_row(row: Dictionary, ids: Dictionary) -> void:
|
|
expect(not ids.has(row.id), "%s is globally unique" % row.id)
|
|
ids[row.id] = true
|
|
expect(not String(row.name_en).is_empty() and not String(row.name_zh).is_empty(), "%s has both names" % row.id)
|
|
expect(not String(row.preview).is_empty() and row.response_tags.size() >= 3, "%s has preview and response tags" % row.id)
|
|
expect(String(row.execution_key).begins_with("execute.") and String(row.mechanic_signature).length() == 64, "%s has runtime dispatch and signature" % row.id)
|
|
|
|
|
|
func verify_negative_boundaries(canonical: Dictionary) -> void:
|
|
var source_drift := canonical.duplicate(true)
|
|
source_drift.source.economy_blob = "0".repeat(40)
|
|
expect_rejected(source_drift, "source drift", "source:economy_blob")
|
|
|
|
var locale_missing := canonical.duplicate(true)
|
|
locale_missing.names_zh.erase("EN-R2-10")
|
|
expect_rejected(locale_missing, "missing locale", "names_zh_ids")
|
|
|
|
var duplicate_item := canonical.duplicate(true)
|
|
duplicate_item.equipment[1].id = "EQ-049"
|
|
expect_rejected(duplicate_item, "duplicate item", "duplicate_global_id:EQ-049")
|
|
|
|
var unknown_enemy := canonical.duplicate(true)
|
|
unknown_enemy.routes[0].enemy_ids[0] = "EN-R2-99"
|
|
unknown_enemy.routes[0].wave_order[0].enemy_id = "EN-R2-99"
|
|
expect_rejected(unknown_enemy, "unknown route enemy", "route_enemy:RO-R2-M02-Q0")
|
|
|
|
var reward_drift := canonical.duplicate(true)
|
|
reward_drift.routes[4].reward_coin += 1
|
|
expect_rejected(reward_drift, "reward drift", "route_reward:RO-R2-M03-Q1")
|
|
|
|
var executable_boss := canonical.duplicate(true)
|
|
executable_boss.contract.boss_executable = true
|
|
expect_rejected(executable_boss, "Boss execution leak", "boss_boundary")
|
|
|
|
var route_mismatch := canonical.duplicate(true)
|
|
route_mismatch.markets[1].route_ids[0] = "RO-R2-M02-Q0"
|
|
expect_rejected(route_mismatch, "wrong Market route", "market_route:3:0")
|
|
|
|
var item_mismatch := canonical.duplicate(true)
|
|
item_mismatch.markets[2].required_equipment_ids[0] = "EQ-999"
|
|
expect_rejected(item_mismatch, "unknown required item", "market_equipment:4:EQ-999")
|
|
|
|
var filler := canonical.duplicate(true)
|
|
filler.enemies[0].rule = "TODO placeholder"
|
|
expect_rejected(filler, "filler mechanic", "filler:EN-R2-08:todo")
|
|
|
|
|
|
func expect_rejected(content: Dictionary, label: String, expected_error: String) -> void:
|
|
var result := Content.validate(content)
|
|
var found := false
|
|
for error in result.errors:
|
|
if String(error).begins_with(expected_error):
|
|
found = true
|
|
expect(not result.ok and found, "%s rejects with %s; got %s" % [label, expected_error, str(result.errors)])
|
|
if not result.ok and found:
|
|
rejected_cases += 1
|
|
|
|
|
|
func expect(condition: bool, message: String) -> void:
|
|
if not condition:
|
|
failures.append("P10.2 content registry: %s" % message)
|