tts-voice-creator-clone-and.../tests/sonar_triage.test.mjs
mARTin-B78 98a87077d9 feat(qa): sonar triage loop (dry-run default) and multica agent hookup
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-06 02:47:21 +02:00

58 lines
3.4 KiB
JavaScript

import { test } from 'node:test';
import assert from 'node:assert/strict';
import { classify, parseEffort, groupIssues, hasMarker, issueBody, fetchIssues, main, loadConfig } from '../scripts/sonar_triage.mjs';
const cfg = { allow: new Set(['python:S1481']), skip: [{ rule: 'javascript:S2245', file: '^static/js/', reason: 'ids' }] };
const mk = (o) => ({ key: 'K' + Math.random(), rule: 'python:S1481', severity: 'MINOR', type: 'CODE_SMELL', effort: '2min', component: 'p:core/x.py', line: 3, message: 'm', ...o });
test('effort parsing', () => { assert.equal(parseEffort('1h30min'), 90); assert.equal(parseEffort('5min'), 5); assert.equal(parseEffort(undefined), 0); });
test('small: allowlisted + minor in core', () => assert.equal(classify(mk(), cfg).cls, 'small'));
test('small: MAJOR only with effort <= 5min', () => {
assert.equal(classify(mk({ severity: 'MAJOR' }), cfg).cls, 'small');
assert.equal(classify(mk({ severity: 'MAJOR', effort: '10min' }), cfg).cls, 'medium');
});
test('not allowlisted -> medium; critical or >60min -> large', () => {
assert.equal(classify(mk({ rule: 'python:S9' }), cfg).cls, 'medium');
assert.equal(classify(mk({ rule: 'python:S9', severity: 'CRITICAL' }), cfg).cls, 'large');
assert.equal(classify(mk({ rule: 'python:S9', effort: '2h' }), cfg).cls, 'large');
});
test('never small: vulnerability, outside dirs, risky path/message', () => {
assert.notEqual(classify(mk({ type: 'VULNERABILITY' }), cfg).cls, 'small');
assert.notEqual(classify(mk({ component: 'p:tests/a.py' }), cfg).cls, 'small');
assert.notEqual(classify(mk({ component: 'p:core/auth.py' }), cfg).cls, 'small');
assert.notEqual(classify(mk({ message: 'unused var in await call' }), cfg).cls, 'small');
});
test('skip list wins', () => assert.equal(classify(mk({ rule: 'javascript:S2245', component: 'p:static/js/a.js' }), cfg).cls, 'skipped'));
test('shipped config loads and skips server.py bind', () => {
const c = loadConfig();
assert.equal(classify(mk({ rule: 'python:S104', component: 'p:server.py' }), c).cls, 'skipped');
});
test('grouping by rule+file and body marker', () => {
const g = groupIssues([mk({ key: 'b' }), mk({ key: 'a' }), mk({ key: 'c', component: 'p:core/y.py' })].map((i) => ({ ...i, path: i.component.slice(2) })));
assert.equal(g.length, 2);
const body = issueBody(g.find((x) => x.keys.length === 2));
assert.match(body, /<!-- sonar-keys: a,b -->/);
assert.ok(hasMarker([body], ['b']));
assert.ok(!hasMarker([body], ['zzz']));
});
const page = (issues) => ({ ok: true, json: async () => ({ issues, paging: { total: issues.length } }) });
test('fetch sends token as basic username, new-code filter', async () => {
let seen;
await fetchIssues({ token: 'SECRET', fetchFn: async (u, o) => { seen = { u, o }; return page([]); } });
assert.equal(seen.o.headers.Authorization, 'Basic ' + Buffer.from('SECRET:').toString('base64'));
assert.match(seen.u, /inNewCodePeriod=true/);
assert.doesNotMatch(seen.u, /SECRET/);
});
test('dry-run makes only GET requests and never prints the token', async () => {
const methods = []; const out = [];
await main(['--dry-run', '--all'], {
token: 'SECRET-TOKEN-123', cfg, noActions: true, log: (s) => out.push(s),
fetchFn: async (u, o) => { methods.push(o.method || 'GET'); return page([mk(), mk({ rule: 'python:S9' })]); },
});
assert.deepEqual([...new Set(methods)], ['GET']);
assert.ok(out.join('\n').includes('SMALL (1)'));
assert.ok(!out.join('\n').includes('SECRET-TOKEN-123'));
});