import { describe, expect, it } from 'vitest'; import type { ImportedDataset } from '../../src/domain/dataset'; import type { FindingRecord } from '../../src/domain/finding'; import { POLICY_EVALUATION_ENGINE, type PolicyEvaluationResult, type StoredPolicyEvaluation } from '../../src/domain/policy-evaluation'; import type { UnifiedResultProjection } from '../../src/domain/unified-result'; import { appendPolicyObservationsToDataset } from '../../src/features/project-import/policy-observations-to-dataset'; import { makeSheet } from '../../src/importers/sheet-factory'; /** Properties the rebuilt project sheet recomputes from the extended row layout. */ const LAYOUT_KEYS = new Set(['id', 'index', 'title', 'fields', 'filled', 'meta', 'sourceFile', 'sourceSheet', 'highlighted']); function policyInputs() { const observation = { sourceKind: 'policy-observation', sourceResultId: 'synthetic-observation', projectId: 'synthetic', assessmentId: 'slot', policyBackupInstanceId: 'backup-1', title: 'Synthetic setting', importedAt: '2026-01-01T00:00:00Z', policyScope: 'computer', policyDeliveryMechanism: 'registry-policy', policyIdentityStatus: 'resolved', policyIdentityKey: 'synthetic-setting', policyValues: [{ kind: 'registry-policy', operation: 'set-value', key: 'Software\\Policies\\Synthetic', valueName: 'Mode', typeCode: 4, data: { kind: 'uint32', value: 0 } }] } as unknown as UnifiedResultProjection; const comparison = { id: 'synthetic-comparison', observationIds: [observation.sourceResultId], baselineId: 'baseline', baselineLabel: 'Synthetic baseline', identity: { id: 'setting-synthetic', scope: 'computer', target: { kind: 'registry-value', key: 'Synthetic', valueName: 'Mode' } }, baselineState: 'explicit-recommendation', customerState: 'configured', classification: 'confirmed_configured_deviation', diagnosticCodes: [], comparator: { version: 1, comparator: { kind: 'equality' }, relation: 'different', baseline: { kind: 'integer', value: '1' }, actual: { kind: 'integer', value: '0' }, diagnosticCodes: [] } } as unknown as PolicyEvaluationResult; const evaluation = { run: { id: 'evaluation', projectId: 'synthetic', aggregationRunId: 'aggregation', completedAt: '2026-01-01T00:00:00Z', evaluator: POLICY_EVALUATION_ENGINE }, results: [comparison] } as StoredPolicyEvaluation; return { observation, evaluation }; } describe('project sheet merge with GPO results', () => { // Regression 2026-09-26: the merge copied an explicit list of properties, so newly added record metadata // (CIS section titles, recognized references) silently disappeared in timeslots with GPO results. it('keeps every non-layout property of a scanner record, including metadata unknown to the merge', () => { const scannerSheet = makeSheet('Synthetic · All Records', [ ['Title', 'Status', 'Source: Scan'], ['Synthetic scanner finding', 'Fail', 'CIS-CAT Pro · 2026-01-01T00:00:00.000Z'] ], '', 'Synthetic', { isProjectSheet: true }); const original = scannerSheet.analysis.records[0]; Object.assign(original, { findingOccurrenceId: 'occurrence-synthetic', findingDefinitionId: 'definition-synthetic', scanRunId: 'run-synthetic', importFileId: 'file-synthetic', benchmarkSectionTitles: new Map([['2.2', 'Synthetic User Rights Assignment']]), referenceRecognition: { runId: 'reference-run', references: [] }, policyFailingBackupSources: [{ id: 'backup-synthetic', label: 'Synthetic backup' }], policySourceEvidence: [], policyBaselineLabels: ['Synthetic baseline'], policyBaselineComparisons: [], policyReviewMembers: [{ id: 'member-synthetic', baseline: 'Synthetic baseline' }], // Stands for any record metadata added later without touching the merge. syntheticFutureMetadata: { marker: 'survives' } }); const dataset = { sheets: [scannerSheet], rawText: '', profileId: 'generic', scanTool: 'Multiple Tools' } as unknown as ImportedDataset; const { observation, evaluation } = policyInputs(); const merged = appendPolicyObservationsToDataset(dataset, 'Synthetic', [observation], [evaluation])!; const aggregate = merged.sheets[0]; const carried = aggregate.analysis.records[0] as FindingRecord & Record; const metadataKeys = Object.keys(original).filter(key => !LAYOUT_KEYS.has(key)); expect(metadataKeys).toContain('syntheticFutureMetadata'); for (const key of metadataKeys) expect(carried[key], key).toBe((original as unknown as Record)[key]); // Layout properties come from the rebuilt sheet: the GPO columns extend the scanner row. const rebuilt = makeSheet(aggregate.name, aggregate.rows, '', 'Synthetic', { id: aggregate.id, isProjectSheet: true }).analysis.records[0]; expect(carried.id).toBe(rebuilt.id); expect(carried.fields.map(field => field.label)).toEqual(aggregate.rows[0]); expect(carried.fields.map(field => field.label)).toContain('Configured Value'); expect(carried.fields.find(field => field.label === 'Status')?.value).toBe('Fail'); expect(carried).not.toBe(original); }); });