Files
TheTree_mock/tests/s5/test_runtime_authority_cycle.gd

543 lines
18 KiB
GDScript

extends SceneTree
const PLAYER_CASH := &"PLAYER_CASH"
const AUTHORITY_FINE_EXPENSE := &"AUTHORITY_FINE_EXPENSE"
var _checks := 0
var _failures := 0
var _clock: Variant
var _state: Variant
var _runtime: Variant
func _initialize() -> void:
call_deferred("_run")
func _run() -> void:
print("S5: integrated Authority runtime cycle")
_test_incident_visibility_and_daily_evaluation()
_test_resolution_replay_unreachable_exit_and_long_horizon()
_cleanup()
if _failures == 0:
print("PASS: %d checks" % _checks)
else:
push_error("FAIL: %d of %d checks failed" % [_failures, _checks])
quit(_failures)
func _reset_runtime(seed: int, hour := 6, minute := 0) -> void:
_cleanup()
_clock = load("res://autoload/game_clock.gd").new()
_state = load("res://autoload/game_state.gd").new()
_runtime = load("res://autoload/simulation_runtime.gd").new()
_clock.reset(1, hour, minute)
_state.reset()
var report: Dictionary = _runtime.initialize(_clock, _state, seed)
_clock.set_paused(true)
_expect(report[&"valid"], "runtime initializes with the Authority aggregate")
_expect(
_runtime.authority_service.current_minute == _clock.absolute_minute,
"Authority time begins aligned with SimulationKernel"
)
_expect(
_runtime.economy_service.has_account(AUTHORITY_FINE_EXPENSE),
"Authority fine expense account exists at initialization"
)
func _cleanup() -> void:
if _runtime != null:
_runtime.free()
if _clock != null:
_clock.free()
if _state != null:
_state.free()
_runtime = null
_clock = null
_state = null
func _test_incident_visibility_and_daily_evaluation() -> void:
_reset_runtime(5_501, 5, 59)
var reported := _submit(
&"report_authority_incident",
{
&"incident_id": &"incident.runtime.shared",
&"incident_type": &"illegal_facility",
&"detector_id": &"detector.noise",
&"options": {
&"severity": 80,
&"heat_delta": 100,
&"facility_id": &"facility.hidden",
},
},
&"s5.incident.first"
)
var duplicate := _submit(
&"report_authority_incident",
{
&"incident_id": &"incident.runtime.shared",
&"incident_type": &"conflicting_payload",
&"detector_id": &"detector.witness",
&"options": {&"severity": 100, &"heat_delta": 100},
},
&"s5.incident.second-detector"
)
var incident: AuthorityIncident = _runtime.authority_service.get_incident(&"incident.runtime.shared")
_expect(reported.is_success(), "incident report executes through Runtime Command")
_expect(
duplicate.is_success() and duplicate.is_replay,
"incident_id deduplicates reports from independent detectors"
)
_expect(
_runtime.authority_service.get_authority_state().heat == 100,
"duplicate detector cannot apply Heat twice"
)
_expect(
incident.detector_ids == [&"detector.noise", &"detector.witness"]
and incident.incident_type == &"illegal_facility",
"dedup retains sorted detector audit data and the first canonical payload"
)
_expect(
_count_domain_events(&"INCIDENT_CREATED", incident.incident_id) == 1
and _count_domain_events(&"INCIDENT_DETECTOR_ADDED", incident.incident_id) == 1,
"Authority signals project once into DomainEventStream"
)
var wall := Vector3i(1, 0, WorldService.LAYER_FRONT)
var evidence_cell := Vector3i(2, 0, WorldService.LAYER_FRONT)
_expect(_runtime.world_service.set_walkable(wall, false), "LOS fixture wall is installed")
_expect(_runtime.rebuild_path_graph_from_world(), "Path graph rebuilds after wall installation")
var registered := _submit(
&"register_authority_evidence",
{
&"evidence_id": &"evidence.runtime.wall",
&"source": &"visual_trace",
&"severity": 35,
&"options": {
&"incident_id": incident.incident_id,
&"facility_id": &"facility.hidden",
&"location_cell": evidence_cell,
&"expires_at": 2_000,
},
},
&"s5.evidence.register"
)
var blocked := _submit(
&"discover_authority_evidence",
{
&"evidence_id": &"evidence.runtime.wall",
&"inspector_id": &"inspector.los",
&"inspector_cell": Vector3i(0, 0, WorldService.LAYER_FRONT),
&"context": {},
},
&"s5.evidence.blocked"
)
_expect(registered.is_success(), "latent evidence registers through Runtime Command")
_expect(
not blocked.is_success() and blocked.details.get(&"reason", &"") == &"LINE_OF_SIGHT_BLOCKED",
"WorldService wall blocks automatic evidence discovery"
)
_expect(
_runtime.authority_service.get_authority_state().evidence_points == 0,
"wall-blocked evidence contributes no Authority score"
)
_expect(_runtime.world_service.set_walkable(wall, true), "LOS fixture wall is removed")
_expect(_runtime.rebuild_path_graph_from_world(), "Path graph rebuilds after opening sight line")
var discovered := _submit(
&"discover_authority_evidence",
{
&"evidence_id": &"evidence.runtime.wall",
&"inspector_id": &"inspector.los",
&"inspector_cell": Vector3i(0, 0, WorldService.LAYER_FRONT),
&"context": {&"illegal_confirmed": true},
},
&"s5.evidence.visible"
)
_expect(
discovered.is_success() and discovered.details[&"discovery_method"] == &"LINE_OF_SIGHT",
"evidence becomes visible only after the World callback confirms sight"
)
_expect(
_runtime.authority_service.get_authority_state().evidence_points == 35,
"visible evidence contributes its deterministic Incident score"
)
var authority_rng: DeterministicRng = _runtime.get_rng_stream(&"authority")
var expected_rng := DeterministicRng.from_root_seed(0, &"authority") as DeterministicRng
_expect(expected_rng.restore(authority_rng.snapshot()), "Authority RNG snapshot clones for prediction")
var expected_roll := expected_rng.randi_range(0, 99)
var before_six: Dictionary = _runtime.create_snapshot()
_clock.advance_one_minute()
var evaluation := _evaluation_for_day(0)
_expect(
_clock.absolute_minute == 360 and _runtime.authority_service.current_minute == 360,
"INCIDENTS_AND_EVENTS advances Authority time at 06:00"
)
_expect(
int(evaluation.get(&"roll", -1)) == expected_roll
and bool(evaluation.get(&"selected", false)) == (expected_roll < int(evaluation.get(&"chance", 0))),
"06:00 inspection decision consumes only the deterministic Authority RNG stream"
)
_expect(
int(evaluation.get(&"chance", -1)) == 95
and not StringName(evaluation.get(&"inspection_id", &"")).is_empty(),
"Heat and active-case terms deterministically schedule the selected inspection"
)
_expect(
_runtime.get_rng_stream(&"authority").get_counter() == expected_rng.get_counter(),
"06:00 evaluation advances the Authority RNG counter exactly once"
)
_expect(
_count_domain_events(&"AUTHORITY_INSPECTION_EVALUATED") == 1
and _count_domain_events(&"INSPECTION_SCHEDULED") == 1,
"automatic inspection selection emits its Domain Events once"
)
var first_six_snapshot: Dictionary = _runtime.create_snapshot()
_expect(_runtime.restore_snapshot(before_six), "pre-06:00 Runtime snapshot restores atomically")
_clock.advance_one_minute()
_expect(
_runtime.create_snapshot() == first_six_snapshot,
"restored 06:00 Authority selection reproduces the full Runtime snapshot"
)
var stable_before_corruption: Dictionary = _runtime.create_snapshot()
var corrupt := stable_before_corruption.duplicate(true)
corrupt[&"systems"][&"runtime"][&"authority"][&"current_minute"] = 359
_expect(
not _runtime.restore_snapshot(corrupt),
"restore rejects an Authority/SimulationKernel minute mismatch"
)
_expect(
_runtime.create_snapshot() == stable_before_corruption,
"rejected Authority restore leaves the live Runtime snapshot unchanged"
)
_expect(
_runtime.authority_service.validate_invariants()[&"ok"],
"integrated incident, evidence and inspection selection preserve Authority invariants"
)
func _test_resolution_replay_unreachable_exit_and_long_horizon() -> void:
_reset_runtime(5_502)
_expect(_submit(
&"report_authority_incident",
{
&"incident_id": &"incident.runtime.fine",
&"incident_type": &"contraband",
&"detector_id": &"detector.audit",
&"options": {&"severity": 80, &"heat_delta": 20},
},
&"s5.fine.incident"
).is_success(), "fine fixture Incident registers")
_expect(_submit(
&"register_authority_evidence",
{
&"evidence_id": &"evidence.runtime.fine",
&"source": &"physical_record",
&"severity": 60,
&"options": {
&"incident_id": &"incident.runtime.fine",
&"facility_id": &"facility.illegal",
&"location_cell": Vector3i(2, 0, WorldService.LAYER_FRONT),
},
},
&"s5.fine.evidence"
).is_success(), "fine fixture Evidence registers")
_expect(_submit(
&"schedule_authority_inspection",
{&"options": {
&"inspection_id": &"inspection.runtime.fine",
&"inspector_id": &"inspector.runtime.fine",
&"entrance_cell": Vector3i(0, 0, WorldService.LAYER_FRONT),
}},
&"s5.fine.schedule"
).is_success(), "inspection schedules through Runtime Command")
_expect(_submit(
&"start_authority_inspection",
{&"inspection_id": &"inspection.runtime.fine"},
&"s5.fine.start"
).is_success(), "inspection starts through Runtime Command")
_expect(_submit(
&"arrive_authority_inspection",
{&"inspection_id": &"inspection.runtime.fine"},
&"s5.fine.arrive"
).is_success(), "inspector checks in through Runtime Command")
_expect(_submit(
&"plan_authority_inspection",
{
&"inspection_id": &"inspection.runtime.fine",
&"candidates": [{
&"waypoint_id": &"waypoint.illegal.room",
&"cell": Vector3i(2, 0, WorldService.LAYER_FRONT),
&"suspect_score": 100,
&"evidence_potential": 100,
&"travel_minutes": 2,
&"inspection_minutes": 3,
}],
&"time_budget_minutes": 30,
},
&"s5.fine.plan"
).is_success(), "deterministic inspection patrol is planned")
var moved := _submit(
&"advance_authority_inspection",
{&"inspection_id": &"inspection.runtime.fine", &"context": {}},
&"s5.fine.advance"
)
_expect(
moved.is_success() and moved.details.get(&"reachable", false),
"PathService callback advances the inspector to one reachable waypoint"
)
var mid_inspection: Dictionary = _runtime.create_snapshot()
var mid_digest := SimulationDigest.compute(mid_inspection)
var first_final := _run_resolution_timeline()
_expect(first_final.get(&"ok", false), "inspection resolution timeline completes")
var first_final_snapshot: Dictionary = _runtime.create_snapshot()
var first_final_digest := SimulationDigest.compute(first_final_snapshot)
_expect(_runtime.restore_snapshot(mid_inspection), "mid-inspection Runtime snapshot restores")
_expect(
SimulationDigest.compute(_runtime.create_snapshot()) == mid_digest,
"mid-inspection restore reproduces its exact digest"
)
var replay_final := _run_resolution_timeline()
_expect(replay_final.get(&"ok", false), "restored inspection timeline completes again")
_expect(
SimulationDigest.compute(_runtime.create_snapshot()) == first_final_digest
and _runtime.create_snapshot() == first_final_snapshot,
"mid-inspection replay reproduces Authority, ledger and Domain Events exactly"
)
_expect(_run_unreachable_inspection(), "unreachable patrol target follows the safe exit path")
_expect(
_runtime.authority_service.validate_invariants()[&"ok"]
and _runtime.economy_service.validate_ledger()[&"ok"],
"resolved and unreachable inspections preserve Authority and ledger invariants"
)
var horizon_snapshot: Dictionary = _runtime.create_snapshot()
_clock.advance_minutes(1_440)
var first_horizon_digest: String = _runtime.latest_digest
var first_horizon_snapshot: Dictionary = _runtime.create_snapshot()
_expect(
_runtime.authority_service.current_minute == _clock.absolute_minute,
"Authority stays aligned through a 24-hour simulation horizon"
)
_expect(_runtime.restore_snapshot(horizon_snapshot), "24-hour Authority replay origin restores")
_clock.advance_minutes(1_440)
_expect(
_runtime.latest_digest == first_horizon_digest
and _runtime.create_snapshot() == first_horizon_snapshot,
"24-hour Runtime replay reproduces daily Heat and inspection evaluation exactly"
)
_expect(
_runtime.authority_service.validate_invariants()[&"ok"]
and _runtime.economy_service.validate_ledger()[&"ok"],
"long replay ends with valid Authority and Economy aggregates"
)
func _run_resolution_timeline() -> Dictionary:
var inspected := _submit(
&"inspect_authority_waypoint",
{
&"inspection_id": &"inspection.runtime.fine",
&"evidence_ids": [&"evidence.runtime.fine"],
&"context": {&"illegal_confirmed": true},
},
&"s5.fine.inspect"
)
var reviewed := _submit(
&"advance_authority_inspection",
{&"inspection_id": &"inspection.runtime.fine", &"context": {}},
&"s5.fine.review"
)
var authority_before_failure: Dictionary = _runtime.authority_service.create_snapshot()
var economy_before_failure: Dictionary = _runtime.economy_service.create_snapshot()
var rejected := _submit(
&"resolve_authority_inspection",
{
&"inspection_id": &"inspection.runtime.fine",
&"resolution_id": &"resolution.runtime.unfunded",
&"options": {&"fine_per_point": 100},
},
&"s5.fine.resolve.unfunded"
)
var rejection_atomic: bool = not rejected.is_success() \
and _runtime.authority_service.create_snapshot() == authority_before_failure \
and _runtime.economy_service.create_snapshot() == economy_before_failure
var resolved := _submit(
&"resolve_authority_inspection",
{
&"inspection_id": &"inspection.runtime.fine",
&"resolution_id": &"resolution.runtime.once",
&"options": {&"fine_per_point": 10},
},
&"s5.fine.resolve.once"
)
var duplicate := _submit(
&"resolve_authority_inspection",
{
&"inspection_id": &"inspection.runtime.fine",
&"resolution_id": &"resolution.runtime.once",
&"options": {&"fine_per_point": 999},
},
&"s5.fine.resolve.retry"
)
var departed := _submit(
&"depart_authority_inspection",
{&"inspection_id": &"inspection.runtime.fine"},
&"s5.fine.depart"
)
var fine_entries: Array = _runtime.economy_service.get_entries().filter(
func(entry: LedgerEntry) -> bool: return entry.category == &"AUTHORITY_FINE"
)
var incident: AuthorityIncident = _runtime.authority_service.get_incident(&"incident.runtime.fine")
var inspection: AuthorityInspection = _runtime.authority_service.get_inspection(&"inspection.runtime.fine")
_expect(inspected.is_success(), "reached waypoint discovers only the supplied Evidence IDs")
_expect(
reviewed.is_success()
and reviewed.details.get(&"new_state", &"") == &"REVIEWING"
and inspection != null,
"completed patrol enters REVIEWING before resolution"
)
_expect(rejection_atomic, "unfunded Authority fine leaves Authority and Economy snapshots unchanged")
_expect(
resolved.is_success() and resolved.details.get(&"fine_amount", -1) == 600
and resolved.details.get(&"sanction", &"") == &"TEMPORARY_CLOSURE",
"evidence score projects the deterministic fine and sanction"
)
_expect(
duplicate.is_success() and duplicate.is_replay
and duplicate.details.get(&"fine_amount", -1) == 600,
"duplicate resolution_id replays the canonical outcome"
)
_expect(
fine_entries.size() == 1
and _runtime.economy_service.get_balance(AUTHORITY_FINE_EXPENSE) == 600
and _runtime.economy_service.get_balance(PLAYER_CASH) == 4_400
and _state.get_resource(&"gold") == 4_400,
"fine posts exactly once to the ledger and synchronized Gold mirror"
)
_expect(
incident != null and incident.state == AuthorityIncident.State.RESOLVED
and departed.is_success() and inspection.state == AuthorityInspection.State.CLOSED,
"successful resolution closes its Incident and inspection lifecycle"
)
_expect(
_count_domain_events(&"INSPECTION_RESOLVED", &"inspection.runtime.fine") == 1,
"resolution replay emits no duplicate INSPECTION_RESOLVED event"
)
return {
&"ok": inspected.is_success()
and reviewed.is_success()
and rejection_atomic
and resolved.is_success()
and duplicate.is_success()
and departed.is_success(),
}
func _run_unreachable_inspection() -> bool:
var inspection_id := &"inspection.runtime.blocked"
var target := Vector3i(0, 1, WorldService.LAYER_FRONT)
var scheduled := _submit(
&"schedule_authority_inspection",
{&"options": {
&"inspection_id": inspection_id,
&"inspector_id": &"inspector.runtime.blocked",
&"entrance_cell": Vector3i(0, 0, WorldService.LAYER_FRONT),
}},
&"s5.blocked.schedule"
)
var started := _submit(
&"start_authority_inspection",
{&"inspection_id": inspection_id},
&"s5.blocked.start"
)
var arrived := _submit(
&"arrive_authority_inspection",
{&"inspection_id": inspection_id},
&"s5.blocked.arrive"
)
var planned := _submit(
&"plan_authority_inspection",
{
&"inspection_id": inspection_id,
&"candidates": [{
&"waypoint_id": &"waypoint.sealed",
&"cell": target,
&"suspect_score": 90,
&"evidence_potential": 90,
&"travel_minutes": 5,
&"inspection_minutes": 5,
}],
},
&"s5.blocked.plan"
)
var blocked := _submit(
&"advance_authority_inspection",
{&"inspection_id": inspection_id, &"context": {}},
&"s5.blocked.advance"
)
var inspection: AuthorityInspection = _runtime.authority_service.get_inspection(inspection_id)
var safe_departure: bool = blocked.is_success() \
and not bool(blocked.details.get(&"reachable", true)) \
and inspection != null \
and inspection.state == AuthorityInspection.State.DEPARTING \
and inspection.exit_reason == &"PATH_UNREACHABLE" \
and inspection.visited_waypoint_ids.is_empty() \
and inspection.inaccessible_waypoint_ids == [&"waypoint.sealed"] \
and not inspection.result_reason.is_empty() \
and _count_domain_events(&"INSPECTION_PATH_BLOCKED", inspection_id) == 1
var departed := _submit(
&"depart_authority_inspection",
{&"inspection_id": inspection_id},
&"s5.blocked.depart"
)
return scheduled.is_success() \
and started.is_success() \
and arrived.is_success() \
and planned.is_success() \
and safe_departure \
and departed.is_success() \
and inspection.state == AuthorityInspection.State.CLOSED
func _submit(command_type: StringName, payload: Dictionary, idempotency_key: StringName) -> CommandResult:
return _runtime.submit_command(
command_type,
payload,
&"authority_controller",
&"",
80,
-1,
idempotency_key
)
func _evaluation_for_day(day_index: int) -> Dictionary:
var snapshot: Dictionary = _runtime.authority_service.create_snapshot()
for value: Variant in snapshot[&"evaluated_inspection_days"]:
if value is Dictionary and int(value.get(&"day_index", -1)) == day_index:
return (value[&"result"] as Dictionary).duplicate(true)
return {}
func _count_domain_events(event_type: StringName, entity_id: StringName = &"") -> int:
var count := 0
for event: SimulationDomainEvent in _runtime.domain_events.recent_events():
if event.event_type != event_type:
continue
if entity_id.is_empty() or entity_id in event.entity_ids:
count += 1
return count
func _expect(condition: bool, label: String) -> void:
_checks += 1
if condition:
print(" ✓ %s" % label)
return
_failures += 1
push_error(" ✗ %s" % label)