import { describe, expect, it } from 'vitest'; import type { UnifiedResultProjection } from '../../src/domain/unified-result'; import { POLICY_EVALUATION_ENGINE, type PolicyEvaluationResult, type StoredPolicyEvaluation } from '../../src/domain/policy-evaluation'; import type { CanonicalAssessmentModel } from '../../src/domain/canonical-model'; import { buildReportProjection } from '../../src/domain/report-projection'; import { assembleReportProjectionInput } from '../../src/features/reports/report-input-assembler'; import { loadCanonicalProjectWorkspace } from '../../src/features/project-import/load-canonical-project-workspace'; import type { AssessorRepository } from '../../src/persistence/assessor-repository'; function observation(id: string): UnifiedResultProjection { return { sourceKind: 'policy-observation', sourceResultId: id, resultProjectionId: 'policy-observation:' + id, projectId: 'synthetic', assessmentId: 'slot', importArtifactId: 'import-one', title: 'Mode', importedAt: '2026-09-06T00:00:00Z', policyScope: 'computer', policyDeliveryMechanism: 'registry-policy', policyState: 'configured', policyIdentityStatus: 'resolved', policyIdentityKey: 'identity-mode', policyBackupInstanceId: 'backup-' + id, policyValues: [{ kind: 'registry-policy', key: 'Software\\Policies\\Synthetic', valueName: 'Mode', operation: 'set-value', typeCode: 4, data: { kind: 'uint32', value: 1 } }] } as unknown as UnifiedResultProjection; } function comparison(id: string, ids: string[], classification: PolicyEvaluationResult['classification']): PolicyEvaluationResult { return { id, observationIds: ids, baselineId: 'ms-baseline', baselineLabel: 'Synthetic Microsoft Baseline', baselineState: 'explicit-recommendation', classification, customerState: 'configured', comparator: { version: 1, comparator: { kind: 'equality' }, relation: classification === 'configured_match' ? 'match' : 'different', actual: { kind: 'integer', value: '1' }, baseline: { kind: 'integer', value: '0' }, diagnosticCodes: [] }, identity: { id: 'identity-mode', scope: 'computer', target: { kind: 'registry-value', hive: 'HKEY_LOCAL_MACHINE', key: 'Software\\Policies\\Synthetic', valueName: 'Mode' } }, diagnosticCodes: [] } as unknown as PolicyEvaluationResult; } function evaluation(id: string, aggregationRunId: string, completedAt: string, results: PolicyEvaluationResult[]): StoredPolicyEvaluation { return { run: { id, aggregationRunId, evaluator: POLICY_EVALUATION_ENGINE, baselineAnalysisIds: ['baseline'], completedAt }, results } as StoredPolicyEvaluation; } describe('report GPO counts', () => { // Regression 2026-09-28: the report counted every GPO setting once per stored evaluation run // (50 Deviation / 50 Passed instead of Work's 25 / 25). it('counts each GPO policy setting once, from the same current evaluation run as Work', async () => { const observations = [observation('a'), observation('b')]; const superseded = evaluation('superseded', 'old-aggregation', '2026-09-07T10:00:00Z', [comparison('old', ['a', 'b'], 'confirmed_configured_deviation')]); const current = evaluation('current', 'current-aggregation', '2026-09-08T10:00:00Z', [comparison('now', ['a', 'b'], 'confirmed_configured_deviation')]); const model = { schemaVersion: 1, project: { id: 'synthetic', name: 'Synthetic' }, assessment: { id: 'slot', projectId: 'synthetic' }, scanRuns: [], importFiles: [], sourceTables: [], findingDefinitions: [], findingOccurrences: [], evidence: [], reviews: [], diagnostics: [] } as unknown as CanonicalAssessmentModel; const repository = { getProject: async () => ({ id: 'synthetic', name: 'Synthetic', attributes: {}, createdAt: '', updatedAt: '' }), listAssessments: async () => [model.assessment], loadAssessmentModel: async () => model, queryUnifiedResults: async (query: { sourceKind?: string[] }) => { const items = query.sourceKind?.includes('policy-observation') ? observations : []; return { items, total: items.length, offset: 0, limit: 500 }; }, listPolicyEvaluations: async () => [superseded, current], listMicrosoftSecurityBaselineAnalyses: async () => [], getEnvironmentSnapshot: async () => ({ observations: [], sections: [] }), listReviews: async () => [], listProjectProcessingRuns: async () => [], listGpoPolicyAnalyses: async () => [], listPolicySnapshotAggregations: async () => [] } as unknown as AssessorRepository; const getPolicyAnalysisOverview = async () => ({ current: { evaluationRunIds: ['current'] } as never }); const scope = { projectId: 'synthetic', assessmentIds: ['slot'], generatedAt: '2026-09-28T10:00:00Z' }; const report = buildReportProjection(await assembleReportProjectionInput(repository, scope, { getPolicyAnalysisOverview })); const work = await loadCanonicalProjectWorkspace({ listProjectAssessments: repository.listAssessments, loadAssessmentModel: async () => model, queryUnifiedResults: repository.queryUnifiedResults as never, listPolicyEvaluations: async () => ({ records: [superseded, current] }), getPolicyAnalysisOverview }, 'synthetic', undefined, ['slot']); // A GPO-only timeslot has a single GPO sheet and no project sheet; the report must still read it. const workRows = work.dataset!.sheets[0].analysis.records; expect(report.policyEntries).toHaveLength(workRows.length); expect(report.gpo?.counts.Fail).toBe(1); // Without the current-run overview the superseded run is counted as well; this is the regressed path. const unscoped = buildReportProjection(await assembleReportProjectionInput(repository, scope)); expect(unscoped.gpo?.counts.Fail).toBe(2); }); });