feat: optimize exclusion filtering

This commit is contained in:
2026-05-18 02:06:36 +00:00
parent b06a89f2b9
commit d18c4a4a8e
3 changed files with 196 additions and 11 deletions
+45 -1
View File
@@ -3,7 +3,7 @@ import assert from 'node:assert/strict';
import fs from 'node:fs';
import os from 'node:os';
import path from 'node:path';
import { loadOldFindings, loadExclusions, applyExclusions } from './findings.js';
import { loadOldFindings, loadExclusions, applyExclusions, filterFalsePositivesWithAI } from './findings.js';
import { EXCLUSIONS_PATH, FINDINGS_PATH } from './config.js';
describe('findings exclusions', () => {
@@ -56,6 +56,50 @@ describe('findings exclusions', () => {
assert.equal(filtered[0].location, 'README.md:12');
});
it('dedupes repeated exclusions when loading exclusions', () => {
const fullPath = path.join(workspace, EXCLUSIONS_PATH);
fs.mkdirSync(path.dirname(fullPath), { recursive: true });
fs.writeFileSync(fullPath, JSON.stringify([
{ location: 'entrypoint.sh:180', title: 'fetch_package_versions jq overhead' },
{ location: 'entrypoint.sh:999', title: 'fetch_package_versions jq overhead' },
{ location: 'entrypoint.sh:180', title: 'fetch_package_versions jq overhead' },
], null, 2));
const exclusions = loadExclusions(workspace);
assert.equal(exclusions.length, 1);
assert.equal(exclusions[0].filePath, 'entrypoint.sh');
assert.equal(exclusions[0].text, 'fetch_package_versions jq overhead');
});
it('builds a compact exclusion hint for AI', async () => {
const findings = [
{ level: 'warning', role: 'Maya', location: 'src/app.cs:12', suggestion: 'update tests' },
];
const exclusions = [
{ location: 'src/app.cs:1', original_finding: '更新套件後請補上測試驗證' },
{ location: 'src/app.cs:99', original_finding: '更新套件後請補上測試驗證 ' },
{ location: 'src/service.cs:3', original_finding: '更新套件後請補上測試驗證' },
{ location: 'src/service.cs:8', title: '請確認安全性變更' },
];
let capturedSystemPrompt = '';
let capturedUserContent = '';
const result = await filterFalsePositivesWithAI(findings, exclusions, async (systemPrompt, userContent) => {
capturedSystemPrompt = systemPrompt;
capturedUserContent = userContent;
return findings;
});
assert.equal(result.length, 1);
assert.ok(capturedSystemPrompt.includes('已知誤報清單(原始 4 筆,整理後 3 筆,分成 2 類)'));
assert.ok(capturedSystemPrompt.includes('更新套件後請補上測試驗證'));
assert.ok(capturedSystemPrompt.includes('paths=src/app.cs, src/service.cs'));
assert.ok(capturedSystemPrompt.includes('請確認安全性變更'));
assert.ok(capturedUserContent.includes('"location":"src/app.cs:12"'));
assert.ok(capturedUserContent.includes('"suggestion":"update tests"'));
});
it('logs exclusions file metadata and repo state when loading exclusions', () => {
const fullPath = path.join(workspace, EXCLUSIONS_PATH);
fs.mkdirSync(path.dirname(fullPath), { recursive: true });