TenantAtlas/tests/Feature/PolicySyncEnrollmentConfigurationTypeCollisionTest.php
ahmido 817ad208da feat/027-enrollment-config-subtypes (#31)
expose enrollment config subtypes as their own policy types (limit/platform restrictions/notifications) with preview-only restore risk and proper Graph contracts
classify enrollment configs by their @odata.type + deviceEnrollmentConfigurationType so sync only keeps ESP in windowsEnrollmentStatusPage and the rest stay in their own types, including new restore-normalizer UI blocks + warnings
hydrate enrollment notifications: snapshot fetch now downloads each notification template + localized messages, normalized view surfaces template names/subjects/messages, and restore previews keep preview-only behavior
tenant UI tweaks: Tenant list and detail actions moved into an action group; “Open in Entra” re-added in index, and detail now has “Deactivate” + tests covering the new menu layout and actions
tests added/updated for sync, snapshots, restores, normalized settings, tenant UI, plus Pint/test suite run

Co-authored-by: Ahmed Darrazi <ahmeddarrazi@adsmac.local>
Reviewed-on: #31
2026-01-04 13:25:15 +00:00

251 lines
8.9 KiB
PHP

<?php
use App\Models\Policy;
use App\Models\Tenant;
use App\Services\Graph\GraphClientInterface;
use App\Services\Graph\GraphResponse;
use App\Services\Intune\PolicySyncService;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Mockery\MockInterface;
uses(RefreshDatabase::class);
test('policy sync does not let enrollmentRestriction claim ESP items and reclassifies existing wrong rows', function () {
$tenant = Tenant::create([
'tenant_id' => 'tenant-sync-collision',
'name' => 'Tenant Sync Collision',
'metadata' => [],
'is_current' => true,
]);
$tenant->makeCurrent();
// Simulate an older bug: ESP row was synced under enrollmentRestriction.
$wrong = Policy::create([
'tenant_id' => $tenant->id,
'external_id' => 'esp-1',
'policy_type' => 'enrollmentRestriction',
'display_name' => 'ESP Misclassified',
'platform' => 'all',
]);
$this->mock(GraphClientInterface::class, function (MockInterface $mock) {
$espPayload = [
'id' => 'esp-1',
'displayName' => 'Enrollment Status Page',
'@odata.type' => '#microsoft.graph.windows10EnrollmentCompletionPageConfiguration',
'deviceEnrollmentConfigurationType' => 'windows10EnrollmentCompletionPageConfiguration',
];
$mock->shouldReceive('listPolicies')
->andReturnUsing(function (string $policyType) use ($espPayload) {
if ($policyType === 'enrollmentRestriction') {
// Shared endpoint can return ESP items if unfiltered.
return new GraphResponse(true, [$espPayload]);
}
if ($policyType === 'windowsEnrollmentStatusPage') {
return new GraphResponse(true, [$espPayload]);
}
return new GraphResponse(true, []);
});
});
$service = app(PolicySyncService::class);
$service->syncPolicies($tenant, [
[
'type' => 'enrollmentRestriction',
'platform' => 'all',
'filter' => null,
],
[
'type' => 'windowsEnrollmentStatusPage',
'platform' => 'all',
'filter' => null,
],
]);
$wrong->refresh();
expect($wrong->policy_type)->toBe('windowsEnrollmentStatusPage');
});
test('policy sync classifies ESP items without relying on Graph isof filter', function () {
$tenant = Tenant::create([
'tenant_id' => 'tenant-sync-esp-no-filter',
'name' => 'Tenant Sync ESP No Filter',
'metadata' => [],
'is_current' => true,
]);
$tenant->makeCurrent();
$this->mock(GraphClientInterface::class, function (MockInterface $mock) {
$payload = [
[
'id' => 'esp-1',
'displayName' => 'Enrollment Status Page',
'@odata.type' => '#microsoft.graph.windows10EnrollmentCompletionPageConfiguration',
'deviceEnrollmentConfigurationType' => 'windows10EnrollmentCompletionPageConfiguration',
],
[
'id' => 'restriction-1',
'displayName' => 'Default Enrollment Restriction',
'@odata.type' => '#microsoft.graph.deviceEnrollmentPlatformRestrictionConfiguration',
'deviceEnrollmentConfigurationType' => 'deviceEnrollmentPlatformRestrictionConfiguration',
],
[
'id' => 'other-1',
'displayName' => 'Other Enrollment Config',
'@odata.type' => '#microsoft.graph.someOtherEnrollmentConfiguration',
'deviceEnrollmentConfigurationType' => 'someOtherEnrollmentConfiguration',
],
];
$mock->shouldReceive('listPolicies')
->andReturnUsing(function (string $policyType) use ($payload) {
if (in_array($policyType, [
'enrollmentRestriction',
'windowsEnrollmentStatusPage',
'deviceEnrollmentPlatformRestrictionsConfiguration',
], true)) {
return new GraphResponse(true, $payload);
}
return new GraphResponse(true, []);
});
});
$service = app(PolicySyncService::class);
$service->syncPolicies($tenant, [
[
'type' => 'windowsEnrollmentStatusPage',
'platform' => 'all',
'filter' => null,
],
[
'type' => 'deviceEnrollmentPlatformRestrictionsConfiguration',
'platform' => 'all',
'filter' => null,
],
[
'type' => 'enrollmentRestriction',
'platform' => 'all',
'filter' => null,
],
]);
$espIds = Policy::query()
->where('tenant_id', $tenant->id)
->where('policy_type', 'windowsEnrollmentStatusPage')
->pluck('external_id')
->all();
$restrictionIds = Policy::query()
->where('tenant_id', $tenant->id)
->where('policy_type', 'enrollmentRestriction')
->orderBy('external_id')
->pluck('external_id')
->all();
$platformRestrictionIds = Policy::query()
->where('tenant_id', $tenant->id)
->where('policy_type', 'deviceEnrollmentPlatformRestrictionsConfiguration')
->orderBy('external_id')
->pluck('external_id')
->all();
expect($espIds)->toMatchArray(['esp-1']);
expect($platformRestrictionIds)->toMatchArray(['restriction-1']);
expect($restrictionIds)->toMatchArray(['other-1']);
});
test('policy sync classifies enrollment configuration subtypes separately', function () {
$tenant = Tenant::create([
'tenant_id' => 'tenant-sync-enrollment-subtypes',
'name' => 'Tenant Sync Enrollment Subtypes',
'metadata' => [],
'is_current' => true,
]);
$tenant->makeCurrent();
$this->mock(GraphClientInterface::class, function (MockInterface $mock) {
$limitPayload = [
'id' => 'limit-1',
'displayName' => 'Enrollment Limit',
'@odata.type' => '#microsoft.graph.deviceEnrollmentLimitConfiguration',
'deviceEnrollmentConfigurationType' => 'deviceEnrollmentLimitConfiguration',
'limit' => 5,
];
$platformRestrictionsPayload = [
'id' => 'platform-1',
'displayName' => 'Platform Restrictions',
'@odata.type' => '#microsoft.graph.deviceEnrollmentPlatformRestrictionsConfiguration',
'deviceEnrollmentConfigurationType' => 'deviceEnrollmentPlatformRestrictionsConfiguration',
];
$notificationPayload = [
'id' => 'notify-1',
'displayName' => 'Enrollment Notifications',
'@odata.type' => '#microsoft.graph.deviceEnrollmentNotificationConfiguration',
'deviceEnrollmentConfigurationType' => 'EnrollmentNotificationsConfiguration',
];
$unfilteredPayload = [
$limitPayload,
$platformRestrictionsPayload,
$notificationPayload,
];
$mock->shouldReceive('listPolicies')
->andReturnUsing(function (string $policyType) use ($notificationPayload, $unfilteredPayload) {
if ($policyType === 'deviceEnrollmentNotificationConfiguration') {
return new GraphResponse(true, [$notificationPayload]);
}
if (in_array($policyType, [
'enrollmentRestriction',
'deviceEnrollmentLimitConfiguration',
'deviceEnrollmentPlatformRestrictionsConfiguration',
'windowsEnrollmentStatusPage',
], true)) {
return new GraphResponse(true, $unfilteredPayload);
}
return new GraphResponse(true, []);
});
});
$service = app(PolicySyncService::class);
$service->syncPolicies($tenant, [
['type' => 'deviceEnrollmentLimitConfiguration', 'platform' => 'all', 'filter' => null],
['type' => 'deviceEnrollmentPlatformRestrictionsConfiguration', 'platform' => 'all', 'filter' => null],
['type' => 'deviceEnrollmentNotificationConfiguration', 'platform' => 'all', 'filter' => null],
['type' => 'enrollmentRestriction', 'platform' => 'all', 'filter' => null],
]);
expect(Policy::query()
->where('tenant_id', $tenant->id)
->where('policy_type', 'deviceEnrollmentLimitConfiguration')
->pluck('external_id')
->all())->toMatchArray(['limit-1']);
expect(Policy::query()
->where('tenant_id', $tenant->id)
->where('policy_type', 'deviceEnrollmentPlatformRestrictionsConfiguration')
->pluck('external_id')
->all())->toMatchArray(['platform-1']);
expect(Policy::query()
->where('tenant_id', $tenant->id)
->where('policy_type', 'deviceEnrollmentNotificationConfiguration')
->pluck('external_id')
->all())->toMatchArray(['notify-1']);
});