extends SceneTree const FacilityDefinitionType = preload("res://scripts/data/facility_definition.gd") const PlacementReportType = preload("res://scripts/construction/placement_report.gd") const ConstructionJobType = preload("res://scripts/construction/construction_job.gd") const ConstructionServiceType = preload("res://scripts/construction/construction_service.gd") const WorldServiceType = preload("res://scripts/world/world_service.gd") var _checks := 0 var _failures := 0 class FakeResourceState: extends RefCounted var resources := {&"gold": 1_000, &"materials": 100} var mutate_then_fail_spend := false var fail_apply := false var fail_restore := false var reentrant_service: Variant var reentrant_attempted := false var reentrant_succeeded := false func get_resource(resource_name: StringName) -> int: return int(resources.get(resource_name, 0)) func can_afford(cost: Dictionary) -> bool: for resource_name: Variant in cost: if get_resource(StringName(resource_name)) < int(cost[resource_name]): return false return true func spend(cost: Dictionary) -> bool: if reentrant_service != null and not reentrant_attempted: reentrant_attempted = true var nested: Variant = reentrant_service.start_construction( _valid_definition_for_reentry(), Vector3i(2, 2, 0) ) reentrant_succeeded = nested.is_success() if mutate_then_fail_spend: resources[&"gold"] = int(resources[&"gold"]) - int(cost.get(&"gold", 0)) return false if not can_afford(cost): return false var deltas: Dictionary = {} for resource_name: Variant in cost: deltas[StringName(resource_name)] = -int(cost[resource_name]) return apply_resource_deltas(deltas) func apply_resource_deltas(deltas: Dictionary) -> bool: if fail_apply: return false var next := resources.duplicate(true) for resource_name: Variant in deltas: var key := StringName(resource_name) next[key] = int(next.get(key, 0)) + int(deltas[resource_name]) if int(next[key]) < 0: return false resources = next return true func create_snapshot() -> Dictionary: return {&"resources": resources.duplicate(true)} func restore_snapshot(snapshot: Dictionary) -> bool: if fail_restore: return false if not snapshot.get(&"resources", null) is Dictionary: return false resources = snapshot[&"resources"].duplicate(true) return true func _valid_definition_for_reentry() -> Dictionary: return { &"id": &"nested_room", &"footprint": Vector2i.ONE, &"zone": FacilityDefinitionType.MapZone.ANY, &"layer": FacilityDefinitionType.ViewLayer.FRONT, &"construction_cost": {&"gold": 100, &"materials": 10}, &"build_minutes": 60, &"weight_per_cell": 1, } class FakeWorld: extends RefCounted var min_x := 0 var max_x := 8 var min_y := -2 var max_y := 8 var occupancy: Dictionary = {} var unsupported_cells: Array[Vector3i] = [] var fail_next_occupy := false var fail_next_release := false var fail_all_occupy := false var excavation: Dictionary = {} var forced_cells: Array[Vector3i] = [] func cells_for_footprint( origin: Vector3i, footprint: Vector2i, rotation: int, _layer: int ) -> Array[Vector3i]: if not forced_cells.is_empty(): return forced_cells.duplicate() var width := footprint.x var height := footprint.y if rotation % 180 != 0: width = footprint.y height = footprint.x var cells: Array[Vector3i] = [] for y_offset in height: for x_offset in width: cells.append(origin + Vector3i(x_offset, y_offset, 0)) return cells func is_in_bounds(cell: Vector3i) -> bool: return cell.x >= min_x and cell.x <= max_x and cell.y >= min_y and cell.y <= max_y func get_zone(cell: Vector3i) -> int: if cell.y > 0: return FacilityDefinitionType.MapZone.ABOVE_GROUND if cell.y < 0: return FacilityDefinitionType.MapZone.UNDERGROUND return FacilityDefinitionType.MapZone.BOUNDARY func get_occupant(cell: Vector3i) -> Variant: return occupancy.get(cell, &"") func is_excavated(cell: Vector3i) -> bool: return bool(excavation.get(Vector2i(cell.x, cell.y), true)) func validate_support(_cells: Array[Vector3i], _weight_per_cell: int) -> Dictionary: return { &"ok": unsupported_cells.is_empty(), &"unsupported_cells": unsupported_cells.duplicate(), &"reason": "Support capacity is insufficient.", } func occupy(occupant_id: StringName, cells: Array[Vector3i], _kind: StringName) -> bool: if fail_all_occupy: return false if fail_next_occupy: fail_next_occupy = false return false for cell: Vector3i in cells: if not String(occupancy.get(cell, &"")).is_empty(): return false for cell: Vector3i in cells: occupancy[cell] = occupant_id return true func release(occupant_id: StringName) -> bool: if fail_next_release: fail_next_release = false return false var cells: Array[Vector3i] = [] for cell: Vector3i in occupancy: if occupancy[cell] == occupant_id: cells.append(cell) if cells.is_empty(): return false for cell: Vector3i in cells: occupancy.erase(cell) return true func _initialize() -> void: call_deferred("_run") func _run() -> void: print("S1: Construction Service") _test_report_collects_every_reason() _test_start_is_atomic() _test_cancellation_refund() _test_completion_and_demolition() _test_completion_rollback_and_retry() _test_completion_rollback_failure_quarantines() _test_progress_replay_is_idempotent() _test_deferred_completion_commit_and_reentry() _test_deferred_failure_discard_and_restore() _test_ordinary_advance_notifications_unchanged() _test_snapshot_round_trip() _test_snapshot_strictness_and_v1_migration() _test_definition_type_spoof_and_reentry() _test_failed_rollback_quarantines_service() _test_real_world_service_integration() _test_underground_requires_excavation() _test_every_footprint_cell_must_match_zone() _test_boundary_facility_anchor_rule() if _failures == 0: print("PASS: %d checks" % _checks) else: push_error("FAIL: %d of %d checks failed" % [_failures, _checks]) quit(_failures) func _test_report_collects_every_reason() -> void: var world := FakeWorld.new() world.max_x = 2 var state := FakeResourceState.new() state.resources = {&"gold": 0, &"materials": 0} var definition := _definition( &"invalid_location", Vector2i(2, 1), FacilityDefinitionType.MapZone.ABOVE_GROUND, FacilityDefinitionType.ViewLayer.FRONT, {&"gold": 100, &"materials": 10} ) var origin := Vector3i(2, -1, FacilityDefinitionType.ViewLayer.BACK) world.occupancy[origin] = &"existing" world.unsupported_cells = [origin] var service := ConstructionServiceType.new(world, state) var report = service.validate_placement(definition, origin) _expect(not report.can_build, "invalid placement is rejected") _expect(report.has_error(PlacementReportType.ERROR_OUT_OF_BOUNDS), "bounds error is reported") _expect(report.has_error(PlacementReportType.ERROR_ZONE_MISMATCH), "zone error is reported") _expect(report.has_error(PlacementReportType.ERROR_LAYER_MISMATCH), "layer error is reported") _expect(report.has_error(PlacementReportType.ERROR_OVERLAP), "overlap error is reported") _expect(report.has_error(PlacementReportType.ERROR_UNSUPPORTED), "support error is reported") _expect( report.has_error(PlacementReportType.ERROR_INSUFFICIENT_RESOURCES), "cost error is reported" ) _expect(report.reasons.size() == 6, "all independent placement failures are retained") func _test_start_is_atomic() -> void: var world := FakeWorld.new() var state := FakeResourceState.new() var service := ConstructionServiceType.new(world, state) var definition := _valid_definition() var resources_before := state.resources.duplicate(true) world.fail_next_occupy = true var failed = service.start_construction(definition, Vector3i(1, 1, 0)) _expect(not failed.is_success(), "failed world reservation rejects construction") _expect(state.resources == resources_before, "failed reservation rolls resource payment back") _expect(world.occupancy.is_empty(), "failed reservation leaves no occupied cell") _expect(service.get_jobs().is_empty(), "failed reservation creates no job") var started = service.start_construction(definition, Vector3i(1, 1, 0), 0, 4, 600) _expect(started.is_success(), "valid construction starts") var job = service.get_job(started.details[&"job_id"]) _expect(job != null and job.state == ConstructionJobType.State.PLANNED, "new job starts PLANNED") _expect(state.resources == {&"gold": 900, &"materials": 90}, "full construction cost is paid once") _expect(world.get_occupant(Vector3i(1, 1, 0)) == job.job_id, "job atomically reserves its cells") func _test_cancellation_refund() -> void: var world := FakeWorld.new() var state := FakeResourceState.new() var service := ConstructionServiceType.new(world, state) var first = service.start_construction(_valid_definition(), Vector3i(1, 1, 0), 0, 4) var first_job_id: StringName = first.details[&"job_id"] var cancelled = service.cancel_job(first_job_id) _expect(cancelled.is_success(), "zero-progress job cancels") _expect(state.resources == {&"gold": 1_000, &"materials": 100}, "zero-progress cancellation refunds all") _expect(world.occupancy.is_empty(), "cancellation releases reserved cells") var second = service.start_construction(_valid_definition(), Vector3i(2, 2, 0), 0, 4) var second_job_id: StringName = second.details[&"job_id"] service.progress_job(second_job_id) var job = service.get_job(second_job_id) _expect(job.progress == 2_500 and job.state == ConstructionJobType.State.BUILDING, "one minute advances exact integer progress") var partial = service.cancel_job(second_job_id) _expect(partial.details[&"refund"] == {&"gold": 75, &"materials": 7}, "partial cancellation refunds the unbuilt ratio") _expect(state.resources == {&"gold": 975, &"materials": 97}, "partial refund is applied once") func _test_completion_and_demolition() -> void: var world := FakeWorld.new() var state := FakeResourceState.new() var service := ConstructionServiceType.new(world, state) var started = service.start_construction(_valid_definition(), Vector3i(3, 3, 0), 0, 2) var job_id: StringName = started.details[&"job_id"] var first_tick = service.progress_job(job_id, ConstructionJobType.NORMAL_WORK_RATE, 601) _expect(first_tick.is_success() and service.get_job(job_id).progress == 5_000, "first build minute reaches half progress") var second_tick = service.progress_job(job_id, ConstructionJobType.NORMAL_WORK_RATE, 602) var job = service.get_job(job_id) _expect(second_tick.is_success() and job.state == ConstructionJobType.State.COMPLETED, "final minute completes the job") var instance_id: StringName = second_tick.details[&"instance_id"] _expect(not service.get_facility(instance_id).is_empty(), "completion creates a facility instance") _expect(world.get_occupant(Vector3i(3, 3, 0)) == instance_id, "completion replaces job reservation with facility occupancy") var demolished = service.demolish_facility(instance_id) _expect(demolished.is_success(), "completed facility can be demolished") _expect(world.occupancy.is_empty(), "demolition releases facility cells") _expect(state.resources == {&"gold": 900, &"materials": 92}, "demolition returns 25 percent of materials") func _test_completion_rollback_and_retry() -> void: var world := FakeWorld.new() var service := ConstructionServiceType.new(world, FakeResourceState.new()) var started = service.start_construction(_valid_definition(), Vector3i(3, 3, 0), 0, 2) var job_id: StringName = started.details[&"job_id"] service.progress_job(job_id, ConstructionJobType.NORMAL_WORK_RATE, 10) world.fail_next_occupy = true var failed = service.progress_job(job_id, ConstructionJobType.NORMAL_WORK_RATE, 11) var job = service.get_job(job_id) _expect(not failed.is_success() and failed.details[&"rolled_back"], "failed completion reports a verified world rollback") _expect(job.state == ConstructionJobType.State.BLOCKED and job.progress == ConstructionJobType.MAX_PROGRESS, "interrupted completion retains all finished work") _expect(world.get_occupant(Vector3i(3, 3, 0)) == job_id, "failed facility transfer restores the construction reservation") var retry = service.progress_job(job_id, ConstructionJobType.NORMAL_WORK_RATE, 12) _expect(retry.is_success() and service.get_job(job_id).state == ConstructionJobType.State.COMPLETED, "a later minute deterministically retries the blocked completion") func _test_completion_rollback_failure_quarantines() -> void: var world := FakeWorld.new() var service := ConstructionServiceType.new(world, FakeResourceState.new()) var job_id: StringName = service.start_construction(_valid_definition(), Vector3i(3, 3, 0), 0, 2).details[&"job_id"] service.progress_job(job_id, ConstructionJobType.NORMAL_WORK_RATE, 30) world.fail_all_occupy = true var failed = service.progress_job(job_id, ConstructionJobType.NORMAL_WORK_RATE, 31) _expect(not failed.is_success() and failed.details[&"rollback_failed"], "completion exposes failure to restore its reservation") _expect(service.is_quarantined(), "unrecoverable completion rollback quarantines construction mutations") _expect(not service.cancel_job(job_id).is_success(), "quarantined completion state cannot be mutated further") func _test_progress_replay_is_idempotent() -> void: var world := FakeWorld.new() var service := ConstructionServiceType.new(world, FakeResourceState.new()) var job_id: StringName = service.start_construction(_valid_definition(), Vector3i(1, 1, 0), 0, 4).details[&"job_id"] service.progress_job(job_id, ConstructionJobType.NORMAL_WORK_RATE, 20) var progress_once := service.get_job(job_id).progress var replay = service.progress_job(job_id, ConstructionJobType.NORMAL_WORK_RATE, 20) _expect(replay.is_success() and replay.is_replay, "same-minute construction retry is an explicit replay") _expect(service.get_job(job_id).progress == progress_once, "same-minute construction retry cannot add work twice") func _test_deferred_completion_commit_and_reentry() -> void: var world := FakeWorld.new() var resources := FakeResourceState.new() var service := ConstructionServiceType.new(world, resources) var definition := _valid_definition() var started = service.start_construction(definition, Vector3i(1, 1, 0), 0, 1) var job_id: StringName = started.details[&"job_id"] var before_advance: Dictionary = service.create_snapshot() var order: Array[StringName] = [] var delivered_jobs: Array[Dictionary] = [] var delivered_facilities: Array[Dictionary] = [] var reentry: Dictionary = {} var job_changed_callback := func(job: ConstructionJob) -> void: order.append(&"job_changed") delivered_jobs.append(job.create_snapshot()) job.assigned_builder_ids.append(&"listener_tamper") var nested_advance: Array[CommandResult] = service.advance_one_minute(102) reentry[&"advance_rejected"] = ( nested_advance.size() == 1 and not nested_advance[0].is_success() ) reentry[&"commit_rejected"] = not service.commit_deferred_notifications() reentry[&"discard_rejected"] = not service.discard_deferred_notifications() reentry[&"snapshot_rejected"] = service.create_snapshot().is_empty() reentry[&"restore_rejected"] = not service.restore_snapshot(before_advance) reentry[&"mutation_rejected"] = not service.start_construction( definition, Vector3i(5, 5, 0) ).is_success() var facility_completed_callback := func(instance: Dictionary) -> void: order.append(&"facility_completed") delivered_facilities.append(instance.duplicate(true)) (instance[&"occupied_cells"] as Array).clear() instance[&"definition_id"] = &"listener_tamper" service.job_changed.connect(job_changed_callback) service.facility_completed.connect(facility_completed_callback) var advanced: Array[CommandResult] = service.advance_one_minute(101, {}, true) _expect( advanced.size() == 1 and advanced[0].is_success(), "deferred advance completes construction state" ) var instance_id := StringName(advanced[0].details.get(&"instance_id", &"")) _expect( order.is_empty() and delivered_jobs.is_empty() and delivered_facilities.is_empty(), "deferred completion emits no raw signal before commit" ) _expect(service.has_deferred_notifications(), "deferred completion exposes a pending notification batch") _expect(service.create_snapshot().is_empty(), "pending notifications fail closed for snapshot capture") _expect( not service.restore_snapshot(before_advance), "pending notifications reject snapshot restore" ) var minute_before_rejected_advance := service.current_minute var pending_advance: Array[CommandResult] = service.advance_one_minute(102) _expect( pending_advance.size() == 1 and not pending_advance[0].is_success() and pending_advance[0].details.get(&"code", &"") == &"DEFERRED_NOTIFICATIONS_PENDING" and service.current_minute == minute_before_rejected_advance, "a pending batch rejects another advance before changing its minute" ) _expect( not service.start_construction(definition, Vector3i(5, 5, 0)).is_success(), "a pending batch rejects unrelated mutation until its disposition is chosen" ) # get_job historically returns the live aggregate. Mutating it after staging # proves the queued notification captured its own defensive point-in-time copy. service.get_job(job_id).assigned_builder_ids.append(&"late_live_builder") _expect(service.commit_deferred_notifications(), "deferred notifications commit explicitly") _expect( order == [&"job_changed", &"facility_completed"] and delivered_jobs.size() == 1 and delivered_facilities.size() == 1, "commit emits completion signals exactly once in Construction order" ) _expect( delivered_jobs[0][&"assigned_builder_ids"].is_empty() and service.get_job(job_id).assigned_builder_ids == [&"late_live_builder"], "queued and delivered ConstructionJob payloads are defensive copies" ) var live_facility: Dictionary = service.get_facility(instance_id) _expect( live_facility.get(&"definition_id", &"") == definition.id and live_facility.get(&"occupied_cells", []) == [Vector3i(1, 1, 0)], "listener mutation of a deferred facility payload cannot alter live state" ) _expect( bool(reentry.get(&"advance_rejected", false)) and bool(reentry.get(&"commit_rejected", false)) and bool(reentry.get(&"discard_rejected", false)) and bool(reentry.get(&"snapshot_rejected", false)) and bool(reentry.get(&"restore_rejected", false)) and bool(reentry.get(&"mutation_rejected", false)), "signal callbacks cannot reenter notification dispatch, restore, snapshot, or mutation" ) var committed_order := order.duplicate() _expect( not service.has_deferred_notifications() and service.commit_deferred_notifications() and order == committed_order, "an empty repeat commit succeeds without emitting twice" ) service.job_changed.disconnect(job_changed_callback) service.facility_completed.disconnect(facility_completed_callback) func _test_deferred_failure_discard_and_restore() -> void: var world := FakeWorld.new() var service := ConstructionServiceType.new(world, FakeResourceState.new()) var started = service.start_construction(_valid_definition(), Vector3i(2, 2, 0), 0, 1) var before_advance: Dictionary = service.create_snapshot() var changed_count := [0] var completed_count := [0] var job_changed_callback := func(_job: ConstructionJob) -> void: changed_count[0] += 1 var facility_completed_callback := func(_instance: Dictionary) -> void: completed_count[0] += 1 service.job_changed.connect(job_changed_callback) service.facility_completed.connect(facility_completed_callback) world.fail_next_occupy = true var advanced: Array[CommandResult] = service.advance_one_minute(201, {}, true) _expect( advanced.size() == 1 and not advanced[0].is_success(), "a deferred completion retains its internal failure result" ) _expect( service.has_deferred_notifications() and changed_count[0] == 0 and completed_count[0] == 0, "notifications queued before an internal failure remain silent and pending" ) _expect( service.create_snapshot().is_empty() and not service.restore_snapshot(before_advance), "failed deferred advances still block snapshot and restore while pending" ) _expect(service.discard_deferred_notifications(), "a failed deferred batch can be discarded") _expect( not service.has_deferred_notifications() and service.discard_deferred_notifications() and service.commit_deferred_notifications() and changed_count[0] == 0 and completed_count[0] == 0, "discard is replay-safe and never emits the rejected batch" ) _expect( service.restore_snapshot(before_advance) and service.create_snapshot() == before_advance, "discard permits the caller to restore Construction after failure" ) service.job_changed.disconnect(job_changed_callback) service.facility_completed.disconnect(facility_completed_callback) func _test_ordinary_advance_notifications_unchanged() -> void: var service := ConstructionServiceType.new(FakeWorld.new(), FakeResourceState.new()) var job_id: StringName = service.start_construction( _valid_definition(), Vector3i(3, 3, 0), 0, 1 ).details[&"job_id"] var order: Array[StringName] = [] var delivered_jobs: Array[ConstructionJob] = [] var job_changed_callback := func(job: ConstructionJob) -> void: order.append(&"job_changed") delivered_jobs.append(job) var facility_completed_callback := func(_instance: Dictionary) -> void: order.append(&"facility_completed") service.job_changed.connect(job_changed_callback) service.facility_completed.connect(facility_completed_callback) var advanced: Array[CommandResult] = service.advance_one_minute(301, {}) _expect( advanced.size() == 1 and advanced[0].is_success(), "the existing two-argument advance call still completes normally" ) _expect( order == [&"job_changed", &"facility_completed"] and delivered_jobs.size() == 1, "ordinary advance still emits both signals immediately in prior order" ) _expect( delivered_jobs[0] == service.get_job(job_id), "ordinary job_changed retains its historical live-job payload" ) _expect( not service.has_deferred_notifications() and not service.create_snapshot().is_empty(), "ordinary advance creates no ephemeral pending batch" ) service.job_changed.disconnect(job_changed_callback) service.facility_completed.disconnect(facility_completed_callback) func _test_snapshot_round_trip() -> void: var world := FakeWorld.new() var state := FakeResourceState.new() var service := ConstructionServiceType.new(world, state) var started = service.start_construction(_valid_definition(), Vector3i(4, 4, 0), 0, 8, 700) var job_id: StringName = started.details[&"job_id"] service.assign_builders(job_id, [&"builder_b", &"builder_a"]) service.progress_job(job_id, 5_000, 701) var snapshot := service.create_snapshot() var restored := ConstructionServiceType.new(world, state) _expect(restored.restore_snapshot(snapshot), "valid construction snapshot restores") _expect(restored.create_snapshot() == snapshot, "snapshot restore preserves all construction state") var before_invalid := restored.create_snapshot() _expect(not restored.restore_snapshot({&"schema_version": 999}), "unsupported snapshot schema is rejected") _expect(restored.create_snapshot() == before_invalid, "rejected restore does not mutate live state") func _test_snapshot_strictness_and_v1_migration() -> void: var world := FakeWorld.new() var source := ConstructionServiceType.new(world, FakeResourceState.new()) var job_id: StringName = source.start_construction(_valid_definition(), Vector3i(4, 4, 0), 0, 8, 700).details[&"job_id"] source.progress_job(job_id, 5_000, 701) var snapshot := source.create_snapshot() var restored := ConstructionServiceType.new(world, FakeResourceState.new()) var unknown := snapshot.duplicate(true) unknown[&"unknown"] = 1 _expect(not restored.restore_snapshot(unknown), "current construction schema rejects unknown top-level keys") var type_spoof := snapshot.duplicate(true) type_spoof[&"schema_version"] = "2" _expect(not restored.restore_snapshot(type_spoof), "current construction schema rejects a string version") var cost_spoof := snapshot.duplicate(true) cost_spoof[&"jobs"][0][&"construction_cost"][&"gold"] = "100" _expect(not restored.restore_snapshot(cost_spoof), "current construction schema rejects numeric-string costs") var derived_tamper := snapshot.duplicate(true) derived_tamper[&"jobs"][0][&"progress"] += 1 _expect(not restored.restore_snapshot(derived_tamper), "current construction schema rejects derived progress tampering") var cell_tamper := snapshot.duplicate(true) cell_tamper[&"jobs"][0][&"reserved_cells"] = [[5, 4, 0]] _expect(not restored.restore_snapshot(cell_tamper), "construction restore rejects world reservation cross-link tampering") var high_water_tamper := snapshot.duplicate(true) high_water_tamper[&"next_job_sequence"] = 1 _expect(not restored.restore_snapshot(high_water_tamper), "construction restore rejects a sequence below the ID high-water mark") var before := restored.create_snapshot() _expect(before[&"jobs"].is_empty(), "rejected construction snapshots leave the target untouched") var legacy := snapshot.duplicate(true) legacy[&"schema_version"] = source.LEGACY_SNAPSHOT_VERSION legacy.erase(&"quarantined") legacy.erase(&"quarantine_reason") for saved_job: Dictionary in legacy[&"jobs"]: saved_job.erase(&"last_progress_at") _expect(restored.restore_snapshot(legacy), "legacy v1 construction snapshot migrates through its exact old shape") _expect(restored.create_snapshot()[&"schema_version"] == source.SNAPSHOT_VERSION, "legacy construction state is re-emitted as strict v2") _expect(restored.progress_job(job_id, ConstructionJobType.NORMAL_WORK_RATE, 701).is_replay, "v1 migration conservatively prevents replaying prior work") func _test_definition_type_spoof_and_reentry() -> void: var world := FakeWorld.new() var resources := FakeResourceState.new() var service := ConstructionServiceType.new(world, resources) var spoofed := { &"id": &"spoofed", &"footprint": Vector2i.ONE, &"zone": FacilityDefinitionType.MapZone.ANY, &"layer": FacilityDefinitionType.ViewLayer.FRONT, &"construction_cost": {&"gold": "100", &"materials": 10}, &"build_minutes": 60, } _expect(not service.validate_placement(spoofed, Vector3i(1, 1, 0)).can_build, "numeric-string definition costs are rejected instead of normalized") resources.reentrant_service = service var started = service.start_construction(_valid_definition(), Vector3i(1, 1, 0)) _expect(started.is_success() and resources.reentrant_attempted, "resource callback attempts a nested construction command") _expect(not resources.reentrant_succeeded and service.get_jobs().size() == 1, "reentrant construction mutation cannot create or charge a second job") resources.reentrant_service = null func _test_failed_rollback_quarantines_service() -> void: var world := FakeWorld.new() var resources := FakeResourceState.new() resources.mutate_then_fail_spend = true resources.fail_restore = true var service := ConstructionServiceType.new(world, resources) var failed = service.start_construction(_valid_definition(), Vector3i(1, 1, 0)) _expect(not failed.is_success() and failed.details[&"rollback_failed"], "failed construction payment reports an unrecoverable rollback") _expect(service.is_quarantined(), "unrecoverable construction rollback quarantines the service") _expect(not service.start_construction(_valid_definition(), Vector3i(2, 2, 0)).is_success(), "quarantined construction service refuses later mutations") func _test_real_world_service_integration() -> void: var world = WorldServiceType.new() world.reset({&"min_x": 0, &"max_x": 5, &"min_y": 0, &"max_y": 5}) world.set_support(Vector3i(1, 1, 0), 0, true) var state := FakeResourceState.new() var service := ConstructionServiceType.new(world, state) var definition := _definition( &"rotated_room", Vector2i(2, 1), FacilityDefinitionType.MapZone.ABOVE_GROUND, FacilityDefinitionType.ViewLayer.FRONT, {&"gold": 100, &"materials": 10} ) var expected_cells: Array[Vector3i] = [Vector3i(1, 1, 0), Vector3i(1, 2, 0)] var report = service.validate_placement(definition, Vector3i(1, 1, 0), 90) _expect(report.can_build, "real WorldService accepts enum-to-StringName zone matching") _expect(report.cells == expected_cells, "90-degree placement swaps rectangular footprint axes") var report_270 = service.validate_placement(definition, Vector3i(1, 1, 0), 270) _expect(report_270.cells == expected_cells, "270-degree placement swaps rectangular footprint axes") var started = service.start_construction(definition, Vector3i(1, 1, 0), 90, 4) _expect(started.is_success(), "real WorldService occupancy signature starts construction") var job_id: StringName = started.details[&"job_id"] _expect(world.get_entity_cells(job_id) == expected_cells, "real WorldService stores the complete rotated reservation") _expect(service.cancel_job(job_id).is_success(), "real WorldService release signature cancels construction") _expect(world.get_entity_cells(job_id).is_empty(), "real WorldService release removes the reservation") var restarted = service.start_construction(definition, Vector3i(1, 1, 0), 270, 1) var restarted_job_id: StringName = restarted.details[&"job_id"] var completed = service.progress_job(restarted_job_id, ConstructionJobType.NORMAL_WORK_RATE, 900) _expect(completed.is_success(), "real WorldService completes a construction reservation") var instance_id: StringName = completed.details[&"instance_id"] _expect(world.get_entity_cells(instance_id) == expected_cells, "completion transfers real world occupancy to the facility") _expect(service.demolish_facility(instance_id).is_success(), "real WorldService facility can be demolished") _expect(world.get_entity_cells(instance_id).is_empty(), "real WorldService demolition releases facility occupancy") func _test_underground_requires_excavation() -> void: var world = WorldServiceType.new() world.reset({&"min_x": 0, &"max_x": 3, &"min_y": -2, &"max_y": 0}) world.set_support(Vector3i(0, -1, 0), 0, true) var state := FakeResourceState.new() var service := ConstructionServiceType.new(world, state) var definition := _definition( &"underground_room", Vector2i(2, 1), FacilityDefinitionType.MapZone.UNDERGROUND, FacilityDefinitionType.ViewLayer.FRONT, {&"gold": 100, &"materials": 10} ) var blocked = service.validate_placement(definition, Vector3i(0, -1, 0)) _expect(not blocked.can_build, "underground facility cannot occupy solid earth") _expect(blocked.has_error(PlacementReportType.ERROR_NOT_EXCAVATED), "placement report identifies unexcavated footprint cells") _expect(blocked.unexcavated_cells == [Vector3i(0, -1, 0), Vector3i(1, -1, 0)], "every blocked underground footprint cell is reported") world.set_excavated(Vector3i(0, -1, 0), true) var half_open = service.validate_placement(definition, Vector3i(0, -1, 0)) _expect(not half_open.can_build and half_open.unexcavated_cells == [Vector3i(1, -1, 0)], "partial excavation is still insufficient") world.set_excavated(Vector3i(1, -1, 1), true) var ready = service.validate_placement(definition, Vector3i(0, -1, 0)) _expect(ready.can_build, "fully excavated spatial cells permit underground construction on either layer") func _test_every_footprint_cell_must_match_zone() -> void: var world = WorldServiceType.new() world.reset({&"min_x": 0, &"max_x": 2, &"min_y": 0, &"max_y": 2}) world.set_support(Vector3i(0, 0, 0), 0, true) var service := ConstructionServiceType.new(world, FakeResourceState.new()) var definition := _definition( &"above_ground_tower", Vector2i(1, 2), FacilityDefinitionType.MapZone.ABOVE_GROUND, FacilityDefinitionType.ViewLayer.FRONT, {&"gold": 100, &"materials": 10} ) var crossing = service.validate_placement(definition, Vector3i(0, 0, 0)) _expect(not crossing.can_build, "a footprint cannot straddle the boundary and above-ground zones") _expect(crossing.zone_mismatch_cells == [Vector3i(0, 0, 0)], "zone validation reports the exact mismatching footprint cell") var valid = service.validate_placement(definition, Vector3i(0, 1, 0)) _expect(valid.can_build, "the same full footprint is valid when every cell matches its zone") func _test_boundary_facility_anchor_rule() -> void: var world := FakeWorld.new() world.max_x = 8 var service := ConstructionServiceType.new(world, FakeResourceState.new()) var reception := _definition( &"boundary_reception", Vector2i(4, 2), FacilityDefinitionType.MapZone.BOUNDARY, FacilityDefinitionType.ViewLayer.FRONT, {&"gold": 100, &"materials": 10} ) var anchored = service.validate_placement(reception, Vector3i(0, 0, 0)) _expect(anchored.can_build, "boundary reception anchored at y=0 may extend into above-ground cells") var above_anchor = service.validate_placement(reception, Vector3i(0, 1, 0)) _expect(not above_anchor.can_build and above_anchor.has_error(PlacementReportType.ERROR_ZONE_MISMATCH), "boundary reception rejects an above-ground y=1 anchor") var below_anchor = service.validate_placement(reception, Vector3i(0, -1, 0)) _expect(not below_anchor.can_build and below_anchor.has_error(PlacementReportType.ERROR_ZONE_MISMATCH), "boundary reception rejects an underground y=-1 anchor") world.forced_cells = [ Vector3i(0, -1, 0), Vector3i(1, -1, 0), Vector3i(2, -1, 0), Vector3i(3, -1, 0), Vector3i(0, 0, 0), Vector3i(1, 0, 0), Vector3i(2, 0, 0), Vector3i(3, 0, 0), ] var underground_spill = service.validate_placement(reception, Vector3i(0, 0, 0)) _expect(not underground_spill.can_build, "boundary footprint may not spill below y=0 even with a valid anchor") _expect(underground_spill.zone_mismatch_cells.has(Vector3i(0, -1, 0)), "boundary spill reports the exact underground cells") func _definition( id: StringName, footprint: Vector2i, zone: int, layer: int, cost: Dictionary ) -> FacilityDefinition: var definition := FacilityDefinitionType.new() definition.id = id definition.display_name = String(id) definition.footprint = footprint definition.zone = zone definition.layer = layer definition.construction_cost = cost.duplicate(true) return definition func _valid_definition() -> FacilityDefinition: return _definition( &"test_room", Vector2i(1, 1), FacilityDefinitionType.MapZone.ANY, FacilityDefinitionType.ViewLayer.FRONT, {&"gold": 100, &"materials": 10} ) func _expect(condition: bool, label: String) -> void: _checks += 1 if condition: print(" ✓ %s" % label) return _failures += 1 push_error(" ✗ %s" % label)