Files
范智鹏 e9762bfbe1 test: 目录对齐赛道二成果物规范(tests/ 迁移+实验报告+提效测量脚本)
- src/test 迁移至根目录 tests/(16 测试 + fixtures/manual),导入改为 ../src/
- 新增 tsconfig.test.json 独立编译测试;.vscode-test.mjs / package.json / .gitignore 同步
- 新增 tests/test-execution-log.md、tests/test-cases.md(116 用例清单)
- 新增 tests/measure/measure-review-time.mjs + performance-comparison.md(提效测量)
- 重命名 3 个中文报告文件为 demo-*-coverage-report.md
- git rm --cached 解除 vsix 跟踪;README 标注演示视频进行中
2026-08-26 19:17:44 +08:00

335 lines
12 KiB
TypeScript

import * as assert from 'assert';
import { mergeResults, MergedReport } from '../src/merger/merger';
import { CustomRuleResult, AIFinding } from '../src/ai/schema';
import { LinterDiagnostic } from '../src/types';
function range(line: number): any {
return new (require('vscode').Range)(line, 0, line, 1);
}
suite('Merger Tests', () => {
test('mergeResults counts correctly', () => {
const staticDiags: LinterDiagnostic[] = [
{ severity: 'error', ruleId: 'eslint:no-unused', message: 'x is unused', range: new (require('vscode').Range)(0, 0, 0, 1) },
];
const customResults: CustomRuleResult[] = [
{ ruleId: 'custom:no-console', line: 5, severity: 'warning', message: 'avoid console.log' },
];
const aiFindings: AIFinding[] = [
{ ruleId: 'hardcoded-secret', severity: 'error', category: 'security', title: 'Hardcoded', description: 'Found secret', suggestion: 'Use env', line: 3 },
];
const report = mergeResults({
staticDiagnostics: staticDiags,
customRuleResults: customResults,
translatedDiagnostics: [],
aiFindings,
errors: [],
degraded: false,
startTime: Date.now(),
filePath: '/test/sample.js',
language: 'javascript',
adapterIds: ['eslint'],
});
assert.strictEqual(report.linterCount, 1);
assert.strictEqual(report.customRuleCount, 1);
assert.strictEqual(report.aiCount, 1);
assert.strictEqual(report.degraded, false);
assert.strictEqual(report.language, 'javascript');
});
test('mergeResults marks degraded when AI fails', () => {
const report = mergeResults({
staticDiagnostics: [],
customRuleResults: [],
translatedDiagnostics: [],
aiFindings: [],
errors: ['AI 请求超时'],
degraded: true,
startTime: Date.now(),
filePath: '/test/sample.js',
language: 'javascript',
adapterIds: ['eslint'],
});
assert.strictEqual(report.degraded, true);
assert.strictEqual(report.errors.length, 1);
});
test('mergeResults sorts diagnostics by severity then line', () => {
const staticDiags: LinterDiagnostic[] = [
{ severity: 'info', ruleId: 'eslint:info-5', message: 'i5', range: range(5) },
{ severity: 'error', ruleId: 'eslint:err-10', message: 'e10', range: range(10) },
{ severity: 'warning', ruleId: 'eslint:warn-3', message: 'w3', range: range(3) },
{ severity: 'error', ruleId: 'eslint:err-1', message: 'e1', range: range(1) },
];
const customResults: CustomRuleResult[] = [
{ ruleId: 'custom:i-9', line: 9, severity: 'info', message: 'i9' },
{ ruleId: 'custom:e-2', line: 2, severity: 'error', message: 'e2' },
];
const aiFindings: AIFinding[] = [
{ ruleId: 'ai:w-7', severity: 'warning', category: 'style', title: 'w7', description: 'w7', line: 7, suggestion: '' },
{ ruleId: 'ai:e-4', severity: 'error', category: 'bug', title: 'e4', description: 'e4', line: 4, suggestion: '' },
];
const report = mergeResults({
staticDiagnostics: staticDiags,
customRuleResults: customResults,
translatedDiagnostics: [],
aiFindings,
errors: [],
degraded: false,
startTime: Date.now(),
filePath: '/test/sample.js',
language: 'javascript',
adapterIds: ['eslint'],
});
assert.deepStrictEqual(
report.linterDiagnostics.map(d => `${d.severity}:${d.range.start.line}`),
['error:1', 'error:10', 'warning:3', 'info:5']
);
assert.deepStrictEqual(
report.customRuleDiagnostics.map(d => `${d.severity}:${d.range.start.line}`),
['error:1', 'info:8']
);
assert.deepStrictEqual(
report.aiFindings.map(f => `${f.severity}:${f.line}`),
['error:3', 'warning:6']
);
assert.deepStrictEqual(report.fixableLinterIndices, []);
assert.deepStrictEqual(report.fixableCustomIndices, []);
});
test('mergeResults marks fixable only diagnostics with fix object', () => {
const staticDiags: LinterDiagnostic[] = [
{ severity: 'error', ruleId: 'eslint:fixable-1', message: 'f1', range: range(1), fix: { range: [0, 5], text: 'x' } },
{ severity: 'warning', ruleId: 'eslint:nofix-2', message: 'n2', range: range(2) },
{ severity: 'error', ruleId: 'eslint:fixable-3', message: 'f3', range: range(3), fix: { range: [6, 9], text: 'y' } },
];
const report = mergeResults({
staticDiagnostics: staticDiags,
customRuleResults: [],
translatedDiagnostics: [],
aiFindings: [],
errors: [],
degraded: false,
startTime: Date.now(),
filePath: '/test/sample.js',
language: 'javascript',
adapterIds: ['eslint'],
});
const sorted = report.linterDiagnostics;
const fixableIndices = sorted.map((_, i) => (sorted[i].fix ? i : -1)).filter(i => i !== -1);
assert.deepStrictEqual(report.fixableLinterIndices, fixableIndices);
assert.strictEqual(report.fixableCustomIndices.length, 0);
});
test('mergeResults converts aiFindings line to 0-based', () => {
const aiFindings: AIFinding[] = [
{ ruleId: 'ai:first', severity: 'warning', category: 'style', title: 'first', description: 'd', line: 1, suggestion: '' },
{ ruleId: 'ai:last', severity: 'info', category: 'design', title: 'last', description: 'd', line: 8, suggestion: '' },
];
const report = mergeResults({
staticDiagnostics: [],
customRuleResults: [],
translatedDiagnostics: [],
aiFindings,
errors: [],
degraded: false,
startTime: Date.now(),
filePath: '/test/sample.js',
language: 'javascript',
adapterIds: ['eslint'],
});
assert.deepStrictEqual(
report.aiFindings.map(f => f.line),
[0, 7]
);
});
test('mergeResults pairs translations by originalRuleId not index', () => {
const staticDiags: LinterDiagnostic[] = [
{ severity: 'error', ruleId: 'eslint:no-unused', message: 'x is unused', range: range(1) },
{ severity: 'warning', ruleId: 'eslint:no-console', message: 'console call', range: range(2) },
{ severity: 'error', ruleId: 'eslint:no-eval', message: 'eval used', range: range(3) },
];
const report = mergeResults({
staticDiagnostics: staticDiags,
customRuleResults: [],
translatedDiagnostics: [
{ originalRuleId: 'eslint:no-eval', translatedMessage: '评估使用', translatedSuggestion: '避免' },
{ originalRuleId: 'eslint:no-unused', translatedMessage: '未使用', translatedSuggestion: '删除' },
],
aiFindings: [],
errors: [],
degraded: false,
startTime: Date.now(),
filePath: '/test/sample.js',
language: 'javascript',
adapterIds: ['eslint'],
});
assert.strictEqual(report.linterDiagnostics[0].message, '未使用');
assert.strictEqual(report.linterDiagnostics[0].suggestion, '删除');
assert.strictEqual(report.linterDiagnostics[1].message, '评估使用');
assert.strictEqual(report.linterDiagnostics[2].message, 'console call');
});
test('mergeResults handles duplicate ruleId translations in order', () => {
const staticDiags: LinterDiagnostic[] = [
{ severity: 'error', ruleId: 'eslint:no-console', message: 'first console', range: range(1) },
{ severity: 'error', ruleId: 'eslint:no-console', message: 'second console', range: range(2) },
];
const report = mergeResults({
staticDiagnostics: staticDiags,
customRuleResults: [],
translatedDiagnostics: [
{ originalRuleId: 'eslint:no-console', translatedMessage: '首次', translatedSuggestion: '' },
{ originalRuleId: 'eslint:no-console', translatedMessage: '二次', translatedSuggestion: '' },
],
aiFindings: [],
errors: [],
degraded: false,
startTime: Date.now(),
filePath: '/test/sample.js',
language: 'javascript',
adapterIds: ['eslint'],
});
assert.strictEqual(report.linterDiagnostics[0].message, '首次');
assert.strictEqual(report.linterDiagnostics[1].message, '二次');
});
test('mergeResults matches translations by normalized ruleId when prefix missing', () => {
const staticDiags: LinterDiagnostic[] = [
{ severity: 'error', ruleId: 'eslint:no-var', message: 'Unexpected var', range: range(1) },
{ severity: 'warning', ruleId: 'eslint:no-console', message: 'console call', range: range(2) },
];
const report = mergeResults({
staticDiagnostics: staticDiags,
customRuleResults: [],
translatedDiagnostics: [
{ originalRuleId: 'no-var', translatedMessage: '应使用 let/const', translatedSuggestion: '改写成 let x = 1' },
{ originalRuleId: 'no-console', translatedMessage: '避免 console', translatedSuggestion: '' },
],
aiFindings: [],
errors: [],
degraded: false,
startTime: Date.now(),
filePath: '/test/sample.js',
language: 'javascript',
adapterIds: ['eslint'],
});
assert.strictEqual(report.linterDiagnostics[0].message, '应使用 let/const');
assert.strictEqual(report.linterDiagnostics[0].suggestion, '改写成 let x = 1');
assert.strictEqual(report.linterDiagnostics[1].message, '避免 console');
});
test('mergeResults matches translations by normalized ruleId with slash variant', () => {
const staticDiags: LinterDiagnostic[] = [
{ severity: 'error', ruleId: 'eslint:@typescript-eslint/no-unused-vars', message: 'x unused', range: range(1) },
];
const report = mergeResults({
staticDiagnostics: staticDiags,
customRuleResults: [],
translatedDiagnostics: [
{ originalRuleId: '@typescript-eslint/no-unused-vars', translatedMessage: '未使用变量', translatedSuggestion: '删除 x' },
],
aiFindings: [],
errors: [],
degraded: false,
startTime: Date.now(),
filePath: '/test/sample.ts',
language: 'typescript',
adapterIds: ['eslint'],
});
assert.strictEqual(report.linterDiagnostics[0].message, '未使用变量');
assert.strictEqual(report.linterDiagnostics[0].suggestion, '删除 x');
});
test('mergeResults prefers exact ruleId match over normalized', () => {
const staticDiags: LinterDiagnostic[] = [
{ severity: 'error', ruleId: 'eslint:no-shadow', message: 'shadow', range: range(1) },
];
const report = mergeResults({
staticDiagnostics: staticDiags,
customRuleResults: [],
translatedDiagnostics: [
{ originalRuleId: 'no-shadow', translatedMessage: '归一化命中', translatedSuggestion: '' },
{ originalRuleId: 'eslint:no-shadow', translatedMessage: '精确命中', translatedSuggestion: '' },
],
aiFindings: [],
errors: [],
degraded: false,
startTime: Date.now(),
filePath: '/test/sample.js',
language: 'javascript',
adapterIds: ['eslint'],
});
assert.strictEqual(report.linterDiagnostics[0].message, '精确命中');
});
test('mergeResults computes aiFixableLinterIndices excluding native fix and sqlfluff', () => {
const staticDiags: LinterDiagnostic[] = [
{ severity: 'error', ruleId: 'eslint:no-var', message: 'v', range: range(1), fix: { range: [0, 3], text: 'let' } },
{ severity: 'error', ruleId: 'eslint:no-undef', message: 'u', range: range(2) },
{ severity: 'error', ruleId: 'sqlfluff:AL01', message: 's', range: range(3) },
{ severity: 'error', ruleId: 'pmd:UnusedLocalVariable', message: 'p', range: range(4) },
];
const report = mergeResults({
staticDiagnostics: staticDiags,
customRuleResults: [],
translatedDiagnostics: [],
aiFindings: [],
errors: [],
degraded: false,
startTime: Date.now(),
filePath: '/test/a.java',
language: 'java',
adapterIds: ['pmd'],
aiFixAvailable: true,
});
assert.strictEqual(report.aiFixAvailable, true);
assert.deepStrictEqual(report.fixableLinterIndices, [0]);
assert.deepStrictEqual(report.aiFixableLinterIndices, [1, 3]);
});
test('mergeResults aiFixableLinterIndices empty when AI unavailable', () => {
const staticDiags: LinterDiagnostic[] = [
{ severity: 'error', ruleId: 'eslint:no-undef', message: 'u', range: range(2) },
];
const report = mergeResults({
staticDiagnostics: staticDiags,
customRuleResults: [],
translatedDiagnostics: [],
aiFindings: [],
errors: [],
degraded: false,
startTime: Date.now(),
filePath: '/test/sample.js',
language: 'javascript',
adapterIds: ['eslint'],
});
assert.deepStrictEqual(report.aiFixableLinterIndices, []);
assert.strictEqual(report.aiFixAvailable, false);
});
});