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); }); });