126 lines
5.9 KiB
GDScript
126 lines
5.9 KiB
GDScript
extends SceneTree
|
|
|
|
const Content = preload("res://p10_0/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 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_profession_tree(first)
|
|
verify_localization_and_execution(first)
|
|
verify_negative_boundaries(first)
|
|
if failures.is_empty():
|
|
print("ABG_P10_0_CONTENT_REGISTRY_OK professions=36 base=12 advanced=24 descendants=2 traits=36 equipment=40 artifacts=8 enemies=12 bosses=2 contracts=2 localized=137 execution_tags=137 signatures=unique source=exact 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.professions.size() == 36, "36 Professions are exposed")
|
|
expect(content.professions.filter(func(row): return row.family == "base").size() == 12, "12 Professions are base")
|
|
expect(content.professions.filter(func(row): return row.family == "advanced").size() == 24, "24 Professions are advanced")
|
|
expect(content.traits.size() == 36, "36 Traits are exposed")
|
|
expect(content.equipment.size() == 40, "EQ-001..EQ-040 are exposed")
|
|
expect(content.artifacts.size() == 8, "AR-001..AR-008 are exposed")
|
|
expect(content.enemies.size() == 12, "EN-R1-01..12 are exposed")
|
|
expect(content.bosses.size() == 2, "both Windglass Bosses are exposed")
|
|
expect(content.contracts.size() == 2, "both ordered Contract descriptors are exposed")
|
|
expect(content.region.id == "RG-01", "Windglass Region identity is exact")
|
|
expect(content.source.path == Content.SOURCE_PATH and content.source.sha256 == Content.SOURCE_SHA256 and content.source.authority_revision == Content.SOURCE_REVISION, "source metadata is exact")
|
|
expect(content.contracts[0].boss_id == "BO-R1-01" and content.contracts[1].boss_id == "BO-R1-02", "Contract order resolves exact Bosses")
|
|
|
|
|
|
func verify_profession_tree(content: Dictionary) -> void:
|
|
var descendants := {}
|
|
for base_index in range(1, 13):
|
|
descendants["PF-B%02d" % base_index] = []
|
|
for row in content.professions:
|
|
if row.family == "advanced":
|
|
descendants[row.parent_profession_id].append(row.id)
|
|
for base_id in descendants:
|
|
expect(descendants[base_id].size() == 2, "%s has exactly two advanced descendants" % base_id)
|
|
|
|
|
|
func verify_localization_and_execution(content: Dictionary) -> void:
|
|
var seen_signatures := {}
|
|
var row_count := 0
|
|
for family in ["professions", "traits", "equipment", "artifacts", "enemies", "bosses", "contracts"]:
|
|
for row in content[family]:
|
|
verify_runtime_row(row, seen_signatures)
|
|
row_count += 1
|
|
verify_runtime_row(content.region, seen_signatures)
|
|
row_count += 1
|
|
expect(row_count == 137 and content.names_zh.size() == 137 and content.execution_tags.size() == 137, "all 137 rows have complete authorities")
|
|
expect(Content.localized_name(content, "PF-B01", "zh_CN") == "护心卫士", "zh_CN lookup uses catalog authority")
|
|
expect(Content.localized_name(content, "PF-B01", "en") == "Hearthguard", "English lookup uses catalog authority")
|
|
|
|
|
|
func verify_runtime_row(row: Dictionary, seen_signatures: Dictionary) -> void:
|
|
expect(not String(row.name_en).is_empty() and not String(row.name_zh).is_empty() and row.name_en != row.name_zh, "%s has distinct localized names" % row.id)
|
|
expect(String(row.execution_key).begins_with("execute."), "%s has an executable dispatch key" % row.id)
|
|
expect(row.semantic_tags.size() >= 3 and not String(row.preview_rule).is_empty(), "%s has semantic tags and a preview rule" % row.id)
|
|
expect(String(row.rule_signature).length() == 64 and not seen_signatures.has(row.rule_signature), "%s has a unique mechanic signature" % row.id)
|
|
seen_signatures[row.rule_signature] = row.id
|
|
|
|
|
|
func verify_negative_boundaries(canonical: Dictionary) -> void:
|
|
var source_drift := canonical.duplicate(true)
|
|
source_drift.source.sha256 = "0".repeat(64)
|
|
expect_rejected(source_drift, "source SHA drift", "source_sha256")
|
|
|
|
var missing_locale := canonical.duplicate(true)
|
|
missing_locale.names_zh.erase("EQ-040")
|
|
expect_rejected(missing_locale, "missing localized authority", "names_zh_ids")
|
|
|
|
var duplicate_id := canonical.duplicate(true)
|
|
duplicate_id.equipment[1].id = "EQ-001"
|
|
expect_rejected(duplicate_id, "duplicate stable ID", "duplicate_id:EQ-001")
|
|
|
|
var bad_parent := canonical.duplicate(true)
|
|
bad_parent.professions[12].parent_profession_id = "PF-B99"
|
|
expect_rejected(bad_parent, "unknown advanced parent", "profession_parent:PF-A01")
|
|
|
|
var unresolved_boss := canonical.duplicate(true)
|
|
unresolved_boss.contracts[0].boss_id = "BO-R1-99"
|
|
expect_rejected(unresolved_boss, "unresolved Contract Boss", "contract_boss:CTR-R1-01")
|
|
|
|
var unknown_tag := canonical.duplicate(true)
|
|
unknown_tag.traits[0].semantic_tags.append("Unknown Tag")
|
|
expect_rejected(unknown_tag, "unknown semantic tag shape", "execution_tag:TR-001:Unknown Tag")
|
|
|
|
var filler := canonical.duplicate(true)
|
|
for field in ["rules", "cadence", "strategy", "semantic_tags", "rule_signature"]:
|
|
filler.professions[1][field] = filler.professions[0][field]
|
|
expect_rejected(filler, "filler-equivalent mechanic", "filler_equivalent:PF-B01:PF-B02")
|
|
|
|
|
|
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.0 content registry: %s" % message)
|