First public release
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,186 @@
|
||||
import { describe as suite, it, expect } from 'vitest'
|
||||
import {
|
||||
type CapabilityReport,
|
||||
cite,
|
||||
citeVerbose,
|
||||
describe as describeDetermination,
|
||||
describeMeasurement,
|
||||
evidence,
|
||||
formatReport,
|
||||
formatRollUp,
|
||||
label,
|
||||
overall,
|
||||
rollUp,
|
||||
validateReport,
|
||||
} from '../src/index.js'
|
||||
|
||||
suite('determination', () => {
|
||||
it('never renders a non-pass as a pass', () => {
|
||||
expect(label('not-assessed')).toBe('NOT ASSESSED')
|
||||
expect(label('not-applicable')).toBe('N/A')
|
||||
expect(describeDetermination('not-assessed')).toContain('not a pass')
|
||||
})
|
||||
|
||||
it('rolls an empty set up to not-assessed, not pass', () => {
|
||||
// [].every(...) === true is the exact trap this closes.
|
||||
expect(rollUp([])).toBe('not-assessed')
|
||||
})
|
||||
|
||||
it('lets one failure dominate', () => {
|
||||
expect(rollUp(['pass', 'pass', 'fail'])).toBe('fail')
|
||||
})
|
||||
|
||||
it('does not let an unassessed item average away', () => {
|
||||
expect(rollUp(['pass', 'not-assessed'])).toBe('not-assessed')
|
||||
})
|
||||
|
||||
it('reports all-N/A as N/A rather than pass', () => {
|
||||
expect(rollUp(['not-applicable', 'not-applicable'])).toBe('not-applicable')
|
||||
})
|
||||
})
|
||||
|
||||
suite('citations', () => {
|
||||
it('cites source as path:line so a terminal can open it', () => {
|
||||
expect(cite(evidence.file('docker-compose.yml', 42, 'ports: ["2222:22"]'))).toBe('docker-compose.yml:42')
|
||||
})
|
||||
|
||||
it('cites a row as table#id with the deciding column', () => {
|
||||
expect(cite(evidence.record('requirements', 'a1b2c3', 'parent_id', 'null')))
|
||||
.toBe('requirements#a1b2c3 (parent_id)')
|
||||
})
|
||||
|
||||
it('carries sample count into the citation, because 100% of nothing is not 100%', () => {
|
||||
const none = evidence.measurement('uptime', null, 0, { unit: '%', window: '2026-07' })
|
||||
const real = evidence.measurement('uptime', 98.3, 8640, { unit: '%', window: '2026-07' })
|
||||
expect(cite(none)).toContain('not computable')
|
||||
expect(cite(none)).toContain('0 samples')
|
||||
expect(cite(real)).toContain('98.3%')
|
||||
expect(cite(real)).toContain('8640 samples')
|
||||
})
|
||||
|
||||
it('reads a zero-sample measurement as uncomputable, not as a value', () => {
|
||||
const s = evidence.measurement('Availability', null, 0, { window: '2026-07' }).source
|
||||
expect(describeMeasurement(s as never)).toBe(
|
||||
'No samples for Availability in 2026-07; it cannot be computed.',
|
||||
)
|
||||
})
|
||||
|
||||
it('includes excerpt and note in the verbose form', () => {
|
||||
const lines = citeVerbose(evidence.file('server/api/x.ts', 7, 'const q = sql(raw)', 'unparameterised'))
|
||||
expect(lines[0]).toBe('server/api/x.ts:7')
|
||||
expect(lines.join('\n')).toContain('const q = sql(raw)')
|
||||
expect(lines.join('\n')).toContain('unparameterised')
|
||||
})
|
||||
})
|
||||
|
||||
const clean: CapabilityReport = {
|
||||
capability: 'SideDoor',
|
||||
scope: '49 compose services',
|
||||
examined: 49,
|
||||
findings: [],
|
||||
}
|
||||
|
||||
suite('report', () => {
|
||||
it('derives not-assessed from an empty scan instead of reporting clean', () => {
|
||||
const empty: CapabilityReport = { capability: 'SideDoor', scope: 'nothing', examined: 0, findings: [] }
|
||||
expect(overall(empty)).toBe('not-assessed')
|
||||
const out = formatReport(empty)
|
||||
expect(out).toContain('NOT ASSESSED')
|
||||
expect(out).not.toContain('✓')
|
||||
})
|
||||
|
||||
it('reports a genuine clean run as a pass', () => {
|
||||
expect(overall(clean)).toBe('pass')
|
||||
expect(formatReport(clean)).toContain('✓')
|
||||
})
|
||||
|
||||
it('rejects a conclusion with no evidence behind it', () => {
|
||||
const asserted: CapabilityReport = {
|
||||
capability: 'CertPack',
|
||||
scope: '3 controls',
|
||||
examined: 3,
|
||||
findings: [{ id: 'f1', summary: 'AC-2 implemented', determination: 'pass', severity: 'info', evidence: [] }],
|
||||
}
|
||||
const problems = validateReport(asserted)
|
||||
expect(problems).toHaveLength(1)
|
||||
expect(problems[0]!.problem).toContain('no evidence')
|
||||
})
|
||||
|
||||
it('allows a not-assessed finding to carry no evidence', () => {
|
||||
const unassessed: CapabilityReport = {
|
||||
capability: 'CertPack',
|
||||
scope: '3 controls',
|
||||
examined: 3,
|
||||
findings: [{ id: 'f1', summary: 'AC-2', determination: 'not-assessed', severity: 'high', evidence: [] }],
|
||||
}
|
||||
expect(validateReport(unassessed)).toHaveLength(0)
|
||||
})
|
||||
|
||||
it('catches duplicate finding ids', () => {
|
||||
const dup: CapabilityReport = {
|
||||
capability: 'X', scope: 's', examined: 2,
|
||||
findings: [
|
||||
{ id: 'f1', summary: 'a', determination: 'pass', severity: 'info', evidence: [evidence.file('a.ts')] },
|
||||
{ id: 'f1', summary: 'b', determination: 'pass', severity: 'info', evidence: [evidence.file('b.ts')] },
|
||||
],
|
||||
}
|
||||
expect(validateReport(dup).some(p => p.problem === 'duplicate finding id')).toBe(true)
|
||||
})
|
||||
|
||||
it('shows a missing citation loudly rather than silently', () => {
|
||||
const r: CapabilityReport = {
|
||||
capability: 'X', scope: 's', examined: 1,
|
||||
findings: [{ id: 'f1', summary: 'something is wrong', determination: 'fail', severity: 'high', evidence: [] }],
|
||||
}
|
||||
expect(formatReport(r)).toContain('(no evidence attached)')
|
||||
})
|
||||
|
||||
it('does not label an unassessed finding as a missing citation', () => {
|
||||
// Having nothing to cite IS the content of a not-assessed finding.
|
||||
// Calling it a missing citation reads as a defect in the report rather
|
||||
// than the gap the report is describing.
|
||||
const r: CapabilityReport = {
|
||||
capability: 'X', scope: 's', examined: 1,
|
||||
findings: [{ id: 'f1', summary: 'rule covered nothing', determination: 'not-assessed', severity: 'medium', evidence: [] }],
|
||||
}
|
||||
const out = formatReport(r)
|
||||
expect(out).toContain('(nothing was observed)')
|
||||
expect(out).not.toContain('(no evidence attached)')
|
||||
})
|
||||
|
||||
it('sorts worst first so a critical cannot hide under info lines', () => {
|
||||
const r: CapabilityReport = {
|
||||
capability: 'SideDoor', scope: '2 services', examined: 2,
|
||||
findings: [
|
||||
{ id: 'f2', summary: 'loopback bind', determination: 'pass', severity: 'info', evidence: [evidence.file('a.yml', 3)] },
|
||||
{ id: 'f1', summary: 'SSH published on a host port', determination: 'fail', severity: 'critical', evidence: [evidence.file('b.yml', 9, '2222:22')] },
|
||||
],
|
||||
}
|
||||
const out = formatReport(r)
|
||||
expect(out.indexOf('SSH published')).toBeLessThan(out.indexOf('loopback bind'))
|
||||
expect(overall(r)).toBe('fail')
|
||||
})
|
||||
|
||||
it('prints every citation in full mode', () => {
|
||||
const r: CapabilityReport = {
|
||||
capability: 'X', scope: 's', examined: 1,
|
||||
findings: [{
|
||||
id: 'f1', summary: 'drift', determination: 'fail', severity: 'high',
|
||||
evidence: [evidence.file('a.ts', 1), evidence.file('b.ts', 2)],
|
||||
}],
|
||||
}
|
||||
expect(formatReport(r, { evidence: 'inline' })).toContain('(+1 more)')
|
||||
const full = formatReport(r, { evidence: 'full' })
|
||||
expect(full).toContain('a.ts:1')
|
||||
expect(full).toContain('b.ts:2')
|
||||
})
|
||||
})
|
||||
|
||||
suite('roll-up', () => {
|
||||
it('does not let healthy capabilities dilute an unrun one', () => {
|
||||
const unrun: CapabilityReport = { capability: 'UnrunCheck', scope: 'none', examined: 0, findings: [] }
|
||||
const out = formatRollUp([clean, unrun])
|
||||
expect(out).toContain('NOT ASSESSED')
|
||||
expect(rollUp([overall(clean), overall(unrun)])).toBe('not-assessed')
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user