## Summary - remove legacy tenant-scoped routing and middleware paths in favor of the current environment/workspace context flow - update Filament pages and resources to use the cleaned-up admin surface and environment filter context - add the related spec 317 artifacts and targeted tests for environment filter state and legacy context cleanup ## Testing - not run as part of this commit/push/PR workflow Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de> Reviewed-on: #372
225 lines
7.8 KiB
PHP
225 lines
7.8 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
use App\Filament\Pages\Reviews\CustomerReviewWorkspace;
|
|
use App\Filament\Resources\EnvironmentReviewResource;
|
|
use App\Support\ManagedEnvironmentLinks;
|
|
use App\Support\Workspaces\WorkspaceContext;
|
|
use Filament\Facades\Filament;
|
|
use Illuminate\Support\Facades\Route;
|
|
|
|
/**
|
|
* @return list<string>
|
|
*/
|
|
function spec317Files(array $roots): array
|
|
{
|
|
$files = [];
|
|
|
|
foreach ($roots as $root) {
|
|
if (is_file($root)) {
|
|
$files[] = $root;
|
|
|
|
continue;
|
|
}
|
|
|
|
if (! is_dir($root)) {
|
|
continue;
|
|
}
|
|
|
|
$iterator = new RecursiveIteratorIterator(
|
|
new RecursiveDirectoryIterator($root, FilesystemIterator::SKIP_DOTS),
|
|
);
|
|
|
|
foreach ($iterator as $file) {
|
|
if (! $file instanceof SplFileInfo || ! $file->isFile()) {
|
|
continue;
|
|
}
|
|
|
|
if ($file->getFilename() === 'LegacyTenantPlatformContextCleanupTest.php') {
|
|
continue;
|
|
}
|
|
|
|
if (! in_array($file->getExtension(), ['php', 'md'], true)) {
|
|
continue;
|
|
}
|
|
|
|
$files[] = $file->getPathname();
|
|
}
|
|
}
|
|
|
|
sort($files);
|
|
|
|
return array_values(array_unique($files));
|
|
}
|
|
|
|
/**
|
|
* @param list<string> $files
|
|
* @param list<string> $patterns
|
|
* @return list<string>
|
|
*/
|
|
function spec317PatternHits(array $files, array $patterns): array
|
|
{
|
|
$hits = [];
|
|
|
|
foreach ($files as $path) {
|
|
$contents = file_get_contents($path);
|
|
|
|
if (! is_string($contents)) {
|
|
continue;
|
|
}
|
|
|
|
$lines = preg_split('/\R/', $contents) ?: [];
|
|
|
|
foreach ($patterns as $pattern) {
|
|
foreach ($lines as $lineNumber => $line) {
|
|
if (preg_match($pattern, $line) !== 1) {
|
|
continue;
|
|
}
|
|
|
|
$hits[] = str_replace(repo_path().'/', '', $path).':'.($lineNumber + 1).' -> '.trim($line);
|
|
}
|
|
}
|
|
}
|
|
|
|
return $hits;
|
|
}
|
|
|
|
it('removes retired platform-context helper and class names from active runtime seams', function (): void {
|
|
$files = spec317Files([
|
|
base_path('app'),
|
|
base_path('bootstrap/app.php'),
|
|
base_path('routes/web.php'),
|
|
base_path('tests/Feature/Guards'),
|
|
base_path('tests/Feature/Navigation'),
|
|
base_path('tests/Feature/Reviews'),
|
|
]);
|
|
|
|
$hits = spec317PatternHits($files, [
|
|
'/\btenantPrefilterUrl\s*\(/',
|
|
'/\bCanonicalAdminTenantFilterState\b/',
|
|
'/\bWorkspaceScopedTenantRoutes\b/',
|
|
'/\bTenantPageCategory\b/',
|
|
'/\bEnsureFilamentTenantSelected\b/',
|
|
'/ensure-filament-tenant-selected/',
|
|
'/\blastTenantId\s*\(/',
|
|
'/\brememberedTenant\s*\(/',
|
|
'/\brememberTenantContext\s*\(/',
|
|
'/\bLAST_TENANT_IDS_SESSION_KEY\b/',
|
|
'/\bTenantBound\b/',
|
|
'/\bTenantScopedEvidence\b/',
|
|
]);
|
|
|
|
expect($hits)->toBeEmpty("Retired Tenant platform-context names remain:\n".implode("\n", $hits));
|
|
});
|
|
|
|
it('keeps current product-truth docs on workspace and environment terminology', function (): void {
|
|
$files = spec317Files([
|
|
repo_path('docs/HANDOVER.md'),
|
|
repo_path('docs/product/spec-candidates.md'),
|
|
repo_path('docs/product/implementation-ledger.md'),
|
|
repo_path('docs/product/roadmap.md'),
|
|
repo_path('docs/product/principles.md'),
|
|
repo_path('docs/ui'),
|
|
repo_path('docs/architecture-guidelines.md'),
|
|
repo_path('docs/filament-guidelines.md'),
|
|
repo_path('docs/testing-guidelines.md'),
|
|
]);
|
|
|
|
$hits = spec317PatternHits($files, [
|
|
'/\btenantPrefilterUrl\b/',
|
|
'/\bCanonicalAdminTenantFilterState\b/',
|
|
'/\bWorkspaceScopedTenantRoutes\b/',
|
|
'/\bTenantPageCategory\b/',
|
|
'/\bEnsureFilamentTenantSelected\b/',
|
|
'/\blastTenantId\b/',
|
|
'/\btenantScopedUrl\b/',
|
|
]);
|
|
|
|
expect($hits)->toBeEmpty("Current docs still describe retired Tenant platform context:\n".implode("\n", $hits));
|
|
});
|
|
|
|
it('keeps workspace hubs free of hidden Filament or remembered Environment scope fallbacks', function (): void {
|
|
$files = spec317Files([
|
|
base_path('app/Filament/Pages/Monitoring/Operations.php'),
|
|
base_path('app/Filament/Pages/Monitoring/FindingExceptionsQueue.php'),
|
|
base_path('app/Filament/Pages/Governance/GovernanceInbox.php'),
|
|
base_path('app/Filament/Pages/Governance/DecisionRegister.php'),
|
|
base_path('app/Filament/Pages/Monitoring/EvidenceOverview.php'),
|
|
base_path('app/Filament/Pages/Reviews/ReviewRegister.php'),
|
|
base_path('app/Filament/Pages/Reviews/CustomerReviewWorkspace.php'),
|
|
base_path('app/Filament/Resources/ProviderConnectionResource.php'),
|
|
base_path('app/Filament/Resources/ProviderConnectionResource/Pages/ListProviderConnections.php'),
|
|
]);
|
|
|
|
$hits = spec317PatternHits($files, [
|
|
'/\bFilament::getTenant\s*\(/',
|
|
'/\blastEnvironmentId\s*\(/',
|
|
'/\brememberedEnvironment\s*\(/',
|
|
'/\brememberEnvironmentContext\s*\(/',
|
|
]);
|
|
|
|
expect($hits)->toBeEmpty("Workspace hubs must not derive scope from Filament tenant or remembered Environment state:\n".implode("\n", $hits));
|
|
});
|
|
|
|
it('keeps helper APIs hard-cut to Environment names and canonical filter keys', function (): void {
|
|
expect(method_exists(CustomerReviewWorkspace::class, 'tenantPrefilterUrl'))->toBeFalse()
|
|
->and(method_exists(CustomerReviewWorkspace::class, 'environmentFilterUrl'))->toBeTrue();
|
|
|
|
$reviewUrlHelper = new ReflectionMethod(EnvironmentReviewResource::class, 'environmentScopedUrl');
|
|
|
|
$providerConnectionResource = (string) file_get_contents(base_path('app/Filament/Resources/ProviderConnectionResource.php'));
|
|
|
|
expect(method_exists(EnvironmentReviewResource::class, 'tenantScopedUrl'))->toBeFalse()
|
|
->and($reviewUrlHelper->getNumberOfParameters())->toBe(3)
|
|
->and($providerConnectionResource)
|
|
->not->toContain("array_key_exists('tenant', \$parameters)")
|
|
->not->toContain('resolveRequestedTenantExternalId() ?? static::resolveContextTenantExternalId()');
|
|
});
|
|
|
|
it('keeps active environment dashboard links free of retired tenant query aliases', function (): void {
|
|
[$user, $environment] = createUserWithTenant(role: 'owner', workspaceRole: 'owner');
|
|
|
|
$workspace = $environment->workspace()->firstOrFail();
|
|
|
|
$this->actingAs($user);
|
|
Filament::setTenant($environment, true);
|
|
|
|
$response = $this
|
|
->withSession([
|
|
WorkspaceContext::SESSION_KEY => (int) $workspace->getKey(),
|
|
WorkspaceContext::LAST_ENVIRONMENT_IDS_SESSION_KEY => [
|
|
(string) $workspace->getKey() => (int) $environment->getKey(),
|
|
],
|
|
])
|
|
->get(ManagedEnvironmentLinks::viewUrl($environment));
|
|
|
|
$response->assertOk();
|
|
|
|
$content = html_entity_decode((string) $response->getContent(), ENT_QUOTES | ENT_HTML5);
|
|
|
|
expect($content)
|
|
->not->toContain('/admin/t')
|
|
->not->toContain('?tenant=')
|
|
->not->toContain('&tenant=')
|
|
->not->toContain('tenant_id=')
|
|
->not->toContain('managed_environment_id=')
|
|
->not->toContain('tenant_scope=');
|
|
});
|
|
|
|
it('does not register active legacy tenant panel routes or providers', function (): void {
|
|
$legacyRouteUris = collect(Route::getRoutes())
|
|
->map(fn ($route): string => ltrim((string) $route->uri(), '/'))
|
|
->filter(fn (string $uri): bool => preg_match('#^admin/t(?:/|$)#', $uri) === 1)
|
|
->values();
|
|
|
|
$registeredProviders = require base_path('bootstrap/providers.php');
|
|
$tenantPanelProviders = collect($registeredProviders)
|
|
->filter(fn (string $provider): bool => str_contains($provider, 'TenantPanelProvider'))
|
|
->values();
|
|
|
|
expect($legacyRouteUris)->toBeEmpty()
|
|
->and($tenantPanelProviders)->toBeEmpty()
|
|
->and(file_exists(app_path('Providers/Filament/TenantPanelProvider.php')))->toBeFalse();
|
|
});
|