TenantAtlas/tests/Support/AssertsDriftEvidenceContract.php
ahmido da1adbdeb5 Spec 119: Drift cutover to Baseline Compare (golden master) (#144)
Implements Spec 119 (Drift Golden Master Cutover):

- Baseline Compare is the only drift writer (`source = baseline.compare`).
- Drift findings now store diff-compatible `evidence_jsonb` (summary.kind, baseline/current policy_version_id refs, fidelity + provenance).
- Findings UI renders one-sided diffs for `missing_policy`/`unexpected_policy` when a single ref exists; otherwise shows explicit “diff unavailable”.
- Removes legacy drift generator runtime (jobs/services/UI) and related tests.
- Adds one-time migration to delete legacy drift findings (`finding_type=drift` where source is null or != baseline.compare).
- Scopes baseline capture & landing duplicate warnings to latest completed inventory sync.
- Canonicalizes compliance `scheduledActionsForRule` drift signal and keeps legacy snapshots comparable.

Tests:
- `vendor/bin/sail artisan test --compact` (full suite per tasks)
- Focused pack: BaselinePolicyVersionResolverTest, BaselineCompareDriftEvidenceContractTest, DriftFindingDiffUnavailableTest, LegacyDriftFindingsCleanupMigrationTest, ComplianceNoncomplianceActionsDriftTest

Notes:
- Livewire v4+ / Filament v5 compatible (no legacy APIs).
- No new external dependencies.

Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de>
Reviewed-on: #144
2026-03-06 14:30:49 +00:00

115 lines
4.1 KiB
PHP

<?php
declare(strict_types=1);
namespace Tests\Support;
use PHPUnit\Framework\Assert;
final class AssertsDriftEvidenceContract
{
/**
* @var list<string>
*/
private const array AllowedSummaryKinds = [
'policy_snapshot',
'policy_assignments',
'policy_scope_tags',
];
/**
* @var list<string>
*/
private const array AllowedChangeTypes = [
'missing_policy',
'unexpected_policy',
'different_version',
];
/**
* @var list<string>
*/
private const array AllowedFidelity = [
'content',
'meta',
'mixed',
];
/**
* @param array<string, mixed> $evidence
*/
public static function assertValid(array $evidence): void
{
foreach (['change_type', 'policy_type', 'subject_key', 'summary', 'baseline', 'current', 'fidelity', 'provenance'] as $key) {
Assert::assertArrayHasKey($key, $evidence);
}
$changeType = is_string($evidence['change_type'] ?? null) ? (string) $evidence['change_type'] : '';
Assert::assertContains($changeType, self::AllowedChangeTypes);
$summary = is_array($evidence['summary'] ?? null) ? $evidence['summary'] : [];
Assert::assertArrayHasKey('kind', $summary);
$kind = is_string($summary['kind'] ?? null) ? (string) $summary['kind'] : '';
Assert::assertContains($kind, self::AllowedSummaryKinds);
$baseline = is_array($evidence['baseline'] ?? null) ? $evidence['baseline'] : [];
$current = is_array($evidence['current'] ?? null) ? $evidence['current'] : [];
Assert::assertArrayHasKey('policy_version_id', $baseline);
Assert::assertArrayHasKey('policy_version_id', $current);
$baselineId = is_numeric($baseline['policy_version_id'] ?? null) ? (int) $baseline['policy_version_id'] : null;
$currentId = is_numeric($current['policy_version_id'] ?? null) ? (int) $current['policy_version_id'] : null;
$expectedFidelity = self::expectedFidelity($baselineId, $currentId);
$fidelity = is_string($evidence['fidelity'] ?? null) ? (string) $evidence['fidelity'] : '';
Assert::assertContains($fidelity, self::AllowedFidelity);
Assert::assertSame($expectedFidelity, $fidelity);
$provenance = is_array($evidence['provenance'] ?? null) ? $evidence['provenance'] : [];
foreach (['baseline_profile_id', 'baseline_snapshot_id', 'compare_operation_run_id', 'inventory_sync_run_id'] as $key) {
Assert::assertArrayHasKey($key, $provenance);
}
Assert::assertIsInt($provenance['baseline_profile_id']);
Assert::assertIsInt($provenance['baseline_snapshot_id']);
Assert::assertIsInt($provenance['compare_operation_run_id']);
$inventorySyncRunId = $provenance['inventory_sync_run_id'] ?? null;
Assert::assertTrue($inventorySyncRunId === null || is_numeric($inventorySyncRunId));
}
public static function isDiffRenderable(array $evidence): bool
{
$changeType = is_string($evidence['change_type'] ?? null) ? (string) $evidence['change_type'] : '';
$baseline = is_array($evidence['baseline'] ?? null) ? $evidence['baseline'] : [];
$current = is_array($evidence['current'] ?? null) ? $evidence['current'] : [];
$baselineId = is_numeric($baseline['policy_version_id'] ?? null) ? (int) $baseline['policy_version_id'] : null;
$currentId = is_numeric($current['policy_version_id'] ?? null) ? (int) $current['policy_version_id'] : null;
return match ($changeType) {
'missing_policy' => $baselineId !== null,
'unexpected_policy' => $currentId !== null,
default => $baselineId !== null && $currentId !== null,
};
}
private static function expectedFidelity(?int $baselinePolicyVersionId, ?int $currentPolicyVersionId): string
{
if ($baselinePolicyVersionId !== null && $currentPolicyVersionId !== null) {
return 'content';
}
if ($baselinePolicyVersionId !== null || $currentPolicyVersionId !== null) {
return 'mixed';
}
return 'meta';
}
}