feat(ai-review 對話收斂): 讀 PR review 留言判斷解決狀態並收斂 findings #42
@@ -32,6 +32,18 @@ describe('parseBotReviewComment', () => {
|
||||
assert.equal(f.suggestion, '移除硬編碼密鑰');
|
||||
});
|
||||
|
||||
it('falls back to 問題 content when 建議 is absent', () => {
|
||||
const body = '**審查員**:Maya\n**問題**:缺少邊界測試';
|
||||
const f = parseBotReviewComment(body);
|
||||
assert.equal(f.problem, '缺少邊界測試');
|
||||
assert.equal(f.suggestion, '缺少邊界測試');
|
||||
});
|
||||
|
||||
it('defaults level to warning when 嚴重等級/等級 is missing', () => {
|
||||
const body = '**審查員**:Maya\n**問題**:p\n**建議**:s';
|
||||
assert.equal(parseBotReviewComment(body).level, 'warning');
|
||||
});
|
||||
|
||||
it('returns null for free-form human comments', () => {
|
||||
assert.equal(parseBotReviewComment('我覺得這段可以再想想'), null);
|
||||
assert.equal(parseBotReviewComment(''), null);
|
||||
@@ -93,6 +105,23 @@ describe('judgeConversationsResolved', () => {
|
||||
assert.deepEqual(verdicts, [{ idx: 0, resolved: false }]);
|
||||
});
|
||||
|
||||
it('filters out AI results missing idx or resolved and keeps valid ones', async () => {
|
||||
const items = [{ idx: 0 }, { idx: 1 }];
|
||||
const chatFn = async () => [{ resolved: true }, { idx: 1, resolved: true }, { idx: 0 }];
|
||||
const verdicts = await judgeConversationsResolved(items, chatFn);
|
||||
assert.deepEqual(verdicts, [
|
||||
{ idx: 0, resolved: false }, // {idx:0} 缺 resolved → 視為 false;缺 idx 的整筆被過濾
|
||||
{ idx: 1, resolved: true },
|
||||
]);
|
||||
});
|
||||
|
||||
it('propagates errors thrown by chatFn to the caller', async () => {
|
||||
await assert.rejects(
|
||||
() => judgeConversationsResolved([{ idx: 0 }], async () => { throw new Error('LLM down'); }),
|
||||
/LLM down/,
|
||||
);
|
||||
});
|
||||
|
||||
it('returns [] for no items', async () => {
|
||||
assert.deepEqual(await judgeConversationsResolved([]), []);
|
||||
});
|
||||
@@ -134,6 +163,20 @@ describe('reconcileConversations', () => {
|
||||
assert.equal(result.carriedFindings.length, 2);
|
||||
});
|
||||
|
||||
it('treats all conversations as unresolved when the judge throws', async () => {
|
||||
const resolvedIds = [];
|
||||
const deps = baseDeps();
|
||||
deps.judge = async () => { throw new Error('judge boom'); };
|
||||
deps.resolveComment = async (id) => { resolvedIds.push(id); return { ok: true }; };
|
||||
|
||||
const result = await reconcileConversations(deps);
|
||||
|
||||
assert.deepEqual(resolvedIds, []); // 無任何對話被 resolve
|
||||
assert.equal(result.resolvedCount, 0);
|
||||
assert.equal(result.carriedFindings.length, 2); // a.js + b.js 皆加回(c.js 已解決略過)
|
||||
assert.deepEqual(result.carriedFindings.map(f => f.suggestion).sort(), ['fix one', 'fix two']);
|
||||
});
|
||||
|
||||
it('returns empty result and does not throw when listing comments fails', async () => {
|
||||
const result = await reconcileConversations({ listComments: async () => { throw new Error('boom'); } });
|
||||
assert.deepEqual(result, { resolvedFindings: [], carriedFindings: [], resolvedCount: 0, unresolvedCount: 0 });
|
||||
|
||||
Reference in New Issue
Block a user