create(); $workspace = Workspace::factory()->create(); WorkspaceMembership::factory()->create([ 'workspace_id' => $workspace->getKey(), 'user_id' => $user->getKey(), 'role' => 'owner', ]); $response = $this->actingAs($user)->get('/admin/_test/workspace-context'); // Should redirect via tenant branching (not to chooser). $response->assertRedirect(); $location = $response->headers->get('Location'); expect($location)->not->toContain('choose-workspace'); }); // --- T006: it_emits_audit_event_on_auto_selection_single_membership --- it('emits audit event on auto selection single membership', function (): void { $user = User::factory()->create(); $workspace = Workspace::factory()->create(); WorkspaceMembership::factory()->create([ 'workspace_id' => $workspace->getKey(), 'user_id' => $user->getKey(), 'role' => 'owner', ]); $this->actingAs($user)->get('/admin/_test/workspace-context'); $auditLog = AuditLog::query() ->where('action', AuditActionId::WorkspaceAutoSelected->value) ->where('workspace_id', $workspace->getKey()) ->first(); expect($auditLog)->not->toBeNull(); expect($auditLog->metadata)->toMatchArray([ 'method' => 'auto', 'reason' => 'single_membership', ]); expect($auditLog->resource_type)->toBe('workspace'); expect($auditLog->resource_id)->toBe((string) $workspace->getKey()); }); // --- T007: it_redirects_via_tenant_count_branching_after_single_auto_resume --- it('redirects to managed tenants index when single workspace has zero tenants', function (): void { $user = User::factory()->create(); $workspace = Workspace::factory()->create(); WorkspaceMembership::factory()->create([ 'workspace_id' => $workspace->getKey(), 'user_id' => $user->getKey(), 'role' => 'owner', ]); $response = $this->actingAs($user)->get('/admin/_test/workspace-context'); $expectedRoute = route('admin.workspace.managed-tenants.index', [ 'workspace' => $workspace->slug ?? $workspace->getKey(), ]); $response->assertRedirect($expectedRoute); }); it('redirects to tenant dashboard when single workspace has one active tenant', function (): void { $user = User::factory()->create(); $workspace = Workspace::factory()->create(); WorkspaceMembership::factory()->create([ 'workspace_id' => $workspace->getKey(), 'user_id' => $user->getKey(), 'role' => 'owner', ]); $tenant = Tenant::factory()->create([ 'workspace_id' => $workspace->getKey(), 'status' => 'active', ]); $user->tenants()->syncWithoutDetaching([ $tenant->getKey() => ['role' => 'owner'], ]); $response = $this->actingAs($user)->get('/admin/_test/workspace-context'); $response->assertRedirect(); $location = $response->headers->get('Location'); expect($location)->toContain('/admin/t/'); }); // --- T008: it_allows_request_when_session_workspace_is_valid --- it('allows request when session workspace is valid', function (): void { $user = User::factory()->create(); $workspace = Workspace::factory()->create(); WorkspaceMembership::factory()->create([ 'workspace_id' => $workspace->getKey(), 'user_id' => $user->getKey(), 'role' => 'owner', ]); $response = $this->actingAs($user) ->withSession([WorkspaceContext::SESSION_KEY => (int) $workspace->getKey()]) ->get('/admin/_test/workspace-context'); // Should pass through (200) since session is valid. $response->assertOk(); $response->assertJson(['workspace_id' => (int) $workspace->getKey()]); }); // --- T010: it_auto_resumes_to_last_used_workspace_when_membership_valid --- it('auto resumes to last used workspace when membership valid', function (): void { $user = User::factory()->create(); $workspaceA = Workspace::factory()->create(); $workspaceB = Workspace::factory()->create(); WorkspaceMembership::factory()->create([ 'workspace_id' => $workspaceA->getKey(), 'user_id' => $user->getKey(), 'role' => 'owner', ]); WorkspaceMembership::factory()->create([ 'workspace_id' => $workspaceB->getKey(), 'user_id' => $user->getKey(), 'role' => 'operator', ]); // Set last_workspace_id to workspaceB. $user->forceFill(['last_workspace_id' => (int) $workspaceB->getKey()])->save(); $response = $this->actingAs($user)->get('/admin/_test/workspace-context'); // Should redirect via tenant branching (not to chooser). $response->assertRedirect(); $location = $response->headers->get('Location'); expect($location)->not->toContain('choose-workspace'); }); // --- T011: it_emits_audit_event_on_auto_selection_last_used --- it('emits audit event on auto selection last used', function (): void { $user = User::factory()->create(); $workspaceA = Workspace::factory()->create(); $workspaceB = Workspace::factory()->create(); WorkspaceMembership::factory()->create([ 'workspace_id' => $workspaceA->getKey(), 'user_id' => $user->getKey(), 'role' => 'owner', ]); WorkspaceMembership::factory()->create([ 'workspace_id' => $workspaceB->getKey(), 'user_id' => $user->getKey(), 'role' => 'operator', ]); $user->forceFill(['last_workspace_id' => (int) $workspaceB->getKey()])->save(); $this->actingAs($user)->get('/admin/_test/workspace-context'); $auditLog = AuditLog::query() ->where('action', AuditActionId::WorkspaceAutoSelected->value) ->where('workspace_id', $workspaceB->getKey()) ->first(); expect($auditLog)->not->toBeNull(); expect($auditLog->metadata)->toMatchArray([ 'method' => 'auto', 'reason' => 'last_used', ]); }); // --- T012: it_falls_back_to_chooser_when_multiple_workspaces_and_no_last_used --- it('falls back to chooser when multiple workspaces and no last used', function (): void { $user = User::factory()->create(); $user->forceFill(['last_workspace_id' => null])->save(); $workspaceA = Workspace::factory()->create(); $workspaceB = Workspace::factory()->create(); WorkspaceMembership::factory()->create([ 'workspace_id' => $workspaceA->getKey(), 'user_id' => $user->getKey(), 'role' => 'owner', ]); WorkspaceMembership::factory()->create([ 'workspace_id' => $workspaceB->getKey(), 'user_id' => $user->getKey(), 'role' => 'operator', ]); $response = $this->actingAs($user)->get('/admin/_test/workspace-context'); $response->assertRedirect('/admin/choose-workspace'); }); // --- T023: it_clears_session_when_active_workspace_membership_revoked --- it('clears session when active workspace membership revoked', function (): void { $user = User::factory()->create(); $workspace = Workspace::factory()->create(); // Set session but don't create membership — simulates revoked access. $response = $this->actingAs($user) ->withSession([WorkspaceContext::SESSION_KEY => (int) $workspace->getKey()]) ->get('/admin/_test/workspace-context'); // Should redirect to no-access or chooser since user has no memberships. $response->assertRedirect(); }); // --- T024: it_redirects_to_chooser_when_last_workspace_membership_revoked_and_shows_warning --- it('redirects to chooser when last workspace membership revoked', function (): void { $user = User::factory()->create(); $workspaceA = Workspace::factory()->create(); $workspaceB = Workspace::factory()->create(); $workspaceC = Workspace::factory()->create(); // User is member of A and C but NOT B. last_workspace_id points to B. WorkspaceMembership::factory()->create([ 'workspace_id' => $workspaceA->getKey(), 'user_id' => $user->getKey(), 'role' => 'owner', ]); WorkspaceMembership::factory()->create([ 'workspace_id' => $workspaceC->getKey(), 'user_id' => $user->getKey(), 'role' => 'operator', ]); $user->forceFill(['last_workspace_id' => (int) $workspaceB->getKey()])->save(); $response = $this->actingAs($user)->get('/admin/_test/workspace-context'); // last_workspace_id should be cleared. $user->refresh(); expect($user->last_workspace_id)->toBeNull(); // Should redirect to chooser since user has 2 valid workspaces and last_workspace was invalid. $response->assertRedirect('/admin/choose-workspace'); }); it('redirects to chooser when last workspace is archived', function (): void { $user = User::factory()->create(); $workspaceA = Workspace::factory()->create(); $workspaceB = Workspace::factory()->create(['archived_at' => now()]); WorkspaceMembership::factory()->create([ 'workspace_id' => $workspaceA->getKey(), 'user_id' => $user->getKey(), 'role' => 'owner', ]); WorkspaceMembership::factory()->create([ 'workspace_id' => $workspaceB->getKey(), 'user_id' => $user->getKey(), 'role' => 'operator', ]); $user->forceFill(['last_workspace_id' => (int) $workspaceB->getKey()])->save(); $response = $this->actingAs($user)->get('/admin/_test/workspace-context'); // Step 5 auto-resumes to workspaceA (only selectable). setCurrentWorkspace updates last_workspace_id. $user->refresh(); expect($user->last_workspace_id)->toBe((int) $workspaceA->getKey()); // Only workspaceA is selectable → single membership auto-resume. $response->assertRedirect(); $location = $response->headers->get('Location'); expect($location)->not->toContain('choose-workspace'); }); // --- T025: it_handles_archived_workspace_in_session --- it('handles archived workspace in session', 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', ]); $response = $this->actingAs($user) ->withSession([WorkspaceContext::SESSION_KEY => (int) $workspace->getKey()]) ->get('/admin/_test/workspace-context'); // Session with archived workspace should be treated as stale. $response->assertRedirect(); $location = $response->headers->get('Location'); // Should redirect to chooser or no-access. expect($location)->toMatch('/choose-workspace|no-access/'); }); // --- T030: it_forces_chooser_with_choose_param --- it('forces chooser with choose param', function (): void { $user = User::factory()->create(); $workspace = Workspace::factory()->create(); WorkspaceMembership::factory()->create([ 'workspace_id' => $workspace->getKey(), 'user_id' => $user->getKey(), 'role' => 'owner', ]); $response = $this->actingAs($user) ->withSession([WorkspaceContext::SESSION_KEY => (int) $workspace->getKey()]) ->get('/admin/_test/workspace-context?choose=1'); $response->assertRedirect('/admin/choose-workspace'); }); it('forces chooser with choose param even when single workspace', function (): void { $user = User::factory()->create(); $workspace = Workspace::factory()->create(); WorkspaceMembership::factory()->create([ 'workspace_id' => $workspace->getKey(), 'user_id' => $user->getKey(), 'role' => 'owner', ]); // No session set — normally would auto-resume, but ?choose=1 forces chooser. $response = $this->actingAs($user)->get('/admin/_test/workspace-context?choose=1'); $response->assertRedirect('/admin/choose-workspace'); });