feat: enhance exclusions.json with new suggestions and refactor roles.js for dynamic path resolution

This commit is contained in:
2026-05-12 09:57:33 +00:00
parent 2aba414d36
commit c751a53d43
4 changed files with 92 additions and 7 deletions
+64
View File
@@ -0,0 +1,64 @@
import { describe, it, afterEach, mock } from 'node:test';
import assert from 'node:assert/strict';
import axios from 'axios';
// gitea.js reads env vars at module load time (ESM cache), so we test
// the actual values baked in at import time and verify behavior via axios mocks.
afterEach(() => mock.restoreAll());
describe('gitea', async () => {
const { getPRDiff, postComment } = await import('./gitea.js');
it('getPRDiff calls Gitea diff API with Authorization header', async () => {
let capturedUrl, capturedOpts;
mock.method(axios, 'get', async (url, opts) => {
capturedUrl = url;
capturedOpts = opts;
return { data: 'diff content' };
});
const result = await getPRDiff();
assert.equal(result, 'diff content');
assert.ok(capturedUrl.includes('/api/v1/repos/'));
assert.ok(capturedUrl.endsWith('.diff'));
assert.ok(capturedOpts.headers['Authorization'].startsWith('token '));
assert.equal(capturedOpts.headers['Content-Type'], 'application/json');
});
it('postComment calls Gitea issues comments API with body', async () => {
let capturedUrl, capturedBody, capturedOpts;
mock.method(axios, 'post', async (url, body, opts) => {
capturedUrl = url;
capturedBody = body;
capturedOpts = opts;
return { data: { id: 1 } };
});
const result = await postComment('hello world');
assert.deepEqual(result, { id: 1 });
assert.ok(capturedUrl.includes('/api/v1/repos/'));
assert.ok(capturedUrl.endsWith('/comments'));
assert.equal(capturedBody.body, 'hello world');
assert.ok(capturedOpts.headers['Authorization'].startsWith('token '));
});
it('does not set httpsAgent by default (GITEA_SKIP_TLS_VERIFY not true)', async () => {
let capturedOpts;
mock.method(axios, 'get', async (_url, opts) => {
capturedOpts = opts;
return { data: '' };
});
await getPRDiff();
// httpsAgent is undefined when GITEA_SKIP_TLS_VERIFY !== 'true'
assert.equal(capturedOpts.httpsAgent, undefined);
});
it('getPRDiff propagates axios errors', async () => {
mock.method(axios, 'get', async () => { throw new Error('network error'); });
await assert.rejects(() => getPRDiff(), /network error/);
});
it('postComment propagates axios errors', async () => {
mock.method(axios, 'post', async () => { throw new Error('api error'); });
await assert.rejects(() => postComment('test'), /api error/);
});
});
+6 -6
View File
@@ -52,13 +52,13 @@ async function main() {
// Step2: 各角色分析 diff 產生新 findings
console.log('\n📊 Step2: Findings 產生');
const results = await Promise.allSettled(roles.map(role => analyzeWithRole(role, diff)));
const newFindings = [];
for (const role of roles) {
try {
const found = await analyzeWithRole(role, diff);
newFindings.push(...found);
} catch (e) {
console.log(` ⚠️ [${role.name}] 分析失敗(跳過): ${e.message}`);
for (let i = 0; i < results.length; i++) {
if (results[i].status === 'fulfilled') {
newFindings.push(...results[i].value);
} else {
console.log(` ⚠️ [${roles[i].name}] 分析失敗(跳過): ${results[i].reason?.message}`);
}
}
console.log(` Step2 完成: 新 findings 總計 ${newFindings.length}`);
+2 -1
View File
@@ -1,8 +1,9 @@
import fs from 'fs';
import path from 'path';
import { fileURLToPath } from 'url';
import yaml from 'js-yaml';
const ROLES_DIR = '/action/app/prompts/roles';
const ROLES_DIR = path.join(fileURLToPath(import.meta.url), '..', 'prompts', 'roles');
export function loadRoles() {
return fs.readdirSync(ROLES_DIR)