/** * Tests for CLI flag parsing and exit code logic. * These test the logic functions extracted from the CLI, not the CLI process itself. */ describe('CLI exit code logic', () => { const severityRank: Record = { low: 0, medium: 1, high: 2, critical: 3 }; function shouldFailOnSeverity( anomalies: Array<{ severity: string }>, threshold: string ): boolean { const thresholdRank = severityRank[threshold] ?? 0; return anomalies.some((a) => (severityRank[a.severity] ?? 0) >= thresholdRank); } it('exit 0 when no anomalies', () => { expect(shouldFailOnSeverity([], 'high')).toBe(false); }); it('exit 1 when anomaly at threshold', () => { expect(shouldFailOnSeverity([{ severity: 'high' }], 'high')).toBe(true); }); it('exit 1 when anomaly above threshold', () => { expect(shouldFailOnSeverity([{ severity: 'critical' }], 'high')).toBe(true); }); it('exit 0 when anomaly below threshold', () => { expect(shouldFailOnSeverity([{ severity: 'low' }], 'high')).toBe(false); }); it('exit 0 when medium anomaly and threshold is high', () => { expect(shouldFailOnSeverity([{ severity: 'medium' }], 'high')).toBe(false); }); }); describe('JUnit XML generation', () => { function buildJunit( anomalies: Array<{ id: string; type: string; severity: string; description: string }>, url: string ): string { function escapeXml(s: string): string { return s .replace(/&/g, '&') .replace(//g, '>') .replace(/"/g, '"'); } const cases = anomalies .map( (a) => ` \n` + ` ${escapeXml(a.id)}\n` + ` ` ) .join('\n'); return ( `\n` + `\n` + cases + '\n' + `\n` ); } it('generates valid XML with one anomaly', () => { const xml = buildJunit( [{ id: 'a1', type: 'http_error', severity: 'high', description: 'Server error on form' }], 'http://localhost:3000' ); expect(xml).toContain(' { const xml = buildJunit([], 'http://localhost:3000'); expect(xml).toContain('tests="0"'); expect(xml).not.toContain(' { const xml = buildJunit( [{ id: 'a1', type: 'http_error', severity: 'high', description: '' }], 'http://app.com' ); expect(xml).not.toContain('