Some checks failed
PR Fast Feedback / fast-feedback (pull_request) Failing after 4m7s
Implemented deterministic Baseline Result Semantics (Spec 383), introducing CompareSubjectResult and CompareEvidenceResult. Replaced generic arrays with strict Data Transfer Objects for Baseline engine output.
155 lines
6.3 KiB
PHP
155 lines
6.3 KiB
PHP
<?php
|
|
|
|
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\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\ResourceIdentity;
|
|
use Tests\Feature\Baselines\Support\AssertsStructuredBaselineGaps;
|
|
|
|
it('treats duplicate provider identity matches as an evidence gap and suppresses findings', function () {
|
|
[$user, $tenant] = createUserWithTenant(role: 'owner');
|
|
$identity = ResourceIdentity::providerResource('fake-provider', 'policy', 'duplicate-provider-id');
|
|
$subjectKey = spec382AmbiguousSubjectKey($identity);
|
|
$displayName = 'Duplicate Policy';
|
|
$meta = spec382AmbiguousMeta($identity, $displayName, 'baseline-etag');
|
|
|
|
$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()]);
|
|
|
|
BaselineSnapshotItem::factory()->create([
|
|
'baseline_snapshot_id' => (int) $snapshot->getKey(),
|
|
'subject_type' => 'policy',
|
|
'subject_external_id' => BaselineSubjectKey::workspaceSafeSubjectExternalId('deviceConfiguration', $subjectKey),
|
|
'subject_key' => $subjectKey,
|
|
'policy_type' => 'deviceConfiguration',
|
|
'baseline_hash' => hash('sha256', 'baseline'),
|
|
'meta_jsonb' => $meta + [
|
|
'evidence' => [
|
|
'fidelity' => 'content',
|
|
'source' => 'policy_version',
|
|
'observed_at' => now()->toIso8601String(),
|
|
],
|
|
],
|
|
]);
|
|
|
|
$inventorySyncRun = createInventorySyncOperationRunWithCoverage(
|
|
tenant: $tenant,
|
|
statusByType: ['deviceConfiguration' => 'succeeded'],
|
|
);
|
|
|
|
InventoryItem::factory()->create([
|
|
'managed_environment_id' => (int) $tenant->getKey(),
|
|
'workspace_id' => (int) $tenant->workspace_id,
|
|
'external_id' => 'dup-1',
|
|
'policy_type' => 'deviceConfiguration',
|
|
'display_name' => $displayName,
|
|
'meta_jsonb' => spec382AmbiguousMeta($identity, $displayName, 'E1'),
|
|
'last_seen_operation_run_id' => (int) $inventorySyncRun->getKey(),
|
|
'last_seen_at' => now(),
|
|
]);
|
|
InventoryItem::factory()->create([
|
|
'managed_environment_id' => (int) $tenant->getKey(),
|
|
'workspace_id' => (int) $tenant->workspace_id,
|
|
'external_id' => 'dup-2',
|
|
'policy_type' => 'deviceConfiguration',
|
|
'display_name' => 'Renamed Duplicate Policy',
|
|
'meta_jsonb' => spec382AmbiguousMeta($identity, 'Renamed Duplicate Policy', 'E2'),
|
|
'last_seen_operation_run_id' => (int) $inventorySyncRun->getKey(),
|
|
'last_seen_at' => now(),
|
|
]);
|
|
|
|
$opService = app(OperationRunService::class);
|
|
$run = $opService->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),
|
|
$opService,
|
|
);
|
|
|
|
$run->refresh();
|
|
expect($run->status)->toBe('completed');
|
|
expect($run->outcome)->toBe(OperationRunOutcome::PartiallySucceeded->value);
|
|
|
|
expect(
|
|
Finding::query()
|
|
->where('managed_environment_id', (int) $tenant->getKey())
|
|
->where('source', 'baseline.compare')
|
|
->count(),
|
|
)->toBe(0);
|
|
|
|
$context = is_array($run->context) ? $run->context : [];
|
|
expect(data_get($context, 'baseline_compare.evidence_gaps.by_reason.unresolved_duplicate_candidates'))->toBe(1)
|
|
->and(data_get($context, 'baseline_compare.evidence_gaps.by_actionability.binding_required'))->toBe(1);
|
|
|
|
$gapSubjects = data_get($context, 'baseline_compare.evidence_gaps.subjects');
|
|
expect($gapSubjects)->toBeArray();
|
|
AssertsStructuredBaselineGaps::assertStructuredSubjects($gapSubjects);
|
|
|
|
$ambiguousSubject = collect($gapSubjects)->firstWhere('reason_code', 'unresolved_duplicate_candidates');
|
|
|
|
expect($ambiguousSubject)->toBeArray()
|
|
->and(data_get($ambiguousSubject, 'policy_type'))->toBe('deviceConfiguration')
|
|
->and(data_get($ambiguousSubject, 'subject_class'))->toBe('policy_backed')
|
|
->and(data_get($ambiguousSubject, 'resolution_outcome'))->toBe('unresolved_ambiguous_identity')
|
|
->and(data_get($ambiguousSubject, 'operator_action_category'))->toBe('inspect_subject_mapping')
|
|
->and(data_get($ambiguousSubject, 'semantic_outcome.reason'))->toBe('unresolved_duplicate_candidates')
|
|
->and(data_get($ambiguousSubject, 'subject_key'))->toBe($subjectKey);
|
|
});
|
|
|
|
function spec382AmbiguousSubjectKey(ResourceIdentity $identity): string
|
|
{
|
|
$subjectKey = BaselineSubjectKey::forProviderResourceIdentity(
|
|
subjectDomain: 'baseline',
|
|
subjectClass: SubjectClass::PolicyBacked,
|
|
subjectTypeKey: 'deviceConfiguration',
|
|
identity: $identity,
|
|
);
|
|
|
|
expect($subjectKey)->not->toBeNull();
|
|
|
|
return (string) $subjectKey;
|
|
}
|
|
|
|
function spec382AmbiguousMeta(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,
|
|
];
|
|
}
|