TenantAtlas/apps/platform/tests/Unit/Support/TenantConfiguration/Spec420M365CaptureSourceContractResolverTest.php
ahmido bfb52b84d6 feat: implement spec 427 source contract enablement (#494)
Automated PR for spec 427 Exchange Teams verified source contract enablement.

Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de>
Reviewed-on: #494
2026-07-03 23:12:45 +00:00

51 lines
2.8 KiB
PHP

<?php
declare(strict_types=1);
use App\Models\TenantConfigurationResourceType;
use App\Services\Graph\GraphContractRegistry;
use App\Services\TenantConfiguration\CoverageSourceContractDecision;
use App\Services\TenantConfiguration\CoverageSourceContractResolver;
use App\Services\TenantConfiguration\ResourceTypeRegistry;
use App\Support\TenantConfiguration\CaptureOutcome;
it('Spec420 resolves Conditional Access only through the explicit graph contract mapping', function (): void {
$resourceType = spec420UnitResourceType('conditionalAccessPolicy');
$decision = (new CoverageSourceContractResolver(new GraphContractRegistry))->resolve($resourceType);
expect($decision->outcome)->toBe(CaptureOutcome::Captured)
->and($decision->contractKey)->toBe('conditionalAccessPolicy')
->and($decision->sourceEndpoint)->toBe('/identity/conditionalAccess/policies')
->and($decision->sourceVersion)->toBe('v1.0')
->and($decision->sourceSchemaHash)->toBeString()->not->toBe('')
->and($decision->sourceMetadata['source_contract_key'])->toBe('conditionalAccessPolicy')
->and($decision->sourceMetadata['registry_source_class'])->toBe('tcm')
->and($decision->sourceMetadata['registry_support_state'])->toBe('out_of_scope');
});
it('Spec420 blocks remaining missing-contract M365 types without falling back to unsupported', function (string $canonicalType, string $reasonCode, ?string $contractState): void {
$decision = (new CoverageSourceContractResolver(new GraphContractRegistry))
->resolve(spec420UnitResourceType($canonicalType));
expect($decision->outcome)->toBe(CaptureOutcome::BlockedMissingContract)
->and($decision->reasonCode)->toBe($reasonCode)
->and($decision->sourceContractState)->toBe($contractState)
->and($decision->contractKey)->toBeNull()
->and(config("graph_contracts.types.{$canonicalType}", []))->toBe([]);
})->with([
'Exchange accepted domain' => ['acceptedDomain', CoverageSourceContractDecision::CONTRACT_BLOCKED_REPO_ADAPTER_MISSING, CoverageSourceContractDecision::CONTRACT_BLOCKED_REPO_ADAPTER_MISSING],
'Teams app permission policy' => ['appPermissionPolicy', CoverageSourceContractDecision::CONTRACT_BLOCKED_REPO_ADAPTER_MISSING, CoverageSourceContractDecision::CONTRACT_BLOCKED_REPO_ADAPTER_MISSING],
'Security and Compliance DLP policy' => ['dlpCompliancePolicy', 'missing_source_contract_mapping', null],
]);
function spec420UnitResourceType(string $canonicalType): TenantConfigurationResourceType
{
$definition = collect(ResourceTypeRegistry::defaultDefinitions())
->firstWhere('canonical_type', $canonicalType);
expect($definition)->not->toBeNull("Missing default resource type definition for {$canonicalType}.");
return new TenantConfigurationResourceType($definition);
}