create([ 'tenant_id' => null, 'external_id' => 'platform', 'name' => 'Platform', ]); }); it('is idempotent: after a successful run, preflight reports nothing to do', function () { Queue::fake(); $platformTenant = Tenant::query()->where('external_id', 'platform')->firstOrFail(); $tenant = Tenant::factory()->create([ 'workspace_id' => (int) $platformTenant->workspace_id, ]); Finding::factory()->create([ 'tenant_id' => (int) $tenant->getKey(), 'due_at' => null, ]); $runbook = app(FindingsLifecycleBackfillRunbookService::class); $initial = $runbook->preflight(FindingsLifecycleBackfillScope::singleTenant((int) $tenant->getKey())); expect($initial['affected_count'])->toBe(1); $runbook->start( scope: FindingsLifecycleBackfillScope::singleTenant((int) $tenant->getKey()), initiator: null, reason: null, source: 'system_ui', ); $job = new BackfillFindingLifecycleJob( tenantId: (int) $tenant->getKey(), workspaceId: (int) $tenant->workspace_id, initiatorUserId: null, ); $job->handle( app(OperationRunService::class), app(\App\Services\Findings\FindingSlaPolicy::class), $runbook, ); $after = $runbook->preflight(FindingsLifecycleBackfillScope::singleTenant((int) $tenant->getKey())); expect($after['affected_count'])->toBe(0); expect(fn () => $runbook->start( scope: FindingsLifecycleBackfillScope::singleTenant((int) $tenant->getKey()), initiator: null, reason: null, source: 'system_ui', ))->toThrow(ValidationException::class); });