Save The Tree implementation and acceptance work in progress
Preserve production code, regression fixtures, audio assets and provenance, test infrastructure, and the original-completion plan and progress ledger. Final game and Windows acceptance remain pending; excluded local artifacts and native experiments are not part of this snapshot.
This commit is contained in:
@@ -27,6 +27,8 @@ func _run() -> void:
|
||||
_test_missing_actor_releases_lock_and_cost()
|
||||
_test_content_replacement_boundary()
|
||||
_test_transaction_policies()
|
||||
_test_failed_rollback_is_fatal()
|
||||
_test_failed_cost_release_retains_tokens()
|
||||
_test_event_snapshot_round_trip_and_atomicity()
|
||||
_test_event_recovery_lock_scope_and_strict_filter_parse()
|
||||
if _failures == 0:
|
||||
@@ -439,3 +441,135 @@ func _expect(condition: bool, label: String) -> void:
|
||||
else:
|
||||
_failures += 1
|
||||
push_error(" not ok %s" % label)
|
||||
|
||||
|
||||
func _test_failed_rollback_is_fatal() -> void:
|
||||
var cases: Array[Dictionary] = [
|
||||
{&"name":&"rollback",&"policy":EventDefinitionType.TransactionPolicy.ROLLBACK_ALL},
|
||||
{&"name":&"branch",&"policy":EventDefinitionType.TransactionPolicy.BRANCH_FAILURE},
|
||||
{&"name":&"automatic",&"policy":EventDefinitionType.TransactionPolicy.BRANCH_FAILURE,&"auto":true},
|
||||
{&"name":&"commit",&"policy":EventDefinitionType.TransactionPolicy.ROLLBACK_ALL,&"commit":true},
|
||||
{&"name":&"skip_self",&"policy":EventDefinitionType.TransactionPolicy.SKIP_FAILED,&"self":true},
|
||||
{&"name":&"rollback_self",&"policy":EventDefinitionType.TransactionPolicy.ROLLBACK_ALL,&"self":true},
|
||||
{&"name":&"branch_self",&"policy":EventDefinitionType.TransactionPolicy.BRANCH_FAILURE,&"self":true},
|
||||
]
|
||||
for row: Dictionary in cases:
|
||||
var calls: Array[StringName] = []
|
||||
var participant := {&"value":0,&"branch":0,&"released":0}
|
||||
var service := EventServiceType.new().configure_callbacks(
|
||||
Callable(),
|
||||
func(action: EventAction, phase: StringName, _context: Dictionary, token: Variant) -> Dictionary:
|
||||
if phase == &"validate": return {&"ok":true}
|
||||
if phase == &"rollback":
|
||||
calls.append(action.action_id)
|
||||
if action.action_id == &"second": return {&"ok":false,&"error":"explicit participant refusal"}
|
||||
participant[&"value"] = int(token)
|
||||
return {&"ok":true}
|
||||
if action.action_id == &"fallback": participant[&"branch"] += 1
|
||||
if bool(action.payload.get(&"fail",false)):
|
||||
if row.get(&"self",false): participant[&"value"] += 7
|
||||
return {&"ok":false,&"fatal":bool(row.get(&"self",false)),&"error":"explicit failed apply"}
|
||||
var previous: int = participant[&"value"]
|
||||
participant[&"value"] += int(action.payload.get(&"delta",0))
|
||||
return {&"ok":true,&"token":previous},
|
||||
func(_cost: Dictionary, _context: Dictionary) -> Dictionary: return {&"ok":true,&"token":&"reserved"},
|
||||
func(_token: Variant, _context: Dictionary) -> Dictionary:
|
||||
participant[&"released"] += 1
|
||||
return {&"ok":true},
|
||||
func(_token: Variant, _context: Dictionary) -> Dictionary: return {&"ok":not bool(row.get(&"commit",false)),&"error":"explicit commit refusal"}
|
||||
)
|
||||
var event_id := StringName("failed.rollback."+String(row[&"name"]))
|
||||
var actions: Array = [_delta_action(&"first",2),_delta_action(&"second",3)]
|
||||
if not row.get(&"commit",false): actions.append(_failing_action(&"fail"))
|
||||
var definition := _choice_definition(event_id,10,row[&"policy"],actions,{&"gold":1})
|
||||
definition.failure_actions = [_delta_action(&"fallback",4)]
|
||||
if row.get(&"auto",false):
|
||||
definition.foreground = false
|
||||
definition.choices = []
|
||||
definition.automatic_actions.assign(actions)
|
||||
_expect(service.register_definition(definition).is_success(),"rollback refusal definition registers")
|
||||
var instance_id := StringName(String(event_id)+".instance")
|
||||
var result: CommandResult
|
||||
if row.get(&"auto",false):
|
||||
_expect(service.enqueue_candidate(event_id,[],0,{&"event_instance_id":instance_id}).is_success(),"automatic rollback refusal enqueue")
|
||||
result = service.start_next_event(0)
|
||||
else: result = _run_choice(service,event_id,instance_id)
|
||||
_expect(not result.is_success() and bool(result.details.get(&"fatal",false)),"refused rollback or failed self restoration propagates fatal outcome")
|
||||
_expect(service.get_instance(instance_id).state == EventInstanceType.State.ERROR and service.pause_requested,"fatal event stops at ERROR with pause requested")
|
||||
_expect(participant[&"branch"] == 0,"no fallback executes on unverified rollback")
|
||||
if row[&"policy"] == EventDefinitionType.TransactionPolicy.SKIP_FAILED:
|
||||
_expect(calls.is_empty() and participant[&"value"] == 12,"skip policy stops on fatal self restoration without further actions")
|
||||
else:
|
||||
_expect(calls == [&"second",&"first"] and participant[&"value"] == 0,"all prior restorations attempted in reverse order despite refusal")
|
||||
if not row.get(&"auto",false):
|
||||
_expect(participant[&"released"] == 1,"fatal choice releases reservation once")
|
||||
var instance: EventInstance = service.get_instance(instance_id)
|
||||
var before: Dictionary = service.create_snapshot()
|
||||
var replay := service.submit_choice(instance_id,instance.choice_instance_id,&"accept",0)
|
||||
_expect(replay.is_replay and not replay.is_success() and bool(replay.details.get(&"fatal",false)),"fatal outcome is cached for choice replay")
|
||||
_expect(var_to_bytes(before) == var_to_bytes(service.create_snapshot()) and participant[&"released"] == 1,"fatal replay does not mutate event state or release twice")
|
||||
_expect(service.validate_invariants()[&"ok"],"fatal event lock and queue invariants remain valid")
|
||||
|
||||
|
||||
func _test_failed_cost_release_retains_tokens() -> void:
|
||||
for mode: StringName in [&"action", &"commit", &"cancel", &"missing", &"finish", &"no_releaser"]:
|
||||
var calls: Array[StringName] = []
|
||||
var state := {&"value": 0, &"available": true, &"refuse": true}
|
||||
var service := EventServiceType.new().configure_callbacks(
|
||||
func(_actor: StringName) -> bool: return state[&"available"],
|
||||
func(action: EventAction, phase: StringName, _context: Dictionary, token: Variant) -> Dictionary:
|
||||
if phase == &"apply":
|
||||
if bool(action.payload.get(&"fail", false)): return {&"ok": false, &"error": "explicit apply refusal"}
|
||||
var before := int(state[&"value"])
|
||||
state[&"value"] += int(action.payload.get(&"delta", 0))
|
||||
return {&"ok": true, &"token": before}
|
||||
if phase == &"rollback": state[&"value"] = int(token)
|
||||
return {&"ok": true},
|
||||
func(cost: Dictionary, _context: Dictionary) -> Dictionary: return {&"ok": true, &"token": cost.duplicate(true)},
|
||||
func(token: Variant, _context: Dictionary) -> Dictionary:
|
||||
calls.append(StringName(str(token.get(&"gold", 0))))
|
||||
return {&"ok": not state[&"refuse"], &"error": "explicit release refusal"},
|
||||
func(_token: Variant, _context: Dictionary) -> Dictionary: return {&"ok": mode != &"commit", &"error": "explicit commit refusal"}
|
||||
)
|
||||
var actions: Array = [_delta_action(&"primary", 4)]
|
||||
if mode in [&"action", &"no_releaser"]: actions.append(_failing_action(&"fail"))
|
||||
var definition := _choice_definition(StringName("release_" + mode), 10, EventDefinitionType.TransactionPolicy.ROLLBACK_ALL, actions, {&"gold": 100})
|
||||
definition.choices.append(EventChoiceType.new().configure(&"other", &"other", [], {&"gold": 200}, {}, []))
|
||||
_expect(service.register_definition(definition).is_success(), "%s release definition registers" % mode)
|
||||
service.enqueue_candidate(definition.event_id, [&"actor"], 0, {&"event_instance_id": &"release_instance"})
|
||||
var started := service.start_next_event(0)
|
||||
var instance := service.get_instance(&"release_instance")
|
||||
_expect(service.reserve_choice_cost(instance.event_instance_id, &"accept", 0).is_success(), "%s actual reservation created" % mode)
|
||||
if mode in [&"action", &"commit", &"cancel", &"missing", &"finish"]:
|
||||
_expect(service.reserve_choice_cost(instance.event_instance_id, &"other", 0).is_success(), "%s second reservation created" % mode)
|
||||
if mode == &"no_releaser": service._cost_releaser = Callable()
|
||||
var result: CommandResult
|
||||
if mode == &"cancel":
|
||||
result = service.cancel_event(instance.event_instance_id)
|
||||
elif mode == &"missing":
|
||||
state[&"available"] = false
|
||||
service.audit_actor_availability(1)
|
||||
else:
|
||||
result = service.submit_choice(instance.event_instance_id, started.details[&"choice_instance_id"], &"accept", 0)
|
||||
if result != null:
|
||||
_expect(not result.is_success() and result.details.get(&"fatal", false), "%s release failure cannot report success" % mode)
|
||||
_expect(instance.state == EventInstanceType.State.ERROR and service.pause_requested, "%s release failure stops as ERROR" % mode)
|
||||
_expect(service.get_actor_locks().is_empty() and service.get_foreground_instance() == null, "%s releases event ownership" % mode)
|
||||
var expected_ids: Array[StringName] = [&"accept"]
|
||||
if mode in [&"action", &"commit", &"cancel", &"missing"]: expected_ids.append(&"other")
|
||||
if mode == &"finish": expected_ids = [&"other"]
|
||||
_expect(instance.reserved_costs.keys().size() == expected_ids.size(), "%s retains every refused token only" % mode)
|
||||
for id: StringName in expected_ids:
|
||||
_expect(instance.reserved_costs.has(id), "%s retains token %s" % [mode, id])
|
||||
var expected_calls := 2 if mode in [&"action", &"commit", &"cancel", &"missing"] else 0 if mode == &"no_releaser" else 1
|
||||
_expect(calls.size() == expected_calls, "%s attempts each release once" % mode)
|
||||
_expect(state[&"value"] == (4 if mode == &"finish" else 0), "%s retains actual action outcome" % mode)
|
||||
var snapshot := service.create_snapshot()
|
||||
var restored := EventServiceType.new()
|
||||
restored.register_definition(definition)
|
||||
_expect(restored.restore_snapshot(snapshot) and restored.create_snapshot() == snapshot, "%s ERROR with unreleased tokens restores whole" % mode)
|
||||
_expect(restored.validate_invariants()[&"ok"], "%s retained ERROR token invariants pass" % mode)
|
||||
if mode not in [&"cancel", &"missing"]:
|
||||
var before := service.create_snapshot()
|
||||
var replay := service.submit_choice(instance.event_instance_id, started.details[&"choice_instance_id"], &"accept", 0)
|
||||
_expect(not replay.is_success() and replay.is_replay and service.create_snapshot() == before and calls.size() == expected_calls, "%s failure cache replay never refunds again" % mode)
|
||||
|
||||
Reference in New Issue
Block a user