Files
TheTree_mock/tests/mvp/test_premium_room_guest_flow.gd
T
TakeshiandClaude Opus 5.5 ae0f998ff4 P06-01: Implement approved A06 finite Mana reserve (MP-01-r02 recommended)
Initial 100, capacity 300, world tree +20 per 10 game minutes. Each boundary
pays registered upkeep demand before storing surplus; overflow is discarded
and recorded, 0 stops the whole tower. Furnace offers are judged after
generation and demand payment. New saves carry mana_reserve and Operations
rollback includes it; legacy saves keep a 0..300 amount and reject others.

Outage tests that relied on Mana 0 persisting now hold the outage with a
disabled demand sink, since the world tree recovers at the next boundary.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 17:20:04 +09:00

116 lines
7.1 KiB
GDScript

extends SceneTree
const ManaOutage = preload("res://tests/helpers/mana_outage.gd")
var checks := 0
var failures := 0
var runtime: Variant
var clock: Variant
var state: Variant
func expect(ok: bool, message: String) -> void:
checks += 1
if not ok: failures += 1; push_error(message)
func _initialize() -> void: call_deferred("run")
func build(id: StringName, origin: Vector3i) -> bool:
var result: CommandResult = runtime.submit_command(&"build_facility", {&"definition_id":id,&"origin":origin})
expect(result.is_success(), "normal build %s: %s" % [id,result.message])
if not result.is_success(): return false
clock.advance_minutes(runtime.definition_registry.get_facility(id).build_minutes)
return true
func run() -> void:
runtime=load("res://autoload/simulation_runtime.gd").new()
clock=load("res://autoload/game_clock.gd").new();state=load("res://autoload/game_state.gd").new()
clock.reset(1,6,0);state.reset();runtime.initialize(clock,state,42502);clock.set_paused(true)
if not build(&"reception",Vector3i(-6,0,0)) or not build(&"premium_room",Vector3i(-6,2,0)) or not build(&"guest_stairs",Vector3i(-7,1,0)) or not build(&"guest_stairs",Vector3i(-7,2,0)):
finish();return
var room_id: StringName = &""
for room: FacilityInstance in runtime.facility_service.get_instances():
if room.definition_id==&"premium_room": room_id=room.instance_id
expect(runtime.get_facility_access_report(room_id)[&"reachable"], "normal stairs make premium room reachable from entrance")
var spawn: CommandResult = runtime.submit_command(&"spawn_guest",{&"guest_id":&"premium_walker",&"guest_type_id":&"traveler",&"gold":1000,
&"options":{&"current_cell":Vector3i(-7,0,0),&"state":GuestRuntime.State.DECIDING,&"planned_exit_at":clock.absolute_minute+2000,
&"needs":{&"sleep":100,&"hunger":0,&"hygiene":0,&"medical":0,&"leisure":0,&"intimacy":0}}})
expect(spawn.is_success(), "guest enters deciding state without forcing target or route")
var used_stairs := false
var selected_room := false
var service_id: StringName = &""
var moving_save := {}
for minute in 40:
clock.advance_one_minute()
var guest: GuestRuntime = runtime.guest_service.get_guest(&"premium_walker")
if guest==null: break
selected_room = selected_room or guest.target_facility_id==room_id
if guest.current_cell==Vector3i(-7,1,0):
used_stairs=true
if moving_save.is_empty(): moving_save=runtime.create_snapshot()
if not guest.current_service_id.is_empty():
service_id=guest.current_service_id;break
expect(selected_room, "highest sleep need selects premium room autonomously")
expect(used_stairs, "guest physically traverses first staircase landing")
expect(not service_id.is_empty(), "guest arrival automatically queues and starts lodging")
if service_id.is_empty():
var guest: GuestRuntime=runtime.guest_service.get_guest(&"premium_walker")
print("Guest diagnostic: ", guest.create_snapshot() if guest!=null else {})
finish();return
var started: Dictionary=runtime.create_snapshot()
expect(not moving_save.is_empty() and runtime.restore_snapshot(moving_save), "actual stair movement save restores")
for minute in 40:
clock.advance_one_minute()
if not runtime.guest_service.get_guest(&"premium_walker").current_service_id.is_empty(): break
expect(runtime.create_snapshot()==started, "stair-save replay reproduces arrival and queued service exactly")
clock.advance_minutes(360)
var mid: Dictionary=runtime.create_snapshot()
expect(runtime.restore_snapshot(mid), "real guest and in-progress premium service restore together")
var elapsed: int=runtime.facility_service.get_service(service_id).elapsed_minutes
ManaOutage.hold(runtime, state)
clock.advance_minutes(15)
var paused: ServiceInstance=runtime.facility_service.get_service(service_id)
expect(paused.state==ServiceInstance.State.PAUSED and paused.elapsed_minutes==elapsed, "zero mana pauses actual guest lodging without consuming service time")
expect(runtime.economy_service.get_balance(&"GUEST_WALLET:premium_walker")==1000, "outage does not charge real lodging guest")
var disk_path := "user://saves/premium_guest_outage_%d" % Time.get_ticks_usec()
var outage: Dictionary=runtime.create_snapshot()
expect(SaveService.save_snapshot(disk_path,outage)[&"ok"], "write real guest outage snapshot to disposable save")
var saved := SaveService.load_snapshot(disk_path)
expect(saved[&"ok"] and runtime.restore_snapshot(saved[&"snapshot"]), "restore real guest, paused service and empty mana from disk")
SaveService.delete_save(disk_path)
ManaOutage.release(runtime, state)
for minute in 370:
clock.advance_one_minute()
if runtime.facility_service.get_service(service_id).state==ServiceInstance.State.COMPLETED: break
expect(runtime.facility_service.get_service(service_id).state==ServiceInstance.State.COMPLETED, "autonomous premium stay completes after restored continuation")
expect(runtime.economy_service.get_balance(&"GUEST_WALLET:premium_walker")==780, "real guest pays one premium lodging charge")
expect(runtime.restore_snapshot(runtime.create_snapshot()), "completed guest/service/path/economy crosslinks remain saveable")
clock.advance_minutes(maxi(1440-clock.absolute_minute,0))
expect(runtime.economy_service.validate_ledger()[&"ok"] and runtime.restore_snapshot(runtime.create_snapshot()), "autonomous lodging and upkeep survive midnight ledger close and restore")
capacity_flow()
finish()
func finish() -> void:
runtime.free();clock.free();state.free()
print("Premium guest flow: %d checks, %d failures" % [checks,failures]);quit(failures)
func capacity_flow() -> void:
clock.reset(1,6,0);state.reset();runtime.initialize(clock,state,42503);clock.set_paused(true)
if not build(&"reception",Vector3i(-6,0,0)) or not build(&"premium_room",Vector3i(-6,2,0)) or not build(&"guest_stairs",Vector3i(-7,1,0)) or not build(&"guest_stairs",Vector3i(-7,2,0)): return
var room_id: StringName=&""
for room: FacilityInstance in runtime.facility_service.get_instances():
if room.definition_id==&"premium_room": room_id=room.instance_id
for index in 3:
var id:=StringName("premium_capacity_%d" % index)
expect(runtime.submit_command(&"spawn_guest",{&"guest_id":id,&"guest_type_id":&"traveler",&"gold":1000,
&"options":{&"current_cell":Vector3i(-7-index,0,0),&"state":GuestRuntime.State.DECIDING,&"planned_exit_at":clock.absolute_minute+2000,
&"needs":{&"sleep":100,&"hunger":0,&"hygiene":0,&"medical":0,&"leisure":0,&"intimacy":0}}}).is_success(), "spawn autonomous capacity guest")
var maximum := 0
var saw_queue := false
var exceeded := false
for minute in 60:
clock.advance_one_minute()
var using: int=runtime.facility_service.get_active_services(room_id).size()
maximum=maxi(maximum,using)
exceeded=exceeded or using>2
if using==2:
for guest: GuestRuntime in runtime.guest_service.get_guests():
if guest.target_facility_id==room_id and guest.state==GuestRuntime.State.QUEUING: saw_queue=true
expect(maximum==2 and not exceeded, "three autonomous guests never exceed two simultaneous lodging users")
expect(saw_queue, "third guest waits separately from the two using the room")
var crowded: Dictionary=runtime.create_snapshot()
expect(runtime.restore_snapshot(crowded) and runtime.create_snapshot()==crowded, "occupied room and separate queue restore exactly")