create([ 'tenant_id' => null, 'external_id' => 'platform', 'name' => 'Platform', ]); }); it('disables running when preflight indicates 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(), ]); $user = PlatformUser::factory()->create([ 'capabilities' => [ PlatformCapabilities::ACCESS_SYSTEM_PANEL, PlatformCapabilities::OPS_VIEW, PlatformCapabilities::RUNBOOKS_VIEW, PlatformCapabilities::RUNBOOKS_RUN, PlatformCapabilities::RUNBOOKS_FINDINGS_LIFECYCLE_BACKFILL, ], 'is_active' => true, ]); $this->actingAs($user, 'platform'); Livewire::test(Runbooks::class) ->callAction('preflight', data: [ 'scope_mode' => FindingsLifecycleBackfillScope::MODE_ALL_TENANTS, ]) ->assertSet('preflight.affected_count', 0) ->assertActionDisabled('run'); }); it('requires typed confirmation and a reason for all-tenants runs', 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, ]); $user = PlatformUser::factory()->create([ 'capabilities' => [ PlatformCapabilities::ACCESS_SYSTEM_PANEL, PlatformCapabilities::OPS_VIEW, PlatformCapabilities::RUNBOOKS_VIEW, PlatformCapabilities::RUNBOOKS_RUN, PlatformCapabilities::RUNBOOKS_FINDINGS_LIFECYCLE_BACKFILL, ], 'is_active' => true, ]); $this->actingAs($user, 'platform'); Livewire::test(Runbooks::class) ->callAction('preflight', data: [ 'scope_mode' => FindingsLifecycleBackfillScope::MODE_ALL_TENANTS, ]) ->assertSet('preflight.affected_count', 1) ->callAction('run', data: []) ->assertHasActionErrors([ 'typed_confirmation', 'reason_code', 'reason_text', ]); Livewire::test(Runbooks::class) ->callAction('preflight', data: [ 'scope_mode' => FindingsLifecycleBackfillScope::MODE_ALL_TENANTS, ]) ->assertSet('preflight.affected_count', 1) ->callAction('run', data: [ 'typed_confirmation' => 'backfill', 'reason_code' => 'DATA_REPAIR', 'reason_text' => 'Test run', ]) ->assertHasActionErrors(['typed_confirmation']); }); it('rejects forged single-tenant selector state on run and records no run or start audit', function () { Queue::fake(); $platformTenant = Tenant::query()->where('external_id', 'platform')->firstOrFail(); $allowedTenant = Tenant::factory()->create([ 'workspace_id' => (int) $platformTenant->workspace_id, ]); Finding::factory()->create([ 'tenant_id' => (int) $allowedTenant->getKey(), 'due_at' => null, ]); $user = PlatformUser::factory()->create([ 'capabilities' => [ PlatformCapabilities::ACCESS_SYSTEM_PANEL, PlatformCapabilities::OPS_VIEW, PlatformCapabilities::RUNBOOKS_VIEW, PlatformCapabilities::RUNBOOKS_RUN, PlatformCapabilities::RUNBOOKS_FINDINGS_LIFECYCLE_BACKFILL, ], 'is_active' => true, ]); $this->actingAs($user, 'platform'); Livewire::test(Runbooks::class) ->callAction('preflight', data: [ 'scope_mode' => FindingsLifecycleBackfillScope::MODE_SINGLE_TENANT, 'tenant_id' => (int) $allowedTenant->getKey(), ]) ->assertSet('preflight.affected_count', 1) ->set('findingsScopeMode', FindingsLifecycleBackfillScope::MODE_SINGLE_TENANT) ->set('findingsTenantId', (int) $platformTenant->getKey()) ->set('scopeMode', FindingsLifecycleBackfillScope::MODE_SINGLE_TENANT) ->set('tenantId', (int) $platformTenant->getKey()) ->callAction('run', data: []) ->assertHasActionErrors(); expect(OperationRun::query()->where('type', 'findings.lifecycle.backfill')->count())->toBe(0) ->and(AuditLog::query()->where('action', 'platform.ops.runbooks.start')->count())->toBe(0); }); it('records a start audit with the canonical single-tenant scope when an allowed run is queued', 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, ]); $user = PlatformUser::factory()->create([ 'capabilities' => [ PlatformCapabilities::ACCESS_SYSTEM_PANEL, PlatformCapabilities::OPS_VIEW, PlatformCapabilities::RUNBOOKS_VIEW, PlatformCapabilities::RUNBOOKS_RUN, PlatformCapabilities::RUNBOOKS_FINDINGS_LIFECYCLE_BACKFILL, ], 'is_active' => true, ]); $this->actingAs($user, 'platform'); Livewire::test(Runbooks::class) ->callAction('preflight', data: [ 'scope_mode' => FindingsLifecycleBackfillScope::MODE_SINGLE_TENANT, 'tenant_id' => (int) $tenant->getKey(), ]) ->assertSet('preflight.affected_count', 1) ->callAction('run', data: []) ->assertHasNoActionErrors() ->assertNotified('Findings lifecycle backfill queued'); $run = OperationRun::query() ->where('type', 'findings.lifecycle.backfill') ->latest('id') ->first(); expect($run)->not->toBeNull(); $audit = AuditLog::query() ->where('action', 'platform.ops.runbooks.start') ->latest('id') ->first(); expect($audit)->not->toBeNull() ->and($audit?->resource_id)->toBe((string) $run?->getKey()) ->and($audit?->metadata['scope'] ?? null)->toBe(FindingsLifecycleBackfillScope::MODE_SINGLE_TENANT) ->and($audit?->metadata['target_tenant_id'] ?? null)->toBe((int) $tenant->getKey()) ->and($audit?->metadata['operation_run_id'] ?? null)->toBe((int) $run?->getKey()); }); it('returns 403 for runbook execution when the platform user is in scope but lacks run capability', 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, ]); $user = PlatformUser::factory()->create([ 'capabilities' => [ PlatformCapabilities::ACCESS_SYSTEM_PANEL, PlatformCapabilities::OPS_VIEW, PlatformCapabilities::RUNBOOKS_VIEW, ], 'is_active' => true, ]); $this->actingAs($user, 'platform'); Livewire::test(Runbooks::class) ->callAction('preflight', data: [ 'scope_mode' => FindingsLifecycleBackfillScope::MODE_SINGLE_TENANT, 'tenant_id' => (int) $tenant->getKey(), ]) ->assertSet('preflight.affected_count', 1) ->callAction('run', data: []) ->assertForbidden(); expect(OperationRun::query()->where('type', 'findings.lifecycle.backfill')->count())->toBe(0) ->and(AuditLog::query()->where('action', 'platform.ops.runbooks.start')->count())->toBe(0); });