extends SceneTree var failures: Array[String] = [] func _initialize() -> void: call_deferred("_run") func _run() -> void: _test_registry_integrity() _test_definition_backed_behavior() _test_state_reference_validation() _test_id_round_trip() if failures.is_empty(): print("[TEST] Simulation definitions passed") quit(0) return for failure in failures: push_error("[TEST] " + failure) quit(1) func _test_registry_integrity() -> void: var errors := SimulationDefinitions.validate() _check(errors.is_empty(), "Definition registry should validate: %s" % [errors]) var action_ids: Array[StringName] = [] for definition in SimulationDefinitions.get_actions(): action_ids.append(definition.action_id) _check( SimulationDefinitions.get_action(definition.action_id) == definition, "Action lookup should return '%s'" % definition.action_id ) _check(action_ids.size() == 11, "Registry should contain all eleven executable prototype actions") var profession_ids := SimulationDefinitions.get_profession_ids() _check(profession_ids.size() == 5, "Registry should contain all five prototype professions") var profession_colors := {} for profession_id in profession_ids: var profession := SimulationDefinitions.get_profession(profession_id) _check( profession != null, "Profession lookup should return '%s'" % profession_id ) if profession != null: profession_colors[profession.visual_color.to_html()] = true _check( profession_colors.size() == profession_ids.size(), "Each profession should have a distinct definition-backed visual color" ) func _test_definition_backed_behavior() -> void: var npc := SimNPC.new(500, "DefinitionWorker", SimulationIds.PROFESSION_SCHOLAR, 5.0, 5.0) npc.set_task(SimulationIds.ACTION_STUDY) var study := SimulationDefinitions.get_action(SimulationIds.ACTION_STUDY) _check( is_equal_approx(npc.task_duration, study.default_duration), "SimNPC should use the action definition's default duration" ) _check( study.preferred_profession_id == SimulationIds.PROFESSION_SCHOLAR, "Study should reference the stable scholar profession ID" ) var gather := SimulationDefinitions.get_action(SimulationIds.ACTION_GATHER_FOOD) _check( ( gather.target_type == SimulationIds.TARGET_RESOURCE and gather.resource_action_id == SimulationIds.ACTION_GATHER_FOOD ), "Gather-food target metadata should come from its definition" ) func _test_state_reference_validation() -> void: var npc := SimNPC.new(501, "ValidationWorker", SimulationIds.PROFESSION_FARMER, 5.0, 5.0) var valid_data := NPCStateRecord.capture(npc).to_dictionary() var invalid_profession := valid_data.duplicate(true) invalid_profession["profession"] = "missing_profession" _check( NPCStateRecord.from_dictionary(invalid_profession) == null, "NPC state should reject an unknown profession ID" ) var invalid_action := valid_data.duplicate(true) invalid_action["current_task"] = "missing_action" _check( NPCStateRecord.from_dictionary(invalid_action) == null, "NPC state should reject an unknown action ID" ) func _test_id_round_trip() -> void: var manager: Node = load("res://simulation/SimulationManager.gd").new() manager.debug_logs = false root.add_child(manager) manager.set_process(false) var json_state: String = manager.serialize_state() var restored: Node = load("res://simulation/SimulationManager.gd").new() restored.debug_logs = false root.add_child(restored) restored.set_process(false) _check( restored.restore_state_from_json(json_state), "Definition-backed IDs should restore from serialized state" ) for npc in restored.npcs: _check( typeof(npc.profession) == TYPE_STRING_NAME, "Profession should restore as StringName" ) _check( typeof(npc.current_task) == TYPE_STRING_NAME, "Current action should restore as StringName" ) _check( SimulationDefinitions.get_profession(npc.profession) != null, "Restored profession should resolve through the registry" ) manager.free() restored.free() func _check(condition: bool, message: String) -> void: if not condition: failures.append(message)