create([ 'tenant_id' => null, 'external_id' => 'platform', 'name' => 'Platform', ]); }); it('computes single-tenant preflight counts', function () { $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, ]); Finding::factory()->create([ 'tenant_id' => (int) $tenant->getKey(), ]); $service = app(FindingsLifecycleBackfillRunbookService::class); $result = $service->preflight(FindingsLifecycleBackfillScope::singleTenant((int) $tenant->getKey())); expect($result['total_count'])->toBe(2); expect($result['affected_count'])->toBe(1); }); it('computes all-tenants preflight counts scoped to the platform workspace', function () { $platformTenant = Tenant::query()->where('external_id', 'platform')->firstOrFail(); $tenantA = Tenant::factory()->create([ 'workspace_id' => (int) $platformTenant->workspace_id, ]); $tenantB = Tenant::factory()->create([ 'workspace_id' => (int) $platformTenant->workspace_id, ]); $otherTenant = Tenant::factory()->create(); Finding::factory()->create([ 'tenant_id' => (int) $tenantA->getKey(), 'due_at' => null, ]); Finding::factory()->create([ 'tenant_id' => (int) $tenantB->getKey(), 'sla_days' => null, ]); Finding::factory()->create([ 'tenant_id' => (int) $otherTenant->getKey(), 'due_at' => null, ]); $service = app(FindingsLifecycleBackfillRunbookService::class); $result = $service->preflight(FindingsLifecycleBackfillScope::allTenants()); expect($result['estimated_tenants'])->toBe(2); expect($result['total_count'])->toBe(2); expect($result['affected_count'])->toBe(2); }); it('accepts an allowed single-tenant selection during preflight', function () { $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(\App\Filament\System\Pages\Ops\Runbooks::class) ->callAction('preflight', data: [ 'scope_mode' => FindingsLifecycleBackfillScope::MODE_SINGLE_TENANT, 'tenant_id' => (int) $tenant->getKey(), ]) ->assertHasNoActionErrors() ->assertSet('findingsTenantId', (int) $tenant->getKey()) ->assertSet('preflight.affected_count', 1); }); it('rejects platform tenant selection during preflight', function () { $platformTenant = Tenant::query()->where('external_id', 'platform')->firstOrFail(); $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'); assertScopedSelectorRejected( Livewire::test(\App\Filament\System\Pages\Ops\Runbooks::class), 'preflight', [ 'scope_mode' => FindingsLifecycleBackfillScope::MODE_SINGLE_TENANT, 'tenant_id' => (int) $platformTenant->getKey(), ], ); }); it('resets to an all-tenant trusted scope even when stale single-tenant selector state remains on the page', function () { $platformTenant = Tenant::query()->where('external_id', 'platform')->firstOrFail(); $tenantA = Tenant::factory()->create([ 'workspace_id' => (int) $platformTenant->workspace_id, 'name' => 'Scope Tenant A', ]); $tenantB = Tenant::factory()->create([ 'workspace_id' => (int) $platformTenant->workspace_id, 'name' => 'Scope Tenant B', ]); Finding::factory()->create([ 'tenant_id' => (int) $tenantA->getKey(), 'due_at' => null, ]); Finding::factory()->create([ 'tenant_id' => (int) $tenantB->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(\App\Filament\System\Pages\Ops\Runbooks::class) ->set('findingsScopeMode', FindingsLifecycleBackfillScope::MODE_SINGLE_TENANT) ->set('findingsTenantId', (int) $tenantA->getKey()) ->set('scopeMode', FindingsLifecycleBackfillScope::MODE_SINGLE_TENANT) ->set('tenantId', (int) $tenantA->getKey()) ->callAction('preflight', data: [ 'scope_mode' => FindingsLifecycleBackfillScope::MODE_ALL_TENANTS, 'tenant_id' => (int) $tenantA->getKey(), ]) ->assertHasNoActionErrors() ->assertSet('findingsScopeMode', FindingsLifecycleBackfillScope::MODE_ALL_TENANTS) ->assertSet('findingsTenantId', null) ->assertSet('scopeMode', FindingsLifecycleBackfillScope::MODE_ALL_TENANTS) ->assertSet('tenantId', null) ->assertSet('preflight.estimated_tenants', 2) ->assertSet('preflight.affected_count', 2); });