create(['external_id' => 'tenant-a']); $checks = TenantPermissionCheckClusters::buildChecks($tenant, [ [ 'key' => 'Directory.Read.All', 'type' => 'application', 'description' => null, 'features' => [], 'status' => 'granted', 'details' => null, ], [ 'key' => 'Group.Read.All', 'type' => 'application', 'description' => null, 'features' => [], 'status' => 'missing', 'details' => null, ], ]); $directoryCheck = collect($checks)->firstWhere('key', 'permissions.directory_groups'); expect($directoryCheck)->toBeArray(); expect($directoryCheck['status'] ?? null)->toBe(VerificationCheckStatus::Fail->value); expect($directoryCheck['blocking'] ?? null)->toBeTrue(); expect($directoryCheck['next_steps'][0]['url'] ?? null) ->toBe(RequiredPermissionsLinks::requiredPermissions($tenant)); }); it('marks a cluster as warn and non-blocking when only delegated permissions are missing', function (): void { $tenant = Tenant::factory()->create(['external_id' => 'tenant-b']); $checks = TenantPermissionCheckClusters::buildChecks($tenant, [ [ 'key' => 'DeviceManagementApps.Read.All', 'type' => 'delegated', 'description' => null, 'features' => [], 'status' => 'missing', 'details' => null, ], ]); $appsCheck = collect($checks)->firstWhere('key', 'permissions.intune_apps'); expect($appsCheck)->toBeArray(); expect($appsCheck['status'] ?? null)->toBe(VerificationCheckStatus::Warn->value); expect($appsCheck['blocking'] ?? null)->toBeFalse(); }); it('marks a cluster as skipped when no mapped permissions are present', function (): void { $tenant = Tenant::factory()->create(['external_id' => 'tenant-c']); $checks = TenantPermissionCheckClusters::buildChecks($tenant, []); $rbacCheck = collect($checks)->firstWhere('key', 'permissions.intune_rbac_assignments'); expect($rbacCheck)->toBeArray(); expect($rbacCheck['status'] ?? null)->toBe(VerificationCheckStatus::Skip->value); }); it('marks a cluster as passed when all mapped permissions are granted', function (): void { $tenant = Tenant::factory()->create(['external_id' => 'tenant-d']); $checks = TenantPermissionCheckClusters::buildChecks($tenant, [ [ 'key' => 'Directory.Read.All', 'type' => 'application', 'description' => null, 'features' => [], 'status' => 'granted', 'details' => null, ], [ 'key' => 'Group.Read.All', 'type' => 'application', 'description' => null, 'features' => [], 'status' => 'granted', 'details' => null, ], ]); $directoryCheck = collect($checks)->firstWhere('key', 'permissions.directory_groups'); expect($directoryCheck)->toBeArray(); expect($directoryCheck['status'] ?? null)->toBe(VerificationCheckStatus::Pass->value); expect($directoryCheck['next_steps'] ?? null)->toBeArray()->toBeEmpty(); }); it('degrades permission clusters to warnings when inventory is not fresh', function (): void { $tenant = Tenant::factory()->create(['external_id' => 'tenant-e']); $checks = TenantPermissionCheckClusters::buildChecks( tenant: $tenant, permissions: [ [ 'key' => 'Directory.Read.All', 'type' => 'application', 'description' => null, 'features' => [], 'status' => 'missing', 'details' => null, ], ], inventory: [ 'fresh' => false, 'reason_code' => 'throttled', 'message' => 'Unable to refresh observed permissions inventory during this run. Retry verification.', ], ); $adminConsentCheck = collect($checks)->firstWhere('key', 'permissions.admin_consent'); expect($adminConsentCheck)->toBeArray(); expect($adminConsentCheck['status'] ?? null)->toBe(VerificationCheckStatus::Warn->value); expect($adminConsentCheck['blocking'] ?? null)->toBeFalse(); expect($adminConsentCheck['reason_code'] ?? null)->toBe('throttled'); expect((string) ($adminConsentCheck['message'] ?? ''))->toContain('Unable to refresh observed permissions inventory'); });