157 lines
7.3 KiB
PHP
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');
|
|
});
|