active()->create([ 'workspace_id' => $tenant->workspace_id, 'scope_jsonb' => ['policy_types' => ['deviceConfiguration'], 'foundation_types' => []], ]); $snapshot = BaselineSnapshot::factory()->create([ 'workspace_id' => $tenant->workspace_id, 'baseline_profile_id' => $profile->getKey(), ]); $profile->update(['active_snapshot_id' => $snapshot->getKey()]); $inventorySyncRun = createInventorySyncOperationRunWithCoverage( tenant: $tenant, statusByType: ['deviceConfiguration' => 'succeeded'], ); $policyType = 'deviceConfiguration'; $displayNameA = 'Policy A'; $subjectKeyA = BaselineSubjectKey::fromDisplayName($displayNameA); expect($subjectKeyA)->not->toBeNull(); $workspaceSafeExternalIdA = BaselineSubjectKey::workspaceSafeSubjectExternalId($policyType, (string) $subjectKeyA); $baselineHashA = app(BaselineSnapshotIdentity::class)->hashItemContent( policyType: $policyType, subjectExternalId: 'policy-a-uuid', metaJsonb: ['odata_type' => '#microsoft.graph.deviceConfiguration', 'etag' => 'E_BASELINE_A'], ); $displayNameB = 'Policy B'; $subjectKeyB = BaselineSubjectKey::fromDisplayName($displayNameB); expect($subjectKeyB)->not->toBeNull(); $workspaceSafeExternalIdB = BaselineSubjectKey::workspaceSafeSubjectExternalId($policyType, (string) $subjectKeyB); $baselineHashB = app(BaselineSnapshotIdentity::class)->hashItemContent( policyType: $policyType, subjectExternalId: 'policy-b-uuid', metaJsonb: ['odata_type' => '#microsoft.graph.deviceConfiguration', 'etag' => 'E_BASELINE_B'], ); // Baseline has policyA and policyB BaselineSnapshotItem::factory()->create([ 'baseline_snapshot_id' => $snapshot->getKey(), 'subject_type' => 'policy', 'subject_external_id' => $workspaceSafeExternalIdA, 'subject_key' => (string) $subjectKeyA, 'policy_type' => $policyType, 'baseline_hash' => $baselineHashA, 'meta_jsonb' => ['display_name' => $displayNameA], ]); BaselineSnapshotItem::factory()->create([ 'baseline_snapshot_id' => $snapshot->getKey(), 'subject_type' => 'policy', 'subject_external_id' => $workspaceSafeExternalIdB, 'subject_key' => (string) $subjectKeyB, 'policy_type' => $policyType, 'baseline_hash' => $baselineHashB, 'meta_jsonb' => ['display_name' => $displayNameB], ]); // Tenant has policyA (different content) and policyC (unexpected) InventoryItem::factory()->create([ 'tenant_id' => $tenant->getKey(), 'workspace_id' => $tenant->workspace_id, 'external_id' => 'policy-a-uuid', 'policy_type' => $policyType, 'meta_jsonb' => ['odata_type' => '#microsoft.graph.deviceConfiguration', 'etag' => 'E_CURRENT_A'], 'display_name' => $displayNameA, 'last_seen_operation_run_id' => (int) $inventorySyncRun->getKey(), 'last_seen_at' => now(), ]); InventoryItem::factory()->create([ 'tenant_id' => $tenant->getKey(), 'workspace_id' => $tenant->workspace_id, 'external_id' => 'policy-c-uuid', 'policy_type' => $policyType, 'meta_jsonb' => ['odata_type' => '#microsoft.graph.deviceConfiguration', 'etag' => 'E_CURRENT_C'], 'display_name' => 'Policy C', '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, ); $job = new CompareBaselineToTenantJob($run); $job->handle( app(BaselineSnapshotIdentity::class), app(AuditLogger::class), $opService, ); $run->refresh(); expect($run->status)->toBe('completed'); expect($run->outcome)->toBe('succeeded'); $context = is_array($run->context) ? $run->context : []; $countsByChangeType = $context['findings']['counts_by_change_type'] ?? null; expect($countsByChangeType)->toBe([ 'different_version' => 1, 'missing_policy' => 1, 'unexpected_policy' => 1, ]); $scopeKey = 'baseline_profile:'.$profile->getKey(); $findings = Finding::query() ->where('tenant_id', $tenant->getKey()) ->where('source', 'baseline.compare') ->where('scope_key', $scopeKey) ->get(); // policyB missing (high), policyA different (medium), policyC unexpected (low) = 3 findings expect($findings->count())->toBe(3); expect($findings->every(fn (Finding $finding): bool => filled($finding->recurrence_key) && $finding->recurrence_key === $finding->fingerprint))->toBeTrue(); // Lifecycle v2 fields must be initialized for new findings. expect($findings->pluck('first_seen_at')->filter()->count())->toBe($findings->count()); expect($findings->pluck('last_seen_at')->filter()->count())->toBe($findings->count()); expect($findings->pluck('times_seen')->every(fn ($value) => (int) $value === 1))->toBeTrue(); $severities = $findings->pluck('severity')->sort()->values()->all(); expect($severities)->toContain(Finding::SEVERITY_HIGH); expect($severities)->toContain(Finding::SEVERITY_MEDIUM); expect($severities)->toContain(Finding::SEVERITY_LOW); }); it('does not fail compare when baseline severity mapping setting is missing', function () { [$user, $tenant] = createUserWithTenant(role: 'owner'); $profile = BaselineProfile::factory()->active()->create([ 'workspace_id' => $tenant->workspace_id, 'scope_jsonb' => ['policy_types' => ['deviceConfiguration']], ]); $snapshot = BaselineSnapshot::factory()->create([ 'workspace_id' => $tenant->workspace_id, 'baseline_profile_id' => $profile->getKey(), ]); $profile->update(['active_snapshot_id' => $snapshot->getKey()]); $inventorySyncRun = createInventorySyncOperationRunWithCoverage( tenant: $tenant, statusByType: ['deviceConfiguration' => 'succeeded'], ); $policyType = 'deviceConfiguration'; $displayNameA = 'Policy A'; $subjectKeyA = BaselineSubjectKey::fromDisplayName($displayNameA); expect($subjectKeyA)->not->toBeNull(); $workspaceSafeExternalIdA = BaselineSubjectKey::workspaceSafeSubjectExternalId($policyType, (string) $subjectKeyA); $baselineHashA = app(BaselineSnapshotIdentity::class)->hashItemContent( policyType: $policyType, subjectExternalId: 'policy-a-uuid', metaJsonb: ['odata_type' => '#microsoft.graph.deviceConfiguration', 'etag' => 'E_BASELINE_A'], ); $displayNameB = 'Policy B'; $subjectKeyB = BaselineSubjectKey::fromDisplayName($displayNameB); expect($subjectKeyB)->not->toBeNull(); $workspaceSafeExternalIdB = BaselineSubjectKey::workspaceSafeSubjectExternalId($policyType, (string) $subjectKeyB); $baselineHashB = app(BaselineSnapshotIdentity::class)->hashItemContent( policyType: $policyType, subjectExternalId: 'policy-b-uuid', metaJsonb: ['odata_type' => '#microsoft.graph.deviceConfiguration', 'etag' => 'E_BASELINE_B'], ); BaselineSnapshotItem::factory()->create([ 'baseline_snapshot_id' => $snapshot->getKey(), 'subject_type' => 'policy', 'subject_external_id' => $workspaceSafeExternalIdA, 'subject_key' => (string) $subjectKeyA, 'policy_type' => $policyType, 'baseline_hash' => $baselineHashA, 'meta_jsonb' => ['display_name' => $displayNameA], ]); BaselineSnapshotItem::factory()->create([ 'baseline_snapshot_id' => $snapshot->getKey(), 'subject_type' => 'policy', 'subject_external_id' => $workspaceSafeExternalIdB, 'subject_key' => (string) $subjectKeyB, 'policy_type' => $policyType, 'baseline_hash' => $baselineHashB, 'meta_jsonb' => ['display_name' => $displayNameB], ]); InventoryItem::factory()->create([ 'tenant_id' => $tenant->getKey(), 'workspace_id' => $tenant->workspace_id, 'external_id' => 'policy-a-uuid', 'policy_type' => $policyType, 'meta_jsonb' => ['odata_type' => '#microsoft.graph.deviceConfiguration', 'etag' => 'E_CURRENT_A'], 'display_name' => $displayNameA, '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']], ], initiator: $user, ); $settingsResolver = mock(SettingsResolver::class); $settingsResolver ->shouldReceive('resolveValue') ->andThrow(new InvalidArgumentException('Unknown setting key: baseline.severity_mapping')); $baselineAutoCloseService = new \App\Services\Baselines\BaselineAutoCloseService($settingsResolver); (new CompareBaselineToTenantJob($run))->handle( app(BaselineSnapshotIdentity::class), app(AuditLogger::class), $opService, settingsResolver: $settingsResolver, baselineAutoCloseService: $baselineAutoCloseService, ); $run->refresh(); expect($run->status)->toBe('completed'); expect($run->outcome)->toBe('succeeded'); $scopeKey = 'baseline_profile:'.$profile->getKey(); $findings = Finding::query() ->where('tenant_id', $tenant->getKey()) ->where('source', 'baseline.compare') ->where('scope_key', $scopeKey) ->get(); // policyB missing (high), policyA different (medium) = 2 findings. expect($findings->count())->toBe(2); expect($findings->pluck('severity')->all())->toContain(Finding::SEVERITY_HIGH); expect($findings->pluck('severity')->all())->toContain(Finding::SEVERITY_MEDIUM); }); it('treats inventory items not seen in latest inventory sync as missing', function () { [$user, $tenant] = createUserWithTenant(role: 'owner'); $profile = BaselineProfile::factory()->active()->create([ 'workspace_id' => $tenant->workspace_id, 'scope_jsonb' => ['policy_types' => ['settingsCatalogPolicy']], ]); $snapshot = BaselineSnapshot::factory()->create([ 'workspace_id' => $tenant->workspace_id, 'baseline_profile_id' => $profile->getKey(), ]); $profile->update(['active_snapshot_id' => $snapshot->getKey()]); $olderInventoryRun = createInventorySyncOperationRunWithCoverage( tenant: $tenant, statusByType: ['settingsCatalogPolicy' => 'succeeded'], attributes: [ 'selection_hash' => 'older', 'selection_payload' => ['policy_types' => ['settingsCatalogPolicy']], 'finished_at' => now()->subMinutes(5), ], ); createInventorySyncOperationRunWithCoverage( tenant: $tenant, statusByType: ['settingsCatalogPolicy' => 'succeeded'], attributes: [ 'selection_hash' => 'latest', 'selection_payload' => ['policy_types' => ['settingsCatalogPolicy']], 'finished_at' => now(), ], ); $displayName = 'Settings Catalog A'; $subjectKey = BaselineSubjectKey::fromDisplayName($displayName); expect($subjectKey)->not->toBeNull(); $workspaceSafeExternalId = BaselineSubjectKey::workspaceSafeSubjectExternalId('settingsCatalogPolicy', (string) $subjectKey); BaselineSnapshotItem::factory()->create([ 'baseline_snapshot_id' => $snapshot->getKey(), 'subject_type' => 'policy', 'subject_external_id' => $workspaceSafeExternalId, 'subject_key' => (string) $subjectKey, 'policy_type' => 'settingsCatalogPolicy', 'baseline_hash' => hash('sha256', 'content-a'), 'meta_jsonb' => ['display_name' => $displayName], ]); // Inventory item exists, but it was NOT observed in the latest sync run. InventoryItem::factory()->create([ 'tenant_id' => $tenant->getKey(), 'workspace_id' => $tenant->workspace_id, 'external_id' => 'settings-catalog-policy-uuid', 'policy_type' => 'settingsCatalogPolicy', 'display_name' => $displayName, 'meta_jsonb' => ['etag' => 'abc'], 'last_seen_operation_run_id' => (int) $olderInventoryRun->getKey(), 'last_seen_at' => now()->subMinutes(5), ]); $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' => ['settingsCatalogPolicy']], ], initiator: $user, ); (new CompareBaselineToTenantJob($run))->handle( app(BaselineSnapshotIdentity::class), app(AuditLogger::class), $opService, ); $run->refresh(); expect($run->status)->toBe('completed'); expect($run->outcome)->toBe('succeeded'); $scopeKey = 'baseline_profile:'.$profile->getKey(); $findings = Finding::query() ->where('tenant_id', $tenant->getKey()) ->where('source', 'baseline.compare') ->where('scope_key', $scopeKey) ->get(); expect($findings->count())->toBe(1); expect($findings->first()?->evidence_jsonb['change_type'] ?? null)->toBe('missing_policy'); }); it('uses recurrence-key identity (no hashes in fingerprint) and increments times_seen at most once per run', function () { [$user, $tenant] = createUserWithTenant(role: 'owner'); $profile = BaselineProfile::factory()->active()->create([ 'workspace_id' => $tenant->workspace_id, 'scope_jsonb' => ['policy_types' => ['deviceConfiguration'], 'foundation_types' => []], ]); $snapshot = BaselineSnapshot::factory()->create([ 'workspace_id' => $tenant->workspace_id, 'baseline_profile_id' => $profile->getKey(), ]); $profile->update(['active_snapshot_id' => $snapshot->getKey()]); $inventorySyncRun = createInventorySyncOperationRunWithCoverage( tenant: $tenant, statusByType: ['deviceConfiguration' => 'succeeded'], ); $builder = app(InventoryMetaContract::class); $hasher = app(DriftHasher::class); $baselineContract = $builder->build( policyType: 'deviceConfiguration', subjectExternalId: 'policy-x-uuid', metaJsonb: ['odata_type' => '#microsoft.graph.deviceConfiguration', 'etag' => 'E_BASELINE'], ); $displayName = 'Policy X'; $subjectKey = BaselineSubjectKey::fromDisplayName($displayName); expect($subjectKey)->not->toBeNull(); $workspaceSafeExternalId = BaselineSubjectKey::workspaceSafeSubjectExternalId('deviceConfiguration', (string) $subjectKey); BaselineSnapshotItem::factory()->create([ 'baseline_snapshot_id' => $snapshot->getKey(), 'subject_type' => 'policy', 'subject_external_id' => $workspaceSafeExternalId, 'subject_key' => (string) $subjectKey, 'policy_type' => 'deviceConfiguration', 'baseline_hash' => $hasher->hashNormalized($baselineContract), 'meta_jsonb' => ['display_name' => $displayName], ]); InventoryItem::factory()->create([ 'tenant_id' => $tenant->getKey(), 'workspace_id' => $tenant->workspace_id, 'external_id' => 'policy-x-uuid', 'policy_type' => 'deviceConfiguration', 'meta_jsonb' => ['odata_type' => '#microsoft.graph.deviceConfiguration', 'etag' => 'E_CURRENT_1'], 'display_name' => $displayName, 'last_seen_operation_run_id' => (int) $inventorySyncRun->getKey(), 'last_seen_at' => now(), ]); $opService = app(OperationRunService::class); $run1 = $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, ); $job = new CompareBaselineToTenantJob($run1); $job->handle( app(BaselineSnapshotIdentity::class), app(AuditLogger::class), $opService, ); $scopeKey = 'baseline_profile:'.$profile->getKey(); $finding = Finding::query() ->where('tenant_id', $tenant->getKey()) ->where('source', 'baseline.compare') ->where('scope_key', $scopeKey) ->sole(); expect($finding->recurrence_key)->not->toBeNull(); expect($finding->fingerprint)->toBe($finding->recurrence_key); expect($finding->times_seen)->toBe(1); $fingerprint = (string) $finding->fingerprint; $currentHash1 = (string) ($finding->evidence_jsonb['current_hash'] ?? ''); expect($currentHash1)->not->toBe(''); // Retry the same run ID (job retry): times_seen MUST NOT increment twice for the same run. $job->handle( app(BaselineSnapshotIdentity::class), app(AuditLogger::class), $opService, ); $finding->refresh(); expect($finding->times_seen)->toBe(1); expect((string) $finding->fingerprint)->toBe($fingerprint); expect((string) ($finding->evidence_jsonb['current_hash'] ?? ''))->toBe($currentHash1); // Change inventory evidence (hash changes) and run compare again with a new OperationRun. InventoryItem::query() ->where('tenant_id', $tenant->getKey()) ->where('policy_type', 'deviceConfiguration') ->where('external_id', 'policy-x-uuid') ->update([ 'meta_jsonb' => ['odata_type' => '#microsoft.graph.deviceConfiguration', 'etag' => 'E_CURRENT_2'], ]); $run2 = $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($run2))->handle( app(BaselineSnapshotIdentity::class), app(AuditLogger::class), $opService, ); $finding->refresh(); expect((string) $finding->fingerprint)->toBe($fingerprint); expect($finding->times_seen)->toBe(2); expect((string) ($finding->evidence_jsonb['current_hash'] ?? ''))->not->toBe($currentHash1); }); it('does not create new finding identities when a new snapshot is captured', function () { [$user, $tenant] = createUserWithTenant(role: 'owner'); $profile = BaselineProfile::factory()->active()->create([ 'workspace_id' => $tenant->workspace_id, 'scope_jsonb' => ['policy_types' => ['deviceConfiguration'], 'foundation_types' => []], ]); $inventorySyncRun = createInventorySyncOperationRunWithCoverage( tenant: $tenant, statusByType: ['deviceConfiguration' => 'succeeded'], ); $builder = app(InventoryMetaContract::class); $hasher = app(DriftHasher::class); $baselineContract = $builder->build( policyType: 'deviceConfiguration', subjectExternalId: 'policy-x-uuid', metaJsonb: ['odata_type' => '#microsoft.graph.deviceConfiguration', 'etag' => 'E_BASELINE'], ); $baselineHash = $hasher->hashNormalized($baselineContract); $displayName = 'Policy X'; $subjectKey = BaselineSubjectKey::fromDisplayName($displayName); expect($subjectKey)->not->toBeNull(); $workspaceSafeExternalId = BaselineSubjectKey::workspaceSafeSubjectExternalId('deviceConfiguration', (string) $subjectKey); $snapshot1 = BaselineSnapshot::factory()->create([ 'workspace_id' => $tenant->workspace_id, 'baseline_profile_id' => $profile->getKey(), ]); BaselineSnapshotItem::factory()->create([ 'baseline_snapshot_id' => $snapshot1->getKey(), 'subject_type' => 'policy', 'subject_external_id' => $workspaceSafeExternalId, 'subject_key' => (string) $subjectKey, 'policy_type' => 'deviceConfiguration', 'baseline_hash' => $baselineHash, 'meta_jsonb' => ['display_name' => $displayName], ]); InventoryItem::factory()->create([ 'tenant_id' => $tenant->getKey(), 'workspace_id' => $tenant->workspace_id, 'external_id' => 'policy-x-uuid', 'policy_type' => 'deviceConfiguration', 'meta_jsonb' => ['odata_type' => '#microsoft.graph.deviceConfiguration', 'etag' => 'E_CURRENT'], 'display_name' => $displayName, 'last_seen_operation_run_id' => (int) $inventorySyncRun->getKey(), 'last_seen_at' => now(), ]); $opService = app(OperationRunService::class); $run1 = $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) $snapshot1->getKey(), 'effective_scope' => ['policy_types' => ['deviceConfiguration'], 'foundation_types' => []], ], initiator: $user, ); (new CompareBaselineToTenantJob($run1))->handle( app(BaselineSnapshotIdentity::class), app(AuditLogger::class), $opService, ); $scopeKey = 'baseline_profile:'.$profile->getKey(); $finding = Finding::query() ->where('tenant_id', $tenant->getKey()) ->where('source', 'baseline.compare') ->where('scope_key', $scopeKey) ->sole(); expect($finding->times_seen)->toBe(1); $fingerprint1 = (string) $finding->fingerprint; $snapshot2 = BaselineSnapshot::factory()->create([ 'workspace_id' => $tenant->workspace_id, 'baseline_profile_id' => $profile->getKey(), ]); BaselineSnapshotItem::factory()->create([ 'baseline_snapshot_id' => $snapshot2->getKey(), 'subject_type' => 'policy', 'subject_external_id' => $workspaceSafeExternalId, 'subject_key' => (string) $subjectKey, 'policy_type' => 'deviceConfiguration', 'baseline_hash' => $baselineHash, 'meta_jsonb' => ['display_name' => $displayName], ]); $run2 = $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) $snapshot2->getKey(), 'effective_scope' => ['policy_types' => ['deviceConfiguration'], 'foundation_types' => []], ], initiator: $user, ); (new CompareBaselineToTenantJob($run2))->handle( app(BaselineSnapshotIdentity::class), app(AuditLogger::class), $opService, ); $findings = Finding::query() ->where('tenant_id', $tenant->getKey()) ->where('source', 'baseline.compare') ->where('scope_key', $scopeKey) ->orderBy('id') ->get(); expect($findings)->toHaveCount(1); expect((string) $findings->first()?->fingerprint)->toBe($fingerprint1); expect((int) $findings->first()?->times_seen)->toBe(2); }); it('creates zero findings when baseline matches tenant inventory exactly', function () { [$user, $tenant] = createUserWithTenant(role: 'owner'); $profile = BaselineProfile::factory()->active()->create([ 'workspace_id' => $tenant->workspace_id, 'scope_jsonb' => ['policy_types' => ['deviceConfiguration'], 'foundation_types' => []], ]); $snapshot = BaselineSnapshot::factory()->create([ 'workspace_id' => $tenant->workspace_id, 'baseline_profile_id' => $profile->getKey(), ]); $profile->update(['active_snapshot_id' => $snapshot->getKey()]); $inventorySyncRun = createInventorySyncOperationRunWithCoverage( tenant: $tenant, statusByType: ['deviceConfiguration' => 'succeeded'], ); // Baseline item $metaContent = [ 'odata_type' => '#microsoft.graph.deviceConfiguration', 'etag' => 'E1', 'scope_tag_ids' => ['scope-2', 'scope-1'], 'assignment_target_count' => 3, ]; $builder = app(InventoryMetaContract::class); $driftHasher = app(DriftHasher::class); $contentHash = $driftHasher->hashNormalized($builder->build( policyType: 'deviceConfiguration', subjectExternalId: 'matching-uuid', metaJsonb: $metaContent, )); $displayName = 'Matching Policy'; $subjectKey = BaselineSubjectKey::fromDisplayName($displayName); expect($subjectKey)->not->toBeNull(); $workspaceSafeExternalId = BaselineSubjectKey::workspaceSafeSubjectExternalId('deviceConfiguration', (string) $subjectKey); BaselineSnapshotItem::factory()->create([ 'baseline_snapshot_id' => $snapshot->getKey(), 'subject_type' => 'policy', 'subject_external_id' => $workspaceSafeExternalId, 'subject_key' => (string) $subjectKey, 'policy_type' => 'deviceConfiguration', 'baseline_hash' => $contentHash, 'meta_jsonb' => ['display_name' => $displayName], ]); // Tenant inventory with same content → same hash InventoryItem::factory()->create([ 'tenant_id' => $tenant->getKey(), 'workspace_id' => $tenant->workspace_id, 'external_id' => 'matching-uuid', 'policy_type' => 'deviceConfiguration', 'meta_jsonb' => $metaContent, 'display_name' => $displayName, '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, ); $job = new CompareBaselineToTenantJob($run); $job->handle( app(BaselineSnapshotIdentity::class), app(AuditLogger::class), $opService, ); $run->refresh(); expect($run->status)->toBe('completed'); expect($run->outcome)->toBe('succeeded'); $counts = is_array($run->summary_counts) ? $run->summary_counts : []; expect((int) ($counts['total'] ?? -1))->toBe(0); $findings = Finding::query() ->where('tenant_id', $tenant->getKey()) ->where('source', 'baseline.compare') ->count(); expect($findings)->toBe(0); }); it('does not create missing_policy findings for baseline snapshot items outside effective scope', function () { [$user, $tenant] = createUserWithTenant(role: 'owner'); $profile = BaselineProfile::factory()->active()->create([ 'workspace_id' => $tenant->workspace_id, 'scope_jsonb' => ['policy_types' => ['deviceConfiguration'], 'foundation_types' => []], ]); $snapshot = BaselineSnapshot::factory()->create([ 'workspace_id' => $tenant->workspace_id, 'baseline_profile_id' => $profile->getKey(), ]); $profile->update(['active_snapshot_id' => $snapshot->getKey()]); $inventorySyncRun = createInventorySyncOperationRunWithCoverage( tenant: $tenant, statusByType: ['deviceConfiguration' => 'succeeded'], ); $metaContent = [ 'odata_type' => '#microsoft.graph.deviceConfiguration', 'etag' => 'E1', 'scope_tag_ids' => ['scope-2', 'scope-1'], 'assignment_target_count' => 3, ]; $builder = app(InventoryMetaContract::class); $driftHasher = app(DriftHasher::class); $contentHash = $driftHasher->hashNormalized($builder->build( policyType: 'deviceConfiguration', subjectExternalId: 'matching-uuid', metaJsonb: $metaContent, )); $displayName = 'Matching Policy'; $subjectKey = BaselineSubjectKey::fromDisplayName($displayName); expect($subjectKey)->not->toBeNull(); $workspaceSafeExternalId = BaselineSubjectKey::workspaceSafeSubjectExternalId('deviceConfiguration', (string) $subjectKey); BaselineSnapshotItem::factory()->create([ 'baseline_snapshot_id' => $snapshot->getKey(), 'subject_type' => 'policy', 'subject_external_id' => $workspaceSafeExternalId, 'subject_key' => (string) $subjectKey, 'policy_type' => 'deviceConfiguration', 'baseline_hash' => $contentHash, 'meta_jsonb' => ['display_name' => $displayName], ]); $foundationDisplayName = 'Foundation Template'; $foundationSubjectKey = BaselineSubjectKey::fromDisplayName($foundationDisplayName); expect($foundationSubjectKey)->not->toBeNull(); $foundationWorkspaceSafeExternalId = BaselineSubjectKey::workspaceSafeSubjectExternalId( 'notificationMessageTemplate', (string) $foundationSubjectKey, ); BaselineSnapshotItem::factory()->create([ 'baseline_snapshot_id' => $snapshot->getKey(), 'subject_type' => 'policy', 'subject_external_id' => $foundationWorkspaceSafeExternalId, 'subject_key' => (string) $foundationSubjectKey, 'policy_type' => 'notificationMessageTemplate', 'baseline_hash' => hash('sha256', 'foundation-content'), 'meta_jsonb' => ['display_name' => $foundationDisplayName], ]); InventoryItem::factory()->create([ 'tenant_id' => $tenant->getKey(), 'workspace_id' => $tenant->workspace_id, 'external_id' => 'matching-uuid', 'policy_type' => 'deviceConfiguration', 'meta_jsonb' => $metaContent, 'display_name' => $displayName, 'last_seen_operation_run_id' => (int) $inventorySyncRun->getKey(), 'last_seen_at' => now(), ]); InventoryItem::factory()->create([ 'tenant_id' => $tenant->getKey(), 'workspace_id' => $tenant->workspace_id, 'external_id' => 'foundation-uuid', 'policy_type' => 'notificationMessageTemplate', 'meta_jsonb' => ['some' => 'value'], 'display_name' => $foundationDisplayName, '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 = $run->fresh(); $counts = is_array($run->summary_counts) ? $run->summary_counts : []; expect((int) ($counts['total'] ?? -1))->toBe(0); $scopeKey = 'baseline_profile:'.$profile->getKey(); expect( Finding::query() ->where('tenant_id', $tenant->getKey()) ->where('source', 'baseline.compare') ->where('scope_key', $scopeKey) ->count() )->toBe(0); }); // --- T042: Summary counts severity breakdown tests --- it('writes severity breakdown in summary_counts', function () { [$user, $tenant] = createUserWithTenant(role: 'owner'); $profile = BaselineProfile::factory()->active()->create([ 'workspace_id' => $tenant->workspace_id, 'scope_jsonb' => ['policy_types' => ['deviceConfiguration']], ]); $snapshot = BaselineSnapshot::factory()->create([ 'workspace_id' => $tenant->workspace_id, 'baseline_profile_id' => $profile->getKey(), ]); $profile->update(['active_snapshot_id' => $snapshot->getKey()]); $inventorySyncRun = createInventorySyncOperationRunWithCoverage( tenant: $tenant, statusByType: ['deviceConfiguration' => 'succeeded'], ); // 2 baseline items: one will be missing (high), one will be different (medium) $missingDisplayName = 'Missing Policy'; $missingSubjectKey = BaselineSubjectKey::fromDisplayName($missingDisplayName); expect($missingSubjectKey)->not->toBeNull(); $missingWorkspaceSafeExternalId = BaselineSubjectKey::workspaceSafeSubjectExternalId('deviceConfiguration', (string) $missingSubjectKey); BaselineSnapshotItem::factory()->create([ 'baseline_snapshot_id' => $snapshot->getKey(), 'subject_type' => 'policy', 'subject_external_id' => $missingWorkspaceSafeExternalId, 'subject_key' => (string) $missingSubjectKey, 'policy_type' => 'deviceConfiguration', 'baseline_hash' => hash('sha256', 'missing-content'), 'meta_jsonb' => ['display_name' => $missingDisplayName], ]); $changedDisplayName = 'Changed Policy'; $changedSubjectKey = BaselineSubjectKey::fromDisplayName($changedDisplayName); expect($changedSubjectKey)->not->toBeNull(); $changedWorkspaceSafeExternalId = BaselineSubjectKey::workspaceSafeSubjectExternalId('deviceConfiguration', (string) $changedSubjectKey); BaselineSnapshotItem::factory()->create([ 'baseline_snapshot_id' => $snapshot->getKey(), 'subject_type' => 'policy', 'subject_external_id' => $changedWorkspaceSafeExternalId, 'subject_key' => (string) $changedSubjectKey, 'policy_type' => 'deviceConfiguration', 'baseline_hash' => hash('sha256', 'original-content'), 'meta_jsonb' => ['display_name' => $changedDisplayName], ]); // Tenant only has changed-uuid with different content + extra-uuid (unexpected) InventoryItem::factory()->create([ 'tenant_id' => $tenant->getKey(), 'workspace_id' => $tenant->workspace_id, 'external_id' => 'changed-uuid', 'policy_type' => 'deviceConfiguration', 'meta_jsonb' => ['modified_content' => true], 'display_name' => $changedDisplayName, 'last_seen_operation_run_id' => (int) $inventorySyncRun->getKey(), 'last_seen_at' => now(), ]); InventoryItem::factory()->create([ 'tenant_id' => $tenant->getKey(), 'workspace_id' => $tenant->workspace_id, 'external_id' => 'extra-uuid', 'policy_type' => 'deviceConfiguration', 'meta_jsonb' => ['extra_content' => true], 'display_name' => 'Extra Policy', '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']], ], initiator: $user, ); $job = new CompareBaselineToTenantJob($run); $job->handle( app(BaselineSnapshotIdentity::class), app(AuditLogger::class), $opService, ); $run->refresh(); $counts = is_array($run->summary_counts) ? $run->summary_counts : []; expect((int) ($counts['total'] ?? -1))->toBe(3); expect((int) ($counts['high'] ?? -1))->toBe(1); // missing-uuid expect((int) ($counts['medium'] ?? -1))->toBe(1); // changed-uuid expect((int) ($counts['low'] ?? -1))->toBe(1); // extra-uuid }); it('writes result context with findings breakdown', function () { [$user, $tenant] = createUserWithTenant(role: 'owner'); $profile = BaselineProfile::factory()->active()->create([ 'workspace_id' => $tenant->workspace_id, 'scope_jsonb' => ['policy_types' => ['deviceConfiguration']], ]); $snapshot = BaselineSnapshot::factory()->create([ 'workspace_id' => $tenant->workspace_id, 'baseline_profile_id' => $profile->getKey(), ]); $profile->update(['active_snapshot_id' => $snapshot->getKey()]); createInventorySyncOperationRunWithCoverage( tenant: $tenant, statusByType: ['deviceConfiguration' => 'succeeded'], ); // One missing policy BaselineSnapshotItem::factory()->create([ 'baseline_snapshot_id' => $snapshot->getKey(), 'subject_type' => 'policy', 'subject_external_id' => 'gone-uuid', 'policy_type' => 'deviceConfiguration', 'baseline_hash' => hash('sha256', 'gone-content'), ]); $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']], ], initiator: $user, ); $job = new CompareBaselineToTenantJob($run); $job->handle( app(BaselineSnapshotIdentity::class), app(AuditLogger::class), $opService, ); $run->refresh(); $context = is_array($run->context) ? $run->context : []; $result = $context['result'] ?? []; expect($result)->toHaveKey('findings_total'); expect($result)->toHaveKey('findings_upserted'); expect($result)->toHaveKey('severity_breakdown'); expect((int) $result['findings_total'])->toBe(1); expect((int) $result['findings_upserted'])->toBe(1); }); it('reopens a previously resolved baseline finding when the same drift reappears', function (): void { [$user, $tenant] = createUserWithTenant(role: 'owner'); $profile = BaselineProfile::factory()->active()->create([ 'workspace_id' => $tenant->workspace_id, 'scope_jsonb' => ['policy_types' => ['deviceConfiguration']], ]); $snapshot = BaselineSnapshot::factory()->create([ 'workspace_id' => $tenant->workspace_id, 'baseline_profile_id' => $profile->getKey(), ]); $profile->update(['active_snapshot_id' => $snapshot->getKey()]); createInventorySyncOperationRunWithCoverage( tenant: $tenant, statusByType: ['deviceConfiguration' => 'succeeded'], ); BaselineSnapshotItem::factory()->create([ 'baseline_snapshot_id' => $snapshot->getKey(), 'subject_type' => 'policy', 'subject_external_id' => 'policy-reappears', 'policy_type' => 'deviceConfiguration', 'baseline_hash' => hash('sha256', 'baseline-content'), 'meta_jsonb' => ['display_name' => 'Policy Reappears'], ]); $operationRuns = app(OperationRunService::class); $firstRun = $operationRuns->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']], ], initiator: $user, ); (new CompareBaselineToTenantJob($firstRun))->handle( app(BaselineSnapshotIdentity::class), app(AuditLogger::class), $operationRuns, ); $finding = Finding::query() ->where('tenant_id', (int) $tenant->getKey()) ->where('source', 'baseline.compare') ->sole(); $finding->forceFill([ 'status' => Finding::STATUS_RESOLVED, 'resolved_at' => now()->subMinute(), 'resolved_reason' => 'manually_resolved', ])->save(); $firstRun->update(['completed_at' => now()->subMinute()]); $secondRun = $operationRuns->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']], ], initiator: $user, ); (new CompareBaselineToTenantJob($secondRun))->handle( app(BaselineSnapshotIdentity::class), app(AuditLogger::class), $operationRuns, ); $finding->refresh(); expect($finding->status)->toBe(Finding::STATUS_REOPENED); expect($finding->resolved_at)->toBeNull(); expect($finding->resolved_reason)->toBeNull(); expect($finding->reopened_at)->not->toBeNull(); }); it('preserves an existing open workflow status when the same baseline drift is seen again', function (): void { [$user, $tenant] = createUserWithTenant(role: 'owner'); $profile = BaselineProfile::factory()->active()->create([ 'workspace_id' => $tenant->workspace_id, 'scope_jsonb' => ['policy_types' => ['deviceConfiguration']], ]); $snapshot = BaselineSnapshot::factory()->create([ 'workspace_id' => $tenant->workspace_id, 'baseline_profile_id' => $profile->getKey(), ]); $profile->update(['active_snapshot_id' => $snapshot->getKey()]); createInventorySyncOperationRunWithCoverage( tenant: $tenant, statusByType: ['deviceConfiguration' => 'succeeded'], ); BaselineSnapshotItem::factory()->create([ 'baseline_snapshot_id' => $snapshot->getKey(), 'subject_type' => 'policy', 'subject_external_id' => 'triaged-policy', 'policy_type' => 'deviceConfiguration', 'baseline_hash' => hash('sha256', 'baseline-content'), 'meta_jsonb' => ['display_name' => 'Triaged Policy'], ]); $operationRuns = app(OperationRunService::class); $firstRun = $operationRuns->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']], ], initiator: $user, ); (new CompareBaselineToTenantJob($firstRun))->handle( app(BaselineSnapshotIdentity::class), app(AuditLogger::class), $operationRuns, ); $finding = Finding::query() ->where('tenant_id', (int) $tenant->getKey()) ->where('source', 'baseline.compare') ->sole(); $finding->forceFill([ 'status' => Finding::STATUS_TRIAGED, 'triaged_at' => now()->subMinute(), ])->save(); $firstRun->update(['completed_at' => now()->subMinute()]); $secondRun = $operationRuns->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']], ], initiator: $user, ); (new CompareBaselineToTenantJob($secondRun))->handle( app(BaselineSnapshotIdentity::class), app(AuditLogger::class), $operationRuns, ); $finding->refresh(); expect($finding->status)->toBe(Finding::STATUS_TRIAGED); expect($finding->current_operation_run_id)->toBe((int) $secondRun->getKey()); }); it('applies the workspace baseline severity mapping by change type', function (): void { [$user, $tenant] = createUserWithTenant(role: 'owner'); WorkspaceSetting::query()->create([ 'workspace_id' => (int) $tenant->workspace_id, 'domain' => 'baseline', 'key' => 'severity_mapping', 'value' => [ 'missing_policy' => Finding::SEVERITY_CRITICAL, 'different_version' => Finding::SEVERITY_LOW, 'unexpected_policy' => Finding::SEVERITY_MEDIUM, ], 'updated_by_user_id' => (int) $user->getKey(), ]); $profile = BaselineProfile::factory()->active()->create([ 'workspace_id' => $tenant->workspace_id, 'scope_jsonb' => ['policy_types' => ['deviceConfiguration']], ]); $snapshot = BaselineSnapshot::factory()->create([ 'workspace_id' => $tenant->workspace_id, 'baseline_profile_id' => $profile->getKey(), ]); $profile->update(['active_snapshot_id' => $snapshot->getKey()]); $inventorySyncRun = createInventorySyncOperationRunWithCoverage( tenant: $tenant, statusByType: ['deviceConfiguration' => 'succeeded'], ); $missingDisplayName = 'Missing Policy'; $missingSubjectKey = BaselineSubjectKey::fromDisplayName($missingDisplayName); expect($missingSubjectKey)->not->toBeNull(); $missingWorkspaceSafeExternalId = BaselineSubjectKey::workspaceSafeSubjectExternalId('deviceConfiguration', (string) $missingSubjectKey); BaselineSnapshotItem::factory()->create([ 'baseline_snapshot_id' => $snapshot->getKey(), 'subject_type' => 'policy', 'subject_external_id' => $missingWorkspaceSafeExternalId, 'subject_key' => (string) $missingSubjectKey, 'policy_type' => 'deviceConfiguration', 'baseline_hash' => hash('sha256', 'baseline-a'), 'meta_jsonb' => ['display_name' => $missingDisplayName], ]); $differentDisplayName = 'Different Policy'; $differentSubjectKey = BaselineSubjectKey::fromDisplayName($differentDisplayName); expect($differentSubjectKey)->not->toBeNull(); $differentWorkspaceSafeExternalId = BaselineSubjectKey::workspaceSafeSubjectExternalId('deviceConfiguration', (string) $differentSubjectKey); BaselineSnapshotItem::factory()->create([ 'baseline_snapshot_id' => $snapshot->getKey(), 'subject_type' => 'policy', 'subject_external_id' => $differentWorkspaceSafeExternalId, 'subject_key' => (string) $differentSubjectKey, 'policy_type' => 'deviceConfiguration', 'baseline_hash' => hash('sha256', 'baseline-b'), 'meta_jsonb' => ['display_name' => $differentDisplayName], ]); InventoryItem::factory()->create([ 'tenant_id' => $tenant->getKey(), 'workspace_id' => $tenant->workspace_id, 'external_id' => 'different-policy', 'policy_type' => 'deviceConfiguration', 'meta_jsonb' => ['different_content' => true], 'display_name' => $differentDisplayName, 'last_seen_operation_run_id' => (int) $inventorySyncRun->getKey(), 'last_seen_at' => now(), ]); InventoryItem::factory()->create([ 'tenant_id' => $tenant->getKey(), 'workspace_id' => $tenant->workspace_id, 'external_id' => 'unexpected-policy', 'policy_type' => 'deviceConfiguration', 'meta_jsonb' => ['unexpected' => true], 'display_name' => 'Unexpected Policy', 'last_seen_operation_run_id' => (int) $inventorySyncRun->getKey(), 'last_seen_at' => now(), ]); $operationRuns = app(OperationRunService::class); $run = $operationRuns->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']], ], initiator: $user, ); (new CompareBaselineToTenantJob($run))->handle( app(BaselineSnapshotIdentity::class), app(AuditLogger::class), $operationRuns, ); $findings = Finding::query() ->where('tenant_id', (int) $tenant->getKey()) ->where('source', 'baseline.compare') ->get() ->keyBy(fn (Finding $finding): string => (string) data_get($finding->evidence_jsonb, 'change_type')); expect($findings['missing_policy']->severity)->toBe(Finding::SEVERITY_CRITICAL); expect($findings['different_version']->severity)->toBe(Finding::SEVERITY_LOW); expect($findings['unexpected_policy']->severity)->toBe(Finding::SEVERITY_MEDIUM); }); it('writes numeric-only summary_counts with whitelisted keys for capture and compare runs', function (): void { [$user, $tenant] = createUserWithTenant(role: 'owner'); $profile = BaselineProfile::factory()->active()->create([ 'workspace_id' => (int) $tenant->workspace_id, 'scope_jsonb' => ['policy_types' => ['deviceConfiguration'], 'foundation_types' => []], ]); InventoryItem::factory()->create([ 'tenant_id' => (int) $tenant->getKey(), 'workspace_id' => (int) $tenant->workspace_id, 'external_id' => 'policy-a', 'policy_type' => 'deviceConfiguration', 'meta_jsonb' => ['odata_type' => '#microsoft.graph.deviceConfiguration', 'etag' => 'E1'], ]); $operationRuns = app(OperationRunService::class); $captureRun = $operationRuns->ensureRunWithIdentity( tenant: $tenant, type: OperationRunType::BaselineCapture->value, identityInputs: ['baseline_profile_id' => (int) $profile->getKey()], context: [ 'baseline_profile_id' => (int) $profile->getKey(), 'source_tenant_id' => (int) $tenant->getKey(), 'effective_scope' => ['policy_types' => ['deviceConfiguration'], 'foundation_types' => []], ], initiator: $user, ); (new CaptureBaselineSnapshotJob($captureRun))->handle( app(BaselineSnapshotIdentity::class), app(InventoryMetaContract::class), app(AuditLogger::class), $operationRuns, ); $captureRun->refresh(); $inventorySyncRun = createInventorySyncOperationRunWithCoverage( tenant: $tenant, statusByType: ['deviceConfiguration' => 'succeeded'], ); InventoryItem::query() ->where('tenant_id', (int) $tenant->getKey()) ->update([ 'last_seen_operation_run_id' => (int) $inventorySyncRun->getKey(), 'last_seen_at' => now(), ]); $snapshotId = (int) ($profile->fresh()?->active_snapshot_id ?? 0); expect($snapshotId)->toBeGreaterThan(0); $compareRun = $operationRuns->ensureRunWithIdentity( tenant: $tenant, type: OperationRunType::BaselineCompare->value, identityInputs: ['baseline_profile_id' => (int) $profile->getKey()], context: [ 'baseline_profile_id' => (int) $profile->getKey(), 'baseline_snapshot_id' => $snapshotId, 'effective_scope' => ['policy_types' => ['deviceConfiguration'], 'foundation_types' => []], ], initiator: $user, ); (new CompareBaselineToTenantJob($compareRun))->handle( app(BaselineSnapshotIdentity::class), app(AuditLogger::class), $operationRuns, ); $allowedKeys = OperationSummaryKeys::all(); foreach ([$captureRun->fresh(), $compareRun->fresh()] as $run) { $counts = is_array($run?->summary_counts) ? $run->summary_counts : []; foreach ($counts as $key => $value) { expect($allowedKeys)->toContain((string) $key); expect(is_array($value))->toBeFalse(); expect(is_numeric($value))->toBeTrue(); } } });