TenantAtlas/apps/platform/tests/Unit/Baselines/BaselineScopeTest.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');
});