6.9 KiB
6.9 KiB
Step 20 — Phase 6.3: 测试
依赖: Step 19(构建完成)
参考设计: §16
目标
实现测试:适配器测试、配置测试、合并逻辑测试、完整流程测试。
文件变更
| # | 文件 | 操作 | 说明 |
|---|---|---|---|
| 1 | src/test/fixtures/ |
新建 | 测试用代码样本目录 |
| 2 | src/test/adapter.test.ts |
新建 | 适配器输出解析测试 |
| 3 | src/test/config.test.ts |
新建 | 配置读取测试 |
| 4 | src/test/merger.test.ts |
新建 | 多源结果合并 + 统计计算测试 |
| 5 | src/test/pipeline.test.ts |
新建 | 全链路集成测试 |
| 6 | src/test/extension.test.ts |
修改 | 替换占位测试 |
1. src/test/fixtures/ 目录
src/test/fixtures/sample.js
function test() {
var unused = 1;
console.log('debug');
return "hello world";
}
src/test/fixtures/sample.css
.hello { color: black; background: #FFF; }
#test { margin: 0px; }
src/test/fixtures/Sample.java
public class Sample {
public void test() {
String password = "admin123";
System.out.println("debug");
System.out.println("debug");
}
}
2. src/test/adapter.test.ts
import * as assert from 'assert';
import * as vscode from 'vscode';
import { ESLintAdapter } from '../adapters/eslint';
import { StylelintAdapter } from '../adapters/stylelint';
suite('Adapter Tests', () => {
test('ESLintAdapter has correct id and languages', () => {
const adapter = new ESLintAdapter();
assert.strictEqual(adapter.id, 'eslint');
assert.deepStrictEqual(adapter.supportedLanguages, ['javascript', 'typescript']);
});
test('StylelintAdapter has correct id and languages', () => {
const adapter = new StylelintAdapter();
assert.strictEqual(adapter.id, 'stylelint');
assert.deepStrictEqual(adapter.supportedLanguages, ['css']);
});
test('ESLintAdapter check returns AdapterResult structure', async () => {
const adapter = new ESLintAdapter();
if (!adapter.isAvailable()) { return; }
const doc = await vscode.workspace.openTextDocument({
content: 'const x = 1;\nconsole.log(x);\n',
language: 'javascript',
});
const result = await adapter.check(doc, __dirname);
assert.ok(result.status === 'ok' || result.status === 'tool-unavailable');
assert.ok(Array.isArray(result.diagnostics));
});
});
3. src/test/config.test.ts
import * as assert from 'assert';
import { getAIConfig } from '../config/ai';
import { getLinterConfig } from '../config/linter';
import { getFixerConfig } from '../config/fixer';
suite('Config Tests', () => {
test('getAIConfig returns default values', () => {
const config = getAIConfig();
assert.strictEqual(config.provider, 'deepseek');
assert.strictEqual(config.model, 'deepseek-chat');
assert.strictEqual(config.temperature, 0.2);
assert.strictEqual(config.timeout, 300);
assert.strictEqual(config.outputLanguage, 'zh-CN');
});
test('getLinterConfig returns default language map', () => {
const config = getLinterConfig();
assert.strictEqual(config.languageMap.javascript, 'eslint');
assert.strictEqual(config.languageMap.java, 'pmd');
assert.strictEqual(config.languageMap.css, 'stylelint');
assert.strictEqual(config.languageMap.jsp, 'jsp');
});
test('getFixerConfig returns default values', () => {
const config = getFixerConfig();
assert.strictEqual(config.contextLines, 5);
});
});
4. src/test/merger.test.ts
import * as assert from 'assert';
import { mergeResults, MergedReport } from '../merger/merger';
import { CustomRuleResult, AIFinding } from '../ai/schema';
import { LinterDiagnostic } from '../types';
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);
});
});
5. src/test/pipeline.test.ts
import * as assert from 'assert';
import * as vscode from 'vscode';
import * as path from 'path';
import { ESLintAdapter } from '../adapters/eslint';
import { mergeResults } from '../merger/merger';
suite('Pipeline Tests', () => {
test('Full pipeline: linter check + merge', async () => {
const adapter = new ESLintAdapter();
if (!adapter.isAvailable()) { return; }
const doc = await vscode.workspace.openTextDocument({
content: 'var x = 1;\nvar y = 2;\n',
language: 'javascript',
});
const staticResult = await adapter.check(doc, __dirname);
assert.ok(staticResult.status === 'ok');
const report = mergeResults({
staticDiagnostics: staticResult.diagnostics,
customRuleResults: [],
translatedDiagnostics: [],
aiFindings: [],
errors: [],
degraded: false,
startTime: Date.now(),
filePath: 'virtual-doc',
language: 'javascript',
adapterIds: ['eslint'],
});
assert.ok(typeof report.duration === 'number');
assert.ok(typeof report.linterCount === 'number');
assert.strictEqual(report.language, 'javascript');
});
});
验证命令
npm test # compile + lint + test
# 或
npm run compile && npm run lint && npm run test
验收
- 所有测试文件创建完成
- 测试夹具文件(fixtures)就位
npm run compile通过npm run lint通过npm test通过