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