getRoutes()->match($request); $request->setRouteResolver(static fn () => $route); app()->instance('request', $request); } function workspaceSidebarLabelsByGroup(): array { return collect(app(NavigationManager::class)->get()) ->mapWithKeys(static function (NavigationGroup $group): array { return [ $group->getLabel() ?? '' => collect($group->getItems()) ->map(static fn (NavigationItem $item): string => $item->getLabel()) ->values() ->all(), ]; }) ->all(); } function seedWorkspaceSidebarVisibleDecision(ManagedEnvironment $tenant, User $actor): void { $finding = Finding::factory()->for($tenant)->create([ 'workspace_id' => (int) $tenant->workspace_id, ]); $exception = FindingException::query()->create([ 'workspace_id' => (int) $tenant->workspace_id, 'managed_environment_id' => (int) $tenant->getKey(), 'finding_id' => (int) $finding->getKey(), 'requested_by_user_id' => (int) $actor->getKey(), 'owner_user_id' => (int) $actor->getKey(), 'status' => FindingException::STATUS_PENDING, 'current_validity_state' => FindingException::VALIDITY_MISSING_SUPPORT, 'request_reason' => 'Workspace sidebar composition contract', 'requested_at' => now()->subDay(), 'review_due_at' => now()->addDay(), 'evidence_summary' => ['reference_count' => 0], ]); $decision = $exception->decisions()->create([ 'workspace_id' => (int) $tenant->workspace_id, 'managed_environment_id' => (int) $tenant->getKey(), 'actor_user_id' => (int) $actor->getKey(), 'decision_type' => FindingExceptionDecision::TYPE_REQUESTED, 'reason' => 'Visible workspace sidebar decision', 'metadata' => [], 'decided_at' => now()->subDay(), ]); $exception->forceFill(['current_decision_id' => (int) $decision->getKey()])->save(); } it('hides environment-owned navigation classes on workspace surfaces', function (string $class): void { Filament::setCurrentPanel('admin'); bindNavigationRequestPath('/admin/workspaces/workspace-alpha'); expect($class::shouldRegisterNavigation())->toBeFalse(); })->with('environment visible navigation classes'); it('keeps workspace surface navigation independent from environment query hints', function (string $path): void { Filament::setCurrentPanel('admin'); bindNavigationRequestPath($path); expect(NavigationScope::isWorkspaceSurface())->toBeTrue() ->and(NavigationScope::isEnvironmentSurface())->toBeFalse(); })->with('workspace surface paths with environment query hints'); it('uses the canonical grouped workspace sidebar on representative workspace-wide surfaces', function (string $surface, callable $urlFactory): void { [$user, $tenant] = createUserWithTenant(role: 'owner'); seedWorkspaceSidebarVisibleDecision($tenant, $user); Filament::setTenant($tenant, true); $workspace = $tenant->workspace()->firstOrFail(); $url = $urlFactory($workspace, $tenant); $this->actingAs($user) ->withSession([ WorkspaceContext::SESSION_KEY => (int) $workspace->getKey(), WorkspaceContext::LAST_ENVIRONMENT_IDS_SESSION_KEY => [ (string) $workspace->getKey() => (int) $tenant->getKey(), ], ]) ->get($url) ->assertOk(); expect(workspaceSidebarLabelsByGroup())->toBe([ '' => ['Overview'], 'Monitoring' => ['Finding exceptions', 'Operations', 'Alerts', 'Evidence', 'Audit Log'], 'Reporting' => ['Reviews', 'Customer reviews'], 'Settings' => ['Manage workspaces', 'Integrations', 'Settings'], 'Governance' => ['Governance inbox', 'Decision register'], ]); })->with([ 'workspace overview' => [ 'workspace overview', fn ($workspace): string => route('admin.workspace.home', ['workspace' => $workspace]), ], 'operations' => [ 'operations', fn ($workspace): string => route('admin.operations.index', ['workspace' => $workspace]), ], 'evidence overview' => [ 'evidence overview', fn (): string => route('admin.evidence.overview'), ], 'customer reviews' => [ 'customer reviews', fn (): string => CustomerReviewWorkspace::getUrl(panel: 'admin'), ], 'governance inbox' => [ 'governance inbox', fn (): string => GovernanceInbox::getUrl(panel: 'admin'), ], 'decision register' => [ 'decision register', fn (): string => DecisionRegister::getUrl(panel: 'admin'), ], ]); it('keeps the grouped workspace sidebar when environment query filters are present', function (string $surface, callable $urlFactory): void { [$user, $tenant] = createUserWithTenant(role: 'owner'); seedWorkspaceSidebarVisibleDecision($tenant, $user); Filament::setTenant($tenant, true); $workspace = $tenant->workspace()->firstOrFail(); $url = $urlFactory($workspace, $tenant); $this->actingAs($user) ->withSession([ WorkspaceContext::SESSION_KEY => (int) $workspace->getKey(), WorkspaceContext::LAST_ENVIRONMENT_IDS_SESSION_KEY => [ (string) $workspace->getKey() => (int) $tenant->getKey(), ], ]) ->get($url) ->assertOk(); expect(workspaceSidebarLabelsByGroup())->toBe([ '' => ['Overview'], 'Monitoring' => ['Finding exceptions', 'Operations', 'Alerts', 'Evidence', 'Audit Log'], 'Reporting' => ['Reviews', 'Customer reviews'], 'Settings' => ['Manage workspaces', 'Integrations', 'Settings'], 'Governance' => ['Governance inbox', 'Decision register'], ]); })->with([ 'operations with tenant query' => [ 'operations', fn ($workspace, ManagedEnvironment $tenant): string => route('admin.operations.index', [ 'workspace' => $workspace, 'tenant' => (string) $tenant->external_id, ]), ], 'customer reviews with tenant query' => [ 'customer reviews', fn ($workspace, ManagedEnvironment $tenant): string => CustomerReviewWorkspace::getUrl(panel: 'admin', parameters: [ 'tenant' => (string) $tenant->external_id, ]), ], 'decision register with environment query' => [ 'decision register', fn ($workspace, ManagedEnvironment $tenant): string => DecisionRegister::getUrl(panel: 'admin', parameters: [ 'managed_environment_id' => (string) $tenant->getKey(), ]), ], ]); it('keeps environment navigation on canonical environment routes even when query filters are present', function (): void { Filament::setCurrentPanel('admin'); bindNavigationRequestPath('/admin/workspaces/workspace-alpha/environments/environment-alpha/inventory?tenant=other-environment'); expect(NavigationScope::isEnvironmentSurface())->toBeTrue() ->and(NavigationScope::isWorkspaceSurface())->toBeFalse(); }); it('registers environment-owned surfaces only on environment surfaces', function (string $class): void { [$user, $tenant] = createUserWithTenant(role: 'owner'); $this->actingAs($user); session()->put(WorkspaceContext::SESSION_KEY, (int) $tenant->workspace_id); Filament::setCurrentPanel('admin'); bindNavigationRequestPath(ManagedEnvironmentLinks::viewUrl($tenant)); expect($class::shouldRegisterNavigation())->toBeTrue(); })->with('environment visible navigation classes'); it('keeps retired tenant-panel entry routes unavailable', function (): void { [$user, $tenant] = createUserWithTenant(role: 'owner'); $this->actingAs($user) ->withSession([ WorkspaceContext::SESSION_KEY => (int) $tenant->workspace_id, ]) ->get("/admin/t/{$tenant->external_id}") ->assertNotFound(); $this->actingAs($user) ->withSession([ WorkspaceContext::SESSION_KEY => (int) $tenant->workspace_id, ]) ->get("/admin/tenants/{$tenant->external_id}") ->assertNotFound(); }); it('keeps baseline navigation route scoped and off retired tenant routes', function (): void { [$user, $tenant] = createUserWithTenant(role: 'owner'); Filament::setCurrentPanel('admin'); bindNavigationRequestPath('/admin/workspaces/workspace-alpha'); expect(BaselineProfileResource::shouldRegisterNavigation())->toBeFalse(); expect(BaselineSnapshotResource::shouldRegisterNavigation())->toBeFalse(); bindNavigationRequestPath('/admin/workspaces/workspace-alpha/environments/environment-alpha'); expect(BaselineProfileResource::shouldRegisterNavigation())->toBeTrue(); expect(BaselineSnapshotResource::shouldRegisterNavigation())->toBeTrue(); $this->actingAs($user) ->withSession([ WorkspaceContext::SESSION_KEY => (int) $tenant->workspace_id, ]) ->get("/admin/t/{$tenant->external_id}/baseline-profiles") ->assertNotFound(); $this->actingAs($user) ->withSession([ WorkspaceContext::SESSION_KEY => (int) $tenant->workspace_id, ]) ->get("/admin/t/{$tenant->external_id}/baseline-snapshots") ->assertNotFound(); }); it('keeps the workspace panel sidebar free of tenant-sensitive entries even with a remembered environment', function (): void { [$user, $tenant] = createUserWithTenant(role: 'owner'); Filament::setTenant($tenant, true); $response = $this->actingAs($user) ->withSession([ WorkspaceContext::SESSION_KEY => (int) $tenant->workspace_id, WorkspaceContext::LAST_ENVIRONMENT_IDS_SESSION_KEY => [ (string) $tenant->workspace_id => (int) $tenant->getKey(), ], ]) ->get(route('admin.workspace.home', ['workspace' => $tenant->workspace_id])) ->assertOk(); $response->assertSeeText('Overview'); $response->assertSeeText('Operations'); $response->assertSeeText('Alerts'); $response->assertSeeText('Audit Log'); $response->assertSeeText('Governance inbox'); $response->assertSeeText('Customer reviews'); $response->assertSeeText('Manage workspaces'); $response->assertSeeText('Integrations'); $response->assertSeeText('Settings'); $response->assertDontSee('>Items', false); $response->assertDontSee('>Policies', false); $response->assertDontSee('>Policy Versions', false); $response->assertDontSee('>Groups', false); $response->assertDontSee('/entra-groups', false); $response->assertDontSee('>Backup Schedules', false); $response->assertDontSee('>Backup Sets', false); $response->assertDontSee('>Restore Runs', false); $response->assertDontSee('>Findings', false); $response->assertDontSee('>Baselines', false); $response->assertDontSee('>Baseline Snapshots', false); $response->assertDontSee('>Baseline Compare', false); $response->assertDontSee('>Evidence', false); $response->assertDontSee('>Risk exceptions', false); }); it('shows environment-owned sidebar entries on the canonical environment route', function (): void { [$user, $tenant] = createUserWithTenant(role: 'owner'); $response = $this->actingAs($user) ->withSession([ WorkspaceContext::SESSION_KEY => (int) $tenant->workspace_id, ]) ->get(ManagedEnvironmentLinks::viewUrl($tenant)) ->assertOk(); $response->assertSeeText('Policies'); $response->assertSeeText('Policy Versions'); $response->assertSeeText('Groups'); $response->assertSee((string) parse_url(EntraGroupResource::getUrl(panel: 'admin', tenant: $tenant), PHP_URL_PATH), false); $response->assertDontSee('/admin/entra-groups', false); $response->assertSeeText('Items'); $response->assertSeeText('Backup Schedules'); $response->assertSeeText('Backup Sets'); $response->assertSeeText('Restore Runs'); $response->assertSeeText('Findings'); $response->assertSeeText('Baselines'); $response->assertSeeText('Baseline Snapshots'); $response->assertSeeText('Baseline Compare'); $response->assertSee((string) parse_url(ManagedEnvironmentLinks::baselineCompareUrl($tenant), PHP_URL_PATH), false); $response->assertDontSee('/admin/baseline-compare-landing', false); $response->assertSeeText('Evidence'); $response->assertSeeText('Risk exceptions'); $this->actingAs($user) ->withSession([ WorkspaceContext::SESSION_KEY => (int) $tenant->workspace_id, ]) ->get(InventoryItemResource::getUrl('index', panel: 'admin', tenant: $tenant)) ->assertOk() ->assertSeeText('Coverage'); });