TenantAtlas/tests/Feature/Onboarding/OnboardingVerificationClustersTest.php
ahmido 05a604cfb6 Spec 076: Tenant Required Permissions (enterprise remediation UX) (#92)
Implements Spec 076 enterprise remediation UX for tenant required permissions.

Highlights
- Above-the-fold overview (impact + counts) with missing-first experience
- Feature-based grouping, filters/search, copy-to-clipboard for missing app/delegated permissions
- Tenant-scoped deny-as-not-found semantics; DB-only viewing
- Centralized badge semantics (no ad-hoc status mapping)

Testing
- Feature tests for default filters, grouping, copy output, and non-member 404 behavior.

Integration
- Adds deep links from verification checks to the Required permissions page.

Co-authored-by: Ahmed Darrazi <ahmeddarrazi@MacBookPro.fritz.box>
Reviewed-on: #92
2026-02-05 22:08:51 +00:00

176 lines
5.9 KiB
PHP

<?php
declare(strict_types=1);
use App\Models\OperationRun;
use App\Models\Tenant;
use App\Models\TenantOnboardingSession;
use App\Models\User;
use App\Models\Workspace;
use App\Models\WorkspaceMembership;
use App\Filament\Pages\Workspaces\ManagedTenantOnboardingWizard;
use App\Support\Links\RequiredPermissionsLinks;
use App\Support\Verification\VerificationReportWriter;
use App\Support\Workspaces\WorkspaceContext;
use Livewire\Livewire;
it('renders clustered verification checks issues-first in the onboarding wizard verify step', 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());
$entraTenantId = 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa';
$tenant = Tenant::factory()->create([
'workspace_id' => (int) $workspace->getKey(),
'tenant_id' => $entraTenantId,
'external_id' => 'tenant-clusters-a',
'status' => 'onboarding',
]);
$checks = [
[
'key' => 'provider.connection.check',
'title' => 'Provider connection check',
'status' => 'pass',
'severity' => 'info',
'blocking' => false,
'reason_code' => 'ok',
'message' => 'Connection is healthy.',
'evidence' => [],
'next_steps' => [],
],
[
'key' => 'permissions.admin_consent',
'title' => 'Admin consent granted',
'status' => 'fail',
'severity' => 'critical',
'blocking' => true,
'reason_code' => 'ext.missing_permission',
'message' => 'Missing required application permissions.',
'evidence' => [
['kind' => 'missing_permission', 'value' => 'DeviceManagementConfiguration.Read.All'],
],
'next_steps' => [
[
'label' => 'Open required permissions',
'url' => RequiredPermissionsLinks::requiredPermissions($tenant),
],
],
],
[
'key' => 'permissions.intune_configuration',
'title' => 'Intune configuration access',
'status' => 'pass',
'severity' => 'info',
'blocking' => false,
'reason_code' => 'ok',
'message' => 'All required permissions are granted.',
'evidence' => [],
'next_steps' => [],
],
];
$verificationReport = VerificationReportWriter::build('provider.connection.check', $checks);
$run = OperationRun::factory()->create([
'workspace_id' => (int) $workspace->getKey(),
'tenant_id' => (int) $tenant->getKey(),
'type' => 'provider.connection.check',
'status' => 'completed',
'outcome' => 'succeeded',
'context' => [
'target_scope' => [
'entra_tenant_id' => $entraTenantId,
'entra_tenant_name' => 'Contoso',
],
'verification_report' => $verificationReport,
],
]);
TenantOnboardingSession::query()->create([
'workspace_id' => (int) $workspace->getKey(),
'tenant_id' => (int) $tenant->getKey(),
'entra_tenant_id' => $entraTenantId,
'current_step' => 'verify',
'state' => [
'verification_operation_run_id' => (int) $run->getKey(),
],
'started_by_user_id' => (int) $user->getKey(),
'updated_by_user_id' => (int) $user->getKey(),
]);
$this->actingAs($user)
->get('/admin/onboarding')
->assertSuccessful()
->assertSee('Technical details')
->assertSee('Admin consent granted')
->assertSee('Open required permissions')
->assertSee('Issues')
->assertSee($entraTenantId);
});
it('can open the onboarding verification technical details slideover without errors', 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());
$entraTenantId = 'bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb';
$tenant = Tenant::factory()->create([
'workspace_id' => (int) $workspace->getKey(),
'tenant_id' => $entraTenantId,
'external_id' => 'tenant-clusters-b',
'status' => 'onboarding',
]);
$verificationReport = VerificationReportWriter::build('provider.connection.check', []);
$run = OperationRun::factory()->create([
'workspace_id' => (int) $workspace->getKey(),
'tenant_id' => (int) $tenant->getKey(),
'type' => 'provider.connection.check',
'status' => 'completed',
'outcome' => 'succeeded',
'context' => [
'target_scope' => [
'entra_tenant_id' => $entraTenantId,
'entra_tenant_name' => 'Contoso',
],
'verification_report' => $verificationReport,
],
]);
TenantOnboardingSession::query()->create([
'workspace_id' => (int) $workspace->getKey(),
'tenant_id' => (int) $tenant->getKey(),
'entra_tenant_id' => $entraTenantId,
'current_step' => 'verify',
'state' => [
'verification_operation_run_id' => (int) $run->getKey(),
],
'started_by_user_id' => (int) $user->getKey(),
'updated_by_user_id' => (int) $user->getKey(),
]);
$this->actingAs($user);
Livewire::test(ManagedTenantOnboardingWizard::class)
->mountAction('wizardVerificationTechnicalDetails')
->assertSuccessful();
});