test(app): 補齊 isSafeRepoPath/extractBalancedJSON/normalizeText/repairJSONArrayWithAI/格式化函式測試

為可測性 export 三個內部函式(isSafeRepoPath、extractBalancedJSON/extractJSONText、normalizeText)。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Jeffery
2026-06-26 14:41:41 +08:00
co-authored by Claude Opus 4.8
parent fc10ab7266
commit 775cc575ae
8 changed files with 354 additions and 5 deletions
+94
View File
@@ -420,3 +420,97 @@ describe('postFindingsReview', () => {
assert.deepEqual(inlineCalls.map(c => `${c.path}:${c.line}`), ['app/a.js:5', 'app/b.js:9']);
});
});
describe('formatFindingsStats markdown formatting', () => {
// 代表性輸入:critical/warning/info 混合,含 is_new:false(舊問題)與未分類等級(custom)
const mixedFindings = [
{ level: 'critical', is_new: true },
{ level: 'critical', is_new: true },
{ level: 'warning', is_new: true },
{ level: 'info' }, // 未設 is_new 視為新問題
{ level: 'custom', is_new: true }, // 無法標示(不在 LEVEL_ORDER
{ level: 'critical', is_new: false }, // 舊問題
{ level: 'warning', is_new: false }, // 舊問題
{ level: 'mystery', is_new: false }, // 舊問題 + 無法標示
];
it('emits the exact header, separator and one row per type for mixed findings', () => {
const stats = formatFindingsStats(mixedFindings);
assert.equal(stats, [
'| 類型 | 🔴 嚴重 | 🟡 警告 | 🔵 建議 | ⚪ 無法標示 |',
'| --- | --- | --- | --- | --- |',
'| 新問題 | 2 筆 | 1 筆 | 1 筆 | 1 筆 |',
'| 舊問題 | 1 筆 | 1 筆 | 0 筆 | 1 筆 |',
].join('\n'));
});
it('produces a structurally valid markdown table (4 lines, 6 pipes each, 5 columns)', () => {
const lines = formatFindingsStats(mixedFindings).split('\n');
// 表頭 + 分隔列 + 新問題列 + 舊問題列
assert.equal(lines.length, 4);
// 每列皆以 pipe 起訖
for (const row of lines) {
assert.ok(row.startsWith('| '), `row should start with a pipe: ${row}`);
assert.ok(row.endsWith(' |'), `row should end with a pipe: ${row}`);
// 5 欄 => 6 個 pipe 分隔符
assert.equal((row.match(/\|/g) || []).length, 6, `row should have 6 pipes: ${row}`);
}
// 分隔列每格皆為 ---
assert.equal(lines[1], '| --- | --- | --- | --- | --- |');
// 資料列以 類型 標籤起頭
assert.match(lines[2], /^\| 新問題 \|/);
assert.match(lines[3], /^\| 舊問題 \|/);
});
it('returns a stable, non-broken table for an empty findings array', () => {
let stats;
assert.doesNotThrow(() => { stats = formatFindingsStats([]); });
assert.equal(stats, [
'| 類型 | 🔴 嚴重 | 🟡 警告 | 🔵 建議 | ⚪ 無法標示 |',
'| --- | --- | --- | --- | --- |',
'| 新問題 | 0 筆 | 0 筆 | 0 筆 | 0 筆 |',
'| 舊問題 | 0 筆 | 0 筆 | 0 筆 | 0 筆 |',
].join('\n'));
// 即使無資料仍維持 4 列、每列 6 個 pipe 的結構
const lines = stats.split('\n');
assert.equal(lines.length, 4);
for (const row of lines) {
assert.equal((row.match(/\|/g) || []).length, 6, `row should have 6 pipes: ${row}`);
}
});
});
describe('formatFindingsStatsLine markdown formatting', () => {
const mixedFindings = [
{ level: 'critical', is_new: true },
{ level: 'critical', is_new: true },
{ level: 'warning', is_new: true },
{ level: 'info' },
{ level: 'custom', is_new: true },
{ level: 'critical', is_new: false },
{ level: 'warning', is_new: false },
{ level: 'mystery', is_new: false },
];
it('produces the exact single-line summary for mixed findings', () => {
assert.equal(
formatFindingsStatsLine(mixedFindings),
'新: 嚴重2 / 警告1 / 建議1 / 無法標示1;舊: 嚴重1 / 警告1 / 建議0 / 無法標示1',
);
});
it('returns a stable single line with zero counts for an empty findings array', () => {
let lineSummary;
assert.doesNotThrow(() => { lineSummary = formatFindingsStatsLine([]); });
assert.equal(
lineSummary,
'新: 嚴重0 / 警告0 / 建議0 / 無法標示0;舊: 嚴重0 / 警告0 / 建議0 / 無法標示0',
);
// 單行:不含換行
assert.ok(!lineSummary.includes('\n'));
});
});