import { describe, expect, test } from 'bun:test'; import type { Alert } from '../../db/schema'; import { alertDetail, humaniseAge, humaniseRemaining, moduleHealthCell, renderAlertTable, renderMonitorRunMessage, sortAlertRows, toAlertRow, } from './format'; import type { MonitorRunOutcomeSummary } from './run-monitor'; const NOW = new Date('2026-07-28T12:00:00Z'); const ago = (minutes: number) => new Date(NOW.getTime() - minutes * 60_000); const ahead = (minutes: number) => new Date(NOW.getTime() + minutes * 60_000); function alert(over: Partial = {}): Alert { return { id: 'a1', key: 'module:caddy/check:disk-space', activeKey: 'module:caddy/check:disk-space', monitorId: 'mon-1', state: 'firing', severity: 'critical', firstFiredAt: ago(30), lastSeenAt: NOW, graceUntil: ago(29), suppressedByAlertId: null, suppressedByWindowId: null, unsuppressedAt: null, awaitingConfirmation: false, ackedBy: null, ackedAt: null, silencedUntil: null, escalationStep: 0, nextEscalationAt: null, message: '/var 94% used', details: null, resolvedAt: null, ...over, } as Alert; } describe('humaniseAge', () => { test.each([ [0, '0s'], [0.5, '30s'], [5, '5m'], [90, '1h'], [60 * 26, '1d'], ])('%p minutes → %p', (minutes, expected) => { expect(humaniseAge(ago(minutes), NOW)).toBe(expected); }); test('a future instant clamps to zero rather than going negative', () => { expect(humaniseAge(ahead(5), NOW)).toBe('0s'); }); }); describe('humaniseRemaining — rounds up so a countdown never overstates', () => { test.each([ [90, '2h'], [59, '59m'], [61, '2h'], [0, '0s'], ])('%p minutes remaining → %p', (minutes, expected) => { expect(humaniseRemaining(ahead(minutes), NOW)).toBe(expected); }); test('a past instant clamps to zero', () => { expect(humaniseRemaining(ago(5), NOW)).toBe('0s'); }); }); describe('alertDetail — says the most actionable thing', () => { test('an acked alert says so, not its message', () => { expect(alertDetail(alert({ state: 'acked' }), NOW)).toBe('acked'); }); test('a suppressed alert explains that something upstream covers it', () => { expect(alertDetail(alert({ state: 'suppressed', suppressedByAlertId: 'x' }), NOW)).toBe( 'explained by an upstream alert', ); }); test('a deploy window is named specifically, not lumped in', () => { expect(alertDetail(alert({ state: 'suppressed', suppressedByWindowId: 'w' }), NOW)).toBe( 'deploy in progress', ); }); // Rounded UP: with 1h30m left, "1h" would read as nearly over. test('a silenced alert reports how long is left, rounded up', () => { expect(alertDetail(alert({ silencedUntil: ahead(90) }), NOW)).toBe('silenced for 2h'); }); test('a silence just short of its full duration does not read as an hour less', () => { expect(alertDetail(alert({ silencedUntil: ahead(119) }), NOW)).toBe('silenced for 2h'); }); test('an expired silence no longer claims to be silenced', () => { expect(alertDetail(alert({ silencedUntil: ago(5) }), NOW)).toBe('/var 94% used'); }); test('an alert awaiting confirmation says so', () => { expect(alertDetail(alert({ awaitingConfirmation: true }), NOW)).toBe('awaiting confirmation'); }); test('a pending alert inside its grace window says so', () => { expect(alertDetail(alert({ state: 'pending', graceUntil: ahead(1) }), NOW)).toBe( 'within grace window', ); }); test('otherwise the alert message earns the column', () => { expect(alertDetail(alert(), NOW)).toBe('/var 94% used'); }); }); describe('sortAlertRows — most actionable first', () => { test('firing outranks pending, acked, and suppressed', () => { const rows = [ toAlertRow(alert({ key: 'k-suppressed', state: 'suppressed' }), NOW), toAlertRow(alert({ key: 'k-acked', state: 'acked' }), NOW), toAlertRow(alert({ key: 'k-pending', state: 'pending' }), NOW), toAlertRow(alert({ key: 'k-firing', state: 'firing' }), NOW), ]; expect(sortAlertRows(rows).map((r) => r.key)).toEqual([ 'k-firing', 'k-pending', 'k-acked', 'k-suppressed', ]); }); }); describe('renderAlertTable', () => { test('an empty list says so rather than printing bare headers', () => { expect(renderAlertTable([])).toBe('No alerts.'); }); test('columns line up and the header is present', () => { const shortKey = 'module:a'; const longKey = 'module:a-much-longer-module-name'; const table = renderAlertTable([ toAlertRow(alert({ key: shortKey }), NOW), toAlertRow(alert({ key: longKey, state: 'acked' }), NOW), ]); const lines = table.split('\n'); expect(lines[0]).toContain('KEY'); expect(lines[0]).toContain('DETAIL'); // The second column begins at the widest key plus the two-space gutter, // on every line including the header. const offset = longKey.length + 2; for (const line of lines) { expect(line[offset]).not.toBe(' '); } }); test('no trailing whitespace on any line', () => { const table = renderAlertTable([toAlertRow(alert(), NOW)]); for (const line of table.split('\n')) { expect(line).toBe(line.trimEnd()); } }); }); describe('moduleHealthCell', () => { // The lie this column exists to stop telling. test('an unmonitored module reads as "not observed", never "ok"', () => { const cell = moduleHealthCell({ monitored: false, firingCount: 0, suppressed: false }); expect(cell).toBe('not observed'); expect(cell).not.toBe('ok'); }); test('a healthy monitored module reads ok', () => { expect(moduleHealthCell({ monitored: true, firingCount: 0, suppressed: false })).toBe('ok'); }); test('a failing module reports how many alerts', () => { expect(moduleHealthCell({ monitored: true, firingCount: 2, suppressed: false })).toBe( '2 firing', ); }); test('suppressed outranks the firing count — it is why you should not chase it', () => { expect(moduleHealthCell({ monitored: true, firingCount: 3, suppressed: true })).toBe( 'suppressed', ); }); }); describe('renderMonitorRunMessage — names the failing check (#1266)', () => { function summary(over: Partial): MonitorRunOutcomeSummary { return { monitorId: 'mon-1', outcome: 'success', createdIds: [], resolvedIds: [], ...over, } as MonitorRunOutcomeSummary; } const caddyRunning = { key: 'module:caddy/check:caddy_running', severity: 'critical' as const, message: 'caddy systemd service is not active', }; test('a failing check is named, with its reason', () => { const message = renderMonitorRunMessage('caddy', summary({ failingKeys: [caddyRunning] })); expect(message).toContain('module:caddy/check:caddy_running'); expect(message).toContain('caddy systemd service is not active'); expect(message).toContain('1 failing'); }); test('every failing key is named, not just the first', () => { const diskSpace = { key: 'module:caddy/check:disk-space', severity: 'warning' as const, message: '/var 94% used', }; const message = renderMonitorRunMessage( 'caddy', summary({ failingKeys: [caddyRunning, diskSpace] }), ); expect(message).toContain('module:caddy/check:caddy_running'); expect(message).toContain('module:caddy/check:disk-space'); expect(message).toContain('2 failing'); }); test('a healthy run keeps the zero the suite greps for', () => { const message = renderMonitorRunMessage('caddy', summary({ failingKeys: [] })); expect(message).toContain('0 failing'); expect(message).toContain('0 resolved'); }); test('a run that could not execute says why instead of inventing a count', () => { const message = renderMonitorRunMessage( 'caddy', summary({ outcome: 'error', errorMessage: 'hook exited 1' }), ); expect(message).toContain('check could not run'); expect(message).toContain('hook exited 1'); }); });