create(); $workspace = Workspace::factory()->create(['name' => 'Hidden Workspace']); WorkspaceMembership::factory()->create([ 'workspace_id' => (int) Workspace::factory()->create()->getKey(), 'user_id' => (int) $user->getKey(), 'role' => 'owner', ]); $this->actingAs($user) ->withSession([WorkspaceContext::SESSION_KEY => (int) $workspace->getKey()]) ->get('/admin') ->assertNotFound(); }); it('falls back to workspace-safe operations recovery when only workspace-level activity is actionable', function (): void { $tenant = \App\Models\Tenant::factory()->create(['status' => 'active']); [$user, $tenant] = createUserWithTenant($tenant, role: 'readonly', workspaceRole: 'readonly'); [$profile, $snapshot] = seedActiveBaselineForTenant($tenant); seedBaselineCompareRun($tenant, $profile, $snapshot, workspaceOverviewCompareCoverage()); \App\Models\OperationRun::factory()->tenantlessForWorkspace($tenant->workspace()->firstOrFail())->create([ 'status' => \App\Support\OperationRunStatus::Running->value, 'outcome' => \App\Support\OperationRunOutcome::Pending->value, ]); $workspace = $tenant->workspace()->firstOrFail(); $overview = app(\App\Support\Workspaces\WorkspaceOverviewBuilder::class)->build($workspace, $user); expect($overview['attention_items'])->toBe([]) ->and($overview['calmness']['is_calm'])->toBeFalse() ->and($overview['calmness']['next_action']['kind'])->toBe('operations_index') ->and($overview['calmness']['next_action']['url'])->toContain('tenant_scope=all') ->and($overview['calmness']['next_action']['url'])->toContain('activeTab=blocked'); }); it('uses switch workspace as the zero-tenant recovery action', function (): void { $user = User::factory()->create(); $workspace = Workspace::factory()->create(); WorkspaceMembership::factory()->create([ 'workspace_id' => (int) $workspace->getKey(), 'user_id' => (int) $user->getKey(), 'role' => 'readonly', ]); $overview = app(\App\Support\Workspaces\WorkspaceOverviewBuilder::class)->build($workspace, $user); expect($overview['calmness']['is_calm'])->toBeFalse() ->and($overview['calmness']['next_action']['kind'])->toBe('switch_workspace') ->and($overview['attention_empty_state']['action_label'])->toBe('Switch workspace'); });