TenantAtlas/apps/platform/tests/Feature/TenantConfiguration/Spec437ExchangeComparablePromotionFeatureTest.php
Ahmed Darrazi f8c298632b
Some checks failed
PR Fast Feedback / fast-feedback (pull_request) Failing after 1m46s
feat: add exchange comparable payload builder
2026-07-09 21:10:17 +02:00

274 lines
14 KiB
PHP

<?php
declare(strict_types=1);
use App\Models\OperationRun;
use App\Models\ProviderConnection;
use App\Models\TenantConfigurationResource;
use App\Models\TenantConfigurationResourceEvidence;
use App\Models\TenantConfigurationResourceType;
use App\Services\TenantConfiguration\ExchangePowerShellComparablePayloadBuilder;
use App\Services\TenantConfiguration\ExchangePowerShellCommandContracts;
use App\Services\TenantConfiguration\ExchangePowerShellTargetEvidenceNormalizer;
use App\Services\TenantConfiguration\ResourceTypeRegistry;
use App\Support\OperationRunOutcome;
use App\Support\OperationRunStatus;
use App\Support\OperationRunType;
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 Illuminate\Support\Facades\File;
use Illuminate\Support\Facades\Schema;
beforeEach(function (): void {
app(ResourceTypeRegistry::class)->syncDefaults();
});
it('Spec437 derives comparable payloads without mutating evidence resource resource-type or OperationRun truth', function (): void {
[$user, $environment] = createMinimalUserWithTenant(role: 'owner');
$connection = ProviderConnection::factory()->withCredential()->create([
'workspace_id' => (int) $environment->workspace_id,
'managed_environment_id' => (int) $environment->getKey(),
]);
$resourceType = spec437FeatureResourceType('transportRule');
$operationRun = OperationRun::factory()->withUser($user)->forTenant($environment)->create([
'type' => OperationRunType::TenantConfigurationCapture->value,
'status' => OperationRunStatus::Completed->value,
'outcome' => OperationRunOutcome::Succeeded->value,
'context' => [
'target_scope' => [
'workspace_id' => (int) $environment->workspace_id,
'managed_environment_id' => (int) $environment->getKey(),
'provider_connection_id' => (int) $connection->getKey(),
],
'resource_types' => ['transportRule'],
],
]);
$resource = TenantConfigurationResource::factory()->create([
'workspace_id' => (int) $environment->workspace_id,
'managed_environment_id' => (int) $environment->getKey(),
'provider_connection_id' => (int) $connection->getKey(),
'resource_type_id' => (int) $resourceType->getKey(),
'canonical_type' => 'transportRule',
'latest_evidence_state' => EvidenceState::ContentBacked->value,
'latest_identity_state' => IdentityState::Stable->value,
'latest_claim_state' => ClaimState::InternalOnly->value,
'latest_payload_hash' => spec437FeaturePayloadHash(),
]);
$evidence = TenantConfigurationResourceEvidence::factory()->create([
'resource_id' => (int) $resource->getKey(),
'workspace_id' => (int) $environment->workspace_id,
'managed_environment_id' => (int) $environment->getKey(),
'provider_connection_id' => (int) $connection->getKey(),
'resource_type_id' => (int) $resourceType->getKey(),
'operation_run_id' => (int) $operationRun->getKey(),
'source_contract_key' => 'exchange_powershell.transportRule',
'source_endpoint' => 'exchange_online_powershell_rest:Get-TransportRule',
'source_metadata' => ['spec' => 437, 'source_identity' => 'must-not-override'],
'raw_payload' => ['Guid' => 'spec437-rule-guid-1', 'SubjectContainsWords' => ['secret-subject']],
'normalized_payload' => spec437FeatureTransportRuleNormalizedPayload(),
'payload_hash' => spec437FeaturePayloadHash(),
'permission_context' => ['provider_connection_id' => (int) $connection->getKey()],
'evidence_state' => EvidenceState::ContentBacked->value,
'coverage_level' => CoverageLevel::ContentBacked->value,
'capture_outcome' => CaptureOutcome::Captured->value,
]);
$resource->forceFill([
'latest_evidence_id' => (int) $evidence->getKey(),
'latest_payload_hash' => spec437FeaturePayloadHash(),
])->save();
$beforeEvidence = $evidence->fresh();
$beforeResource = $resource->fresh();
$beforeResourceType = $resourceType->fresh();
$beforeOperationRun = $operationRun->fresh();
$result = app(ExchangePowerShellComparablePayloadBuilder::class)->build(
resourceType: 'transportRule',
normalizedPayload: $beforeEvidence->normalized_payload,
payloadHash: $beforeEvidence->payload_hash,
metadata: [
'source_contract_key' => $beforeEvidence->source_contract_key,
'source_endpoint' => $beforeEvidence->source_endpoint,
'evidence_state' => $beforeEvidence->evidence_state->value,
'coverage_level' => $beforeEvidence->coverage_level->value,
'capture_outcome' => $beforeEvidence->capture_outcome->value,
'source_identity' => ['field' => 'metadata', 'value' => 'unsafe-override'],
],
);
$afterEvidence = $evidence->fresh();
$afterResource = $resource->fresh();
$afterResourceType = $resourceType->fresh();
$afterOperationRun = $operationRun->fresh();
expect($result['comparable'])->toBeTrue()
->and($result['payload']['provenance']['coverage_level'])->toBe(CoverageLevel::ContentBacked->value)
->and($result['payload']['identity']['field'])->toBe('Guid')
->and($afterEvidence->raw_payload)->toBe($beforeEvidence->raw_payload)
->and($afterEvidence->normalized_payload)->toBe($beforeEvidence->normalized_payload)
->and($afterEvidence->payload_hash)->toBe($beforeEvidence->payload_hash)
->and($afterEvidence->operation_run_id)->toBe($beforeEvidence->operation_run_id)
->and($afterEvidence->source_metadata)->toBe($beforeEvidence->source_metadata)
->and($afterEvidence->coverage_level)->toBe(CoverageLevel::ContentBacked)
->and($afterResource->latest_evidence_id)->toBe($beforeResource->latest_evidence_id)
->and($afterResource->latest_evidence_state)->toBe($beforeResource->latest_evidence_state)
->and($afterResource->latest_claim_state)->toBe($beforeResource->latest_claim_state)
->and($afterResource->latest_payload_hash)->toBe($beforeResource->latest_payload_hash)
->and($afterResourceType->default_coverage_level)->toBe($beforeResourceType->default_coverage_level)
->and($afterOperationRun->type)->toBe($beforeOperationRun->type)
->and($afterOperationRun->context)->toBe($beforeOperationRun->context);
});
it('Spec437 returns no comparable artifact for missing unsafe or unsupported evidence input', function (): void {
$builder = app(ExchangePowerShellComparablePayloadBuilder::class);
$missing = $builder->build('transportRule', []);
$unsafe = $builder->build('remoteDomain', array_replace_recursive(spec437FeatureRemoteDomainNormalizedPayload(), [
'source_identity' => ['field' => 'DomainName', 'value' => 'contoso.example'],
]), payloadHash: spec437FeaturePayloadHash(), metadata: spec437FeatureEvidenceMetadata());
$unsupported = $builder->build('outboundConnector', spec437FeatureRemoteDomainNormalizedPayload());
expect($missing['comparable'])->toBeFalse()
->and($missing['payload'])->toBeNull()
->and($unsafe['comparable'])->toBeFalse()
->and($unsafe['payload'])->toBeNull()
->and($unsupported['comparable'])->toBeFalse()
->and($unsupported['payload'])->toBeNull()
->and(TenantConfigurationResource::query()->count())->toBe(0)
->and(TenantConfigurationResourceEvidence::query()->count())->toBe(0);
});
it('Spec437 introduces no product surface trigger persisted compare table tenant-id ownership or OperationRun type', function (): void {
$builderSource = file_get_contents(app_path('Services/TenantConfiguration/ExchangePowerShellComparablePayloadBuilder.php')) ?: '';
$operationTypes = OperationRunType::values();
expect($builderSource)
->not->toContain('GenericPayloadNormalizer')
->not->toContain('ExchangeTeamsComparablePayloadNormalizer')
->not->toContain('CoverageEvidenceWriter')
->not->toContain('TenantConfigurationResourceEvidence::')
->and(Schema::hasColumn('tenant_configuration_resources', 'tenant_id'))->toBeFalse()
->and(Schema::hasColumn('tenant_configuration_resource_evidence', 'tenant_id'))->toBeFalse()
->and(glob(app_path('Filament/**/*ExchangePowerShellComparable*')) ?: [])->toBe([])
->and(glob(app_path('Livewire/**/*ExchangePowerShellComparable*')) ?: [])->toBe([])
->and(glob(base_path('routes/*exchange*comparable*')) ?: [])->toBe([])
->and(glob(resource_path('views/**/*ExchangePowerShellComparable*')) ?: [])->toBe([])
->and(glob(database_path('migrations/*exchange*comparable*')) ?: [])->toBe([])
->and(glob(database_path('migrations/*tenant*configuration*compare*')) ?: [])->toBe([])
->and(File::exists(app_path('Jobs/TenantConfiguration/BuildExchangePowerShellComparablePayloadJob.php')))->toBeFalse()
->and(File::exists(app_path('Console/Commands/BuildExchangePowerShellComparablePayload.php')))->toBeFalse()
->and($operationTypes)
->not->toContain('tenant_configuration.exchange_comparable')
->not->toContain('tenant_configuration.exchange_powershell_compare')
->not->toContain('tenant_configuration.compare');
});
function spec437FeatureResourceType(string $canonicalType): TenantConfigurationResourceType
{
return TenantConfigurationResourceType::query()
->where('canonical_type', $canonicalType)
->firstOrFail();
}
function spec437FeatureTransportRuleNormalizedPayload(): array
{
return [
'canonical_type' => 'transportRule',
'source_surface' => ExchangePowerShellCommandContracts::SOURCE_SURFACE,
'payload_shape_version' => ExchangePowerShellTargetEvidenceNormalizer::PAYLOAD_SHAPE_VERSION,
'normalizer_version' => 'exchange-transport-rule-readiness-v1',
'source_identity' => ['field' => 'Guid', 'value' => 'spec437-rule-guid-1'],
'material' => [
'Enabled' => true,
'Mode' => 'Enforce',
'State' => 'Enabled',
],
'rule_order' => ['priority' => '0'],
'conditions' => [
'sender_domain_is' => spec437FeatureRedactedMarker(count: 1),
'subject_contains_words' => spec437FeatureRedactedMarker(count: 1),
],
'actions' => [
'delete_message' => true,
'set_header_value' => spec437FeatureRedactedMarker(count: 1),
],
'exceptions' => [],
'protected_configuration' => [
'sender_domain_is' => spec437FeatureRedactedMarker(count: 1),
],
'sensitive_content' => [
'subject_contains_words' => spec437FeatureRedactedMarker(count: 1),
],
'diagnostics' => [
'volatile_fields' => ['WhenChanged', 'ExchangeVersion', 'RunspaceId'],
'protected_fields' => ['sender_domain_is'],
'sensitive_fields' => ['subject_contains_words'],
],
'source' => [
'source_surface' => ExchangePowerShellCommandContracts::SOURCE_SURFACE,
'source_contract_key' => 'exchange_powershell.transportRule',
'command_contract_name' => 'Get-TransportRule',
'command_contract_version' => ExchangePowerShellCommandContracts::COMMAND_CONTRACT_VERSION,
'payload_shape_version' => ExchangePowerShellTargetEvidenceNormalizer::PAYLOAD_SHAPE_VERSION,
'normalizer_version' => 'exchange-transport-rule-readiness-v1',
],
];
}
function spec437FeatureRemoteDomainNormalizedPayload(): array
{
return [
'canonical_type' => 'remoteDomain',
'source_surface' => ExchangePowerShellCommandContracts::SOURCE_SURFACE,
'payload_shape_version' => ExchangePowerShellTargetEvidenceNormalizer::PAYLOAD_SHAPE_VERSION,
'normalizer_version' => 'exchange-remote-domain-readiness-v1',
'source_identity' => ['field' => 'Identity', 'value' => 'spec437-remote-domain-1'],
'material' => ['IsDefault' => false],
'remote_domain_class' => [
'domain_name_is_identity' => false,
'is_default' => false,
],
'settings' => [
'auto_reply_enabled' => true,
'auto_forward_enabled' => spec437FeatureRedactedMarker(count: 1),
],
'protected_configuration' => [
'domain_name' => spec437FeatureRedactedMarker(count: 1),
],
'sensitive_content' => [],
'source' => [
'source_surface' => ExchangePowerShellCommandContracts::SOURCE_SURFACE,
'source_contract_key' => 'exchange_powershell.remoteDomain',
'command_contract_name' => 'Get-RemoteDomain',
'command_contract_version' => ExchangePowerShellCommandContracts::COMMAND_CONTRACT_VERSION,
'payload_shape_version' => ExchangePowerShellTargetEvidenceNormalizer::PAYLOAD_SHAPE_VERSION,
'normalizer_version' => 'exchange-remote-domain-readiness-v1',
],
];
}
function spec437FeatureRedactedMarker(int $count = 1, bool $present = true): array
{
return [
'value' => '[redacted]',
'present' => $present,
'count' => $count,
];
}
function spec437FeatureEvidenceMetadata(): array
{
return [
'evidence_state' => EvidenceState::ContentBacked->value,
'coverage_level' => CoverageLevel::ContentBacked->value,
'capture_outcome' => CaptureOutcome::Captured->value,
];
}
function spec437FeaturePayloadHash(): string
{
return str_repeat('b', 64);
}