TenantAtlas/tests/Support/AssertsDriftEvidenceContract.php
ahmido ef41c9193a feat: add Intune RBAC baseline compare support (#156)
## Summary
- add Intune RBAC Role Definition baseline scope support, capture references, compare classification, findings evidence, and landing/detail UI labels
- keep Intune Role Assignments explicitly excluded from baseline compare scope, summaries, findings, and restore messaging
- add focused Pest coverage for baseline scope selection, capture, compare behavior, recurrence, isolation, findings rendering, inventory anchoring, and RBAC summaries

## Verification
- `vendor/bin/sail bin pint --dirty --format agent`
- `vendor/bin/sail artisan test --compact tests/Unit/Inventory/InventoryPolicyTypeMetaBaselineSupportTest.php tests/Unit/Baselines/BaselinePolicyVersionResolverTest.php tests/Unit/Baselines/BaselineScopeTest.php tests/Unit/IntuneRoleDefinitionNormalizerTest.php tests/Feature/Baselines/BaselineCaptureRbacRoleDefinitionsTest.php tests/Feature/Baselines/BaselineCompareRbacRoleDefinitionsTest.php tests/Feature/Baselines/BaselineCompareDriftEvidenceContractRbacTest.php tests/Feature/Baselines/BaselineCompareCoverageGuardTest.php tests/Feature/Baselines/BaselineCompareCrossTenantMatchTest.php tests/Feature/Baselines/BaselineCompareFindingRecurrenceKeyTest.php tests/Feature/Baselines/BaselineCompareWhyNoFindingsReasonCodeTest.php tests/Feature/Filament/BaselineProfileFoundationScopeTest.php tests/Feature/Filament/BaselineSnapshotRbacRoleDefinitionsTest.php tests/Feature/Filament/BaselineCompareLandingRbacLabelsTest.php tests/Feature/Filament/FindingViewRbacEvidenceTest.php tests/Feature/Findings/FindingRecurrenceTest.php tests/Feature/Findings/DriftStaleAutoResolveTest.php tests/Feature/Inventory/InventorySyncButtonTest.php tests/Feature/Inventory/InventorySyncServiceTest.php tests/Feature/RunAuthorizationTenantIsolationTest.php`
- result: `71 passed (467 assertions)`

## Filament / Platform Notes
- Livewire compliance: unchanged and compatible with Livewire v4.0+
- Provider registration: no panel/provider changes; `bootstrap/providers.php` remains the registration location
- Global search: no new globally searchable resource added; existing global search behavior is unchanged
- Destructive actions: no new destructive actions introduced; existing confirmed actions remain unchanged
- Assets: no new Filament assets introduced; deploy asset handling remains unchanged, including `php artisan filament:assets`
- Testing plan covered: baseline profile scope, snapshot detail, compare job, findings recurrence, findings detail, compare landing labels, inventory sync anchoring, and tenant isolation

Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de>
Reviewed-on: #156
2026-03-09 18:49:20 +00:00

116 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',
'rbac_role_definition',
];
/**
* @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';
}
}