Assessor_2.0/tests/domain/report-projection.test.ts

301 lines
13 KiB
TypeScript

import { describe, expect, it } from 'vitest';
import type { CanonicalOutcome, CanonicalSeverity } from '../../src/domain/canonical-model';
import type { UnifiedResultProjection } from '../../src/domain/unified-result';
import {
REPORT_PROJECTION_VERSION,
buildReportProjection,
reportPriorityBases,
type ReportDomain,
type ReportPolicyEntry,
type ReportProjectionInput
} from '../../src/domain/report-projection';
import { toolProfileDefinitions } from '../../src/profiles/profile-definition-catalog';
const domains: ReportDomain[] = [
{ id: 'identity', label: 'Identity & Account Hygiene' },
{ id: 'hardening', label: 'System Hardening' },
{ id: 'gpo', label: 'GPO & Baseline' }
];
let sequence = 0;
function result(overrides: Partial<UnifiedResultProjection> & {
severity?: CanonicalSeverity;
outcome?: CanonicalOutcome;
} = {}): UnifiedResultProjection {
sequence += 1;
const severity = overrides.severity ?? 'unknown';
const outcome = overrides.outcome ?? 'fail';
return {
resultProjectionId: overrides.resultProjectionId ?? `synthetic-result-${sequence}`,
sourceKind: 'scanner-finding',
sourceResultId: `synthetic-source-${sequence}`,
projectId: 'synthetic-project',
collectionRunId: 'synthetic-collection-run',
sourceTool: overrides.sourceTool ?? 'synthetic-tool',
title: overrides.title ?? `Synthetic finding ${sequence}`,
severityOriginal: severity,
severityNormalized: severity,
outcomeOriginal: outcome,
outcomeNormalized: outcome,
technicalExecutionState: outcome === 'not_run' ? 'not-run' : 'completed',
resultType: 'control_result',
isIoe: false,
isDeviation: outcome === 'fail' || outcome === 'warning',
lifecycleState: 'unknown',
mappingConfidence: 1,
reviewStatus: 'unreviewed',
importedAt: '2026-09-18T08:00:00.000Z',
createdAt: '2026-09-18T08:00:00.000Z',
categories: [],
...overrides
} as UnifiedResultProjection;
}
function input(overrides: Partial<ReportProjectionInput> = {}): ReportProjectionInput {
return {
generatedAt: '2026-09-18T10:00:00.000Z',
project: { id: 'synthetic-project', name: 'Synthetic' },
results: [],
domains,
domainOf: () => 'identity',
...overrides
};
}
describe('report projection', () => {
it('carries its version and the caller supplied generation time', () => {
const projection = buildReportProjection(input());
expect(projection.version).toBe(REPORT_PROJECTION_VERSION);
expect(projection.generatedAt).toBe('2026-09-18T10:00:00.000Z');
});
it('orders within one source and never merges sources into one ranking', () => {
const results = [
result({ sourceTool: 'alpha', severity: 'low', title: 'alpha-low' }),
result({ sourceTool: 'alpha', severity: 'critical', title: 'alpha-critical' }),
result({ sourceTool: 'beta', severity: 'medium', title: 'beta-medium' })
];
const projection = buildReportProjection(input({
results,
priorityBases: { alpha: { kind: 'severity' }, beta: { kind: 'severity' } }
}));
const [domain] = projection.priorities;
expect(domain.domainId).toBe('identity');
expect(domain.sources.map(source => source.toolId)).toEqual(['alpha', 'beta']);
expect(domain.sources[0].shown.map(ref => ref.title)).toEqual(['alpha-critical', 'alpha-low']);
expect(domain.sources[1].shown.map(ref => ref.title)).toEqual(['beta-medium']);
});
it('refuses a declared severity basis when the source delivers no severity', () => {
const results = [
result({ sourceTool: 'cis-cat', severity: 'unknown' }),
result({ sourceTool: 'cis-cat', severity: 'unknown' })
];
const projection = buildReportProjection(input({
results,
priorityBases: { 'cis-cat': { kind: 'severity' } }
}));
const [source] = projection.sourcePriorities;
expect(source.declared.kind).toBe('severity');
expect(source.applied.kind).toBe('undeclared');
expect(source.severityCoverage).toBe(0);
expect(source.issues).toContain('declared-severity-without-coverage');
expect(projection.priorities[0].sources[0].ranked).toBe(false);
expect(projection.diagnostics.some(entry =>
entry.code === 'report-priority-declared-severity-without-coverage:cis-cat')).toBe(true);
});
it('marks an undeclared source as unranked instead of inventing an order', () => {
const projection = buildReportProjection(input({
results: [result({ sourceTool: 'undeclared-tool', severity: 'high' })]
}));
expect(projection.sourcePriorities[0].applied.kind).toBe('undeclared');
expect(projection.sourcePriorities[0].issues).toContain('basis-not-declared');
expect(projection.priorities[0].sources[0].ranked).toBe(false);
expect(projection.findings[0].rankValue).toBeUndefined();
});
it('orders a numeric basis by its declared direction', () => {
const results = [
result({ sourceTool: 'pingcastle', score: 5, title: 'five' }),
result({ sourceTool: 'pingcastle', score: 40, title: 'forty' })
];
const projection = buildReportProjection(input({
results,
priorityBases: {
pingcastle: { kind: 'numeric', property: 'points', scale: '0-50+', direction: 'higher-is-worse' }
}
}));
expect(projection.priorities[0].sources[0].shown.map(ref => ref.title)).toEqual(['forty', 'five']);
});
it('caps a domain block without losing the true total', () => {
const results = Array.from({ length: 5 }, (_unused, index) =>
result({ sourceTool: 'alpha', severity: 'high', title: `item-${index}` }));
const projection = buildReportProjection(input({
results,
priorityBases: { alpha: { kind: 'severity' } },
priorityLimitPerDomain: 2
}));
expect(projection.priorities[0].sources[0].shown).toHaveLength(2);
expect(projection.priorities[0].sources[0].total).toBe(5);
});
it('keeps not-run and not-applicable out of the assessed base', () => {
const results = [
result({ outcome: 'fail' }),
result({ outcome: 'pass' }),
result({ outcome: 'not_run' }),
result({ outcome: 'not_applicable' }),
result({ outcome: 'execution_error' })
];
const projection = buildReportProjection(input({ results }));
expect(projection.coverage.total).toBe(5);
expect(projection.coverage.assessed).toBe(2);
expect(projection.coverage.notRun).toBe(1);
expect(projection.coverage.notApplicable).toBe(1);
expect(projection.coverage.executionErrors).toBe(1);
expect(projection.domains.find(entry => entry.domainId === 'identity')?.passRate).toBe(0.5);
});
it('leaves a pass rate absent rather than reporting zero when nothing was assessed', () => {
const projection = buildReportProjection(input({ results: [result({ outcome: 'not_run' })] }));
expect(projection.domains.find(entry => entry.domainId === 'identity')?.passRate).toBeUndefined();
});
it('keeps GPO equal-weight with its verdict counts and diagnostic flags separate', () => {
const policyEntries: ReportPolicyEntry[] = [
{
id: 'p1', verdict: 'Fail', conflict: false, duplicate: true, title: 'setting one',
scope: 'computer', identity: 'MinimumPasswordLength', backupInstanceIds: ['b1', 'b2']
},
{
id: 'p2', verdict: 'Not configured', conflict: false, duplicate: false, title: 'setting two',
scope: 'computer', identity: 'SomeOtherSetting', backupInstanceIds: []
},
{
id: 'p3', verdict: 'Fail', conflict: true, duplicate: false, title: 'setting three',
scope: 'computer', identity: 'PasswordComplexity', backupInstanceIds: ['b1']
}
];
const projection = buildReportProjection(input({ policyEntries, policyScopeLimited: true }));
expect(projection.gpo?.basis.kind).toBe('equal-weight');
expect(projection.gpo?.counts.Fail).toBe(2);
expect(projection.gpo?.counts['Not configured']).toBe(1);
expect(projection.gpo?.conflicts).toBe(1);
expect(projection.gpo?.duplicates).toBe(1);
expect(projection.gpo?.affectedSettings).toBe(2);
expect(projection.gpo?.affectedBackups).toBe(2);
expect(projection.gpo?.scopeLimited).toBe(true);
});
it('omits the remediation section entirely when no reviews exist', () => {
const projection = buildReportProjection(input({ results: [result()] }));
expect(projection.remediation).toBeUndefined();
expect(projection.workItems).toEqual([]);
});
it('curates work items through include and marks the summary as self reported', () => {
const selected = result({ title: 'selected' });
const skipped = result({ title: 'skipped' });
const projection = buildReportProjection(input({
results: [selected, skipped],
reviews: [
{
resultProjectionId: selected.resultProjectionId, include: true, status: 'Actionable',
priority: 'High', owner: 'team-ad', markers: ['quickwin'], riskAccepted: false
},
{
resultProjectionId: skipped.resultProjectionId, include: false, status: 'Open',
priority: 'Medium', markers: [], riskAccepted: false
}
]
}));
expect(projection.workItems.map(item => item.title)).toEqual(['selected']);
expect(projection.remediation?.selfReported).toBe(true);
expect(projection.remediation?.items).toBe(1);
expect(projection.remediation?.withoutDue).toBe(1);
expect(projection.remediation?.byStatus).toEqual({ Actionable: 1 });
});
it('never turns a self reported done status into a resolved finding', () => {
const open = result({ outcome: 'fail', title: 'still failing' });
const projection = buildReportProjection(input({
results: [open],
reviews: [{
resultProjectionId: open.resultProjectionId, include: true, status: 'Done',
priority: 'High', markers: [], riskAccepted: false
}]
}));
expect(projection.coverage.deviations).toBe(1);
expect(projection.findings).toHaveLength(1);
expect(projection.workItems[0].status).toBe('Done');
});
it('reads the stored comparison and derives no pairing of its own', () => {
const open = result({ sourceTool: 'alpha', title: 'persisting' });
const projection = buildReportProjection(input({
results: [open],
delta: {
comparisonId: 'synthetic-comparison',
contractVersion: '1.0.0',
referenceRunId: 'run-reference',
referenceObservedAt: '2026-06-01T00:00:00.000Z',
currentRunId: 'run-current',
currentObservedAt: '2026-09-01T00:00:00.000Z',
entries: [
{ resultProjectionId: open.resultProjectionId, toolId: 'alpha', state: 'confirmed-open', reasons: [] },
{ resultProjectionId: 'gone', toolId: 'alpha', state: 'unverified-absent', reasons: ['check-not-run'] },
{ resultProjectionId: 'fixed', toolId: 'beta', state: 'verified-remediated', reasons: ['fail-to-pass'] }
]
}
}));
expect(projection.delta?.referenceRunId).toBe('run-reference');
expect(projection.delta?.currentRunId).toBe('run-current');
expect(projection.delta?.counts['unverified-absent']).toBe(1);
expect(projection.delta?.counts['verified-remediated']).toBe(1);
expect(projection.delta?.counts['confirmed-open']).toBe(1);
expect(projection.delta?.perSource.map(entry => entry.toolId)).toEqual(['alpha', 'beta']);
expect(projection.delta?.perSource[0].counts['unverified-absent']).toBe(1);
expect(projection.delta?.entries[0].title).toBe('persisting');
});
it('has no delta at all without a stored comparison', () => {
expect(buildReportProjection(input({ results: [result()] })).delta).toBeUndefined();
});
it('adopts the declared basis of every registered profile', () => {
const bases = reportPriorityBases(toolProfileDefinitions);
expect(bases['purple-knight']).toEqual({ kind: 'severity' });
expect(bases.maester).toEqual({ kind: 'severity' });
expect(bases.pingcastle).toMatchObject({ kind: 'numeric', direction: 'higher-is-worse' });
expect(bases['cis-cat'].kind).toBe('equal-weight');
expect(bases.generic.kind).toBe('equal-weight');
});
it('treats a declared unavailable basis as a decision, not a missing declaration', () => {
const projection = buildReportProjection(input({
results: [result({ sourceTool: 'cis-cat', severity: 'unknown' })],
priorityBases: reportPriorityBases(toolProfileDefinitions)
}));
const [source] = projection.sourcePriorities;
expect(source.applied.kind).toBe('equal-weight');
expect(source.issues).toEqual([]);
expect(projection.priorities[0].sources[0].ranked).toBe(false);
expect(projection.diagnostics).toEqual([]);
});
it('ranks a real PingCastle points declaration worst first', () => {
const projection = buildReportProjection(input({
results: [
result({ sourceTool: 'pingcastle', score: 10, title: 'ten' }),
result({ sourceTool: 'pingcastle', score: 35, title: 'thirtyfive' })
],
priorityBases: reportPriorityBases(toolProfileDefinitions)
}));
expect(projection.priorities[0].sources[0].shown.map(ref => ref.title))
.toEqual(['thirtyfive', 'ten']);
});
});