import { describe, expect, test } from 'bun:test'; import { type DriftFinding, computeVerdict } from './types'; const drift: DriftFinding = { category: 'module_versions', severity: 'drift', code: 'module_version_drift', message: 'caddy: 1.2.0 → 1.3.0 available', subject: 'caddy', }; const blocked: DriftFinding = { category: 'capability_abi', severity: 'blocked', code: 'capability_abi_mismatch', message: 'lunacycle requires public_web@2 but caddy provides 1', subject: 'lunacycle', }; const unmeasured: DriftFinding = { category: 'module_integrity', severity: 'unmeasured', code: 'module_integrity_unmeasured', message: 'wireguard-manager: the host did not answer, so nothing was measured', subject: 'wireguard-manager', }; const todo: DriftFinding = { category: 'undeployed_modules', severity: 'todo', code: 'module_undeployed', message: 'namecheap: imported but not deployed (state: IMPORTED)', subject: 'namecheap', }; describe('computeVerdict', () => { test('READY when no findings', () => { expect(computeVerdict([])).toBe('READY'); }); test('DRIFT when only drift-severity findings', () => { expect(computeVerdict([drift, { ...drift, subject: 'iptables' }])).toBe('DRIFT'); }); test('BLOCKED when any finding is blocked', () => { expect(computeVerdict([drift, blocked])).toBe('BLOCKED'); }); test('BLOCKED takes precedence regardless of order', () => { expect(computeVerdict([blocked, drift])).toBe('BLOCKED'); }); // Pinning down the new severity tier: todo findings are // informational reminders and never escalate the verdict beyond // READY. The whole point of this severity level is to let // categories like undeployed_modules and unconfigured_modules // surface a TODO list without making `system update` look like // it has unfinished work. test('READY when only todo-severity findings', () => { expect(computeVerdict([todo, { ...todo, subject: 'caddy' }])).toBe('READY'); }); test('DRIFT when both drift and todo findings exist (drift wins)', () => { expect(computeVerdict([todo, drift])).toBe('DRIFT'); }); test('BLOCKED still wins over todos', () => { expect(computeVerdict([todo, drift, blocked])).toBe('BLOCKED'); }); // D7. An unmeasured check is not a pass, and it is not the same statement as // a measured difference. Before this, a category that could not reach its // subject contributed nothing, and contributing nothing rendered as READY. test('UNKNOWN when something could not be measured', () => { expect(computeVerdict([unmeasured])).toBe('UNKNOWN'); }); test('an unmeasured finding never lets the verdict return READY', () => { expect(computeVerdict([todo, unmeasured])).not.toBe('READY'); }); test('UNKNOWN outranks DRIFT — you cannot act on a diff you are not sure you have', () => { expect(computeVerdict([drift, unmeasured])).toBe('UNKNOWN'); expect(computeVerdict([unmeasured, drift])).toBe('UNKNOWN'); }); test('BLOCKED still outranks UNKNOWN — a hard stop is still a hard stop', () => { expect(computeVerdict([unmeasured, blocked])).toBe('BLOCKED'); expect(computeVerdict([blocked, unmeasured, drift, todo])).toBe('BLOCKED'); }); });