Files
TheTree_mock/tests/s2/test_facility_upkeep_accrual.gd
Takeshi 5dde0c5ec2 Record original-alignment implementation and traceability checkpoint
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.
2026-09-30 23:52:24 +09:00

90 lines
5.1 KiB
GDScript

extends SceneTree
const Accrual = preload("res://scripts/economy/facility_upkeep_accrual.gd")
var checks := 0
var failures := 0
func _initialize() -> void:
call_deferred("_run")
func expect(value: bool, label: String) -> void:
checks += 1
if not value:
failures += 1
push_error(label)
func _run() -> void:
var service := Accrual.new()
expect(not service.advance_to(1, {}).ok, "an explicit starting minute is required; no retroactive fees")
expect(service.start_at(360).ok, "start at actual migration/start time")
expect(not service.start_at(0).ok, "starting twice cannot wipe accumulated charges")
service.advance_to(720, {&"room": 24, &"empty": 0})
expect(service.get_report().pending_facility_units == {"room": 8640}, "six hours at 24/day accrues exactly six gold")
service.advance_to(1080, {&"room": 24, &"new_room": 48})
var checkpoint := service.create_snapshot()
var restored := Accrual.new()
expect(restored.restore_snapshot(checkpoint), "partial day can be restored")
# The absent room has been demolished; its already accrued expense remains.
var close := service.advance_to(1440, {&"new_room": 48})
restored.advance_to(1440, {&"new_room": 48})
expect(close.closed_periods.size() == 1, "00:00 closes exactly one period")
var period: Dictionary = close.closed_periods[0]
expect(period.period_start == 360 and period.period_end == 1440, "first partial day never bills time before initialization")
expect(period.facility_units == {"new_room": 34560, "room": 17280}, "demolition stops future accrual but preserves incurred costs")
expect(service.create_snapshot() == restored.create_snapshot(), "save/load preserves exact fractional accumulation")
var before := service.create_snapshot()
expect(service.advance_to(1440, {&"new_room": 48}).ok and service.create_snapshot() == before, "same-minute replay cannot charge twice")
expect(not service.advance_to(1439, {}).ok and service.create_snapshot() == before, "rewind rejected without mutation")
close.closed_periods[0].facility_units.room = 999
expect(service.create_snapshot() == before, "returned period cannot mutate authoritative accrual")
var report := service.get_report()
report.closed_periods.clear()
expect(service.create_snapshot() == before, "report is a detached copy")
for rates: Dictionary in [{"bad": -1}, {"bad": 0.5}, {"bad": true}, {"": 1}, {"bad": Accrual.MAX_DAILY_RATE + 1}, {1: 5}]:
expect(not service.advance_to(1441, rates).ok and service.create_snapshot() == before, "invalid rates reject atomically: %s" % str(rates))
expect(not service.advance_to(2881, {}).ok and service.create_snapshot() == before, "unbounded batch rejected")
# Integer numerator is retained; neither per-minute nor per-day truncation.
var fractional := Accrual.new()
fractional.start_at(1439)
fractional.advance_to(1441, {"one_gold_per_day": 1})
var fraction_report := fractional.get_report()
expect(fraction_report.closed_periods[0].facility_units.one_gold_per_day == 1, "one minute charge is not rounded to zero")
expect(fraction_report.pending_facility_units.one_gold_per_day == 1, "midnight split conserves the second minute")
expect(fraction_report.gold_denominator == 1440, "settlement receives exact monetary unit denominator")
var split := Accrual.new()
var batched := Accrual.new()
split.start_at(0)
batched.start_at(0)
for minute in range(1, 1441):
split.advance_to(minute, {"b": 17, "a": 5})
batched.advance_to(1440, {"a": 5, "b": 17})
expect(split.create_snapshot() == batched.create_snapshot(), "input order and batching cannot change charges")
var json_copy: Dictionary = JSON.parse_string(JSON.stringify(split.create_snapshot()))
# JSON.parse_string produces floats; the project's snapshot codec handles
# integer preservation. Do not accept lossy/unvalidated raw JSON as a save.
expect(not restored.restore_snapshot(json_copy), "raw floating JSON fields are not silently accepted as integers")
var valid := split.create_snapshot()
var fresh := Accrual.new()
expect(fresh.restore_snapshot(valid), "closed periods restore")
for change: Dictionary in [
{"version": 2}, {"current_minute": 1439}, {"started_at": -2},
{"period_start": 1441}, {"extra": 1}, {"pending_units": {"room": 1}},
{"closed_periods": []}, {"current_minute": true},
]:
var invalid := valid.duplicate(true)
invalid.merge(change, true)
expect(not fresh.restore_snapshot(invalid) and fresh.create_snapshot() == valid, "invalid save rejected atomically: %s" % str(change))
var invalid := valid.duplicate(true)
invalid.closed_periods.append(invalid.closed_periods[0].duplicate(true))
expect(not fresh.restore_snapshot(invalid) and fresh.create_snapshot() == valid, "duplicated financial period rejected")
invalid = valid.duplicate(true)
invalid.closed_periods[0].facility_units.a = Accrual.MAX_DAILY_RATE * 1440 + 1
expect(not fresh.restore_snapshot(invalid), "impossible stored amount rejected")
invalid = valid.duplicate(true)
invalid.closed_periods[0].period_start = 1
expect(not fresh.restore_snapshot(invalid), "period gap rejected")
var empty := Accrual.new()
expect(fresh.restore_snapshot(empty.create_snapshot()), "unstarted state can restore")
print("%s: %d checks" % ["PASS" if failures == 0 else "FAIL", checks])
quit(failures)