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

176 lines
5.3 KiB
JavaScript

import { describe, it, beforeEach, afterEach } from 'node:test';
import assert from 'node:assert/strict';
import { join } from 'node:path';
import { rm, stat, readFile, mkdir } from 'node:fs/promises';
import { tmpdir, homedir } from 'node:os';
import {
saveBaseline,
loadBaseline,
listBaselines,
deleteBaseline,
getBaselinesDir,
} from '../../scanners/lib/baseline.mjs';
// We test against the real baselines dir but use unique names to avoid collisions.
const TEST_PREFIX = `_test_${Date.now()}_`;
function makeTestEnvelope(findingCount = 3) {
const findings = Array.from({ length: findingCount }, (_, i) => ({
id: `CA-CML-${String(i + 1).padStart(3, '0')}`,
scanner: 'CML',
severity: 'low',
title: `Finding ${i + 1}`,
file: 'CLAUDE.md',
}));
return {
meta: { target: '/test/path', timestamp: new Date().toISOString(), version: '2.0.0', tool: 'config-audit' },
scanners: [{
scanner: 'CML',
status: 'ok',
files_scanned: 1,
duration_ms: 5,
findings,
counts: { critical: 0, high: 0, medium: 0, low: findingCount, info: 0 },
}],
aggregate: { total_findings: findingCount, counts: { critical: 0, high: 0, medium: 0, low: findingCount, info: 0 }, risk_score: findingCount, risk_band: 'Low', verdict: 'PASS', scanners_ok: 1, scanners_error: 0, scanners_skipped: 0 },
};
}
// Cleanup helper
const savedNames = [];
async function cleanup() {
for (const name of savedNames) {
await deleteBaseline(name);
}
savedNames.length = 0;
}
describe('saveBaseline', () => {
afterEach(cleanup);
it('writes a file and returns path', async () => {
const name = `${TEST_PREFIX}save1`;
savedNames.push(name);
const envelope = makeTestEnvelope(2);
const result = await saveBaseline(envelope, name);
assert.ok(result.path.endsWith(`${name}.json`));
assert.equal(result.name, name);
// Verify file exists
const s = await stat(result.path);
assert.ok(s.isFile());
});
it('includes _baseline metadata', async () => {
const name = `${TEST_PREFIX}meta`;
savedNames.push(name);
const envelope = makeTestEnvelope(5);
await saveBaseline(envelope, name);
const loaded = await loadBaseline(name);
assert.ok(loaded._baseline);
assert.ok(loaded._baseline.saved_at);
assert.equal(loaded._baseline.target_path, '/test/path');
assert.equal(loaded._baseline.finding_count, 5);
assert.equal(typeof loaded._baseline.score, 'number');
});
it('defaults name to "default"', async () => {
const name = `${TEST_PREFIX}default_test`;
savedNames.push(name);
// We won't actually use the literal 'default' to avoid interfering with real baselines
const result = await saveBaseline(makeTestEnvelope(), name);
assert.equal(result.name, name);
});
it('overwrites existing baseline with same name', async () => {
const name = `${TEST_PREFIX}overwrite`;
savedNames.push(name);
await saveBaseline(makeTestEnvelope(2), name);
await saveBaseline(makeTestEnvelope(7), name);
const loaded = await loadBaseline(name);
assert.equal(loaded._baseline.finding_count, 7);
});
});
describe('loadBaseline', () => {
afterEach(cleanup);
it('loads a previously saved baseline', async () => {
const name = `${TEST_PREFIX}load`;
savedNames.push(name);
const envelope = makeTestEnvelope(3);
await saveBaseline(envelope, name);
const loaded = await loadBaseline(name);
assert.ok(loaded);
assert.equal(loaded.aggregate.total_findings, 3);
assert.equal(loaded.meta.target, '/test/path');
});
it('returns null for unknown name', async () => {
const result = await loadBaseline(`${TEST_PREFIX}nonexistent_${Date.now()}`);
assert.equal(result, null);
});
it('preserves all scanner data', async () => {
const name = `${TEST_PREFIX}preserve`;
savedNames.push(name);
const envelope = makeTestEnvelope(1);
await saveBaseline(envelope, name);
const loaded = await loadBaseline(name);
assert.equal(loaded.scanners.length, 1);
assert.equal(loaded.scanners[0].scanner, 'CML');
assert.equal(loaded.scanners[0].findings.length, 1);
});
});
describe('listBaselines', () => {
afterEach(cleanup);
it('lists saved baselines', async () => {
const name = `${TEST_PREFIX}list`;
savedNames.push(name);
await saveBaseline(makeTestEnvelope(4), name);
const result = await listBaselines();
assert.ok(Array.isArray(result.baselines));
const found = result.baselines.find(b => b.name === name);
assert.ok(found, 'Should find the saved baseline in list');
assert.equal(found.findingCount, 4);
assert.ok(found.savedAt);
});
it('returns empty array when no baselines', async () => {
// This test just verifies the function doesn't crash
const result = await listBaselines();
assert.ok(Array.isArray(result.baselines));
});
});
describe('deleteBaseline', () => {
it('deletes an existing baseline', async () => {
const name = `${TEST_PREFIX}delete`;
await saveBaseline(makeTestEnvelope(), name);
const result = await deleteBaseline(name);
assert.equal(result.deleted, true);
const loaded = await loadBaseline(name);
assert.equal(loaded, null);
});
it('returns false for non-existent baseline', async () => {
const result = await deleteBaseline(`${TEST_PREFIX}nope_${Date.now()}`);
assert.equal(result.deleted, false);
});
});