create(); $user = User::factory()->create(); WorkspaceMembership::factory()->create([ 'workspace_id' => (int) $workspace->getKey(), 'user_id' => (int) $user->getKey(), 'role' => 'owner', ]); session()->put(WorkspaceContext::SESSION_KEY, (int) $workspace->getKey()); $this->actingAs($user) ->get(route('admin.onboarding.draft', ['onboardingDraft' => 999999])) ->assertNotFound(); }); it('returns 404 when a draft is requested from a different selected workspace', function (): void { $workspaceA = Workspace::factory()->create(); $workspaceB = Workspace::factory()->create(); $ownerA = User::factory()->create(); $userB = User::factory()->create(); WorkspaceMembership::factory()->create([ 'workspace_id' => (int) $workspaceA->getKey(), 'user_id' => (int) $ownerA->getKey(), 'role' => 'owner', ]); WorkspaceMembership::factory()->create([ 'workspace_id' => (int) $workspaceB->getKey(), 'user_id' => (int) $userB->getKey(), 'role' => 'owner', ]); $draft = createOnboardingDraft([ 'workspace' => $workspaceA, 'started_by' => $ownerA, 'updated_by' => $ownerA, ]); session()->put(WorkspaceContext::SESSION_KEY, (int) $workspaceB->getKey()); $this->actingAs($userB) ->get(route('admin.onboarding.draft', ['onboardingDraft' => $draft->getKey()])) ->assertNotFound(); }); it('returns 404 when a non-member requests an onboarding draft route', function (): void { $workspace = Workspace::factory()->create(); $owner = User::factory()->create(); $nonMember = User::factory()->create(); WorkspaceMembership::factory()->create([ 'workspace_id' => (int) $workspace->getKey(), 'user_id' => (int) $owner->getKey(), 'role' => 'owner', ]); $draft = createOnboardingDraft([ 'workspace' => $workspace, 'started_by' => $owner, 'updated_by' => $owner, ]); session()->put(WorkspaceContext::SESSION_KEY, (int) $workspace->getKey()); $this->actingAs($nonMember) ->get(route('admin.onboarding.draft', ['onboardingDraft' => $draft->getKey()])) ->assertNotFound(); }); it('returns 404 when the actor can access the workspace but lacks tenant entitlement for an identified draft', function (): void { $workspace = Workspace::factory()->create(); $tenant = Tenant::factory()->create([ 'workspace_id' => (int) $workspace->getKey(), 'status' => Tenant::STATUS_ONBOARDING, ]); $owner = User::factory()->create(); $workspaceOnlyUser = User::factory()->create(); WorkspaceMembership::factory()->create([ 'workspace_id' => (int) $workspace->getKey(), 'user_id' => (int) $owner->getKey(), 'role' => 'owner', ]); WorkspaceMembership::factory()->create([ 'workspace_id' => (int) $workspace->getKey(), 'user_id' => (int) $workspaceOnlyUser->getKey(), 'role' => 'owner', ]); $draft = createOnboardingDraft([ 'workspace' => $workspace, 'tenant' => $tenant, 'started_by' => $owner, 'updated_by' => $owner, ]); session()->put(WorkspaceContext::SESSION_KEY, (int) $workspace->getKey()); $this->actingAs($workspaceOnlyUser) ->get(route('admin.onboarding.draft', ['onboardingDraft' => $draft->getKey()])) ->assertNotFound(); }); it('returns 403 for an in-scope member with tenant entitlement but without onboarding capability', function (): void { $workspace = Workspace::factory()->create(); $tenant = Tenant::factory()->create([ 'workspace_id' => (int) $workspace->getKey(), 'status' => Tenant::STATUS_ONBOARDING, ]); $readonlyUser = User::factory()->create(); createUserWithTenant( tenant: $tenant, user: $readonlyUser, role: 'readonly', workspaceRole: 'readonly', ensureDefaultMicrosoftProviderConnection: false, ); $draft = createOnboardingDraft([ 'workspace' => $workspace, 'tenant' => $tenant, 'started_by' => $readonlyUser, 'updated_by' => $readonlyUser, ]); session()->put(WorkspaceContext::SESSION_KEY, (int) $workspace->getKey()); $this->actingAs($readonlyUser) ->get(route('admin.onboarding.draft', ['onboardingDraft' => $draft->getKey()])) ->assertForbidden(); }); it('mounts the requested draft with canonical persisted continuity state even when other drafts exist', function (): void { $workspace = Workspace::factory()->create(); $tenant = Tenant::factory()->create([ 'workspace_id' => (int) $workspace->getKey(), 'status' => Tenant::STATUS_ONBOARDING, 'tenant_id' => '23232323-2323-2323-2323-232323232323', 'name' => 'Canonical Continuity Tenant', ]); $otherTenant = Tenant::factory()->create([ 'workspace_id' => (int) $workspace->getKey(), 'status' => Tenant::STATUS_ONBOARDING, 'name' => 'Other Draft Tenant', ]); $user = User::factory()->create(); createUserWithTenant( tenant: $tenant, user: $user, role: 'owner', workspaceRole: 'owner', ensureDefaultMicrosoftProviderConnection: false, ); $user->tenants()->syncWithoutDetaching([ $otherTenant->getKey() => ['role' => 'owner'], ]); $connection = ProviderConnection::factory()->create([ 'workspace_id' => (int) $workspace->getKey(), 'tenant_id' => (int) $tenant->getKey(), 'provider' => 'microsoft', 'entra_tenant_id' => (string) $tenant->tenant_id, 'display_name' => 'Canonical continuity connection', 'is_default' => true, ]); createOnboardingDraft([ 'workspace' => $workspace, 'tenant' => $otherTenant, 'started_by' => $user, 'updated_by' => $user, 'current_step' => 'identify', ]); $draft = createOnboardingDraft([ 'workspace' => $workspace, 'tenant' => $tenant, 'started_by' => $user, 'updated_by' => $user, 'current_step' => 'bootstrap', 'state' => [ 'entra_tenant_id' => (string) $tenant->tenant_id, 'tenant_name' => (string) $tenant->name, 'environment' => 'prod', 'notes' => 'Canonical persisted note', 'provider_connection_id' => (int) $connection->getKey(), 'bootstrap_operation_types' => ['inventory.sync'], ], ]); session()->put(WorkspaceContext::SESSION_KEY, (int) $workspace->getKey()); Livewire::actingAs($user) ->test(ManagedTenantOnboardingWizard::class, [ 'onboardingDraft' => (int) $draft->getKey(), ]) ->assertSet('onboardingSessionId', (int) $draft->getKey()) ->assertSet('showDraftPicker', false) ->assertSet('showStartState', false) ->assertSet('selectedProviderConnectionId', (int) $connection->getKey()) ->assertSet('selectedBootstrapOperationTypes', ['inventory.sync']) ->assertSet('data.provider_connection_id', (int) $connection->getKey()) ->assertSet('data.name', (string) $tenant->name); }); it('refreshes continuity state from the requested draft even when public draft state is forged before refresh', function (): void { $workspace = Workspace::factory()->create(); $tenant = Tenant::factory()->create([ 'workspace_id' => (int) $workspace->getKey(), 'status' => Tenant::STATUS_ONBOARDING, 'tenant_id' => '24242424-2424-2424-2424-242424242424', 'name' => 'Requested Draft Tenant', ]); $otherTenant = Tenant::factory()->create([ 'workspace_id' => (int) $workspace->getKey(), 'status' => Tenant::STATUS_ONBOARDING, 'tenant_id' => '25252525-2525-2525-2525-252525252525', 'name' => 'Forged Draft Tenant', ]); $user = User::factory()->create(); createUserWithTenant( tenant: $tenant, user: $user, role: 'owner', workspaceRole: 'owner', ensureDefaultMicrosoftProviderConnection: false, ); $user->tenants()->syncWithoutDetaching([ $otherTenant->getKey() => ['role' => 'owner'], ]); $connection = ProviderConnection::factory()->create([ 'workspace_id' => (int) $workspace->getKey(), 'tenant_id' => (int) $tenant->getKey(), 'provider' => 'microsoft', 'entra_tenant_id' => (string) $tenant->tenant_id, 'display_name' => 'Requested connection', 'is_default' => true, ]); $otherConnection = ProviderConnection::factory()->create([ 'workspace_id' => (int) $workspace->getKey(), 'tenant_id' => (int) $otherTenant->getKey(), 'provider' => 'microsoft', 'entra_tenant_id' => (string) $otherTenant->tenant_id, 'display_name' => 'Forged connection', 'is_default' => true, ]); $draft = createOnboardingDraft([ 'workspace' => $workspace, 'tenant' => $tenant, 'started_by' => $user, 'updated_by' => $user, 'current_step' => 'verify', 'state' => [ 'entra_tenant_id' => (string) $tenant->tenant_id, 'tenant_name' => (string) $tenant->name, 'provider_connection_id' => (int) $connection->getKey(), ], ]); $otherDraft = createOnboardingDraft([ 'workspace' => $workspace, 'tenant' => $otherTenant, 'started_by' => $user, 'updated_by' => $user, 'current_step' => 'connection', 'state' => [ 'entra_tenant_id' => (string) $otherTenant->tenant_id, 'tenant_name' => (string) $otherTenant->name, 'provider_connection_id' => (int) $otherConnection->getKey(), ], ]); session()->put(WorkspaceContext::SESSION_KEY, (int) $workspace->getKey()); Livewire::actingAs($user) ->test(ManagedTenantOnboardingWizard::class, [ 'onboardingDraft' => (int) $draft->getKey(), ]) ->set('onboardingSession', $otherDraft) ->set('managedTenant', $otherTenant) ->call('refreshCheckpointLifecycle') ->assertSet('onboardingSessionId', (int) $draft->getKey()) ->assertSet('managedTenantId', (int) $tenant->getKey()) ->assertSet('selectedProviderConnectionId', (int) $connection->getKey()) ->assertSet('data.provider_connection_id', (int) $connection->getKey()) ->assertSet('data.name', (string) $tenant->name); });