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

66 lines
2.8 KiB
PHP

<?php
declare(strict_types=1);
use App\Models\TenantConfigurationResourceType;
use App\Services\Graph\GraphContractRegistry;
use App\Services\TenantConfiguration\CanonicalIdentityResolver;
use App\Services\TenantConfiguration\CoverageSourceContractDecision;
use App\Services\TenantConfiguration\CoverageSourceContractResolver;
use App\Services\TenantConfiguration\ResourceTypeRegistry;
use App\Support\TenantConfiguration\IdentityState;
it('Spec427 identity handoff metadata uses stable candidates and rejects display-name identity', function (string $canonicalType): void {
$decision = (new CoverageSourceContractResolver(new GraphContractRegistry))
->resolve(spec427IdentityResourceType($canonicalType));
$handoff = $decision->sourceMetadata['identity_handoff'];
expect($decision->sourceContractState)->toBe(CoverageSourceContractDecision::CONTRACT_BLOCKED_REPO_ADAPTER_MISSING)
->and($handoff['identity_stability_class'])->toBe('stable_candidate')
->and($handoff['singleton_or_collection_identity_rule'])->toBe('collection_item_identity_required')
->and($handoff['display_name_is_stable_identity'])->toBeFalse()
->and($handoff['known_identity_risks'])->toContain('display_name_not_stable')
->and($handoff['known_identity_risks'])->toContain('order_and_payload_hash_not_stable_identity');
$identityFields = [
...$handoff['preferred_identity_fields'],
...$handoff['fallback_identity_fields'],
];
expect(array_intersect($identityFields, ['displayName', 'DisplayName', 'name', 'Name', 'Identity']))
->toBe([]);
})->with([
'transportRule',
'acceptedDomain',
'appPermissionPolicy',
'meetingPolicy',
]);
it('Spec427 keeps display-only payloads blocked from stable identity readiness', function (string $canonicalType): void {
$result = app(CanonicalIdentityResolver::class)->resolve(
spec427IdentityResourceType($canonicalType),
['DisplayName' => 'Display-only object', 'Name' => 'Display-only object', 'Identity' => 'Global'],
['source_contract_key' => $canonicalType, 'source_version' => 'not_verified'],
);
expect($result->identityState)->toBe(IdentityState::MissingExternalId)
->and($result->sourceResourceId)->toStartWith('missing:')
->and($result->canonicalResourceKey)->not->toContain('Display-only object');
})->with([
'transportRule',
'acceptedDomain',
'appPermissionPolicy',
'meetingPolicy',
]);
function spec427IdentityResourceType(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);
}