extends SceneTree const Cleanliness = preload("res://scripts/environment/facility_cleanliness_service.gd") var checks := 0 var failures := 0 func expect(ok: bool, label: String) -> void: checks += 1 if not ok: failures += 1; push_error(label) func _initialize() -> void: call_deferred("run") func run() -> void: var room: FacilityDefinition = load("res://data/facilities/expansion/cleaning_room.tres") expect(room.is_valid() and room.footprint == Vector2i(2,1) and room.layer == FacilityDefinition.ViewLayer.BACK, "original cleaning room dimensions and layer") expect(room.construction_cost == {&"gold":350,&"materials":4} and room.build_minutes == 60 and room.maintenance_gold == 2, "approved trial construction and upkeep") expect(room.staff_min == 1 and room.staff_max == 2 and room.required_roles == [&"cleaner"], "approved cleaner staffing") expect(room.customer_capacity == 0 and room.base_price == 0, "cleaning room has no customer use or fee") var policy: Dictionary = JSON.parse_string(FileAccess.get_file_as_string("res://data/environment/cleaning_policy.json")) expect(policy["initial_cleanliness"] == 100 and policy["maximum_cleanliness"] == 100 and policy["clean_at_or_below"] == 75 and policy["work_minutes"] == 8 and policy["recovery_per_completed_task"] == 25, "approved trial cleaning policy") var rules = preload("res://scripts/environment/facility_cleaning_policy.gd").new() expect(rules.load_file(), "approved JSON policy loads through strict validator") var approved: Dictionary = rules.create_snapshot() var corrupt_policies: Array = [] for change: Dictionary in [{"schema_version":2}, {"initial_cleanliness":101}, {"maximum_cleanliness":0}, {"clean_at_or_below":100}, {"work_minutes":0}, {"work_minutes":8.5}, {"recovery_per_completed_task":0}, {"work_minutes":true}, {"work_minutes":INF}]: var invalid_policy: Dictionary = approved.duplicate(true) invalid_policy.merge(change, true) corrupt_policies.append(invalid_policy) var missing_policy: Dictionary = approved.duplicate(true) missing_policy.erase("work_minutes") corrupt_policies.append(missing_policy) for invalid_policy: Dictionary in corrupt_policies: expect(not rules.configure(invalid_policy) and rules.create_snapshot() == approved, "invalid policy rejects without replacing approved values") var detached_policy: Dictionary = rules.create_snapshot() detached_policy["work_minutes"] = 99 expect(rules.create_snapshot()["work_minutes"] == 8, "policy read cannot change work duration") var ordered = Cleanliness.new() var names: Array[String] = [] for index in range(31, -1, -1): var name := "clean_order_%03d" % ((index * 17) % 32) names.append(name) ordered.register_facility(StringName(name), 75, 100) names.sort() var dirty := ordered.get_dirty_facility_ids(75) var rows: Array = ordered.create_snapshot()["facilities"] var text_order_matches := true for index in names.size(): text_order_matches = text_order_matches and String(dirty[index]) == names[index] and rows[index]["id"] == names[index] expect(text_order_matches, "generated IDs use lexical order for saves and cleaning dispatch") var reordered: Dictionary = ordered.create_snapshot() reordered["facilities"].reverse() expect(ordered.restore_snapshot(reordered) and ordered.create_snapshot()["facilities"] == rows, "legacy row order restores to canonical lexical order") var service = Cleanliness.new() expect(service.register_facility(&"b", 100, 100) and service.register_facility(&"a", 50, 100), "explicit initial and maximum values") expect(not service.register_facility(&"b", 100,100), "duplicate facility rejected") expect(not service.register_facility(&"bad", -1,100), "invalid initial value rejected") expect(service.record_use(&"b", &"visit",25), "completed visit soils facility") expect(service.get_facility(&"b")["current"]==75, "soil amount applied") expect(service.get_dirty_facility_ids(75)==[&"a",&"b"], "threshold inclusive with stable ordering") expect(service.get_dirty_facility_ids(74)==[&"a"], "threshold excludes cleaner facility") var saved: Dictionary=service.create_snapshot() expect(service.record_use(&"b", &"visit",25) and service.create_snapshot()==saved, "replayed completion does not double soil") expect(not service.record_use(&"b", &"visit",26) and service.create_snapshot()==saved, "conflicting completion is atomic") expect(not service.record_use(&"missing", &"unknown",1), "unknown facility cannot receive effect") expect(not service.record_cleaning(&"b",&"negative",-1), "negative recovery rejected") expect(service.record_cleaning(&"b",&"work",9223372036854775807) and service.get_facility(&"b")["current"]==100, "recovery clamps without overflow") expect(service.record_use(&"b",&"dirty",9223372036854775807) and service.get_facility(&"b")["current"]==0, "soil clamps without overflow") expect(service.record_cleaning(&"b",&"work",9223372036854775807) and service.get_facility(&"b")["current"]==0, "old cleaning completion does not clean later dirt again") var restored=Cleanliness.new() expect(restored.restore_snapshot(service.create_snapshot()), "history and state restore") expect(restored.create_snapshot()==service.create_snapshot(), "exact roundtrip") expect(restored.record_cleaning(&"b",&"next",25) and service.record_cleaning(&"b",&"next",25) and restored.create_snapshot()==service.create_snapshot(), "restored continuation matches") var detached: Dictionary=restored.get_facility(&"b");detached["current"]=99 expect(restored.get_facility(&"b")["current"]==25, "read result detached") var intact: Dictionary=restored.create_snapshot() var bad_cases: Array=[] var bad: Dictionary=intact.duplicate(true);bad["schema_version"]=2;bad_cases.append(bad) bad=intact.duplicate(true);bad["facilities"][0]["current"]=33;bad_cases.append(bad) bad=intact.duplicate(true);bad["facilities"].append(bad["facilities"][0].duplicate(true));bad_cases.append(bad) bad=intact.duplicate(true);bad["receipts"].append(bad["receipts"][0].duplicate(true));bad_cases.append(bad) bad=intact.duplicate(true);bad["receipts"][0]["facility_id"]="missing";bad_cases.append(bad) bad=intact.duplicate(true);bad["receipts"][0]["amount"]=1.5;bad_cases.append(bad) bad=intact.duplicate(true);bad["receipts"][0]["kind"]="unknown";bad_cases.append(bad) bad=intact.duplicate(true);bad["facilities"]=false;bad_cases.append(bad) bad=intact.duplicate(true);bad["unexpected"]=1;bad_cases.append(bad) for candidate: Dictionary in bad_cases: expect(not restored.restore_snapshot(candidate) and restored.create_snapshot()==intact, "malformed or inconsistent save preserves live state") var mutated: Dictionary=restored.create_snapshot();mutated["receipts"].clear();mutated["facilities"].clear() expect(restored.create_snapshot()==intact, "saved arrays do not alias live state") expect(restored.unregister_facility(&"b") and restored.get_facility(&"b").is_empty(), "demolition removes facility state") var after_remove=Cleanliness.new() expect(after_remove.restore_snapshot(restored.create_snapshot()), "demolition retains no dangling completion receipts") print("Facility cleanliness: %d checks, %d failures" % [checks,failures]);quit(failures)