create([ 'tenant_id' => (int) $tenant->getKey(), 'workspace_id' => (int) $tenant->workspace_id, 'permission_key' => trim($key), 'status' => 'granted', 'details' => ['source' => 'picker-readiness-test'], 'last_checked_at' => $staleDays === null ? now() : now()->subDays($staleDays), ]); } } it('shows a draft picker with resumable draft metadata when multiple drafts exist', function (): void { $workspace = Workspace::factory()->create(); $user = User::factory()->create(); $startedBy = User::factory()->create(['name' => 'Primary Owner']); $updatedBy = User::factory()->create(['name' => 'Second Operator']); foreach ([$user, $startedBy, $updatedBy] as $member) { WorkspaceMembership::factory()->create([ 'workspace_id' => (int) $workspace->getKey(), 'user_id' => (int) $member->getKey(), 'role' => 'owner', ]); } session()->put(WorkspaceContext::SESSION_KEY, (int) $workspace->getKey()); createOnboardingDraft([ 'workspace' => $workspace, 'started_by' => $startedBy, 'updated_by' => $updatedBy, 'state' => [ 'entra_tenant_id' => '11111111-1111-1111-1111-111111111111', 'tenant_name' => 'Contoso', 'environment' => 'prod', 'primary_domain' => 'contoso.example', ], ]); createOnboardingDraft([ 'workspace' => $workspace, 'started_by' => $updatedBy, 'updated_by' => $startedBy, 'current_step' => 'connection', 'state' => [ 'entra_tenant_id' => '22222222-2222-2222-2222-222222222222', 'tenant_name' => 'Fabrikam', 'environment' => 'staging', ], ]); $this->actingAs($user) ->get(route('admin.onboarding')) ->assertSuccessful() ->assertSee('Multiple onboarding drafts are available.') ->assertSee('Contoso') ->assertSee('Fabrikam') ->assertSee('Current stage') ->assertSee('Started by') ->assertSee('Last updated by') ->assertSee('Primary Owner') ->assertSee('Second Operator') ->assertSee('Resume onboarding') ->assertSee('View summary'); }); it('excludes completed and cancelled drafts from the landing picker', 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()); createOnboardingDraft([ 'workspace' => $workspace, 'started_by' => $user, 'updated_by' => $user, 'state' => [ 'entra_tenant_id' => '33333333-3333-3333-3333-333333333333', 'tenant_name' => 'Visible Draft A', ], ]); createOnboardingDraft([ 'workspace' => $workspace, 'started_by' => $user, 'updated_by' => $user, 'state' => [ 'entra_tenant_id' => '44444444-4444-4444-4444-444444444444', 'tenant_name' => 'Visible Draft B', ], ]); createOnboardingDraft([ 'workspace' => $workspace, 'started_by' => $user, 'updated_by' => $user, 'status' => 'completed', 'state' => [ 'entra_tenant_id' => '55555555-5555-5555-5555-555555555555', 'tenant_name' => 'Completed Draft', ], ]); createOnboardingDraft([ 'workspace' => $workspace, 'started_by' => $user, 'updated_by' => $user, 'status' => 'cancelled', 'state' => [ 'entra_tenant_id' => '66666666-6666-6666-6666-666666666666', 'tenant_name' => 'Cancelled Draft', ], ]); $this->actingAs($user) ->get(route('admin.onboarding')) ->assertSuccessful() ->assertSee('Visible Draft A') ->assertSee('Visible Draft B') ->assertDontSee('Completed Draft') ->assertDontSee('Cancelled Draft'); }); it('shows compact readiness snippets for multiple resumable drafts while keeping picker actions', 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', ]); $blockedTenant = Tenant::factory()->onboarding()->create([ 'workspace_id' => (int) $workspace->getKey(), 'tenant_id' => '41414141-4141-4141-4141-414141414141', 'name' => 'Needs Connection Tenant', ]); $readyTenant = Tenant::factory()->onboarding()->create([ 'workspace_id' => (int) $workspace->getKey(), 'tenant_id' => '42424242-4242-4242-4242-424242424242', 'name' => 'Ready Picker Tenant', ]); $user->tenants()->syncWithoutDetaching([ $blockedTenant->getKey() => ['role' => 'owner'], $readyTenant->getKey() => ['role' => 'owner'], ]); seedPickerReadinessPermissions($readyTenant); $connection = ProviderConnection::factory()->platform()->verifiedHealthy()->create([ 'workspace_id' => (int) $workspace->getKey(), 'tenant_id' => (int) $readyTenant->getKey(), 'provider' => 'microsoft', 'entra_tenant_id' => (string) $readyTenant->tenant_id, 'display_name' => 'Ready picker connection', 'is_default' => true, ]); $run = OperationRun::factory()->create([ 'workspace_id' => (int) $workspace->getKey(), 'tenant_id' => (int) $readyTenant->getKey(), 'type' => 'provider.connection.check', 'status' => OperationRunStatus::Completed->value, 'outcome' => OperationRunOutcome::Succeeded->value, 'context' => [ 'provider_connection_id' => (int) $connection->getKey(), 'verification_report' => VerificationReportWriter::build('provider.connection.check', [ [ 'key' => 'provider.connection.check', 'title' => 'Provider connection check', 'status' => 'pass', 'severity' => 'info', 'blocking' => false, 'reason_code' => 'ok', 'message' => 'Connection is healthy.', 'evidence' => [], 'next_steps' => [], ], ]), ], ]); createOnboardingDraft([ 'workspace' => $workspace, 'tenant' => $blockedTenant, 'started_by' => $user, 'updated_by' => $user, 'current_step' => 'connection', 'state' => [ 'entra_tenant_id' => (string) $blockedTenant->tenant_id, 'tenant_name' => (string) $blockedTenant->name, ], ]); createOnboardingDraft([ 'workspace' => $workspace, 'tenant' => $readyTenant, 'started_by' => $user, 'updated_by' => $user, 'current_step' => 'complete', 'state' => [ 'entra_tenant_id' => (string) $readyTenant->tenant_id, 'tenant_name' => (string) $readyTenant->name, 'provider_connection_id' => (int) $connection->getKey(), 'verification_operation_run_id' => (int) $run->getKey(), ], ]); session()->put(WorkspaceContext::SESSION_KEY, (int) $workspace->getKey()); $this->actingAs($user) ->get(route('admin.onboarding')) ->assertSuccessful() ->assertSee('Needs Connection Tenant') ->assertSee('Ready Picker Tenant') ->assertSee('Compact readiness') ->assertSee('Provider connection required') ->assertSee('Connect provider') ->assertSee('Ready for activation') ->assertSee('Verification and permission evidence are current.') ->assertSee('Resume onboarding') ->assertSee('View summary'); }); it('shows stale and mismatched readiness cues across multiple drafts in the picker', 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', ]); $staleTenant = Tenant::factory()->onboarding()->create([ 'workspace_id' => (int) $workspace->getKey(), 'tenant_id' => '44444444-4444-4444-4444-444444444444', 'name' => 'Picker Stale Tenant', ]); $mismatchTenant = Tenant::factory()->onboarding()->create([ 'workspace_id' => (int) $workspace->getKey(), 'tenant_id' => '45454545-4545-4545-4545-454545454545', 'name' => 'Picker Mismatch Tenant', ]); $user->tenants()->syncWithoutDetaching([ $staleTenant->getKey() => ['role' => 'owner'], $mismatchTenant->getKey() => ['role' => 'owner'], ]); seedPickerReadinessPermissions($staleTenant, staleDays: 45); seedPickerReadinessPermissions($mismatchTenant); $staleConnection = ProviderConnection::factory()->platform()->verifiedHealthy()->create([ 'workspace_id' => (int) $workspace->getKey(), 'tenant_id' => (int) $staleTenant->getKey(), 'provider' => 'microsoft', 'entra_tenant_id' => (string) $staleTenant->tenant_id, 'display_name' => 'Stale picker connection', 'is_default' => true, ]); $oldMismatchConnection = ProviderConnection::factory()->platform()->verifiedHealthy()->create([ 'workspace_id' => (int) $workspace->getKey(), 'tenant_id' => (int) $mismatchTenant->getKey(), 'provider' => 'microsoft', 'entra_tenant_id' => '46464646-4646-4646-4646-464646464646', 'display_name' => 'Old mismatch picker connection', ]); $selectedMismatchConnection = ProviderConnection::factory()->platform()->verifiedHealthy()->create([ 'workspace_id' => (int) $workspace->getKey(), 'tenant_id' => (int) $mismatchTenant->getKey(), 'provider' => 'microsoft', 'entra_tenant_id' => (string) $mismatchTenant->tenant_id, 'display_name' => 'Selected mismatch picker connection', 'is_default' => true, ]); $staleRun = OperationRun::factory()->create([ 'workspace_id' => (int) $workspace->getKey(), 'tenant_id' => (int) $staleTenant->getKey(), 'type' => 'provider.connection.check', 'status' => OperationRunStatus::Completed->value, 'outcome' => OperationRunOutcome::Succeeded->value, 'context' => [ 'provider_connection_id' => (int) $staleConnection->getKey(), 'verification_report' => VerificationReportWriter::build('provider.connection.check', [ [ 'key' => 'provider.connection.check', 'title' => 'Provider connection check', 'status' => 'pass', 'severity' => 'info', 'blocking' => false, 'reason_code' => 'ok', 'message' => 'Connection is healthy.', 'evidence' => [], 'next_steps' => [], ], ]), ], ]); $mismatchRun = OperationRun::factory()->create([ 'workspace_id' => (int) $workspace->getKey(), 'tenant_id' => (int) $mismatchTenant->getKey(), 'type' => 'provider.connection.check', 'status' => OperationRunStatus::Completed->value, 'outcome' => OperationRunOutcome::Succeeded->value, 'context' => [ 'provider_connection_id' => (int) $oldMismatchConnection->getKey(), 'verification_report' => VerificationReportWriter::build('provider.connection.check', [ [ 'key' => 'provider.connection.check', 'title' => 'Provider connection check', 'status' => 'pass', 'severity' => 'info', 'blocking' => false, 'reason_code' => 'ok', 'message' => 'Connection is healthy.', 'evidence' => [], 'next_steps' => [], ], ]), ], ]); createOnboardingDraft([ 'workspace' => $workspace, 'tenant' => $staleTenant, 'started_by' => $user, 'updated_by' => $user, 'current_step' => 'complete', 'state' => [ 'entra_tenant_id' => (string) $staleTenant->tenant_id, 'tenant_name' => (string) $staleTenant->name, 'provider_connection_id' => (int) $staleConnection->getKey(), 'verification_operation_run_id' => (int) $staleRun->getKey(), ], ]); createOnboardingDraft([ 'workspace' => $workspace, 'tenant' => $mismatchTenant, 'started_by' => $user, 'updated_by' => $user, 'current_step' => 'complete', 'state' => [ 'entra_tenant_id' => (string) $mismatchTenant->tenant_id, 'tenant_name' => (string) $mismatchTenant->name, 'provider_connection_id' => (int) $selectedMismatchConnection->getKey(), 'verification_operation_run_id' => (int) $mismatchRun->getKey(), ], ]); session()->put(WorkspaceContext::SESSION_KEY, (int) $workspace->getKey()); $this->actingAs($user) ->get(route('admin.onboarding')) ->assertSuccessful() ->assertSee('Picker Stale Tenant') ->assertSee('Picker Mismatch Tenant') ->assertSee('Permission data is older than the 30-day freshness window.') ->assertSee('Verification evidence belongs to a different provider connection.') ->assertSee('Rerun verification'); }); it('preserves the single-draft landing redirect instead of rendering compact readiness', 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', ]); $draft = createOnboardingDraft([ 'workspace' => $workspace, 'started_by' => $user, 'updated_by' => $user, 'state' => [ 'entra_tenant_id' => '43434343-4343-4343-4343-434343434343', 'tenant_name' => 'Single Redirect Tenant', ], ]); session()->put(WorkspaceContext::SESSION_KEY, (int) $workspace->getKey()); $this->actingAs($user) ->get(route('admin.onboarding')) ->assertRedirect(route('admin.onboarding.draft', ['onboardingDraft' => $draft->getKey()])); });