toBe('entra_roles.view') ->and(Capabilities::ENTRA_ROLES_MANAGE)->toBe('entra_roles.manage'); }); it('includes ENTRA_ROLES_VIEW in all() capability registry', function (): void { $all = Capabilities::all(); expect($all)->toContain(Capabilities::ENTRA_ROLES_VIEW) ->and($all)->toContain(Capabilities::ENTRA_ROLES_MANAGE); }); it('maps ENTRA_ROLES_VIEW to Readonly and Operator roles', function (): void { expect(RoleCapabilityMap::hasCapability(TenantRole::Readonly, Capabilities::ENTRA_ROLES_VIEW))->toBeTrue() ->and(RoleCapabilityMap::hasCapability(TenantRole::Operator, Capabilities::ENTRA_ROLES_VIEW))->toBeTrue(); }); it('maps ENTRA_ROLES_MANAGE to Manager and Owner roles only', function (): void { expect(RoleCapabilityMap::hasCapability(TenantRole::Owner, Capabilities::ENTRA_ROLES_MANAGE))->toBeTrue() ->and(RoleCapabilityMap::hasCapability(TenantRole::Manager, Capabilities::ENTRA_ROLES_MANAGE))->toBeTrue() ->and(RoleCapabilityMap::hasCapability(TenantRole::Operator, Capabilities::ENTRA_ROLES_MANAGE))->toBeFalse() ->and(RoleCapabilityMap::hasCapability(TenantRole::Readonly, Capabilities::ENTRA_ROLES_MANAGE))->toBeFalse(); }); it('maps ENTRA_ROLES_VIEW to Manager and Owner roles', function (): void { expect(RoleCapabilityMap::hasCapability(TenantRole::Owner, Capabilities::ENTRA_ROLES_VIEW))->toBeTrue() ->and(RoleCapabilityMap::hasCapability(TenantRole::Manager, Capabilities::ENTRA_ROLES_VIEW))->toBeTrue(); }); // --------------------------------------------------------------------------- // T028 — TenantPermissionService merge tests // --------------------------------------------------------------------------- it('merged required permissions include both Intune and Entra entries', function (): void { $service = app(TenantPermissionService::class); $permissions = $service->getRequiredPermissions(); $keys = array_column($permissions, 'key'); // At least one Intune permission expect($keys)->toContain('DeviceManagementConfiguration.ReadWrite.All') // And the Entra permission ->and($keys)->toContain('RoleManagement.Read.Directory'); }); it('RoleManagement.Read.Directory has correct type and features in merged list', function (): void { $service = app(TenantPermissionService::class); $permissions = $service->getRequiredPermissions(); $entraPermission = collect($permissions)->firstWhere('key', 'RoleManagement.Read.Directory'); expect($entraPermission)->not->toBeNull() ->and($entraPermission['type'])->toBe('application') ->and($entraPermission['features'])->toContain('entra-admin-roles'); }); it('existing Intune permissions unchanged after Entra merge', function (): void { $service = app(TenantPermissionService::class); $merged = $service->getRequiredPermissions(); $intuneOnly = config('intune_permissions.permissions', []); $intuneKeys = array_column($intuneOnly, 'key'); foreach ($intuneKeys as $key) { $original = collect($intuneOnly)->firstWhere('key', $key); $inMerged = collect($merged)->firstWhere('key', $key); expect($inMerged)->not->toBeNull() ->and($inMerged['key'])->toBe($original['key']) ->and($inMerged['type'])->toBe($original['type']); } }); it('empty entra_permissions config returns only Intune entries', function (): void { config()->set('entra_permissions.permissions', []); $service = app(TenantPermissionService::class); $permissions = $service->getRequiredPermissions(); $intuneOnly = config('intune_permissions.permissions', []); expect($permissions)->toHaveCount(count($intuneOnly)); $keys = array_column($permissions, 'key'); expect($keys)->not->toContain('RoleManagement.Read.Directory'); });