create([ 'tenant_id' => (int) $tenant->getKey(), 'workspace_id' => (int) $tenant->workspace_id, 'user_id' => (int) $user->getKey(), 'type' => 'policy.sync', 'status' => OperationRunStatus::Running->value, 'outcome' => OperationRunOutcome::Pending->value, ]); $job = new class($run) { use BridgesFailedOperationRun; public function __construct(public OperationRun $operationRun) {} }; $job->failed(new FakeTimeoutExceededException('Queue worker timed out.')); expect($run->fresh()->status)->toBe(OperationRunStatus::Completed->value) ->and($run->fresh()->outcome)->toBe(OperationRunOutcome::Failed->value) ->and(data_get($run->fresh()->context, 'reconciliation.reason_code'))->toBe('run.infrastructure_timeout_or_abandonment') ->and(data_get($run->fresh()->context, 'reconciliation.source'))->toBe('failed_callback'); }); it('bridges exhausted-attempt failures back to the owning operation run', function (): void { [$user, $tenant] = createUserWithTenant(role: 'owner'); $run = OperationRun::factory()->create([ 'tenant_id' => (int) $tenant->getKey(), 'workspace_id' => (int) $tenant->workspace_id, 'user_id' => (int) $user->getKey(), 'type' => 'inventory_sync', 'status' => OperationRunStatus::Running->value, 'outcome' => OperationRunOutcome::Pending->value, ]); $job = new class((int) $run->getKey()) { use BridgesFailedOperationRun; public function __construct(public int $operationRunId) {} }; $job->failed(new FakeMaxAttemptsExceededException('Max attempts exceeded.')); expect($run->fresh()->status)->toBe(OperationRunStatus::Completed->value) ->and($run->fresh()->outcome)->toBe(OperationRunOutcome::Failed->value) ->and(data_get($run->fresh()->context, 'reconciliation.reason_code'))->toBe('run.infrastructure_timeout_or_abandonment'); });