253 lines
6.9 KiB
Markdown
253 lines
6.9 KiB
Markdown
# 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`
|
|
|
|
```javascript
|
|
function test() {
|
|
var unused = 1;
|
|
console.log('debug');
|
|
return "hello world";
|
|
}
|
|
```
|
|
|
|
### `src/test/fixtures/sample.css`
|
|
|
|
```css
|
|
.hello { color: black; background: #FFF; }
|
|
#test { margin: 0px; }
|
|
```
|
|
|
|
### `src/test/fixtures/Sample.java`
|
|
|
|
```java
|
|
public class Sample {
|
|
public void test() {
|
|
String password = "admin123";
|
|
System.out.println("debug");
|
|
System.out.println("debug");
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 2. `src/test/adapter.test.ts`
|
|
|
|
```typescript
|
|
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`
|
|
|
|
```typescript
|
|
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`
|
|
|
|
```typescript
|
|
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`
|
|
|
|
```typescript
|
|
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');
|
|
});
|
|
});
|
|
```
|
|
|
|
---
|
|
|
|
## 验证命令
|
|
|
|
```bash
|
|
npm test # compile + lint + test
|
|
# 或
|
|
npm run compile && npm run lint && npm run test
|
|
```
|
|
|
|
---
|
|
|
|
## 验收
|
|
|
|
- [ ] 所有测试文件创建完成
|
|
- [ ] 测试夹具文件(fixtures)就位
|
|
- [ ] `npm run compile` 通过
|
|
- [ ] `npm run lint` 通过
|
|
- [ ] `npm test` 通过
|