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); }