TenantAtlas/apps/platform/tests/Feature/Baselines/BaselineCompareProviderResourceBindingCanonicalIdentityTest.php
ahmido 788efee1c2 feat(baselines): implement baseline matching canonicalization (#453)
Replaced legacy tenant and environment bindings in the BaselineDriftEngine with the new ProviderResourceIdentity framework as defined in Spec 382. This ensures cross-environment compatibility and deterministic baseline matching.

Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de>
Reviewed-on: #453
2026-06-15 22:48:48 +00:00

222 lines
8.7 KiB
PHP

<?php
declare(strict_types=1);
use App\Jobs\CompareBaselineToTenantJob;
use App\Models\BaselineProfile;
use App\Models\BaselineSnapshot;
use App\Models\BaselineSnapshotItem;
use App\Models\Finding;
use App\Models\InventoryItem;
use App\Models\ProviderResourceBinding;
use App\Services\Baselines\BaselineSnapshotIdentity;
use App\Services\Intune\AuditLogger;
use App\Services\OperationRunService;
use App\Support\Baselines\BaselineSubjectKey;
use App\Support\Baselines\SubjectClass;
use App\Support\OperationRunOutcome;
use App\Support\OperationRunType;
use App\Support\Resources\ProviderResourceResolutionMode;
use App\Support\Resources\ResourceIdentity;
it('consumes active provider resource bindings before canonical identity matching during baseline compare', function (): void {
[$user, $tenant] = createUserWithTenant(role: 'owner');
$identity = ResourceIdentity::providerResource('fake-provider', 'policy', 'bound-policy-2');
$displayName = 'Duplicate Provider Policy';
$canonicalSubjectKey = spec382CompareSubjectKey($identity);
$currentMeta = spec382CompareMeta($identity, $displayName, 'etag-bound-policy-2');
[$profile, $snapshot] = spec382CompareProfileAndSnapshot($tenant);
ProviderResourceBinding::factory()
->providerResource($identity)
->create([
'managed_environment_id' => (int) $tenant->getKey(),
'workspace_id' => (int) $tenant->workspace_id,
'canonical_subject_key' => $canonicalSubjectKey,
'display_label' => $displayName,
'resolution_mode' => ProviderResourceResolutionMode::ManualBinding->value,
]);
BaselineSnapshotItem::factory()->create([
'baseline_snapshot_id' => (int) $snapshot->getKey(),
'subject_type' => 'policy',
'subject_external_id' => BaselineSubjectKey::workspaceSafeSubjectExternalId('deviceConfiguration', $canonicalSubjectKey),
'subject_key' => $canonicalSubjectKey,
'policy_type' => 'deviceConfiguration',
'baseline_hash' => app(BaselineSnapshotIdentity::class)->hashItemContent('deviceConfiguration', 'bound-policy-2', $currentMeta),
'meta_jsonb' => $currentMeta,
]);
$inventorySyncRun = createInventorySyncOperationRunWithCoverage(
tenant: $tenant,
statusByType: ['deviceConfiguration' => 'succeeded'],
);
spec382CompareInventoryItem(
tenant: $tenant,
identity: ResourceIdentity::providerResource('fake-provider', 'policy', 'bound-policy-1'),
externalId: 'bound-policy-1',
displayName: $displayName,
etag: 'etag-bound-policy-1',
inventorySyncRunId: (int) $inventorySyncRun->getKey(),
);
spec382CompareInventoryItem(
tenant: $tenant,
identity: $identity,
externalId: 'bound-policy-2',
displayName: $displayName,
etag: 'etag-bound-policy-2',
inventorySyncRunId: (int) $inventorySyncRun->getKey(),
);
$run = spec382RunCompare($tenant, $user, $profile, $snapshot);
$run->refresh();
$context = is_array($run->context) ? $run->context : [];
expect($run->outcome)->toBe(OperationRunOutcome::Succeeded->value)
->and(data_get($context, 'baseline_compare.matching.active_bindings_considered'))->toBe(1)
->and(data_get($context, 'baseline_compare.matching.by_reason.active_provider_resource_binding'))->toBe(1)
->and(data_get($context, 'baseline_compare.evidence_gaps.by_reason.ambiguous_match'))->toBeNull()
->and(data_get($context, 'baseline_compare.evidence_gaps.by_reason.identity_required'))->toBeNull()
->and(Finding::query()
->where('managed_environment_id', (int) $tenant->getKey())
->where('source', 'baseline.compare')
->where('subject_external_id', 'bound-policy-2')
->exists())->toBeFalse();
});
it('turns old subject-key and display-only baseline subjects into identity-required gaps', function (): void {
[$user, $tenant] = createUserWithTenant(role: 'owner');
$displayName = 'Display Only Match Policy';
[$profile, $snapshot] = spec382CompareProfileAndSnapshot($tenant);
$meta = [
'display_name' => $displayName,
'etag' => 'display-only-etag',
];
BaselineSnapshotItem::factory()->create([
'baseline_snapshot_id' => (int) $snapshot->getKey(),
'subject_type' => 'policy',
'subject_external_id' => BaselineSubjectKey::workspaceSafeSubjectExternalId('deviceConfiguration', 'old-display-subject-key'),
'subject_key' => 'old-display-subject-key',
'policy_type' => 'deviceConfiguration',
'baseline_hash' => app(BaselineSnapshotIdentity::class)->hashItemContent('deviceConfiguration', 'display-only-policy', $meta),
'meta_jsonb' => $meta,
]);
$inventorySyncRun = createInventorySyncOperationRunWithCoverage(
tenant: $tenant,
statusByType: ['deviceConfiguration' => 'succeeded'],
);
InventoryItem::factory()->create([
'managed_environment_id' => (int) $tenant->getKey(),
'workspace_id' => (int) $tenant->workspace_id,
'external_id' => 'display-only-policy',
'policy_type' => 'deviceConfiguration',
'display_name' => $displayName,
'meta_jsonb' => $meta,
'last_seen_operation_run_id' => (int) $inventorySyncRun->getKey(),
'last_seen_at' => now(),
]);
$run = spec382RunCompare($tenant, $user, $profile, $snapshot);
$run->refresh();
$context = is_array($run->context) ? $run->context : [];
expect($run->outcome)->toBe(OperationRunOutcome::PartiallySucceeded->value)
->and(data_get($context, 'baseline_compare.matching.by_reason.identity_required'))->toBe(1)
->and(data_get($context, 'baseline_compare.evidence_gaps.by_reason.identity_required'))->toBe(1)
->and(data_get($context, 'baseline_compare.matching.by_reason.canonical_subject_key'))->toBeNull();
});
function spec382CompareProfileAndSnapshot($tenant): array
{
$profile = BaselineProfile::factory()->active()->create([
'workspace_id' => (int) $tenant->workspace_id,
'scope_jsonb' => ['policy_types' => ['deviceConfiguration'], 'foundation_types' => []],
]);
$snapshot = BaselineSnapshot::factory()->create([
'workspace_id' => (int) $tenant->workspace_id,
'baseline_profile_id' => (int) $profile->getKey(),
'captured_at' => now()->subMinute(),
]);
$profile->update(['active_snapshot_id' => (int) $snapshot->getKey()]);
return [$profile, $snapshot];
}
function spec382CompareSubjectKey(ResourceIdentity $identity): string
{
$subjectKey = BaselineSubjectKey::forProviderResourceIdentity(
subjectDomain: 'baseline',
subjectClass: SubjectClass::PolicyBacked,
subjectTypeKey: 'deviceConfiguration',
identity: $identity,
);
expect($subjectKey)->not->toBeNull();
return (string) $subjectKey;
}
function spec382CompareMeta(ResourceIdentity $identity, string $displayName, string $etag): array
{
return [
'display_name' => $displayName,
'provider_key' => $identity->providerKey,
'provider_resource_identity' => $identity->toArray(),
'provider_resource_fingerprint' => $identity->fingerprint(),
'odata_type' => 'fake.policy',
'etag' => $etag,
];
}
function spec382CompareInventoryItem(
$tenant,
ResourceIdentity $identity,
string $externalId,
string $displayName,
string $etag,
int $inventorySyncRunId,
): InventoryItem {
return InventoryItem::factory()->create([
'managed_environment_id' => (int) $tenant->getKey(),
'workspace_id' => (int) $tenant->workspace_id,
'external_id' => $externalId,
'policy_type' => 'deviceConfiguration',
'display_name' => $displayName,
'meta_jsonb' => spec382CompareMeta($identity, $displayName, $etag),
'last_seen_operation_run_id' => $inventorySyncRunId,
'last_seen_at' => now(),
]);
}
function spec382RunCompare($tenant, $user, BaselineProfile $profile, BaselineSnapshot $snapshot)
{
$operationRunService = app(OperationRunService::class);
$run = $operationRunService->ensureRunWithIdentity(
tenant: $tenant,
type: OperationRunType::BaselineCompare->value,
identityInputs: ['baseline_profile_id' => (int) $profile->getKey()],
context: [
'baseline_profile_id' => (int) $profile->getKey(),
'baseline_snapshot_id' => (int) $snapshot->getKey(),
'effective_scope' => ['policy_types' => ['deviceConfiguration'], 'foundation_types' => []],
],
initiator: $user,
);
(new CompareBaselineToTenantJob($run))->handle(
app(BaselineSnapshotIdentity::class),
app(AuditLogger::class),
$operationRunService,
);
return $run;
}