230 lines
8.2 KiB
PHP
230 lines
8.2 KiB
PHP
<?php
|
|
|
|
use App\Support\Baselines\BaselineScope;
|
|
|
|
it('expands empty policy_types to supported policy types (excluding foundations) and defaults foundation_types to none', function () {
|
|
config()->set('tenantpilot.supported_policy_types', [
|
|
['type' => 'deviceConfiguration', 'label' => 'Device Configuration'],
|
|
['type' => 'deviceCompliancePolicy', 'label' => 'Device Compliance'],
|
|
]);
|
|
|
|
config()->set('tenantpilot.foundation_types', [
|
|
['type' => 'assignmentFilter', 'label' => 'Assignment Filter', 'baseline_compare' => ['supported' => true]],
|
|
]);
|
|
|
|
$scope = BaselineScope::fromJsonb([
|
|
'policy_types' => [],
|
|
'foundation_types' => [],
|
|
])->expandDefaults();
|
|
|
|
expect($scope->policyTypes)->toBe([
|
|
'deviceCompliancePolicy',
|
|
'deviceConfiguration',
|
|
]);
|
|
|
|
expect($scope->foundationTypes)->toBe([]);
|
|
expect($scope->allTypes())->toBe([
|
|
'deviceCompliancePolicy',
|
|
'deviceConfiguration',
|
|
]);
|
|
});
|
|
|
|
it('filters unknown types and does not allow foundations inside policy_types', function () {
|
|
config()->set('tenantpilot.supported_policy_types', [
|
|
['type' => 'deviceConfiguration'],
|
|
]);
|
|
|
|
config()->set('tenantpilot.foundation_types', [
|
|
['type' => 'assignmentFilter', 'baseline_compare' => ['supported' => true]],
|
|
]);
|
|
|
|
$scope = BaselineScope::fromJsonb([
|
|
'policy_types' => ['deviceConfiguration', 'assignmentFilter', 'unknown'],
|
|
'foundation_types' => ['assignmentFilter', 'unknown'],
|
|
])->expandDefaults();
|
|
|
|
expect($scope->policyTypes)->toBe(['deviceConfiguration']);
|
|
expect($scope->foundationTypes)->toBe(['assignmentFilter']);
|
|
expect($scope->allTypes())->toBe(['assignmentFilter', 'deviceConfiguration']);
|
|
});
|
|
|
|
it('normalizes canonical v2 entries and preserves canonical storage', function (): void {
|
|
config()->set('tenantpilot.supported_policy_types', [
|
|
['type' => 'deviceConfiguration', 'label' => 'Device Configuration'],
|
|
['type' => 'deviceCompliancePolicy', 'label' => 'Device Compliance'],
|
|
]);
|
|
|
|
config()->set('tenantpilot.foundation_types', [
|
|
['type' => 'assignmentFilter', 'label' => 'Assignment Filter', 'baseline_compare' => ['supported' => true]],
|
|
]);
|
|
|
|
$scope = BaselineScope::fromJsonb([
|
|
'version' => 2,
|
|
'entries' => [
|
|
[
|
|
'domain_key' => 'intune',
|
|
'subject_class' => 'policy',
|
|
'subject_type_keys' => ['deviceConfiguration', 'deviceCompliancePolicy', 'deviceConfiguration'],
|
|
'filters' => [],
|
|
],
|
|
[
|
|
'domain_key' => 'intune',
|
|
'subject_class' => 'policy',
|
|
'subject_type_keys' => ['deviceCompliancePolicy'],
|
|
'filters' => [],
|
|
],
|
|
[
|
|
'domain_key' => 'platform_foundation',
|
|
'subject_class' => 'configuration_resource',
|
|
'subject_type_keys' => ['assignmentFilter'],
|
|
'filters' => [],
|
|
],
|
|
],
|
|
]);
|
|
|
|
expect($scope->policyTypes)->toBe(['deviceCompliancePolicy', 'deviceConfiguration'])
|
|
->and($scope->foundationTypes)->toBe(['assignmentFilter'])
|
|
->and($scope->toStoredJsonb())->toBe([
|
|
'version' => 2,
|
|
'entries' => [
|
|
[
|
|
'domain_key' => 'intune',
|
|
'subject_class' => 'policy',
|
|
'subject_type_keys' => ['deviceCompliancePolicy', 'deviceConfiguration'],
|
|
'filters' => [],
|
|
],
|
|
[
|
|
'domain_key' => 'platform_foundation',
|
|
'subject_class' => 'configuration_resource',
|
|
'subject_type_keys' => ['assignmentFilter'],
|
|
'filters' => [],
|
|
],
|
|
],
|
|
])
|
|
->and($scope->normalizationLineage())->toMatchArray([
|
|
'source_shape' => 'canonical_v2',
|
|
'normalized_on_read' => false,
|
|
'save_forward_required' => false,
|
|
]);
|
|
});
|
|
|
|
it('treats a missing legacy bucket like its empty default when the other bucket is present', function (): void {
|
|
config()->set('tenantpilot.supported_policy_types', [
|
|
['type' => 'deviceConfiguration'],
|
|
['type' => 'deviceCompliancePolicy'],
|
|
]);
|
|
|
|
config()->set('tenantpilot.foundation_types', [
|
|
['type' => 'assignmentFilter', 'baseline_compare' => ['supported' => true]],
|
|
]);
|
|
|
|
$policyOnly = BaselineScope::fromJsonb([
|
|
'policy_types' => ['deviceConfiguration'],
|
|
]);
|
|
$foundationOnly = BaselineScope::fromJsonb([
|
|
'foundation_types' => ['assignmentFilter'],
|
|
]);
|
|
|
|
expect($policyOnly->policyTypes)->toBe(['deviceConfiguration'])
|
|
->and($policyOnly->foundationTypes)->toBe([])
|
|
->and($foundationOnly->policyTypes)->toBe(['deviceCompliancePolicy', 'deviceConfiguration'])
|
|
->and($foundationOnly->foundationTypes)->toBe(['assignmentFilter']);
|
|
});
|
|
|
|
it('rejects mixed legacy and canonical payloads', function (): void {
|
|
expect(fn () => BaselineScope::fromJsonb([
|
|
'policy_types' => ['deviceConfiguration'],
|
|
'version' => 2,
|
|
'entries' => [
|
|
[
|
|
'domain_key' => 'intune',
|
|
'subject_class' => 'policy',
|
|
'subject_type_keys' => ['deviceConfiguration'],
|
|
],
|
|
],
|
|
]))->toThrow(InvalidArgumentException::class, 'must not mix legacy buckets');
|
|
});
|
|
|
|
it('rejects unsupported filters for current domains', function (): void {
|
|
config()->set('tenantpilot.supported_policy_types', [
|
|
['type' => 'deviceConfiguration'],
|
|
]);
|
|
|
|
expect(fn () => BaselineScope::fromJsonb([
|
|
'version' => 2,
|
|
'entries' => [
|
|
[
|
|
'domain_key' => 'intune',
|
|
'subject_class' => 'policy',
|
|
'subject_type_keys' => ['deviceConfiguration'],
|
|
'filters' => ['tenant_ids' => ['tenant-a']],
|
|
],
|
|
],
|
|
]))->toThrow(InvalidArgumentException::class, 'Filters are not supported');
|
|
});
|
|
|
|
it('treats empty legacy override payloads as no override when requested', function (): void {
|
|
$scope = BaselineScope::fromJsonb([
|
|
'policy_types' => [],
|
|
'foundation_types' => [],
|
|
], allowEmptyLegacyAsNoOverride: true);
|
|
|
|
expect($scope->isEmpty())->toBeTrue();
|
|
});
|
|
|
|
it('rejects unknown governance domains', function (): void {
|
|
expect(fn () => BaselineScope::fromJsonb([
|
|
'version' => 2,
|
|
'entries' => [
|
|
[
|
|
'domain_key' => 'unknown_domain',
|
|
'subject_class' => 'policy',
|
|
'subject_type_keys' => ['deviceConfiguration'],
|
|
'filters' => [],
|
|
],
|
|
],
|
|
]))->toThrow(InvalidArgumentException::class, 'Unknown governance domain');
|
|
});
|
|
|
|
it('rejects invalid subject classes for known domains', function (): void {
|
|
expect(fn () => BaselineScope::fromJsonb([
|
|
'version' => 2,
|
|
'entries' => [
|
|
[
|
|
'domain_key' => 'intune',
|
|
'subject_class' => 'configuration_resource',
|
|
'subject_type_keys' => ['deviceConfiguration'],
|
|
'filters' => [],
|
|
],
|
|
],
|
|
]))->toThrow(InvalidArgumentException::class, 'is not valid for domain');
|
|
});
|
|
|
|
it('rejects inactive subject types in canonical scope entries', function (): void {
|
|
expect(fn () => BaselineScope::fromJsonb([
|
|
'version' => 2,
|
|
'entries' => [
|
|
[
|
|
'domain_key' => 'platform_foundation',
|
|
'subject_class' => 'configuration_resource',
|
|
'subject_type_keys' => ['intuneRoleAssignment'],
|
|
'filters' => [],
|
|
],
|
|
],
|
|
]))->toThrow(InvalidArgumentException::class, 'Inactive subject type');
|
|
});
|
|
|
|
it('rejects future-domain selections that have no active subject type mapping yet', function (): void {
|
|
expect(fn () => BaselineScope::fromJsonb([
|
|
'version' => 2,
|
|
'entries' => [
|
|
[
|
|
'domain_key' => 'entra',
|
|
'subject_class' => 'control',
|
|
'subject_type_keys' => ['conditionalAccessPolicy'],
|
|
'filters' => [],
|
|
],
|
|
],
|
|
]))->toThrow(InvalidArgumentException::class, 'Unknown subject type');
|
|
});
|