create(); $workspaceA = Workspace::factory()->create(['name' => 'My Workspace']); $workspaceB = Workspace::factory()->create(['name' => 'Other Workspace']); WorkspaceMembership::factory()->create([ 'workspace_id' => $workspaceA->getKey(), 'user_id' => $user->getKey(), 'role' => 'owner', ]); // User is NOT a member of workspaceB. WorkspaceMembership::factory()->create([ 'workspace_id' => $workspaceB->getKey(), 'user_id' => User::factory()->create()->getKey(), 'role' => 'owner', ]); $user->forceFill(['last_workspace_id' => (int) $workspaceA->getKey()])->save(); $this->actingAs($user) ->get(route('filament.admin.pages.choose-workspace')) ->assertOk() ->assertSee('My Workspace') ->assertDontSee('Other Workspace'); }); it('excludes archived workspaces from the list', function (): void { $user = User::factory()->create(); $activeWorkspace = Workspace::factory()->create(['name' => 'Active WS']); $archivedWorkspace = Workspace::factory()->create(['name' => 'Archived WS', 'archived_at' => now()]); WorkspaceMembership::factory()->create([ 'workspace_id' => $activeWorkspace->getKey(), 'user_id' => $user->getKey(), 'role' => 'owner', ]); WorkspaceMembership::factory()->create([ 'workspace_id' => $archivedWorkspace->getKey(), 'user_id' => $user->getKey(), 'role' => 'owner', ]); $user->forceFill(['last_workspace_id' => (int) $activeWorkspace->getKey()])->save(); $this->actingAs($user) ->get(route('filament.admin.pages.choose-workspace')) ->assertOk() ->assertSee('Active WS') ->assertDontSee('Archived WS'); }); // --- T017: it_shows_name_role_and_tenants_count_per_workspace --- it('shows name role and tenants count per workspace', function (): void { $user = User::factory()->create(); $workspace = Workspace::factory()->create(['name' => 'Test Corp']); WorkspaceMembership::factory()->create([ 'workspace_id' => $workspace->getKey(), 'user_id' => $user->getKey(), 'role' => 'manager', ]); // Create 2 active tenants. Tenant::factory()->count(2)->create([ 'workspace_id' => $workspace->getKey(), 'status' => 'active', ]); // Create 1 inactive tenant (should not count). Tenant::factory()->create([ 'workspace_id' => $workspace->getKey(), 'status' => 'pending_validation', ]); $user->forceFill(['last_workspace_id' => (int) $workspace->getKey()])->save(); $this->actingAs($user) ->get(route('filament.admin.pages.choose-workspace')) ->assertOk() ->assertSee('Test Corp') ->assertSee('Manager') ->assertSee('2 tenants'); }); it('shows singular tenant label when count is one', function (): void { $user = User::factory()->create(); $workspace = Workspace::factory()->create(['name' => 'Solo Corp']); WorkspaceMembership::factory()->create([ 'workspace_id' => $workspace->getKey(), 'user_id' => $user->getKey(), 'role' => 'owner', ]); Tenant::factory()->create([ 'workspace_id' => $workspace->getKey(), 'status' => 'active', ]); $user->forceFill(['last_workspace_id' => (int) $workspace->getKey()])->save(); $this->actingAs($user) ->get(route('filament.admin.pages.choose-workspace')) ->assertOk() ->assertSee('1 tenant'); }); // --- T018: it_shows_empty_state_when_no_memberships --- it('shows empty state when no memberships', function (): void { $user = User::factory()->create(); $this->actingAs($user) ->get(route('filament.admin.pages.choose-workspace')) ->assertOk() ->assertSee("You don't have access to any workspace yet.", false); }); // --- T019: manage link visibility --- it('shows manage link for owner role', function (): void { $user = User::factory()->create(); $workspace = Workspace::factory()->create(); WorkspaceMembership::factory()->create([ 'workspace_id' => $workspace->getKey(), 'user_id' => $user->getKey(), 'role' => 'owner', ]); $user->forceFill(['last_workspace_id' => (int) $workspace->getKey()])->save(); $this->actingAs($user) ->get(route('filament.admin.pages.choose-workspace')) ->assertOk() ->assertSee('Manage workspaces'); }); it('hides manage link for non-owner roles', function (): void { $user = User::factory()->create(); $workspace = Workspace::factory()->create(); WorkspaceMembership::factory()->create([ 'workspace_id' => $workspace->getKey(), 'user_id' => $user->getKey(), 'role' => 'operator', ]); $user->forceFill(['last_workspace_id' => (int) $workspace->getKey()])->save(); $this->actingAs($user) ->get(route('filament.admin.pages.choose-workspace')) ->assertOk() ->assertDontSee('Manage workspaces'); }); // --- T020: it_has_no_n_plus_1_queries_in_chooser --- it('has no n plus 1 queries in chooser', function (): void { $user = User::factory()->create(); // Create 5 workspaces with memberships. $workspaces = Workspace::factory()->count(5)->create(); foreach ($workspaces as $workspace) { WorkspaceMembership::factory()->create([ 'workspace_id' => $workspace->getKey(), 'user_id' => $user->getKey(), 'role' => 'owner', ]); Tenant::factory()->count(2)->create([ 'workspace_id' => $workspace->getKey(), 'status' => 'active', ]); } $user->forceFill(['last_workspace_id' => (int) $workspaces->first()->getKey()])->save(); DB::enableQueryLog(); $this->actingAs($user) ->get(route('filament.admin.pages.choose-workspace')) ->assertOk(); $queryCount = count(DB::getQueryLog()); DB::disableQueryLog(); // Should be bounded: auth query + workspaces (with count) + memberships for roles + minimal Filament overhead. // Not proportional to workspace count. expect($queryCount)->toBeLessThan(20); }); // --- T031: it_persists_last_used_workspace_on_manual_selection --- it('persists last used workspace on manual selection', function (): void { $user = User::factory()->create(); $workspace = Workspace::factory()->create(); WorkspaceMembership::factory()->create([ 'workspace_id' => $workspace->getKey(), 'user_id' => $user->getKey(), 'role' => 'owner', ]); Livewire::actingAs($user) ->test(\App\Filament\Pages\ChooseWorkspace::class) ->call('selectWorkspace', (int) $workspace->getKey()); $user->refresh(); expect($user->last_workspace_id)->toBe((int) $workspace->getKey()); }); it('emits audit event on manual selection', function (): void { $user = User::factory()->create(); $workspace = Workspace::factory()->create(); WorkspaceMembership::factory()->create([ 'workspace_id' => $workspace->getKey(), 'user_id' => $user->getKey(), 'role' => 'owner', ]); Livewire::actingAs($user) ->test(\App\Filament\Pages\ChooseWorkspace::class) ->call('selectWorkspace', (int) $workspace->getKey()); $auditLog = AuditLog::query() ->where('action', AuditActionId::WorkspaceSelected->value) ->where('workspace_id', $workspace->getKey()) ->first(); expect($auditLog)->not->toBeNull(); expect($auditLog->metadata)->toMatchArray([ 'method' => 'manual', 'reason' => 'chooser', ]); }); // --- T033: it_rejects_non_member_workspace_selection_with_404 --- it('rejects non member workspace selection with 404', function (): void { $user = User::factory()->create(); $workspace = Workspace::factory()->create(); // User is NOT a member. Livewire::actingAs($user) ->test(\App\Filament\Pages\ChooseWorkspace::class) ->call('selectWorkspace', (int) $workspace->getKey()) ->assertStatus(404); }); it('rejects archived workspace selection with 404', function (): void { $user = User::factory()->create(); $workspace = Workspace::factory()->create(['archived_at' => now()]); WorkspaceMembership::factory()->create([ 'workspace_id' => $workspace->getKey(), 'user_id' => $user->getKey(), 'role' => 'owner', ]); Livewire::actingAs($user) ->test(\App\Filament\Pages\ChooseWorkspace::class) ->call('selectWorkspace', (int) $workspace->getKey()) ->assertStatus(404); });