create([ 'tenant_id' => null, 'external_id' => 'platform', 'name' => 'Platform', ]); config()->set('tenantpilot.break_glass.enabled', true); config()->set('tenantpilot.break_glass.ttl_minutes', 15); }); it('requires a reason when break-glass is active and records break-glass on the run + audit', function () { Queue::fake(); $platformTenant = Tenant::query()->where('external_id', 'platform')->firstOrFail(); $customerTenant = Tenant::factory()->create([ 'workspace_id' => (int) $platformTenant->workspace_id, ]); Finding::factory()->create([ 'tenant_id' => (int) $customerTenant->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, PlatformCapabilities::USE_BREAK_GLASS, ], 'is_active' => true, ]); $this->actingAs($user, 'platform'); Livewire::test(Dashboard::class) ->callAction('enter_break_glass', data: [ 'reason' => 'Recovery test', ]) ->assertHasNoActionErrors(); Livewire::test(Runbooks::class) ->callAction('preflight', data: [ 'scope_mode' => FindingsLifecycleBackfillScope::MODE_SINGLE_TENANT, 'tenant_id' => (int) $customerTenant->getKey(), ]) ->assertSet('preflight.affected_count', 1) ->callAction('run', data: []) ->assertHasActionErrors(['reason_code', 'reason_text']); Livewire::test(Runbooks::class) ->callAction('preflight', data: [ 'scope_mode' => FindingsLifecycleBackfillScope::MODE_SINGLE_TENANT, 'tenant_id' => (int) $customerTenant->getKey(), ]) ->assertSet('preflight.affected_count', 1) ->callAction('run', data: [ 'reason_code' => 'INCIDENT', 'reason_text' => 'Break-glass backfill required', ]) ->assertHasNoActionErrors() ->assertNotified(); $run = OperationRun::query() ->where('type', 'findings.lifecycle.backfill') ->latest('id') ->first(); expect($run)->not->toBeNull(); expect((int) $run?->tenant_id)->toBe((int) $customerTenant->getKey()); expect(data_get($run?->context, 'platform_initiator.is_break_glass'))->toBeTrue(); expect(data_get($run?->context, 'reason.reason_code'))->toBe('INCIDENT'); expect(data_get($run?->context, 'reason.reason_text'))->toBe('Break-glass backfill required'); $audit = AuditLog::query() ->where('action', 'platform.ops.runbooks.start') ->latest('id') ->first(); expect($audit)->not->toBeNull(); expect($audit?->metadata['is_break_glass'] ?? null)->toBe(true); expect($audit?->metadata['reason_code'] ?? null)->toBe('INCIDENT'); expect($audit?->metadata['reason_text'] ?? null)->toBe('Break-glass backfill required'); });