create(['status' => 'active']); [$user, $tenantA] = createUserWithTenant($tenantA, role: 'owner', workspaceRole: 'readonly'); Tenant::factory()->create([ 'status' => 'active', 'workspace_id' => (int) $tenantA->workspace_id, 'name' => 'Inaccessible Tenant', ]); $workspace = $tenantA->workspace()->firstOrFail(); $overview = app(WorkspaceOverviewBuilder::class)->build($workspace, $user); $quickActionKeys = collect($overview['quick_actions'])->pluck('key')->all(); expect($overview['accessible_tenant_count'])->toBe(1) ->and($quickActionKeys)->toContain('switch_workspace') ->and($quickActionKeys)->not->toContain('manage_workspaces'); }); it('keeps governance attention visible but non-clickable when the tenant membership does not grant drill-through capability', function (): void { $tenant = Tenant::factory()->create(['status' => 'active']); [$user, $tenant] = createUserWithTenant($tenant, role: 'readonly', workspaceRole: 'readonly'); [$profile, $snapshot] = seedActiveBaselineForTenant($tenant); seedBaselineCompareRun($tenant, $profile, $snapshot, workspaceOverviewCompareCoverage()); \App\Models\Finding::factory()->for($tenant)->create([ 'workspace_id' => (int) $tenant->workspace_id, 'status' => \App\Models\Finding::STATUS_TRIAGED, 'due_at' => now()->subDay(), ]); mock(CapabilityResolver::class, function ($mock) use ($tenant): void { $mock->shouldReceive('primeMemberships')->once(); $mock->shouldReceive('can') ->andReturnUsing(static function (\App\Models\User $user, Tenant $resolvedTenant, string $capability) use ($tenant): bool { expect((int) $resolvedTenant->getKey())->toBe((int) $tenant->getKey()); return match ($capability) { Capabilities::TENANT_VIEW, Capabilities::TENANT_FINDINGS_VIEW => false, default => false, }; }); }); $workspace = $tenant->workspace()->firstOrFail(); $overview = app(WorkspaceOverviewBuilder::class)->build($workspace, $user); $item = collect($overview['attention_items'])->firstWhere('key', 'tenant_overdue_findings'); expect($item['action_disabled'])->toBeTrue() ->and($item['destination']['kind'])->toBe('tenant_findings') ->and($item['helper_text'])->not->toBeNull(); });