ktg-plugin-marketplace/plugins/config-audit/tests/lib/output.test.mjs

149 lines
5.3 KiB
JavaScript

import { describe, it, beforeEach } from 'node:test';
import assert from 'node:assert/strict';
import { finding, scannerResult, envelope, resetCounter } from '../../scanners/lib/output.mjs';
describe('resetCounter', () => {
it('resets finding ID counter', () => {
resetCounter();
const f1 = finding({ scanner: 'TST', severity: 'info', title: 'a', description: 'b' });
assert.strictEqual(f1.id, 'CA-TST-001');
resetCounter();
const f2 = finding({ scanner: 'TST', severity: 'info', title: 'c', description: 'd' });
assert.strictEqual(f2.id, 'CA-TST-001');
});
});
describe('finding', () => {
beforeEach(() => { resetCounter(); });
it('generates correct ID format', () => {
const f = finding({ scanner: 'CML', severity: 'high', title: 't', description: 'd' });
assert.match(f.id, /^CA-CML-\d{3}$/);
});
it('auto-increments IDs', () => {
const f1 = finding({ scanner: 'CML', severity: 'info', title: 'a', description: 'b' });
const f2 = finding({ scanner: 'CML', severity: 'info', title: 'c', description: 'd' });
assert.strictEqual(f1.id, 'CA-CML-001');
assert.strictEqual(f2.id, 'CA-CML-002');
});
it('includes all required fields', () => {
const f = finding({
scanner: 'SET',
severity: 'critical',
title: 'Test',
description: 'Desc',
file: '/foo.json',
line: 10,
evidence: 'x=1',
category: 'Structure',
recommendation: 'Fix it',
autoFixable: true,
});
assert.strictEqual(f.scanner, 'SET');
assert.strictEqual(f.severity, 'critical');
assert.strictEqual(f.title, 'Test');
assert.strictEqual(f.description, 'Desc');
assert.strictEqual(f.file, '/foo.json');
assert.strictEqual(f.line, 10);
assert.strictEqual(f.evidence, 'x=1');
assert.strictEqual(f.category, 'Structure');
assert.strictEqual(f.recommendation, 'Fix it');
assert.strictEqual(f.autoFixable, true);
});
it('defaults nullable fields to null', () => {
const f = finding({ scanner: 'TST', severity: 'info', title: 't', description: 'd' });
assert.strictEqual(f.file, null);
assert.strictEqual(f.line, null);
assert.strictEqual(f.evidence, null);
assert.strictEqual(f.category, null);
assert.strictEqual(f.recommendation, null);
assert.strictEqual(f.autoFixable, false);
});
});
describe('scannerResult', () => {
beforeEach(() => { resetCounter(); });
it('counts severity correctly', () => {
const findings = [
finding({ scanner: 'TST', severity: 'critical', title: 'a', description: 'b' }),
finding({ scanner: 'TST', severity: 'high', title: 'c', description: 'd' }),
finding({ scanner: 'TST', severity: 'high', title: 'e', description: 'f' }),
finding({ scanner: 'TST', severity: 'info', title: 'g', description: 'h' }),
];
const r = scannerResult('TST', 'ok', findings, 5, 100);
assert.strictEqual(r.counts.critical, 1);
assert.strictEqual(r.counts.high, 2);
assert.strictEqual(r.counts.medium, 0);
assert.strictEqual(r.counts.low, 0);
assert.strictEqual(r.counts.info, 1);
});
it('includes error message when provided', () => {
const r = scannerResult('TST', 'error', [], 0, 50, 'boom');
assert.strictEqual(r.error, 'boom');
});
it('omits error when not provided', () => {
const r = scannerResult('TST', 'ok', [], 3, 100);
assert.strictEqual(r.error, undefined);
});
it('returns correct structure', () => {
const r = scannerResult('CML', 'ok', [], 2, 42);
assert.strictEqual(r.scanner, 'CML');
assert.strictEqual(r.status, 'ok');
assert.strictEqual(r.files_scanned, 2);
assert.strictEqual(r.duration_ms, 42);
assert.deepStrictEqual(r.findings, []);
});
});
describe('envelope', () => {
beforeEach(() => { resetCounter(); });
it('aggregates across scanners', () => {
const r1 = scannerResult('A', 'ok', [
finding({ scanner: 'A', severity: 'high', title: 'x', description: 'y' }),
], 1, 10);
resetCounter();
const r2 = scannerResult('B', 'ok', [
finding({ scanner: 'B', severity: 'critical', title: 'a', description: 'b' }),
finding({ scanner: 'B', severity: 'low', title: 'c', description: 'd' }),
], 2, 20);
const env = envelope('/target', [r1, r2], 50);
assert.strictEqual(env.aggregate.total_findings, 3);
assert.strictEqual(env.aggregate.counts.critical, 1);
assert.strictEqual(env.aggregate.counts.high, 1);
assert.strictEqual(env.aggregate.counts.low, 1);
assert.strictEqual(env.aggregate.scanners_ok, 2);
});
it('counts scanner statuses', () => {
const r1 = scannerResult('A', 'ok', [], 1, 10);
const r2 = scannerResult('B', 'skipped', [], 0, 5);
const r3 = scannerResult('C', 'error', [], 0, 3, 'fail');
const env = envelope('/t', [r1, r2, r3], 30);
assert.strictEqual(env.aggregate.scanners_ok, 1);
assert.strictEqual(env.aggregate.scanners_skipped, 1);
assert.strictEqual(env.aggregate.scanners_error, 1);
});
it('includes meta with version and tool', () => {
const env = envelope('/t', [], 0);
assert.strictEqual(env.meta.version, '2.2.0');
assert.strictEqual(env.meta.tool, 'config-audit');
assert.strictEqual(env.meta.target, '/t');
assert.ok(env.meta.timestamp);
});
it('calculates verdict correctly', () => {
const env = envelope('/t', [], 0);
assert.strictEqual(env.aggregate.verdict, 'PASS');
});
});