Capture current source, content data, tests, asset licenses, and original-specification traceability. Original implementation remains incomplete. The accepted matching UI generation passed 154 suites and 12,253 checks, with eight rendered staff-contract screens reviewed. Latest native Windows and full-original acceptance remain outstanding.
289 lines
17 KiB
GDScript
289 lines
17 KiB
GDScript
extends SceneTree
|
|
## Boundary coverage for trusted per-person character definitions.
|
|
## The synthetic worker borrows existing metadata solely to exercise validators.
|
|
## It is not Fine, a proposed profile, recruitment or an active Runtime effect.
|
|
|
|
const CatalogType := preload("res://scripts/data/mvp_content_catalog.gd")
|
|
const FIXTURE_ID: StringName = &"heroine_policy_cleaner_fixture"
|
|
|
|
class EntryProbe:
|
|
extends MvpContentCatalog
|
|
func inspect_entry(entry: Dictionary) -> Dictionary:
|
|
_reset()
|
|
_validate_entry(&"unique_characters", StringName(_value(entry, &"id", &"")), entry)
|
|
return get_report()
|
|
func inspect_links(entries: Array, staff_index: Dictionary = {}) -> Dictionary:
|
|
_reset()
|
|
var index: Dictionary = {}
|
|
var ids: Array[StringName] = []
|
|
for entry: Dictionary in entries:
|
|
var entry_id: StringName = StringName(_value(entry, &"id", &""))
|
|
var errors_before: int = _errors.size()
|
|
_validate_entry(&"unique_characters", entry_id, entry)
|
|
if _errors.size() == errors_before:
|
|
index[entry_id] = _normalized_entry(entry)
|
|
ids.append(entry_id)
|
|
ids.sort_custom(_name_less)
|
|
_entries_by_type[&"unique_characters"] = index
|
|
_entries_by_type[&"staff_candidates"] = staff_index.duplicate(true)
|
|
_ordered_ids_by_type[&"unique_characters"] = ids
|
|
_validate_cross_references()
|
|
return get_report()
|
|
func policy_copy(entry_id: StringName) -> Dictionary:
|
|
return _unique_validation_policy(entry_id)
|
|
|
|
class ExplicitWorkerPolicyProbe:
|
|
extends EntryProbe
|
|
func _unique_validation_policy(entry_id: StringName) -> Dictionary:
|
|
if entry_id == &"heroine_policy_cleaner_fixture":
|
|
return {
|
|
&"character_kind": &"heroine",
|
|
&"mechanical_effect_ids": [&"cleaning_work_rate_multiplier"],
|
|
}
|
|
return super._unique_validation_policy(entry_id)
|
|
|
|
var checks := 0
|
|
var failures := 0
|
|
|
|
func _initialize() -> void:
|
|
call_deferred("run")
|
|
|
|
func run() -> void:
|
|
baseline_contract()
|
|
explicit_worker_boundary()
|
|
person_effect_isolation()
|
|
pair_boundary()
|
|
common_safety_boundary()
|
|
roster_atomicity_and_recovery()
|
|
if failures == 0:
|
|
print("PASS: UniqueCharacterValidationPolicy (%d checks)" % checks)
|
|
else:
|
|
push_error("FAIL: UniqueCharacterValidationPolicy (%d of %d checks failed)" % [failures, checks])
|
|
quit(failures)
|
|
|
|
func expect(ok: bool, label: String) -> void:
|
|
checks += 1
|
|
if not ok:
|
|
failures += 1
|
|
push_error("UNIQUE_POLICY: " + label)
|
|
|
|
func codes(report: Dictionary) -> Array[StringName]:
|
|
var result: Array[StringName] = []
|
|
for issue: Dictionary in report[&"errors"]:
|
|
result.append(StringName(issue[&"code"]))
|
|
return result
|
|
|
|
func documents() -> Dictionary:
|
|
var result: Dictionary = {}
|
|
for content_type: StringName in CatalogType.DEFAULT_PATHS:
|
|
var file := FileAccess.open(CatalogType.DEFAULT_PATHS[content_type], FileAccess.READ)
|
|
if file == null:
|
|
expect(false, "canonical input readable: " + String(content_type))
|
|
return {}
|
|
result[content_type] = JSON.parse_string(file.get_as_text())
|
|
file.close()
|
|
return result
|
|
|
|
func unique_entry(entry_id: StringName) -> Dictionary:
|
|
for entry: Dictionary in documents()[&"unique_characters"]["entries"]:
|
|
if StringName(entry["id"]) == entry_id:
|
|
return entry.duplicate(true)
|
|
return {}
|
|
|
|
func worker_entry() -> Dictionary:
|
|
var entry: Dictionary = unique_entry(&"heroine_anri")
|
|
entry["id"] = String(FIXTURE_ID)
|
|
entry["display_name"] = "検証専用・架空の清掃担当"
|
|
entry["role_id"] = "cleaner"
|
|
entry["mechanical_effects"] = {"cleaning_work_rate_multiplier": 3}
|
|
entry.erase("counterpart_id")
|
|
return entry
|
|
|
|
func baseline_contract() -> void:
|
|
var catalog := CatalogType.new()
|
|
var report: Dictionary = catalog.rebuild()
|
|
expect(bool(report[&"valid"]) and int(report[&"error_count"]) == 0, "unchanged two-person JSON validates")
|
|
expect(catalog.get_unique_character_ids() == [&"heroine_anri", &"rival_rai"], "normal unique roster remains exactly Anri and Rai")
|
|
expect(catalog.get_recruitable_staff_ids().size() == 22, "21 paid candidates plus approved Anri only")
|
|
expect(catalog.get_recruitment_entry(&"rival_rai").is_empty(), "Rai is not silently made recruitable")
|
|
expect(catalog.get_recruitment_entry(&"heroine_fine").is_empty(), "Fine is not registered")
|
|
expect(catalog.get_recruitment_entry(FIXTURE_ID).is_empty(), "synthetic worker is not registered")
|
|
var anri: Dictionary = catalog.get_entry(&"unique_characters", &"heroine_anri")
|
|
var rai: Dictionary = catalog.get_entry(&"unique_characters", &"rival_rai")
|
|
expect(anri[&"mechanical_effects"] == {&"assigned_floor_revenue_multiplier_bp": 12000}, "Anri's existing approved revenue definition is preserved")
|
|
expect(rai[&"mechanical_effects"] == {&"base_shield_attack": 8, &"night_shield_attack_bonus": 4}, "Rai's existing bounded attack definition is preserved")
|
|
expect(anri[&"counterpart_id"] == &"rival_rai" and rai[&"counterpart_id"] == &"heroine_anri", "explicit current MVP pair is preserved")
|
|
var payload: Dictionary = catalog.staff_hire_payload(&"heroine_anri")
|
|
expect(payload[&"options"][&"employment_kind"] == &"UNIQUE" and payload[&"options"][&"daily_wage"] == 0, "existing Anri hire payload remains free UNIQUE")
|
|
var probe := EntryProbe.new()
|
|
var policy: Dictionary = probe.policy_copy(&"heroine_anri")
|
|
policy[&"mechanical_effect_ids"].clear()
|
|
policy[&"counterpart_id"] = &"fabricated"
|
|
var pristine: Dictionary = probe.policy_copy(&"heroine_anri")
|
|
expect(pristine[&"mechanical_effect_ids"] == [&"assigned_floor_revenue_multiplier_bp"] and pristine[&"counterpart_id"] == &"rival_rai", "returned policy is an owned nested copy")
|
|
expect(probe.policy_copy(FIXTURE_ID).is_empty() and probe.policy_copy(&"heroine_fine").is_empty(), "production policy cannot infer synthetic or Fine identities")
|
|
|
|
func explicit_worker_boundary() -> void:
|
|
var probe := ExplicitWorkerPolicyProbe.new()
|
|
var entry: Dictionary = worker_entry()
|
|
var report: Dictionary = probe.inspect_entry(entry)
|
|
expect(int(report[&"error_count"]) == 0, "explicit synthetic policy accepts cleaning 3 without revenue or counterpart")
|
|
expect(not entry.has("counterpart_id") and not entry["mechanical_effects"].has("assigned_floor_revenue_multiplier_bp"), "positive fixture has neither an invented partner nor Anri effect")
|
|
report = probe.inspect_links([entry])
|
|
expect(int(report[&"error_count"]) == 0, "unpaired synthetic worker has no orphan or reciprocal requirement")
|
|
var ordinary_probe := EntryProbe.new()
|
|
report = ordinary_probe.inspect_entry(entry)
|
|
expect(&"missing_unique_policy" in codes(report), "same data without a trusted ID policy rejects")
|
|
for raw_counterpart: Variant in [null, "", "rival_rai", FIXTURE_ID, 7, true]:
|
|
entry = worker_entry()
|
|
entry["counterpart_id"] = raw_counterpart
|
|
report = probe.inspect_entry(entry)
|
|
expect(&"unexpected_counterpart" in codes(report), "unpaired policy rejects present counterpart value %s" % str(raw_counterpart))
|
|
entry = worker_entry()
|
|
entry[&"counterpart_id"] = &""
|
|
expect(&"unexpected_counterpart" in codes(probe.inspect_entry(entry)), "StringName counterpart presence also rejects")
|
|
for raw_value: Variant in [3, 3.0]:
|
|
entry = worker_entry()
|
|
entry["mechanical_effects"]["cleaning_work_rate_multiplier"] = raw_value
|
|
expect(int(probe.inspect_entry(entry)[&"error_count"]) == 0, "integer-compatible exact 3 follows existing numeric contract")
|
|
for raw_value: Variant in [0, 1, 2, 4, -3, 1000001, "3", true, null, 3.5, NAN, INF]:
|
|
entry = worker_entry()
|
|
entry["mechanical_effects"]["cleaning_work_rate_multiplier"] = raw_value
|
|
expect(&"invalid_mechanical_effect" in codes(probe.inspect_entry(entry)), "non-exact cleaning multiplier rejects: %s" % str(raw_value))
|
|
entry = worker_entry()
|
|
entry["id"] = "heroine_unknown_worker"
|
|
expect(&"missing_unique_policy" in codes(probe.inspect_entry(entry)), "fixture override does not accept arbitrary other workers")
|
|
entry = worker_entry()
|
|
entry["unique_policy"] = {"mechanical_effect_ids": ["cleaning_work_rate_multiplier"]}
|
|
expect(&"unknown_field" in codes(probe.inspect_entry(entry)), "JSON cannot inject or widen trusted policy")
|
|
|
|
func person_effect_isolation() -> void:
|
|
var probe := ExplicitWorkerPolicyProbe.new()
|
|
for person_id: StringName in [&"heroine_anri", &"rival_rai"]:
|
|
var entry: Dictionary = unique_entry(person_id)
|
|
entry["mechanical_effects"]["cleaning_work_rate_multiplier"] = 3
|
|
expect(&"effect_kind_mismatch" in codes(probe.inspect_entry(entry)), "reserved cleaning validator does not grant effect to " + String(person_id))
|
|
entry = unique_entry(person_id)
|
|
entry["mechanical_effects"] = {"cleaning_work_rate_multiplier": 3}
|
|
var issues: Array[StringName] = codes(probe.inspect_entry(entry))
|
|
expect(&"missing_mechanical_effect" in issues and &"effect_kind_mismatch" in issues, "replacement cleaning effect cannot satisfy " + String(person_id))
|
|
var worker: Dictionary = worker_entry()
|
|
worker["mechanical_effects"]["assigned_floor_revenue_multiplier_bp"] = 12000
|
|
expect(&"effect_kind_mismatch" in codes(probe.inspect_entry(worker)), "worker cannot acquire Anri's revenue effect")
|
|
worker = worker_entry()
|
|
worker["mechanical_effects"] = {"assigned_floor_revenue_multiplier_bp": 12000}
|
|
var worker_codes: Array[StringName] = codes(probe.inspect_entry(worker))
|
|
expect(&"missing_mechanical_effect" in worker_codes and &"effect_kind_mismatch" in worker_codes, "heroine kind is not a fallback to Anri effect")
|
|
worker = worker_entry()
|
|
worker["mechanical_effects"]["base_shield_attack"] = 8
|
|
expect(&"effect_kind_mismatch" in codes(probe.inspect_entry(worker)), "worker cannot acquire Rai's attack")
|
|
worker = worker_entry()
|
|
worker["mechanical_effects"]["arbitrary_runtime_hook"] = 1
|
|
var hook_codes: Array[StringName] = codes(probe.inspect_entry(worker))
|
|
expect(&"invalid_mechanical_effect" in hook_codes and &"effect_kind_mismatch" in hook_codes, "unknown hook remains rejected by value and person boundaries")
|
|
worker = worker_entry()
|
|
worker["mechanical_effects"] = {}
|
|
expect(&"missing_mechanical_effect" in codes(probe.inspect_entry(worker)), "an explicit worker still requires its own effect")
|
|
var anri: Dictionary = unique_entry(&"heroine_anri")
|
|
anri["character_kind"] = "rival"
|
|
expect(&"character_policy_mismatch" in codes(probe.inspect_entry(anri)), "known person's kind cannot be forged")
|
|
for person_id: StringName in [&"heroine_anri", &"rival_rai"]:
|
|
var entry: Dictionary = unique_entry(person_id)
|
|
entry["mechanical_effects"] = {"arbitrary_runtime_hook": 1}
|
|
var issues: Array[StringName] = codes(probe.inspect_entry(entry))
|
|
expect(&"invalid_mechanical_effect" in issues and &"effect_kind_mismatch" in issues and &"missing_mechanical_effect" in issues, "existing arbitrary hook safety contract retained for " + String(person_id))
|
|
for raw_value: Variant in [2499, 30001, "12000", true, NAN, INF]:
|
|
anri = unique_entry(&"heroine_anri")
|
|
anri["mechanical_effects"]["assigned_floor_revenue_multiplier_bp"] = raw_value
|
|
expect(&"invalid_mechanical_effect" in codes(probe.inspect_entry(anri)), "existing Anri bounds and type rejection retained")
|
|
for raw_value: Variant in [-1, 101, "8", true, NAN, INF]:
|
|
var rai: Dictionary = unique_entry(&"rival_rai")
|
|
rai["mechanical_effects"]["base_shield_attack"] = raw_value
|
|
expect(&"invalid_mechanical_effect" in codes(probe.inspect_entry(rai)), "existing Rai bounds and type rejection retained")
|
|
|
|
func pair_boundary() -> void:
|
|
var probe := ExplicitWorkerPolicyProbe.new()
|
|
var anri: Dictionary = unique_entry(&"heroine_anri")
|
|
var rai: Dictionary = unique_entry(&"rival_rai")
|
|
expect(int(probe.inspect_links([anri, rai, worker_entry()])[&"error_count"]) == 0, "existing explicit pair and a declared unpaired worker coexist at link boundary")
|
|
for person_id: StringName in [&"heroine_anri", &"rival_rai"]:
|
|
var paired_entry: Dictionary = unique_entry(person_id)
|
|
for raw_counterpart: Variant in [null, "", String(person_id), 7, true]:
|
|
paired_entry = unique_entry(person_id)
|
|
paired_entry["counterpart_id"] = raw_counterpart
|
|
expect(&"invalid_counterpart" in codes(probe.inspect_entry(paired_entry)), "paired person retains invalid counterpart rejection: " + String(person_id))
|
|
paired_entry = unique_entry(person_id)
|
|
paired_entry.erase("counterpart_id")
|
|
expect(&"invalid_counterpart" in codes(probe.inspect_entry(paired_entry)), "paired person cannot omit explicit partner: " + String(person_id))
|
|
anri = unique_entry(&"heroine_anri")
|
|
anri["counterpart_id"] = "missing_actor"
|
|
expect(&"missing_counterpart" in codes(probe.inspect_links([anri, rai])), "legacy orphan-reference error remains observable")
|
|
anri = unique_entry(&"heroine_anri")
|
|
anri["counterpart_id"] = String(FIXTURE_ID)
|
|
var issues: Array[StringName] = codes(probe.inspect_links([anri, rai, worker_entry()]))
|
|
expect(&"counterpart_policy_mismatch" in issues, "Anri cannot redirect to another declared worker")
|
|
expect(&"counterpart_not_reciprocal" in issues and &"counterpart_kind_conflict" in issues, "redirected relation still fails reciprocity and kind boundaries")
|
|
issues = codes(probe.inspect_links([worker_entry()], {FIXTURE_ID: {}}))
|
|
expect(&"runtime_actor_id_collision" in issues, "unpaired workers still participate in Runtime actor collision validation")
|
|
|
|
func common_safety_boundary() -> void:
|
|
var probe := ExplicitWorkerPolicyProbe.new()
|
|
for raw_verified: Variant in [false, "true", 1, null]:
|
|
var entry: Dictionary = worker_entry()
|
|
entry["adult_verified"] = raw_verified
|
|
expect(&"adult_verification_required" in codes(probe.inspect_entry(entry)), "declared policy cannot bypass explicit adult bool")
|
|
for raw_age: Variant in [17, 10001, "25", true, NAN, INF]:
|
|
var entry: Dictionary = worker_entry()
|
|
entry["age"] = raw_age
|
|
expect(&"adult_age_required" in codes(probe.inspect_entry(entry)), "declared policy cannot bypass existing age type/bounds")
|
|
for trait_id: String in ["unique", "unsellable"]:
|
|
var entry: Dictionary = worker_entry()
|
|
entry["traits"].erase(trait_id)
|
|
expect(&"unique_safety_traits_required" in codes(probe.inspect_entry(entry)), "declared worker still requires " + trait_id)
|
|
var entry: Dictionary = worker_entry()
|
|
entry["role_id"] = "maid"
|
|
expect(&"unknown_role" in codes(probe.inspect_entry(entry)), "policy cannot invent an unregistered role")
|
|
entry = worker_entry()
|
|
entry["skills"]["operation"] = 101
|
|
expect(&"map_value_out_of_range" in codes(probe.inspect_entry(entry)), "existing skill ceiling remains enforced")
|
|
entry = worker_entry()
|
|
entry["skills"].erase("operation")
|
|
expect(&"missing_map_key" in codes(probe.inspect_entry(entry)), "all canonical skills still required")
|
|
entry = worker_entry()
|
|
entry["mechanical_effects"][42] = 3
|
|
expect(&"canonical_key_collision" in codes(probe.inspect_entry(entry)), "non-text effect key cannot be coerced into a trusted effect ID")
|
|
entry = worker_entry()
|
|
entry["preferred_shift"] = "invalid_shift"
|
|
expect(&"unknown_shift" in codes(probe.inspect_entry(entry)), "known policy still requires registered shift")
|
|
entry = worker_entry()
|
|
entry["display_name"] = ""
|
|
expect(&"missing_display_name" in codes(probe.inspect_entry(entry)), "known policy still requires safe visible name")
|
|
|
|
func roster_atomicity_and_recovery() -> void:
|
|
var catalog := ExplicitWorkerPolicyProbe.new()
|
|
var baseline: Dictionary = documents()
|
|
var report: Dictionary = catalog.rebuild_from_documents(baseline)
|
|
expect(bool(report[&"valid"]), "fixture policy override does not alter current full baseline")
|
|
var generation: int = catalog.get_manifest_generation()
|
|
var changed: Dictionary = documents()
|
|
changed[&"unique_characters"]["entries"].append(worker_entry())
|
|
report = catalog.rebuild_from_documents(changed)
|
|
var issues: Array[StringName] = codes(report)
|
|
expect(not bool(report[&"valid"]) and &"mvp_count_mismatch" in issues and &"mvp_id_set_mismatch" in issues, "even explicitly declared third actor cannot enter the unchanged full roster")
|
|
expect(catalog.get_unique_character_ids().is_empty() and catalog.get_staff_candidate_ids().is_empty() and catalog.build_all_guest_definitions().is_empty(), "failed roster rebuild exposes no partial content")
|
|
expect(catalog.get_recruitable_staff_ids().is_empty() and catalog.staff_hire_payload(&"heroine_anri").is_empty(), "failed catalog cannot generate recruitment payloads")
|
|
expect(catalog.get_manifest_generation() == generation + 1, "failed rebuild invalidates transient generation exactly once")
|
|
report = catalog.rebuild_from_documents(baseline)
|
|
expect(bool(report[&"valid"]) and catalog.get_unique_character_ids() == [&"heroine_anri", &"rival_rai"], "exact baseline recovers without retaining the worker")
|
|
expect(catalog.get_manifest_generation() == generation + 2, "recovery invalidates generation exactly once")
|
|
changed = documents()
|
|
changed[&"unique_characters"]["entries"][0]["id"] = "heroine_unknown"
|
|
report = catalog.rebuild_from_documents(changed)
|
|
expect(not bool(report[&"valid"]) and &"missing_unique_policy" in codes(report), "same-count unknown identity is rejected before normal exposure")
|
|
expect(catalog.get_unique_character_ids().is_empty() and catalog.get_recruitable_staff_ids().is_empty(), "unknown person leaves no accepted or recruitable partial content")
|
|
changed = documents()
|
|
changed[&"unique_characters"]["entries"][0]["counterpart_id"] = "missing_actor"
|
|
var first: Dictionary = catalog.rebuild_from_documents(changed)
|
|
var second: Dictionary = catalog.rebuild_from_documents(changed)
|
|
expect(first[&"errors"] == second[&"errors"] and &"missing_counterpart" in codes(second), "original deterministic orphan error contract retained")
|