Spec 430: Exchange PowerShell adapter contract slice 1. Validation was not rerun during PR creation handoff; branch contains implementation report and tests. Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de> Reviewed-on: #497
122 lines
6.8 KiB
PHP
122 lines
6.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;
|
|
use App\Support\TenantConfiguration\ClaimState;
|
|
use App\Support\TenantConfiguration\CoverageLevel;
|
|
use App\Support\TenantConfiguration\EvidenceState;
|
|
use App\Support\TenantConfiguration\IdentityState;
|
|
use App\Support\TenantConfiguration\ResourceClass;
|
|
use App\Support\TenantConfiguration\RestoreTier;
|
|
use App\Support\TenantConfiguration\SourceClass;
|
|
use App\Support\TenantConfiguration\SupportState;
|
|
use App\Support\TenantConfiguration\Workload;
|
|
|
|
it('Spec430 resolves included Exchange PowerShell types as verified pending-capture adapter contracts', function (string $canonicalType, string $commandName): void {
|
|
$decision = (new CoverageSourceContractResolver(new GraphContractRegistry))
|
|
->resolve(spec430ResolverResourceType($canonicalType));
|
|
|
|
expect($decision->outcome)->toBe(CaptureOutcome::BlockedMissingContract)
|
|
->and($decision->reasonCode)->toBe(CoverageSourceContractDecision::CONTRACT_VERIFIED_PENDING_CAPTURE)
|
|
->and($decision->sourceContractState)->toBe(CoverageSourceContractDecision::CONTRACT_VERIFIED_PENDING_CAPTURE)
|
|
->and($decision->contractKey)->toBe('exchange_powershell.'.$canonicalType)
|
|
->and($decision->sourceEndpoint)->toBeNull()
|
|
->and($decision->capturable())->toBeFalse()
|
|
->and($decision->sourceMetadata['source_contract_state'])->toBe(CoverageSourceContractDecision::CONTRACT_VERIFIED_PENDING_CAPTURE)
|
|
->and($decision->sourceMetadata['provider_adapter_state'])->toBe('adapter_contract_available')
|
|
->and($decision->sourceMetadata['capture_eligibility_state'])->toBe('pending_capture')
|
|
->and($decision->sourceMetadata['source_surface'])->toBe('exchange_online_powershell_rest')
|
|
->and($decision->sourceMetadata['adapter_pattern'])->toBe('new_exchange_powershell_adapter')
|
|
->and($decision->sourceMetadata['provider_calls_allowed'])->toBeFalse()
|
|
->and($decision->sourceMetadata['execution_enabled'])->toBeFalse()
|
|
->and($decision->sourceMetadata['evidence_promotion_allowed'])->toBeFalse()
|
|
->and($decision->sourceMetadata['command_contract']['command_name'])->toBe($commandName)
|
|
->and(config("graph_contracts.types.{$canonicalType}", []))->toBe([]);
|
|
})->with([
|
|
'transportRule' => ['transportRule', 'Get-TransportRule'],
|
|
'remoteDomain' => ['remoteDomain', 'Get-RemoteDomain'],
|
|
'inboundConnector' => ['inboundConnector', 'Get-InboundConnector'],
|
|
]);
|
|
|
|
it('Spec430 preserves blocked or deferred states for excluded Exchange and Teams types', function (string $canonicalType, Workload $workload): void {
|
|
$decision = (new CoverageSourceContractResolver(new GraphContractRegistry))
|
|
->resolve(spec430AdHocResolverResourceType($canonicalType, $workload));
|
|
|
|
expect($decision->sourceContractState)->not->toBe(CoverageSourceContractDecision::CONTRACT_VERIFIED_PENDING_CAPTURE)
|
|
->and($decision->sourceMetadata['provider_adapter_state'] ?? null)->not->toBe('adapter_contract_available')
|
|
->and($decision->capturable())->toBeFalse()
|
|
->and(config("graph_contracts.types.{$canonicalType}", []))->toBe([]);
|
|
})->with([
|
|
'acceptedDomain' => ['acceptedDomain', Workload::Exchange],
|
|
'organizationConfig' => ['organizationConfig', Workload::Exchange],
|
|
'mailboxPlan' => ['mailboxPlan', Workload::Exchange],
|
|
'outboundConnector' => ['outboundConnector', Workload::Exchange],
|
|
'sharingPolicy' => ['sharingPolicy', Workload::Exchange],
|
|
'appPermissionPolicy' => ['appPermissionPolicy', Workload::Teams],
|
|
'appSetupPolicy' => ['appSetupPolicy', Workload::Teams],
|
|
'meetingPolicy' => ['meetingPolicy', Workload::Teams],
|
|
'messagingPolicy' => ['messagingPolicy', Workload::Teams],
|
|
'teamsUpdateManagementPolicy' => ['teamsUpdateManagementPolicy', Workload::Teams],
|
|
'teamsChannelsPolicy' => ['teamsChannelsPolicy', Workload::Teams],
|
|
'externalAccessPolicy' => ['externalAccessPolicy', Workload::Teams],
|
|
]);
|
|
|
|
it('Spec430 adds inboundConnector only to the inactive Exchange planning registry denominator', function (): void {
|
|
$definitions = collect(ResourceTypeRegistry::defaultDefinitions());
|
|
$inbound = $definitions->firstWhere('canonical_type', 'inboundConnector');
|
|
|
|
expect($inbound)->not->toBeNull()
|
|
->and($inbound['workload'])->toBe(Workload::Exchange->value)
|
|
->and($inbound['source_class'])->toBe(SourceClass::Tcm->value)
|
|
->and($inbound['support_state'])->toBe(SupportState::OutOfScope->value)
|
|
->and($inbound['default_coverage_level'])->toBe(CoverageLevel::Detected->value)
|
|
->and($inbound['default_evidence_state'])->toBe(EvidenceState::NotCaptured->value)
|
|
->and($inbound['default_claim_state'])->toBe(ClaimState::InternalOnly->value)
|
|
->and($inbound['restore_tier'])->toBe(RestoreTier::NotRestorable->value)
|
|
->and($definitions->firstWhere('canonical_type', 'outboundConnector'))->toBeNull()
|
|
->and($definitions->firstWhere('canonical_type', 'sharingPolicy'))->toBeNull()
|
|
->and($definitions->firstWhere('canonical_type', 'externalAccessPolicy'))->toBeNull();
|
|
});
|
|
|
|
function spec430ResolverResourceType(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);
|
|
}
|
|
|
|
function spec430AdHocResolverResourceType(string $canonicalType, Workload $workload): TenantConfigurationResourceType
|
|
{
|
|
$definition = collect(ResourceTypeRegistry::defaultDefinitions())
|
|
->firstWhere('canonical_type', $canonicalType);
|
|
|
|
if (is_array($definition)) {
|
|
return new TenantConfigurationResourceType($definition);
|
|
}
|
|
|
|
return new TenantConfigurationResourceType([
|
|
'canonical_type' => $canonicalType,
|
|
'display_name' => $canonicalType,
|
|
'source_class' => SourceClass::Tcm->value,
|
|
'workload' => $workload->value,
|
|
'resource_class' => ResourceClass::Configuration->value,
|
|
'support_state' => SupportState::OutOfScope->value,
|
|
'default_coverage_level' => CoverageLevel::Detected->value,
|
|
'default_evidence_state' => EvidenceState::NotCaptured->value,
|
|
'default_identity_state' => IdentityState::Derived->value,
|
|
'default_claim_state' => ClaimState::InternalOnly->value,
|
|
'restore_tier' => RestoreTier::NotRestorable->value,
|
|
'is_active' => true,
|
|
'metadata' => ['kernel' => 'coverage_v2'],
|
|
]);
|
|
}
|