onboarding()->create(); $owner = User::factory()->create(); $otherWorkspaceUser = User::factory()->create(); $otherWorkspace = \App\Models\Workspace::factory()->create(); createUserWithTenant( tenant: $tenant, user: $owner, role: 'owner', workspaceRole: 'owner', ensureDefaultMicrosoftProviderConnection: false, ); WorkspaceMembership::factory()->create([ 'workspace_id' => (int) $otherWorkspace->getKey(), 'user_id' => (int) $otherWorkspaceUser->getKey(), 'role' => 'owner', ]); $draft = createOnboardingDraft([ 'workspace' => $tenant->workspace, 'tenant' => $tenant, 'started_by' => $owner, 'updated_by' => $owner, ]); session()->put(WorkspaceContext::SESSION_KEY, (int) $otherWorkspace->getKey()); $result = app(TenantOnboardingSessionPolicy::class)->view($otherWorkspaceUser, $draft); expect($result)->toBeInstanceOf(Response::class) ->and($result->allowed())->toBeFalse() ->and($result->status())->toBe(404); }); it('returns an honest forbidden message for entitled actors missing onboarding capability', function (): void { $tenant = Tenant::factory()->onboarding()->create(); $readonlyUser = User::factory()->create(); createUserWithTenant( tenant: $tenant, user: $readonlyUser, role: 'readonly', workspaceRole: 'readonly', ensureDefaultMicrosoftProviderConnection: false, ); $draft = createOnboardingDraft([ 'workspace' => $tenant->workspace, 'tenant' => $tenant, 'started_by' => $readonlyUser, 'updated_by' => $readonlyUser, ]); session()->put(WorkspaceContext::SESSION_KEY, (int) $tenant->workspace_id); $viewResult = app(TenantOnboardingSessionPolicy::class)->view($readonlyUser, $draft); $cancelResult = app(TenantOnboardingSessionPolicy::class)->cancel($readonlyUser, $draft); expect($viewResult)->toBeInstanceOf(Response::class) ->and($viewResult->allowed())->toBeFalse() ->and($viewResult->message())->toBe('You do not have permission to continue this onboarding draft.') ->and($cancelResult)->toBeInstanceOf(Response::class) ->and($cancelResult->allowed())->toBeFalse() ->and($cancelResult->message())->toBe('You do not have permission to cancel this onboarding draft.'); });