import { describe, expect, test } from 'bun:test'; import { auditUndeployedModules } from './undeployed-modules'; describe('auditUndeployedModules', () => { test('skips INSTALLED and VERIFIED modules', async () => { const result = await auditUndeployedModules({ modules: [ { id: 'caddy', state: 'INSTALLED' }, { id: 'iptables', state: 'VERIFIED' }, ], }); expect(result).toEqual([]); }); test('flags IMPORTED modules with actionable deploy command', async () => { const result = await auditUndeployedModules({ modules: [{ id: 'authentik', state: 'IMPORTED' }], }); expect(result).toHaveLength(1); expect(result[0]).toMatchObject({ category: 'undeployed_modules', severity: 'todo', code: 'module_undeployed', remediation: 'celilo module deploy authentik', actionable: true, subject: 'authentik', }); }); test('also flags VALIDATED, CONFIGURED', async () => { const result = await auditUndeployedModules({ modules: [ { id: 'a', state: 'VALIDATED' }, { id: 'b', state: 'CONFIGURED' }, ], }); expect(result).toHaveLength(2); expect(result.every((f) => f.code === 'module_undeployed')).toBe(true); }); test('skips transient states (DEPLOYING, GENERATING, UNINSTALLING)', async () => { const result = await auditUndeployedModules({ modules: [ { id: 'a', state: 'DEPLOYING' }, { id: 'b', state: 'GENERATING' }, { id: 'c', state: 'UNINSTALLING' }, ], }); expect(result).toEqual([]); }); test('ERROR state surfaces as blocked, non-actionable, with investigation guidance', async () => { const result = await auditUndeployedModules({ modules: [{ id: 'broken', state: 'ERROR' }], }); expect(result).toHaveLength(1); expect(result[0]).toMatchObject({ category: 'undeployed_modules', severity: 'blocked', code: 'module_in_error_state', actionable: false, subject: 'broken', }); expect(result[0].details).toContain('Investigate'); }); });