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()); $draft = createOnboardingDraft([ 'workspace' => $workspace, 'started_by' => $user, 'updated_by' => $user, 'status' => 'completed', 'state' => [ 'entra_tenant_id' => '11111111-1111-1111-1111-111111111111', 'tenant_name' => 'Completed Contoso', 'environment' => 'prod', ], ]); $this->actingAs($user) ->get(route('admin.onboarding.draft', ['onboardingDraft' => $draft->getKey()])) ->assertSuccessful() ->assertSee('This onboarding draft is Completed.') ->assertSee('Completed, cancelled, and lifecycle-locked drafts remain viewable, but they cannot return to editable wizard mode.') ->assertSee('Return to onboarding') ->assertDontSee('Cancel draft'); }); it('shows a safe non-editable summary for cancelled drafts', function (): void { $workspace = Workspace::factory()->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()); $draft = createOnboardingDraft([ 'workspace' => $workspace, 'started_by' => $user, 'updated_by' => $user, 'status' => 'cancelled', 'state' => [ 'entra_tenant_id' => '22222222-2222-2222-2222-222222222222', 'tenant_name' => 'Cancelled Contoso', 'environment' => 'prod', ], ]); $this->actingAs($user) ->get(route('admin.onboarding.draft', ['onboardingDraft' => $draft->getKey()])) ->assertSuccessful() ->assertSee('This onboarding draft is Cancelled.') ->assertSee('Completed, cancelled, and lifecycle-locked drafts remain viewable, but they cannot return to editable wizard mode.') ->assertSee('Return to onboarding') ->assertDontSee('Cancel draft'); }); it('shows a linked tenant action when the onboarding draft has a viewable tenant record', function (): void { $tenant = Tenant::factory()->onboarding()->create([ 'name' => 'Linked Onboarding Tenant', ]); [$user, $tenant] = createUserWithTenant(tenant: $tenant, role: 'owner', ensureDefaultMicrosoftProviderConnection: false); $draft = createOnboardingDraft([ 'workspace' => $tenant->workspace, 'tenant' => $tenant, 'started_by' => $user, 'updated_by' => $user, 'state' => [ 'entra_tenant_id' => (string) $tenant->tenant_id, 'tenant_name' => (string) $tenant->name, ], ]); Livewire::actingAs($user) ->test(ManagedTenantOnboardingWizard::class, [ 'onboardingDraft' => (int) $draft->getKey(), ]) ->assertActionVisible('view_linked_tenant'); }); it('cancels a resumable onboarding draft through the confirmed header action', function (): void { $workspace = Workspace::factory()->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()); $draft = createOnboardingDraft([ 'workspace' => $workspace, 'started_by' => $user, 'updated_by' => $user, 'state' => [ 'entra_tenant_id' => '33333333-3333-3333-3333-333333333333', 'tenant_name' => 'Draft To Cancel', ], ]); Livewire::actingAs($user) ->test(ManagedTenantOnboardingWizard::class, [ 'onboardingDraft' => (int) $draft->getKey(), ]) ->mountAction('cancel_onboarding_draft') ->callMountedAction() ->assertNotified('Onboarding draft cancelled') ->assertRedirect(route('admin.onboarding.draft', ['onboardingDraft' => (int) $draft->getKey()])); $draft->refresh(); expect($draft->cancelled_at)->not->toBeNull() ->and($draft->isResumable())->toBeFalse() ->and($draft->current_step)->toBe('cancelled'); $this->actingAs($user) ->get(route('admin.onboarding.draft', ['onboardingDraft' => (int) $draft->getKey()])) ->assertSuccessful() ->assertSee('This onboarding draft is Cancelled.') ->assertDontSee('Cancel draft'); expect(AuditLog::query() ->where('workspace_id', (int) $workspace->getKey()) ->where('action', AuditActionId::ManagedTenantOnboardingCancelled->value) ->where('resource_id', (string) $draft->getKey()) ->exists())->toBeTrue(); }); it('keeps cancelled drafts out of the landing redirect and picker flow', function (): void { $workspace = Workspace::factory()->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()); $activeDraft = createOnboardingDraft([ 'workspace' => $workspace, 'started_by' => $user, 'updated_by' => $user, 'state' => [ 'entra_tenant_id' => '44444444-4444-4444-4444-444444444444', 'tenant_name' => 'Active Draft', ], ]); createOnboardingDraft([ 'workspace' => $workspace, 'started_by' => $user, 'updated_by' => $user, 'status' => 'cancelled', 'state' => [ 'entra_tenant_id' => '55555555-5555-5555-5555-555555555555', 'tenant_name' => 'Cancelled Draft', ], ]); $this->actingAs($user) ->get(route('admin.onboarding')) ->assertRedirect(route('admin.onboarding.draft', ['onboardingDraft' => $activeDraft->getKey()])); }); it('returns a linked onboarding tenant to draft when its last onboarding draft is cancelled', function (): void { $tenant = Tenant::factory()->onboarding()->create([ 'name' => 'Cancelled Linked Tenant', ]); [$user, $tenant] = createUserWithTenant(tenant: $tenant, role: 'owner', ensureDefaultMicrosoftProviderConnection: false); $draft = createOnboardingDraft([ 'workspace' => $tenant->workspace, 'tenant' => $tenant, 'started_by' => $user, 'updated_by' => $user, 'state' => [ 'entra_tenant_id' => (string) $tenant->tenant_id, 'tenant_name' => (string) $tenant->name, 'environment' => 'dev', ], ]); Livewire::actingAs($user) ->test(ManagedTenantOnboardingWizard::class, [ 'onboardingDraft' => (int) $draft->getKey(), ]) ->mountAction('cancel_onboarding_draft') ->callMountedAction() ->assertNotified('Onboarding draft cancelled'); $tenant->refresh(); expect($tenant->status)->toBe(Tenant::STATUS_DRAFT); expect(AuditLog::query() ->where('workspace_id', (int) $tenant->workspace_id) ->where('tenant_id', (int) $tenant->getKey()) ->where('action', AuditActionId::TenantReturnedToDraft->value) ->exists())->toBeTrue(); });