create(); $tenant = Tenant::factory()->create([ 'workspace_id' => (int) $workspace->getKey(), 'status' => Tenant::STATUS_ONBOARDING, 'name' => 'Shared Tenant', ]); $creator = User::factory()->create(['name' => 'Draft Creator']); $manager = User::factory()->create(['name' => 'Workspace Manager']); createUserWithTenant( tenant: $tenant, user: $creator, role: 'owner', workspaceRole: 'owner', ensureDefaultMicrosoftProviderConnection: false, ); createUserWithTenant( tenant: $tenant, user: $manager, role: 'manager', workspaceRole: 'manager', ensureDefaultMicrosoftProviderConnection: false, ); $draft = createOnboardingDraft([ 'workspace' => $workspace, 'tenant' => $tenant, 'started_by' => $creator, 'updated_by' => $manager, 'state' => [ 'entra_tenant_id' => (string) $tenant->tenant_id, 'tenant_name' => (string) $tenant->name, ], ]); session()->put(WorkspaceContext::SESSION_KEY, (int) $workspace->getKey()); $this->actingAs($manager) ->get(route('admin.onboarding.draft', ['onboardingDraft' => $draft->getKey()])) ->assertSuccessful() ->assertSee('Onboarding draft') ->assertSee('Draft Creator') ->assertSee('Workspace Manager'); }); it('allows a manager to cancel a shared onboarding draft', function (): void { $workspace = Workspace::factory()->create(); $tenant = Tenant::factory()->create([ 'workspace_id' => (int) $workspace->getKey(), 'status' => Tenant::STATUS_ONBOARDING, ]); $creator = User::factory()->create(); $manager = User::factory()->create(); createUserWithTenant( tenant: $tenant, user: $creator, role: 'owner', workspaceRole: 'owner', ensureDefaultMicrosoftProviderConnection: false, ); createUserWithTenant( tenant: $tenant, user: $manager, role: 'manager', workspaceRole: 'manager', ensureDefaultMicrosoftProviderConnection: false, ); $draft = createOnboardingDraft([ 'workspace' => $workspace, 'tenant' => $tenant, 'started_by' => $creator, 'updated_by' => $creator, ]); session()->put(WorkspaceContext::SESSION_KEY, (int) $workspace->getKey()); Livewire::actingAs($manager) ->test(ManagedTenantOnboardingWizard::class, [ 'onboardingDraft' => (int) $draft->getKey(), ]) ->mountAction('cancel_onboarding_draft') ->callMountedAction() ->assertNotified('Onboarding draft cancelled'); expect($draft->fresh()->isCancelled())->toBeTrue(); }); it('returns 404 for non-members when requesting a shared onboarding draft', function (): void { $workspace = Workspace::factory()->create(); $tenant = Tenant::factory()->create([ 'workspace_id' => (int) $workspace->getKey(), 'status' => Tenant::STATUS_ONBOARDING, ]); $creator = User::factory()->create(); $nonMember = User::factory()->create(); createUserWithTenant( tenant: $tenant, user: $creator, role: 'owner', workspaceRole: 'owner', ensureDefaultMicrosoftProviderConnection: false, ); $draft = createOnboardingDraft([ 'workspace' => $workspace, 'tenant' => $tenant, 'started_by' => $creator, 'updated_by' => $creator, ]); session()->put(WorkspaceContext::SESSION_KEY, (int) $workspace->getKey()); $this->actingAs($nonMember) ->get(route('admin.onboarding.draft', ['onboardingDraft' => $draft->getKey()])) ->assertNotFound(); }); it('returns 403 for readonly members even when they have tenant access', function (): void { $workspace = Workspace::factory()->create(); $tenant = Tenant::factory()->create([ 'workspace_id' => (int) $workspace->getKey(), 'status' => Tenant::STATUS_ONBOARDING, ]); $readonly = User::factory()->create(); createUserWithTenant( tenant: $tenant, user: $readonly, role: 'readonly', workspaceRole: 'readonly', ensureDefaultMicrosoftProviderConnection: false, ); $draft = createOnboardingDraft([ 'workspace' => $workspace, 'tenant' => $tenant, 'started_by' => $readonly, 'updated_by' => $readonly, ]); session()->put(WorkspaceContext::SESSION_KEY, (int) $workspace->getKey()); $this->actingAs($readonly) ->get(route('admin.onboarding.draft', ['onboardingDraft' => $draft->getKey()])) ->assertForbidden(); }); it('returns 404 for workspace members without linked archived tenant entitlement', function (): void { $workspace = Workspace::factory()->create(); $archivedTenant = Tenant::factory()->archived()->create([ 'workspace_id' => (int) $workspace->getKey(), ]); $creator = User::factory()->create(); $workspaceOwner = User::factory()->create(); createUserWithTenant( tenant: $archivedTenant, user: $creator, role: 'owner', workspaceRole: 'owner', ensureDefaultMicrosoftProviderConnection: false, ); \App\Models\WorkspaceMembership::factory()->create([ 'workspace_id' => (int) $workspace->getKey(), 'user_id' => (int) $workspaceOwner->getKey(), 'role' => 'owner', ]); $draft = createOnboardingDraft([ 'workspace' => $workspace, 'tenant' => $archivedTenant, 'started_by' => $creator, 'updated_by' => $creator, 'state' => [ 'entra_tenant_id' => (string) $archivedTenant->tenant_id, 'tenant_name' => (string) $archivedTenant->name, ], ]); session()->put(WorkspaceContext::SESSION_KEY, (int) $workspace->getKey()); $this->actingAs($workspaceOwner) ->get(route('admin.onboarding.draft', ['onboardingDraft' => $draft->getKey()])) ->assertNotFound(); }); it('shows a non-editable summary for entitled operators when the linked tenant is already archived', function (): void { $workspace = Workspace::factory()->create(); $archivedTenant = Tenant::factory()->archived()->create([ 'workspace_id' => (int) $workspace->getKey(), 'name' => 'Archived Linked Tenant', ]); $user = User::factory()->create(); createUserWithTenant( tenant: $archivedTenant, user: $user, role: 'owner', workspaceRole: 'owner', ensureDefaultMicrosoftProviderConnection: false, ); $draft = createOnboardingDraft([ 'workspace' => $workspace, 'tenant' => $archivedTenant, 'started_by' => $user, 'updated_by' => $user, 'state' => [ 'entra_tenant_id' => (string) $archivedTenant->tenant_id, 'tenant_name' => (string) $archivedTenant->name, ], ]); session()->put(WorkspaceContext::SESSION_KEY, (int) $workspace->getKey()); $this->actingAs($user) ->get(route('admin.onboarding.draft', ['onboardingDraft' => $draft->getKey()])) ->assertSuccessful() ->assertSee('This onboarding draft is Draft.') ->assertSee('Completed, cancelled, and lifecycle-locked drafts remain viewable, but they cannot return to editable wizard mode.') ->assertDontSee('Cancel draft') ->assertSee('View tenant'); });