create(); $user = User::factory()->create(); WorkspaceMembership::factory()->create([ 'workspace_id' => (int) $workspace->getKey(), 'user_id' => (int) $user->getKey(), 'role' => 'manager', ]); session()->put(WorkspaceContext::SESSION_KEY, (int) $workspace->getKey()); $entraTenantId = '88888888-8888-8888-8888-888888888888'; $component = Livewire::actingAs($user)->test(ManagedTenantOnboardingWizard::class); $component->call('identifyManagedTenant', [ 'entra_tenant_id' => $entraTenantId, 'environment' => 'prod', 'name' => 'Acme', ]); $tenant = Tenant::query()->where('tenant_id', $entraTenantId)->firstOrFail(); $connection = ProviderConnection::factory()->platform()->consentGranted()->create([ 'workspace_id' => (int) $workspace->getKey(), 'tenant_id' => (int) $tenant->getKey(), 'provider' => 'microsoft', 'entra_tenant_id' => $entraTenantId, 'display_name' => 'Acme connection', 'is_default' => true, 'status' => 'connected', ]); $run = OperationRun::factory()->create([ 'tenant_id' => (int) $tenant->getKey(), 'workspace_id' => (int) $workspace->getKey(), 'user_id' => (int) $user->getKey(), 'type' => 'provider.connection.check', 'status' => OperationRunStatus::Completed->value, 'outcome' => OperationRunOutcome::Succeeded->value, 'context' => [ 'provider' => 'microsoft', 'module' => 'health_check', 'provider_connection_id' => (int) $connection->getKey(), 'target_scope' => [ 'entra_tenant_id' => $connection->entra_tenant_id, ], ], ]); $session = TenantOnboardingSession::query() ->where('workspace_id', (int) $workspace->getKey()) ->where('entra_tenant_id', $entraTenantId) ->firstOrFail(); $session->forceFill([ 'state' => array_merge($session->state ?? [], [ 'provider_connection_id' => (int) $connection->getKey(), 'verification_operation_run_id' => (int) $run->getKey(), ]), ])->save(); $component->set('onboardingSession', $session->fresh()); $component->set('selectedProviderConnectionId', (int) $connection->getKey()); $component ->call('completeOnboarding') ->assertStatus(403); $tenant->refresh(); expect($tenant->status)->not->toBe(Tenant::STATUS_ACTIVE); }); it('requires an override reason when verification is blocked and records an audit event when overridden', function (): void { Queue::fake(); $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()); $entraTenantId = '99999999-9999-9999-9999-999999999999'; $component = Livewire::actingAs($user)->test(ManagedTenantOnboardingWizard::class); $component->call('identifyManagedTenant', [ 'entra_tenant_id' => $entraTenantId, 'environment' => 'prod', 'name' => 'Acme', ]); $component->call('createProviderConnection', [ 'display_name' => 'Acme connection', 'client_id' => '00000000-0000-0000-0000-000000000000', 'client_secret' => 'super-secret', 'is_default' => true, ]); $component->call('startVerification'); $tenant = Tenant::query()->where('tenant_id', $entraTenantId)->firstOrFail(); $run = OperationRun::query() ->where('tenant_id', (int) $tenant->getKey()) ->where('type', 'provider.connection.check') ->firstOrFail(); $run->update([ 'status' => OperationRunStatus::Completed->value, 'outcome' => OperationRunOutcome::Failed->value, ]); $component ->set('data.override_blocked', true) ->set('data.override_reason', '') ->call('completeOnboarding') ->assertHasErrors(['data.override_reason']); $component ->set('data.override_blocked', true) ->set('data.override_reason', 'Temporary exception approved by owner') ->call('completeOnboarding'); $tenant->refresh(); expect($tenant->status)->toBe(Tenant::STATUS_ACTIVE); expect(AuditLog::query() ->where('workspace_id', (int) $workspace->getKey()) ->where('action', 'managed_tenant_onboarding.activation') ->exists())->toBeTrue(); }); it('requires an override when the stored verification report is blocked even if the run outcome says succeeded', function (): void { Queue::fake(); $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()); $entraTenantId = '12121212-3434-5656-7878-909090909090'; $component = Livewire::actingAs($user)->test(ManagedTenantOnboardingWizard::class); $component->call('identifyManagedTenant', [ 'entra_tenant_id' => $entraTenantId, 'environment' => 'prod', 'name' => 'Blocked By Report', ]); $tenant = Tenant::query()->where('tenant_id', $entraTenantId)->firstOrFail(); $connection = ProviderConnection::factory()->platform()->consentGranted()->create([ 'workspace_id' => (int) $workspace->getKey(), 'tenant_id' => (int) $tenant->getKey(), 'provider' => 'microsoft', 'entra_tenant_id' => $entraTenantId, 'display_name' => 'Blocked by report connection', 'is_default' => true, 'status' => 'connected', ]); $run = OperationRun::factory()->create([ 'tenant_id' => (int) $tenant->getKey(), 'workspace_id' => (int) $workspace->getKey(), 'user_id' => (int) $user->getKey(), 'type' => 'provider.connection.check', 'status' => OperationRunStatus::Completed->value, 'outcome' => OperationRunOutcome::Succeeded->value, 'context' => [ 'provider' => 'microsoft', 'module' => 'health_check', 'provider_connection_id' => (int) $connection->getKey(), 'target_scope' => [ 'entra_tenant_id' => $connection->entra_tenant_id, ], 'verification_report' => VerificationReportWriter::build('provider.connection.check', [ [ 'key' => 'permission_check', 'title' => 'Graph permissions', 'status' => 'fail', 'severity' => 'high', 'blocking' => true, 'reason_code' => 'permission_denied', 'message' => 'Missing required Graph permissions.', 'evidence' => [], 'next_steps' => [], ], ]), ], ]); $session = TenantOnboardingSession::query() ->where('workspace_id', (int) $workspace->getKey()) ->where('entra_tenant_id', $entraTenantId) ->firstOrFail(); $session->forceFill([ 'state' => array_merge($session->state ?? [], [ 'provider_connection_id' => (int) $connection->getKey(), 'verification_operation_run_id' => (int) $run->getKey(), ]), ])->save(); $component->set('onboardingSession', $session->fresh()); $component->set('selectedProviderConnectionId', (int) $connection->getKey()); $component->call('completeOnboarding'); $tenant->refresh(); expect($tenant->status)->not->toBe(Tenant::STATUS_ACTIVE); $component ->set('data.override_blocked', true) ->set('data.override_reason', 'Owner approved temporary exception') ->call('completeOnboarding'); $tenant->refresh(); expect($tenant->status)->toBe(Tenant::STATUS_ACTIVE); });