extends SceneTree const Pricing = preload("res://scripts/facility/facility_pricing.gd") const Facilities = preload("res://scripts/facility/facility_service.gd") const Facility = preload("res://scripts/facility/facility_instance.gd") const Service = preload("res://scripts/facility/service_instance.gd") const Guests = preload("res://scripts/guest/guest_service.gd") const Guest = preload("res://scripts/guest/guest_runtime.gd") var _checks := 0 var _failures := 0 func _initialize() -> void: call_deferred("_run") func _run() -> void: print("S2: Facility pricing and operating policy") _test_prices_and_satisfaction() _test_validation_is_atomic() _test_locked_quotes_and_scheduled_changes() _test_closed_service_and_opening_hours() _test_snapshot_restore_and_legacy_policy() _test_queue_release_is_atomic() if _failures == 0: print("PASS: %d checks" % _checks) else: push_error("FAIL: %d of %d checks failed" % [_failures, _checks]) quit(_failures) func _test_prices_and_satisfaction() -> void: var rows := Pricing.modes() _expect(rows.size() == 5, "five price modes are exposed") var expected_prices := [15, 20, 25, 38, 63] var expected_deltas := [10, 5, 0, -5, -20] var expected_softened := [10, 5, 0, -3, -10] for index: int in rows.size(): var policy := {&"price_mode": rows[index][&"id"]} _expect(Pricing.quote(25, policy) == expected_prices[index], "25 G price follows %s policy" % rows[index][&"id"]) _expect(Pricing.satisfaction_modifier(policy, 69, 50) == expected_deltas[index], "nineteen quality points retain %s penalty" % rows[index][&"id"]) _expect(Pricing.satisfaction_modifier(policy, 70, 50) == expected_softened[index], "twenty quality points soften %s penalty" % rows[index][&"id"]) _expect(Pricing.quote(0, policy) == 0, "free service remains free for %s" % rows[index][&"id"]) rows[0][&"percent"] = 999 _expect(Pricing.modes()[0][&"percent"] == 60, "mode reports are defensive copies") _expect(Pricing.quote(35, {&"price_mode": &"expensive"}) == 53, "half gold rounds up using integer arithmetic") _expect(Pricing.quote(3, {"price_mode": "charitable"}) == 2, "JSON string policy keys and IDs quote correctly") _expect(Pricing.quote(120, {}) == 120, "missing legacy price mode defaults to standard") _expect(Pricing.quote(-20, {}) == 0, "negative base quotations clamp to zero") func _test_validation_is_atomic() -> void: var facilities := _facilities() var before := facilities.create_snapshot() for invalid: Variant in [&"premium", "", 1, false, {}, []]: var result := facilities.set_policy(&"inn", {&"price_mode": invalid, &"vip_priority": true}) _expect(not result.is_success(), "unsupported price mode is rejected: %s" % str(invalid)) _expect(facilities.create_snapshot() == before, "invalid price patch is atomic: %s" % str(invalid)) for invalid: Variant in [0, 1, "false", null]: _expect(not facilities.set_policy(&"inn", {&"business_open": invalid}, 25).is_success(), "business_open requires bool: %s" % str(invalid)) _expect(facilities.create_snapshot() == before, "invalid future policy cannot replace pending state") var nested := {&"vip_priority": true, &"custom": {&"tags": [&"quiet"]}} _expect(facilities.set_policy(&"inn", nested).is_success(), "unrelated generic policy keys retain compatibility") nested[&"custom"][&"tags"].append(&"mutated") _expect(facilities.get_instance(&"inn").policy_current[&"custom"][&"tags"] == [&"quiet"], "nested generic patches are copied") _expect(facilities.set_policy(&"inn", {"price_mode": "affordable", "business_open": true}).is_success(), "string-key policy input is accepted") _expect(facilities.get_instance(&"inn").policy_current[&"price_mode"] == &"affordable", "price IDs normalize consistently") func _test_locked_quotes_and_scheduled_changes() -> void: var facilities := _facilities() var instance := facilities.get_instance(&"inn") facilities.set_policy(&"inn", {&"price_mode": &"affordable"}) var queued := facilities.create_request(&"old", &"inn", [&"guest"], &"meal", 0) _expect(queued.quoted_price == 20, "new request uses the facility's policy price") _expect(facilities.enqueue_request(queued).is_success(), "quoted request enters queue") var locked := queued.create_snapshot() facilities.set_policy(&"inn", {&"price_mode": &"exorbitant"}) _expect(queued.create_snapshot() == locked, "queued price and policy remain locked across a price change") var result := facilities.start_next_service(&"inn") _expect(result.is_success(), "old quotation starts after a price change") var active := facilities.get_service(StringName(result.details[&"service_id"])) _expect(active.price_snapshot == 20 and active.policy_snapshot[&"price_mode"] == &"affordable", "active service uses its accepted price and policy") var newest := facilities.create_request(&"new", &"inn", [&"other"], &"meal", 0) _expect(newest.quoted_price == 63, "subsequent request sees the new price") var explicit := facilities.create_request(&"explicit", &"inn", [&"another"], &"meal", 0, {&"quoted_price": 17}) _expect(explicit.quoted_price == 17, "an explicit quote is not multiplied a second time") facilities.set_policy(&"inn", {&"price_mode": &"charitable"}, 5) facilities.advance_one_minute(4) _expect(instance.policy_current[&"price_mode"] == &"exorbitant", "scheduled price does not apply before its minute") facilities.advance_one_minute(5) _expect(instance.policy_current[&"price_mode"] == &"charitable", "scheduled price applies at its exact minute") _expect(active.price_snapshot == 20 and active.policy_snapshot[&"price_mode"] == &"affordable", "completed service history preserves its accepted quotation") _expect(instance.policy_pending.is_empty() and instance.policy_pending_revision == 0, "applied schedule clears its pending metadata") func _test_closed_service_and_opening_hours() -> void: var facilities := _facilities() var instance := facilities.get_instance(&"inn") var running := facilities.create_request(&"running", &"inn", [&"guest"], &"meal", 0) facilities.enqueue_request(running) var started := facilities.start_next_service(&"inn") var active := facilities.get_service(StringName(started.details[&"service_id"])) var waiting := facilities.create_request(&"waiting", &"inn", [&"waiter"], &"meal", 0) facilities.enqueue_request(waiting) _expect(facilities.set_policy(&"inn", {&"business_open": false}).is_success(), "operator can close a facility through policy") _expect(not instance.operator_open and instance.state == Facility.State.CLOSED, "closure synchronizes operator flag and state immediately") for minute: int in range(1, 5): facilities.advance_one_minute(minute) _expect(active.state == Service.State.COMPLETED, "an accepted service completes while closed") _expect(facilities.get_queue(&"inn").size() == 1 and facilities.get_active_services(&"inn").is_empty(), "closure prevents new service starts") facilities.set_policy(&"inn", {&"business_open": true}, 6) facilities.advance_one_minute(5) _expect(not instance.operator_open, "scheduled reopening does not occur early") facilities.advance_one_minute(6) _expect(instance.operator_open and facilities.get_active_services(&"inn").size() == 1, "scheduled reopening allows the next service") facilities.set_opening_hours(&"inn", 600, 1_200, 6) _expect(instance.state == Facility.State.CLOSED and instance.operator_open, "opening hours remain independent from operator intent") facilities.set_policy(&"inn", {&"price_mode": &"expensive"}) _expect(instance.state == Facility.State.CLOSED, "price changes do not bypass opening hours") facilities.advance_one_minute(600) _expect(instance.state == Facility.State.OPEN, "scheduled opening retains business intent") facilities.set_operational_status(&"inn", {&"operator_open": false}, 600) _expect(not instance.operator_open and not instance.policy_current[&"business_open"], "generic operational closure stays synchronized with policy") facilities.set_operational_status(&"inn", {&"operator_open": true}, 600) _expect(instance.operator_open and instance.policy_current[&"business_open"], "generic operational reopening remains supported") func _test_snapshot_restore_and_legacy_policy() -> void: var facilities := _facilities() facilities.set_policy(&"inn", {&"price_mode": &"expensive", &"business_open": true}) var accepted := facilities.create_request(&"accepted", &"inn", [&"guest"], &"meal", 0) facilities.enqueue_request(accepted) facilities.start_next_service(&"inn") var waiting := facilities.create_request(&"waiting", &"inn", [&"waiter"], &"meal", 0) facilities.enqueue_request(waiting) facilities.set_policy(&"inn", {&"price_mode": &"charitable", &"business_open": false}, 8) var snapshot := facilities.create_snapshot() var restored := Facilities.new() _expect(restored.restore_snapshot(snapshot), "current, pending, queued and active pricing survives restore") _expect(restored.create_snapshot() == snapshot, "valid save restores byte-equivalent policies and quotations") for minute: int in range(1, 9): facilities.advance_one_minute(minute) restored.advance_one_minute(minute) _expect(restored.create_snapshot() == facilities.create_snapshot(), "restored pending policy replays deterministically") var bad := snapshot.duplicate(true) bad[&"instances"][0][&"policy_current"][&"price_mode"] = &"invalid" var unchanged := restored.create_snapshot() _expect(not restored.restore_snapshot(bad), "invalid saved price mode is rejected") _expect(restored.create_snapshot() == unchanged, "bad price restore cannot change live state") bad = snapshot.duplicate(true) bad[&"instances"][0][&"policy_pending"][&"business_open"] = 0 _expect(not restored.restore_snapshot(bad), "invalid saved pending business state is rejected") bad = snapshot.duplicate(true) bad[&"requests"][0][&"policy_snapshot"][&"price_mode"] = &"invalid" _expect(not restored.restore_snapshot(bad), "invalid saved request policy is rejected") bad = snapshot.duplicate(true) bad[&"instances"][0][&"operator_open"] = false _expect(not restored.restore_snapshot(bad), "contradictory saved operator and business flags are rejected") var legacy := _facilities() legacy.set_operational_status(&"inn", {&"operator_open": false}) var legacy_snapshot := legacy.create_snapshot() _expect(restored.restore_snapshot(legacy_snapshot), "legacy operator-only saves remain supported") _expect(restored.create_snapshot() == legacy_snapshot and not restored.get_instance(&"inn").operator_open, "legacy closure restores without inserting policy keys") var configured := Facility.new().configure(&"configured", &"cafeteria", {&"policy_current": {&"business_open": false}}) _expect(not configured.operator_open and configured.state == Facility.State.CLOSED, "configured business policy initializes operator state") func _test_queue_release_is_atomic() -> void: var guests := Guests.new() guests.create_definition(&"visitor", {&"base_patience": 30}) var guest := guests.spawn_guest(&"waiting", &"visitor", 0, {&"state": Guest.State.DECIDING}) guests.enter_queue(&"waiting", &"inn", &"request", 0, 17) guest.path_handle = {&"path": [Vector3i.ZERO]} var callback_state := {&"calls": 0, &"allow": false, &"received": []} var callback := func(guest_id: StringName, facility_id: StringName, request_id: StringName) -> bool: callback_state[&"calls"] += 1 callback_state[&"received"] = [guest_id, facility_id, request_id] return bool(callback_state[&"allow"]) var before := guest.create_snapshot() _expect(not bool(guests.release_queue_for_replanning(&"waiting", 1, callback)[&"ok"]), "failed external queue removal rejects redecision") _expect(guest.create_snapshot() == before, "failed external queue removal preserves guest state and patience") callback_state[&"allow"] = true guest.transition_tick = 1 guest.transitions_this_tick = Guests.MAX_TRANSITIONS_PER_TICK before = guest.create_snapshot() _expect(not bool(guests.release_queue_for_replanning(&"waiting", 1, callback)[&"ok"]), "full transition budget defers queue release") _expect(callback_state[&"calls"] == 1 and guest.create_snapshot() == before, "transition preflight protects external ownership and guest state") var reasons: Array[StringName] = [] guests.guest_state_changed.connect(func(_id: StringName, _old: int, _new: int, reason: StringName): reasons.append(reason)) _expect(bool(guests.release_queue_for_replanning(&"waiting", 2, callback)[&"ok"]), "queue release retries on the next available minute") _expect(callback_state[&"received"] == [&"waiting", &"inn", &"request"], "external release receives the exact ownership tuple") _expect(guest.state == Guest.State.DECIDING and guest.patience_remaining == 17, "closure causes redecision without a patience penalty") _expect(guest.target_facility_id.is_empty() and guest.queue_request_id.is_empty() and guest.path_handle == null, "successful release clears obsolete target, request and path") _expect(reasons == [&"FACILITY_CLOSED"], "closure publishes a distinct redecision reason") _expect(not bool(guests.release_queue_for_replanning(&"waiting", 3, callback)[&"ok"]) and callback_state[&"calls"] == 2, "released guest cannot release the request twice") _expect(not bool(guests.release_queue_for_replanning(&"missing", 3)[&"ok"]), "missing guests cannot enter the release path") func _facilities() -> FacilityService: var dining_cells: Array[Vector3i] = [Vector3i(0, 0, 0), Vector3i(1, 0, 0), Vector3i(2, 0, 0), Vector3i(3, 0, 0), Vector3i(0, 1, 0), Vector3i(1, 1, 0), Vector3i(2, 1, 0), Vector3i(3, 1, 0)] var kitchen_cells: Array[Vector3i] = [Vector3i(0, 0, 1), Vector3i(1, 0, 1), Vector3i(2, 0, 1), Vector3i(3, 0, 1), Vector3i(0, 1, 1), Vector3i(1, 1, 1), Vector3i(2, 1, 1), Vector3i(3, 1, 1)] var facilities := Facilities.new() facilities.register_instance(Facility.new().configure(&"inn", &"cafeteria", { &"occupied_cells": dining_cells, &"capacity": 1, &"queue_capacity": 8, &"base_price": 25, &"base_quality": 50, &"base_service_minutes": 4, })) facilities.register_instance(Facility.new().configure(&"kitchen", &"kitchen", {&"origin": Vector3i(0, 0, WorldService.LAYER_BACK), &"occupied_cells": kitchen_cells})) _expect(facilities.set_operational_status(&"inn", {&"related_facility_id": &"kitchen"}).is_success(), "isolated pricing Service uses explicit BACK kitchen; quote calculations unchanged") return facilities func _expect(condition: bool, message: String) -> void: _checks += 1 if condition: print(" ✓ %s" % message) else: _failures += 1 push_error(" ✗ %s" % message)