extends SceneTree const StaffRuntimeType = preload("res://scripts/staff/staff_runtime.gd") const StaffTaskType = preload("res://scripts/staff/staff_task.gd") const StaffServiceType = preload("res://scripts/staff/staff_service.gd") var _checks := 0 var _failures := 0 func _initialize() -> void: call_deferred("_run") func _run() -> void: print("S3: Staff payroll Operations boundary") _test_preview_and_settlement_contract() _test_employment_effective_minute_boundary() _test_replay_conflict_and_atomic_validation() _test_snapshot_contract() _test_service_owned_task_cadence() if _failures == 0: print("PASS: %d checks" % _checks) else: push_error("FAIL: %d of %d checks failed" % [_failures, _checks]) quit(_failures) func _test_preview_and_settlement_contract() -> void: var service := StaffServiceType.new() service.register_staff(_staff(&"z_active", 40, 0)) service.register_staff(_staff(&"a_active", 100, 20)) service.register_staff(_staff(&"m_departed", 70, 90)) service.remove_staff(&"m_departed", &"LEFT", true, 0) var preview := service.preview_daily_payroll(1) _expect(preview[&"ok"], "positive integer day previews") _expect( preview[&"rows"].map(func(row: Dictionary) -> StringName: return row[&"staff_id"]) \ == [&"a_active", &"m_departed", &"z_active"], "preview rows are globally lexical" ) _expect(preview[&"rows"][0][&"prior_arrears"] == 20 and preview[&"rows"][0][&"new_wages"] == 100, "active row separates arrears from today's wage") _expect(preview[&"rows"][1][&"new_wages"] == 0 and preview[&"rows"][1][&"total_due"] == 90, "departed row carries debt without new wage") _expect(preview[&"new_wages_total"] == 140, "Operations receives exact new-wage expense") _expect(preview[&"prior_arrears_total"] == 110, "Operations receives exact carried arrears") _expect(preview[&"total_due"] == 250, "Operations receives exact total obligation") var applied = service.apply_daily_payroll_settlement( &"operations.payroll.1", 1, {&"a_active": 120, &"m_departed": 30, &"z_active": 0} ) _expect(applied.is_success() and not applied.details[&"replayed"], "settlement commits") _expect(applied.details[&"paid_total"] == 150, "settlement reports actual cash allocation independently") _expect(applied.details[&"unpaid_after_total"] == 100, "settlement reports remaining liability") _expect(service.get_staff(&"a_active").unpaid_wages == 0, "full active payment clears balance") _expect(service.get_staff(&"z_active").unpaid_wages == 40, "zero active payment becomes arrears") _expect(service.get_departed_unpaid_wages(&"m_departed") == 60, "partial departed payment preserves remainder") var settlement_copy := service.get_payroll_settlement(&"operations.payroll.1") settlement_copy[&"paid_total"] = 999 _expect(service.get_payroll_settlement(&"operations.payroll.1")[&"paid_total"] == 150, "settlement query returns a defensive deep copy") _expect(service.validate_invariants()[&"ok"], "committed settlement history satisfies Staff invariants") var day_two := service.preview_daily_payroll(2) _expect(day_two[&"new_wages_total"] == 140 and day_two[&"prior_arrears_total"] == 100, "next day accrues once on top of exact arrears") service.apply_daily_payroll_settlement( &"operations.payroll.2", 2, {&"a_active": 50, &"m_departed": 60, &"z_active": 80} ) _expect(service.get_departed_staff_ids().is_empty(), "departed liability disappears after full paydown") _expect(service.get_staff(&"a_active").unpaid_wages == 50, "partial next-day active payment is retained") service.remove_staff(&"a_active", &"LEFT", true, 2) _expect(service.get_departed_unpaid_wages(&"a_active") == 50, "departure transfers active arrears") _expect(service.preview_daily_payroll(3)[&"rows"][0][&"new_wages"] == 0, "transferred departed debt never accrues wage") func _test_employment_effective_minute_boundary() -> void: var service := StaffServiceType.new() service.register_staff(_staff(&"before_boundary", 100, 0, 1_439)) service.register_staff(_staff(&"exact_boundary", 80, 35, 1_440)) service.register_staff(_staff(&"after_boundary", 60, 0, 1_441)) var day_one := service.preview_daily_payroll(1) var rows_by_id: Dictionary = {} for row: Dictionary in day_one[&"rows"]: rows_by_id[row[&"staff_id"]] = row _expect(day_one[&"ok"] and day_one[&"new_wages_total"] == 100, "day close accrues only hires strictly before its end") _expect(rows_by_id[&"before_boundary"][&"new_wages"] == 100, "minute before boundary accrues on that day") _expect(rows_by_id[&"exact_boundary"][&"new_wages"] == 0, "exact-boundary hire starts accruing next day") _expect(rows_by_id[&"exact_boundary"][&"total_due"] == 35, "exact-boundary exclusion preserves prior arrears") _expect(rows_by_id[&"after_boundary"][&"new_wages"] == 0, "post-boundary hire does not accrue early") var settled = service.apply_daily_payroll_settlement( &"effective.day.1", 1, {&"before_boundary": 100, &"exact_boundary": 10} ) _expect(settled.is_success() and settled.details[&"new_wages_total"] == 100, "settlement uses the same effective-time boundary as preview") _expect(service.get_staff(&"exact_boundary").unpaid_wages == 25, "settlement pays carried arrears without inventing a boundary-day wage") var day_two := service.preview_daily_payroll(2) _expect(day_two[&"new_wages_total"] == 240, "all three hires accrue at the following day close") var invalid_service := StaffServiceType.new() var string_minute := StaffRuntimeType.new().configure(&"string_minute", &"worker", { &"daily_wage": 10, &"employment_effective_minute": "1440", }) var float_minute := StaffRuntimeType.new().configure(&"float_minute", &"worker", { &"daily_wage": 10, &"employment_effective_minute": 1_440.0, }) var negative_minute := StaffRuntimeType.new().configure(&"negative_minute", &"worker", { &"daily_wage": 10, &"employment_effective_minute": -1, }) _expect(not invalid_service.register_staff(string_minute).is_success(), "string employment effective option is rejected") _expect(not invalid_service.register_staff(float_minute).is_success(), "floating employment effective option is rejected") _expect(not invalid_service.register_staff(negative_minute).is_success(), "negative employment effective option is rejected") _expect(invalid_service.get_staff_members().is_empty(), "invalid effective-time options never partially register") func _test_replay_conflict_and_atomic_validation() -> void: var service := StaffServiceType.new() service.register_staff(_staff(&"worker", 100, 0)) var before := service.create_snapshot() _expect(not service.preview_daily_payroll(0)[&"ok"], "zero day fails closed") _expect(not service.preview_daily_payroll("1")[&"ok"], "string day fails closed") _expect(not service.apply_daily_payroll_settlement(&"bad id", 1, {}).is_success(), "unstable settlement ID fails closed") _expect(not service.apply_daily_payroll_settlement(&"not_map", 1, []).is_success(), "non-map allocation fails closed") _expect(not service.apply_daily_payroll_settlement(&"string", 1, {&"worker": "1"}).is_success(), "string payment is not coerced") _expect(not service.apply_daily_payroll_settlement(&"float", 1, {&"worker": 1.0}).is_success(), "float payment is not coerced") _expect(not service.apply_daily_payroll_settlement(&"negative", 1, {&"worker": -1}).is_success(), "negative payment fails closed") _expect(not service.apply_daily_payroll_settlement(&"over", 1, {&"worker": 101}).is_success(), "overpayment fails closed") _expect(not service.apply_daily_payroll_settlement(&"unknown", 1, {&"ghost": 0}).is_success(), "unknown Staff ID fails closed") _expect(service.create_snapshot() == before, "all rejected allocations preserve state atomically") var first = service.apply_daily_payroll_settlement(&"stable.1", 1, {&"worker": 25}) var committed := service.create_snapshot() _expect(first.is_success() and service.get_staff(&"worker").unpaid_wages == 75, "valid allocation commits once") var replay = service.apply_daily_payroll_settlement(&"stable.1", 1, {&"worker": 25}) _expect(replay.is_success() and replay.details[&"replayed"], "same ID and fingerprint replays") _expect(service.create_snapshot() == committed, "replay never applies wages twice") _expect(not service.apply_daily_payroll_settlement(&"stable.1", 1, {&"worker": 24}).is_success(), "same ID rejects conflicting payment") _expect(not service.apply_daily_payroll_settlement(&"stable.1", 2, {&"worker": 25}).is_success(), "same ID rejects conflicting day") _expect(not service.apply_daily_payroll_settlement(&"different.1", 1, {}).is_success(), "same day rejects a second ID") _expect(service.create_snapshot() == committed, "all settlement conflicts preserve committed state") func _test_snapshot_contract() -> void: var service := StaffServiceType.new() service.register_staff(_staff(&"a", 100, 20)) service.register_staff(_staff(&"b", 50, 0)) service.apply_daily_payroll_settlement(&"snapshot.1", 1, {&"a": 30, &"b": 0}) service.apply_daily_payroll_settlement(&"snapshot.2", 2, {&"a": 50, &"b": 100}) var snapshot := service.create_snapshot() var restored := StaffServiceType.new() _expect(restored.restore_snapshot(snapshot), "v3 snapshot restores") _expect(restored.create_snapshot() == snapshot, "v3 snapshot round trip is exact") var before := restored.create_snapshot() var fingerprint := snapshot.duplicate(true) fingerprint[&"payroll_settlements"][0][&"fingerprint"] = "corrupt" _expect(not restored.restore_snapshot(fingerprint), "derived fingerprint tamper is rejected") _expect(restored.create_snapshot() == before, "fingerprint rejection is atomic") var total := snapshot.duplicate(true) total[&"payroll_settlements"][0][&"paid_total"] += 1 _expect(not restored.restore_snapshot(total), "derived total tamper is rejected") _expect(restored.create_snapshot() == before, "total rejection is atomic") var order := snapshot.duplicate(true) order[&"payroll_settlements"].reverse() _expect(not restored.restore_snapshot(order), "non-canonical settlement order is rejected") _expect(restored.create_snapshot() == before, "order rejection is atomic") var duplicate_day := snapshot.duplicate(true) duplicate_day[&"payroll_settlements"][1][&"day"] = 1 _expect(not restored.restore_snapshot(duplicate_day), "duplicate settled day is rejected") _expect(restored.create_snapshot() == before, "day uniqueness rejection is atomic") var negative := snapshot.duplicate(true) negative[&"payroll_settlements"][0][&"rows"][0][&"payment"] = -1 _expect(not restored.restore_snapshot(negative), "negative historical amount is rejected") _expect(restored.create_snapshot() == before, "negative history rejection is atomic") var unknown_field := snapshot.duplicate(true) unknown_field[&"payroll_settlements"][0][&"extra"] = 1 _expect(not restored.restore_snapshot(unknown_field), "unknown current history field is rejected") _expect(restored.create_snapshot() == before, "unknown field rejection is atomic") var effective_type := snapshot.duplicate(true) effective_type[&"staff"][0][&"employment_effective_minute"] = 1_440.0 _expect(not restored.restore_snapshot(effective_type), "floating employment effective minute is rejected") _expect(restored.create_snapshot() == before, "effective-minute type rejection is atomic") var effective_negative := snapshot.duplicate(true) effective_negative[&"staff"][0][&"employment_effective_minute"] = -1 _expect(not restored.restore_snapshot(effective_negative), "negative employment effective minute is rejected") _expect(restored.create_snapshot() == before, "negative effective-minute rejection is atomic") var effective_missing := snapshot.duplicate(true) effective_missing[&"staff"][0].erase(&"employment_effective_minute") _expect(not restored.restore_snapshot(effective_missing), "current Staff snapshot rejects a missing employment effective minute") _expect(restored.create_snapshot() == before, "missing effective-minute rejection is atomic") var unversioned_current_row: Dictionary = snapshot[&"staff"][0].duplicate(true) unversioned_current_row.erase(&"snapshot_version") _expect(StaffRuntimeType.from_snapshot(unversioned_current_row) == null, "StaffRuntime rejects an effective minute without its row version") var erased_current_row_schema := snapshot.duplicate(true) erased_current_row_schema[&"staff"][0].erase(&"snapshot_version") erased_current_row_schema[&"staff"][0].erase(&"employment_effective_minute") _expect(not restored.restore_snapshot(erased_current_row_schema), "v3 rejects a Staff row disguised as pre-effective schema") _expect(restored.create_snapshot() == before, "row-schema erasure rejection is atomic") var effective_version := snapshot.duplicate(true) effective_version[&"staff"][0][&"snapshot_version"] = 2 _expect(not restored.restore_snapshot(effective_version), "unknown Staff row snapshot version is rejected") _expect(restored.create_snapshot() == before, "Staff row version rejection is atomic") var outer_downgrade := snapshot.duplicate(true) outer_downgrade[&"version"] = 2 _expect(not restored.restore_snapshot(outer_downgrade), "changing only the outer version cannot downgrade a v3 Staff snapshot") _expect(restored.create_snapshot() == before, "outer version downgrade rejection is atomic") var pre_effective_v2 := snapshot.duplicate(true) pre_effective_v2[&"version"] = 2 pre_effective_v2.erase(&"work_assignments") for staff_snapshot: Dictionary in pre_effective_v2[&"staff"]: staff_snapshot.erase(&"snapshot_version") staff_snapshot.erase(&"employment_effective_minute") var migrated_v2 := StaffServiceType.new() _expect(migrated_v2.restore_snapshot(pre_effective_v2), "pre-effective-minute v2 Staff snapshot migrates") _expect(migrated_v2.get_staff(&"a").employment_effective_minute == 0, "v2 legacy Staff employment starts at minute zero") _expect( migrated_v2.create_snapshot()[&"version"] == StaffServiceType.SNAPSHOT_VERSION and migrated_v2.create_snapshot()[&"staff"][0][&"snapshot_version"] == 1, "v2 legacy Staff row is re-emitted in the current service and row schemas" ) var legacy := snapshot.duplicate(true) legacy[&"version"] = 1 legacy.erase(&"work_assignments") legacy.erase(&"last_advanced_minute") legacy.erase(&"payroll_settlements") for staff_snapshot: Dictionary in legacy[&"staff"]: staff_snapshot.erase(&"snapshot_version") staff_snapshot.erase(&"employment_effective_minute") var migrated := StaffServiceType.new() _expect(migrated.restore_snapshot(legacy), "v1 snapshot migrates") _expect(migrated.get_staff(&"a").unpaid_wages == 140, "v1 migration preserves active unpaid wages") _expect(migrated.get_staff(&"a").employment_effective_minute == 0, "legacy Staff employment starts deterministically at minute zero") _expect(migrated.create_snapshot()[&"payroll_settlements"].is_empty(), "v1 migration starts with empty settlement history") func _test_service_owned_task_cadence() -> void: var service := StaffServiceType.new() var staff := _staff(&"task_worker", 0, 0) var task := StaffTaskType.new().configure(&"task", &"work", 50, 0, { &"required_role_tags": [&"worker"], &"required_skill_min": {&"operation": 1}, &"task_tags": [&"work"], &"estimated_minutes": 2, &"target_cell": Vector3i.ZERO, }) service.register_staff(staff) service.register_task(task) service.dispatch_now(0) service.advance_one_minute(1) _expect(task.state == StaffTaskType.State.WORKING and task.progress_minutes == 0, "RESERVED begins without same-tick work") _expect(not service.progress_task(task.task_id, 1, 1).is_success(), "same-minute explicit progress is blocked") service.advance_one_minute(2) _expect(task.progress_minutes == 1, "WORKING advances exactly one minute per tick") _expect(not service.advance_one_minute(2)[&"ok"] and task.progress_minutes == 1, "duplicate service tick is idempotently rejected") service.advance_one_minute(3) _expect(task.state == StaffTaskType.State.COMPLETED, "automatic work completes exactly") func _staff( staff_id: StringName, daily_wage: int, unpaid_wages: int, employment_effective_minute: int = 0 ) -> StaffRuntime: return StaffRuntimeType.new().configure(staff_id, &"worker", { &"state": StaffRuntimeType.State.AVAILABLE, &"role_tags": [&"worker"], &"permitted_task_tags": [&"work"], &"skills": {&"operation": 10}, &"daily_wage": daily_wage, &"employment_effective_minute": employment_effective_minute, &"unpaid_wages": unpaid_wages, }) func _expect(condition: bool, label: String) -> void: _checks += 1 if condition: print(" ✓ %s" % label) return _failures += 1 push_error(" ✗ %s" % label)