TenantAtlas/apps/platform/tests/Feature/Guards/Spec195ResidualActionSurfaceClosureGuardTest.php
2026-04-13 09:46:47 +02:00

157 lines
7.3 KiB
PHP

<?php
declare(strict_types=1);
use App\Support\Ui\ActionSurface\ActionSurfaceDiscoveredComponent;
use App\Support\Ui\ActionSurface\ActionSurfaceExemptions;
use App\Support\Ui\ActionSurface\ActionSurfaceValidator;
/**
* @return array<int, string>
*/
function spec195DiscoveredClasses(): array
{
return array_map(
static fn (ActionSurfaceDiscoveredComponent $component): string => $component->className,
ActionSurfaceValidator::withBaselineExemptions()->discoveredComponents(),
);
}
/**
* @param array<int, \App\Support\Ui\ActionSurface\ActionSurfaceValidationIssue> $issues
*/
function spec195FormattedIssues(array $issues): string
{
return implode("\n", array_map(
static fn ($issue): string => $issue->format(),
$issues,
));
}
it('keeps every spec 195 residual surface classified exactly once with structured evidence', function (): void {
$inventory = ActionSurfaceExemptions::spec195ResidualSurfaceInventory();
expect(array_keys($inventory))->toEqualCanonicalizing([
\App\Filament\System\Pages\Dashboard::class,
\App\Filament\System\Pages\Ops\ViewRun::class,
\App\Filament\System\Pages\Ops\Runbooks::class,
\App\Filament\System\Pages\RepairWorkspaceOwners::class,
\App\Filament\System\Pages\Directory\ViewTenant::class,
\App\Filament\System\Pages\Directory\ViewWorkspace::class,
\App\Filament\Pages\BreakGlassRecovery::class,
\App\Filament\Pages\ChooseWorkspace::class,
\App\Filament\Pages\ChooseTenant::class,
\App\Filament\Pages\Tenancy\RegisterTenant::class,
\App\Filament\Pages\Workspaces\ManagedTenantOnboardingWizard::class,
\App\Filament\Pages\Workspaces\ManagedTenantsLanding::class,
\App\Filament\Pages\TenantDashboard::class,
]);
$surfaceKeys = collect($inventory)->pluck('surfaceKey')->all();
expect($surfaceKeys)->toHaveCount(count(array_unique($surfaceKeys)));
foreach ($inventory as $className => $surface) {
expect($surface['closureDecision'] ?? null)
->not->toBeNull("{$className} must keep a closure decision.")
->and(trim((string) ($surface['surfaceName'] ?? '')))
->not->toBe('', "{$className} must keep a human-readable surfaceName.")
->and($surface['evidence'] ?? [])
->not->toBeEmpty("{$className} must keep structured evidence.");
}
});
it('keeps the residual system tail explicitly classified instead of silently baseline-exempted', function (): void {
$inventory = ActionSurfaceExemptions::spec195ResidualSurfaceInventory();
$baselineExemptions = ActionSurfaceExemptions::baseline()->all();
expect($inventory[\App\Filament\System\Pages\Ops\ViewRun::class]['closureDecision'] ?? null)->toBe('separately_governed')
->and($inventory[\App\Filament\System\Pages\Ops\Runbooks::class]['closureDecision'] ?? null)->toBe('separately_governed')
->and($inventory[\App\Filament\System\Pages\RepairWorkspaceOwners::class]['closureDecision'] ?? null)->toBe('separately_governed')
->and($inventory[\App\Filament\System\Pages\Directory\ViewTenant::class]['closureDecision'] ?? null)->toBe('harmless_special_case')
->and($inventory[\App\Filament\System\Pages\Directory\ViewWorkspace::class]['closureDecision'] ?? null)->toBe('harmless_special_case')
->and($inventory[\App\Filament\System\Pages\Dashboard::class]['closureDecision'] ?? null)->toBe('separately_governed');
foreach ([
\App\Filament\System\Pages\Dashboard::class,
\App\Filament\System\Pages\Ops\ViewRun::class,
\App\Filament\System\Pages\Ops\Runbooks::class,
\App\Filament\System\Pages\RepairWorkspaceOwners::class,
\App\Filament\System\Pages\Directory\ViewTenant::class,
\App\Filament\System\Pages\Directory\ViewWorkspace::class,
] as $className) {
expect(array_key_exists($className, $baselineExemptions))
->toBeFalse("{$className} must not rely on baseline() for Spec 195 closure.");
}
});
it('retires break glass recovery from live baseline handling', function (): void {
$surface = ActionSurfaceExemptions::spec195ResidualSurface(\App\Filament\Pages\BreakGlassRecovery::class);
expect($surface)->not->toBeNull()
->and($surface['closureDecision'] ?? null)->toBe('retired_no_longer_relevant')
->and($surface['reasonCategory'] ?? null)->toBe('disabled_or_actionless_surface')
->and(ActionSurfaceExemptions::baseline()->hasClass(\App\Filament\Pages\BreakGlassRecovery::class))->toBeFalse();
});
it('fails when a residual candidate is missing a closure decision entry', function (): void {
$inventory = ActionSurfaceExemptions::spec195ResidualSurfaceInventory();
unset($inventory[\App\Filament\System\Pages\Dashboard::class]);
$issues = ActionSurfaceValidator::validateSpec195ResidualInventoryFixture(
inventory: $inventory,
discoveredClasses: spec195DiscoveredClasses(),
baselineExemptions: ActionSurfaceExemptions::baseline()->all(),
residualCandidateClasses: [\App\Filament\System\Pages\Dashboard::class],
);
expect(spec195FormattedIssues($issues))
->toContain('Residual action surface is missing a Spec 195 closure entry')
->toContain(\App\Filament\System\Pages\Dashboard::class);
});
it('fails when a discovered residual exemption loses its reason category', function (): void {
$inventory = ActionSurfaceExemptions::spec195ResidualSurfaceInventory();
$inventory[\App\Filament\Pages\ChooseTenant::class]['reasonCategory'] = null;
$issues = ActionSurfaceValidator::validateSpec195ResidualInventoryFixture(
inventory: $inventory,
discoveredClasses: spec195DiscoveredClasses(),
baselineExemptions: ActionSurfaceExemptions::baseline()->all(),
);
expect(spec195FormattedIssues($issues))
->toContain(\App\Filament\Pages\ChooseTenant::class)
->toContain('reason category is invalid or missing');
});
it('fails when a residual surface loses its structured evidence', function (): void {
$inventory = ActionSurfaceExemptions::spec195ResidualSurfaceInventory();
$inventory[\App\Filament\System\Pages\Directory\ViewTenant::class]['evidence'] = [];
$issues = ActionSurfaceValidator::validateSpec195ResidualInventoryFixture(
inventory: $inventory,
discoveredClasses: spec195DiscoveredClasses(),
baselineExemptions: ActionSurfaceExemptions::baseline()->all(),
);
expect(spec195FormattedIssues($issues))
->toContain(\App\Filament\System\Pages\Directory\ViewTenant::class)
->toContain('require at least one structured evidence descriptor');
});
it('fails when a retired surface is reintroduced as a stale baseline exemption', function (): void {
$baselineExemptions = ActionSurfaceExemptions::baseline()->all();
$baselineExemptions[\App\Filament\Pages\BreakGlassRecovery::class] = 'Stale retired page exemption.';
$issues = ActionSurfaceValidator::validateSpec195ResidualInventoryFixture(
inventory: ActionSurfaceExemptions::spec195ResidualSurfaceInventory(),
discoveredClasses: spec195DiscoveredClasses(),
baselineExemptions: $baselineExemptions,
);
expect(spec195FormattedIssues($issues))
->toContain(\App\Filament\Pages\BreakGlassRecovery::class)
->toContain('must not remain baseline-exempt');
});