TenantAtlas/apps/platform/tests/Unit/Support/TenantConfiguration/Spec427ExchangeTeamsSourceContractStateTest.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

86 lines
4.2 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('Spec427 maps the bounded source-contract state vocabulary without adding parallel truth', function (): void {
expect(CoverageSourceContractDecision::sourceContractStates())->toBe([
'contract_verified_pending_capture',
'contract_blocked_missing_source',
'contract_blocked_permission_unclear',
'contract_blocked_beta_only',
'contract_blocked_response_shape_unsafe',
'contract_blocked_repo_adapter_missing',
'contract_blocked_identity_unsafe',
'contract_blocked_redaction_unsafe',
]);
});
it('Spec427 resolves every target type to an exact non-capturable source-contract blocker', function (string $canonicalType): void {
$decision = (new CoverageSourceContractResolver(new GraphContractRegistry))
->resolve(spec427StateResourceType($canonicalType));
expect($decision->outcome)->toBe(CaptureOutcome::BlockedMissingContract)
->and($decision->reasonCode)->toBe(CoverageSourceContractDecision::CONTRACT_BLOCKED_REPO_ADAPTER_MISSING)
->and($decision->sourceContractState)->toBe(CoverageSourceContractDecision::CONTRACT_BLOCKED_REPO_ADAPTER_MISSING)
->and($decision->sourceMetadata['source_contract_state'])->toBe(CoverageSourceContractDecision::CONTRACT_BLOCKED_REPO_ADAPTER_MISSING)
->and($decision->sourceMetadata['capture_eligibility_state'])->toBe('blocked')
->and($decision->sourceMetadata['provider_adapter_state'])->toBe('missing')
->and($decision->capturable())->toBeFalse()
->and($decision->contractKey)->toBeNull()
->and($decision->sourceEndpoint)->toBeNull()
->and(config("graph_contracts.types.{$canonicalType}", []))->toBe([]);
})->with([
'transportRule',
'acceptedDomain',
'appPermissionPolicy',
'meetingPolicy',
]);
it('Spec427 keeps non-target missing-contract resource types on the existing generic blocker', function (): void {
$decision = (new CoverageSourceContractResolver(new GraphContractRegistry))
->resolve(spec427StateResourceType('dlpCompliancePolicy'));
expect($decision->outcome)->toBe(CaptureOutcome::BlockedMissingContract)
->and($decision->reasonCode)->toBe('missing_source_contract_mapping')
->and($decision->sourceContractState)->toBeNull()
->and($decision->sourceMetadata['reason_code'])->toBe('missing_source_contract_mapping');
});
it('Spec427 does not assign source-contract state to unsupported non-target resource types', function (): void {
$decision = (new CoverageSourceContractResolver(new GraphContractRegistry))
->resolve(spec427StateResourceType('application'));
expect($decision->outcome)->toBe(CaptureOutcome::BlockedUnsupported)
->and($decision->reasonCode)->toBe('resource_type_unsupported')
->and($decision->sourceContractState)->toBeNull()
->and($decision->sourceMetadata['reason_code'])->toBe('resource_type_unsupported')
->and($decision->sourceMetadata)->not->toHaveKey('source_contract_state');
});
it('Spec427 does not relabel already enabled non-target source contracts', function (): void {
$decision = (new CoverageSourceContractResolver(new GraphContractRegistry))
->resolve(spec427StateResourceType('conditionalAccessPolicy'));
expect($decision->outcome)->toBe(CaptureOutcome::Captured)
->and($decision->contractKey)->toBe('conditionalAccessPolicy')
->and($decision->sourceContractState)->toBeNull()
->and($decision->sourceMetadata)->not->toHaveKey('source_contract_state');
});
function spec427StateResourceType(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);
}