// @vitest-environment jsdom import { act } from 'react'; import { createRoot } from 'react-dom/client'; import { renderToStaticMarkup } from 'react-dom/server'; import { afterEach, describe, expect, it, vi } from 'vitest'; import { PolicyAnalysisPanel, PolicyAnalysisPanelView } from '../../src/features/project-import/PolicyAnalysisPanel'; import type { PolicyAnalysisOverview } from '../../src/domain/policy-analysis-overview'; import { AssessorApiClient } from '../../src/api/assessor-api-client'; import { POLICY_EVALUATION_ENGINE, type StoredPolicyEvaluation } from '../../src/domain/policy-evaluation'; import type { UnifiedResultProjection } from '../../src/domain/unified-result'; const overview: PolicyAnalysisOverview = { version: 1, projectId: 'synthetic-project', generatedAt: '2026-08-04T15:00:00.000Z', summary: { identityAnalysisRuns: 1, aggregationRuns: 1, evaluationRuns: 1, findingRuns: 1, configuredMatches: 0, confirmedDeviations: 1, notApplicable: 0, policyFindings: 0, reviewItems: 2, conflicts: 1, unresolvedIdentities: 0, catalogGaps: 1, parserGaps: 0, partialRuns: 1 }, current: { version: 1, summary: { identityAnalysisRuns: 1, aggregationRuns: 1, evaluationRuns: 1, findingRuns: 1, configuredMatches: 0, confirmedDeviations: 1, notApplicable: 0, policyFindings: 0, reviewItems: 2, conflicts: 1, unresolvedIdentities: 0, catalogGaps: 1, parserGaps: 0, partialRuns: 1 }, evaluationRunIds: ['evaluation-1'], findingRunIds: ['finding-run-1'], reviewItems: [{ version: 1, id: 'review-1', category: 'conflict', source: 'snapshot-aggregation', sourceRunId: 'aggregation-1', identityId: 'technical-identity-1', codes: ['policy-snapshot-customer-conflict'], observationCount: 2, backupCount: 2 }, { version: 1, id: 'review-2', category: 'catalog-gap', source: 'policy-evaluation', sourceRunId: 'evaluation-1', codes: ['policy-evaluation-baseline-missing'], observationCount: 1, backupCount: 1 }] }, actions: { version: 1, reprocess: [], aggregate: [], evaluate: [], createFindings: [] }, profiles: [{ version: 1, id: 'profile-1', name: 'Synthetic Windows', revision: 1, active: true, contextCount: 1 }], evaluations: [{ version: 1, evaluationRunId: 'evaluation-1', environmentProfileId: 'profile-1', status: 'partial', completedAt: '2026-08-04T15:00:00.000Z', matchCount: 0, deviationCount: 1, notApplicableCount: 0, reviewCount: 2, findingCount: 0, rejectedFindingCount: 0 }], reviewItems: [{ version: 1, id: 'review-1', category: 'conflict', source: 'snapshot-aggregation', sourceRunId: 'aggregation-1', identityId: 'technical-identity-1', codes: ['policy-snapshot-customer-conflict'], observationCount: 2, backupCount: 2 }, { version: 1, id: 'review-2', category: 'catalog-gap', source: 'policy-evaluation', sourceRunId: 'evaluation-1', codes: ['policy-evaluation-baseline-missing'], observationCount: 1, backupCount: 1 }] }; describe('PolicyAnalysisPanelView', () => { afterEach(() => { vi.restoreAllMocks(); vi.unstubAllGlobals(); }); it('shows absent settings separately without inflating misconfiguration or GPO impact counts', () => { const html = renderToStaticMarkup(); expect(html).toContain('Misconfigured2'); expect(html).toContain('Not configured3000'); expect(html).toContain('Review required0'); // Configured customer settings without a baseline rule stay visible and score-neutral. expect(html).toContain('No baseline rule1687'); expect(html).toContain('GPO backups with failing settings2'); expect(html).toContain('One verdict per technical policy setting'); expect(html).toContain('May overlap with each other and with policy verdicts. Score-neutral.'); // The retained parser gap stays reportable; the current engine can no longer produce a // catalog gap, so its tile is gone while historical review items keep their own label. expect(html).toContain('Comparison not supported'); expect(html).not.toContain('Baseline mapping unavailable'); expect(html).toContain('Baseline mapping unavailable'); }); it('shows only the backend-current evaluation when the same timeslot retains a historical aggregation', async () => { vi.stubGlobal('IS_REACT_ACT_ENVIRONMENT', true); const observation = { sourceKind: 'policy-observation', sourceResultId: 'observation-current', resultProjectionId: 'policy-observation:current', projectId: 'synthetic-project', assessmentId: 'synthetic-timeslot', title: 'Synthetic setting', importedAt: '2026-09-15T08:00:00Z', policyScope: 'computer', policyDeliveryMechanism: 'registry-policy', policyState: 'configured', policyIdentityStatus: 'resolved', policyIdentityKey: 'synthetic-setting', policyBackupInstanceId: 'synthetic-backup', policyValues: [{ kind: 'registry-policy', key: 'Software\\Policies\\Synthetic', valueName: 'Mode', operation: 'set-value', typeCode: 4, data: { kind: 'uint32', value: 1 } }] } as unknown as UnifiedResultProjection; const comparison = (id: string, status: 'current' | 'historical') => ({ id, observationIds: status === 'current' ? ['observation-current'] : [], baselineId: 'synthetic-baseline', baselineLabel: 'Synthetic baseline', baselineState: 'explicit-recommendation', classification: status === 'current' ? 'configured_match' : 'confirmed_configured_deviation', customerState: status === 'current' ? 'configured' : 'missing', comparator: { version: 1, comparator: { kind: 'equality' }, relation: status === 'current' ? 'match' : 'different', actual: status === 'current' ? { kind: 'integer', value: '1' } : undefined, baseline: { kind: 'integer', value: '1' }, diagnosticCodes: [] }, identity: { id: 'synthetic-setting', scope: 'computer', target: { kind: 'registry-value', hive: 'HKEY_LOCAL_MACHINE', key: 'Software\\Policies\\Synthetic', valueName: 'Mode' } }, diagnosticCodes: [] }); const evaluation = (id: string, aggregationRunId: string, status: 'current' | 'historical') => ({ run: { id, aggregationRunId, completedAt: status === 'current' ? '2026-09-15T09:00:00Z' : '2026-09-14T09:00:00Z', baselineAnalysisIds: ['synthetic-baseline-analysis'], evaluator: POLICY_EVALUATION_ENGINE }, results: [comparison(`${id}-result`, status)] }) as unknown as StoredPolicyEvaluation; const current = evaluation('evaluation-current', 'aggregation-current', 'current'); const historical = evaluation('evaluation-historical', 'aggregation-historical', 'historical'); const currentOverview = { ...overview, current: { ...overview.current, evaluationRunIds: ['evaluation-current'] } }; vi.spyOn(AssessorApiClient.prototype, 'getPolicyAnalysisOverview').mockResolvedValue(currentOverview); vi.spyOn(AssessorApiClient.prototype, 'queryUnifiedResults').mockResolvedValue({ total: 1, items: [observation] }); vi.spyOn(AssessorApiClient.prototype, 'listPolicyEvaluations').mockResolvedValue({ version: 1, projectId: 'synthetic-project', records: [historical, current] }); const container = document.createElement('div'); const root = createRoot(container); try { await act(async () => { root.render(); await Promise.resolve(); }); const values = Object.fromEntries([...container.querySelectorAll('.policy-analysis-summary article')].map(article => [ article.querySelector('span')?.textContent, article.querySelector('strong')?.textContent ])); expect(values).toMatchObject({ Misconfigured: '0', Pass: '1', 'Not configured': '0', 'Review required': '0' }); } finally { await act(async () => root.unmount()); } }); it('separates unique verdicts, overlapping diagnostics and analysis gaps', () => { const html = renderToStaticMarkup(); expect(html).toContain('Effective application requires RSoP or GP Results'); expect(html).toContain('Analysis runs'); expect(html).toContain('Evaluation runs'); expect(html).toContain('Policy verdicts'); expect(html).toContain('Misconfigured'); expect(html).toContain('Not configured'); expect(html).toContain('Duplicates'); expect(html).toContain('GPO backups with failing settings'); expect(html).toContain('Diagnostic flags'); expect(html).toContain('Different customer values for one technical setting'); expect(html).toContain('Analysis gaps'); expect(html).toContain('Comparison not supported'); expect(html).toContain('Baseline mapping unavailable'); expect(html).not.toContain('Unsupported settings'); expect(html).not.toContain('GPO sources1'); expect(html).toContain('
Technical details'); expect(html).toContain('Evaluation history'); expect(html).toContain('Current review queue'); expect(html).toContain('Conflict'); expect(html).toContain('Baseline mapping unavailable'); expect(html).toContain('policy-evaluation-baseline-missing'); expect(html).not.toContain('Confirmed policy findings'); expect(html).not.toContain('Severity'); }); });