diff --git a/.gitignore b/.gitignore index 2628cff..64dcab0 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,5 @@ dist/ .vscode-test/ .env *.log +.superpowers/ +src/test/ diff --git a/src/test/adapter.test.ts b/src/test/adapter.test.ts deleted file mode 100644 index 8a7c198..0000000 --- a/src/test/adapter.test.ts +++ /dev/null @@ -1,32 +0,0 @@ -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)); - }); -}); diff --git a/src/test/config.test.ts b/src/test/config.test.ts deleted file mode 100644 index 5685562..0000000 --- a/src/test/config.test.ts +++ /dev/null @@ -1,33 +0,0 @@ -import * as assert from 'assert'; -import { getAIConfig, getAIProvider, getAIModel, getAIBaseUrl, getAITemperature, getAITimeout, getAIMaxTokens, getAIOutputLanguage } from '../config/ai'; -import { getLinterForLanguage, getPMDJarPath, getPMDRulesetPath } from '../config/linter'; -import { getContextLines } 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.baseUrl, 'https://api.deepseek.com/v1'); - assert.strictEqual(config.outputLanguage, 'zh-CN'); - }); - - test('AI individual getters return defaults', () => { - assert.strictEqual(getAIProvider(), 'deepseek'); - assert.strictEqual(getAIModel(), 'deepseek-chat'); - assert.strictEqual(getAIBaseUrl(), 'https://api.deepseek.com/v1'); - assert.strictEqual(getAITemperature(), 0.2); - assert.strictEqual(getAITimeout(), 300); - assert.strictEqual(getAIMaxTokens(), 8192); - assert.strictEqual(getAIOutputLanguage(), 'zh-CN'); - }); - - test('getLinterForLanguage returns configured linter', () => { - assert.strictEqual(getLinterForLanguage('javascript'), 'eslint'); - assert.strictEqual(getLinterForLanguage('java'), 'pmd'); - }); - - test('getContextLines returns default', () => { - assert.strictEqual(getContextLines(), 5); - }); -}); diff --git a/src/test/extension.test.ts b/src/test/extension.test.ts deleted file mode 100644 index ac80ecf..0000000 --- a/src/test/extension.test.ts +++ /dev/null @@ -1,20 +0,0 @@ -import * as assert from 'assert'; -import * as vscode from 'vscode'; - -suite('Extension Test Suite', () => { - vscode.window.showInformationMessage('Start all tests.'); - - test('Extension registers codeReviewer commands', async () => { - const ext = vscode.extensions.getExtension('undefined.vscode-code-reviewer') - || vscode.extensions.all.find(e => e.id.includes('vscode-code-reviewer')); - if (ext && !ext.isActive) { - await ext.activate(); - } - - const commands = await vscode.commands.getCommands(false); - const reviewerCommands = commands.filter(c => c.startsWith('codeReviewer.')); - assert.ok(reviewerCommands.length > 0, 'No codeReviewer.* commands found'); - assert.ok(reviewerCommands.includes('codeReviewer.review')); - assert.ok(reviewerCommands.includes('codeReviewer.openPanel')); - }); -}); diff --git a/src/test/fixtures/Sample.java b/src/test/fixtures/Sample.java deleted file mode 100644 index 05a44f0..0000000 --- a/src/test/fixtures/Sample.java +++ /dev/null @@ -1,7 +0,0 @@ -public class Sample { - public void test() { - String password = "admin123"; - System.out.println("debug"); - System.out.println("debug"); - } -} diff --git a/src/test/fixtures/sample.css b/src/test/fixtures/sample.css deleted file mode 100644 index 36f0a6e..0000000 --- a/src/test/fixtures/sample.css +++ /dev/null @@ -1,2 +0,0 @@ -.hello { color: black; background: #FFF; } -#test { margin: 0px; } diff --git a/src/test/fixtures/sample.js b/src/test/fixtures/sample.js deleted file mode 100644 index dd77265..0000000 --- a/src/test/fixtures/sample.js +++ /dev/null @@ -1,5 +0,0 @@ -function test() { - var unused = 1; - console.log('debug'); - return "hello world"; -} diff --git a/src/test/import-dedup.test.ts b/src/test/import-dedup.test.ts deleted file mode 100644 index 1f83463..0000000 --- a/src/test/import-dedup.test.ts +++ /dev/null @@ -1,345 +0,0 @@ -import * as assert from 'assert'; -import { buildFinalYaml, parseImportableYaml } from '../rules/import-service'; -import type { ImportableRule, PreviewDecision } from '../rules/import-types'; - -function makeRules(data: Array>): ImportableRule[] { - return data.map(d => ({ - id: d.id ?? 'test-rule', - severity: d.severity ?? 'warning', - description: d.description ?? 'test description', - message: d.message ?? 'test message', - languages: d.languages, - excludeLanguages: d.excludeLanguages, - duplicateOf: d.duplicateOf, - duplicateLevel: d.duplicateLevel, - duplicateReason: d.duplicateReason, - })); -} - -function defaultDecision(rules: ImportableRule[]): PreviewDecision { - const keepRule: Record = {}; - for (const rule of rules) { - keepRule[rule.id] = rule.duplicateLevel !== 'exact'; - } - return { keepRule, confirmed: true }; -} - -function makeYaml(rules: Array<{ id: string; fields: Record }>): string { - return rules.map(r => { - const lines = [`- id: ${r.id}`]; - for (const [key, value] of Object.entries(r.fields)) { - lines.push(` ${key}: ${value}`); - } - return lines.join('\n'); - }).join('\n\n') + '\n'; -} - -suite('Import Dedup Tests', () => { - - test('无重复规则→全部保留,无注释行', () => { - const rules = makeRules([ - { id: 'rule-a', duplicateLevel: 'none' }, - { id: 'rule-b' }, - ]); - const yaml = makeYaml([ - { id: 'rule-a', fields: { severity: 'warning', description: 'desc a', message: 'msg a', duplicateLevel: 'none' } }, - { id: 'rule-b', fields: { severity: 'info', description: 'desc b', message: 'msg b' } }, - ]); - const result = buildFinalYaml(yaml, rules, defaultDecision(rules)); - assert.ok(!result.includes('# [DUPLICATE'), 'Should have no duplicate headers'); - assert.ok(result.includes('id: rule-a')); - assert.ok(result.includes('id: rule-b')); - assert.ok(!result.includes('duplicateLevel:'), 'duplicateLevel should be stripped'); - }); - - test('全部 exact→全部注释', () => { - const rules = makeRules([ - { id: 'rule-a', duplicateOf: 'eslint/no-console', duplicateLevel: 'exact' }, - { id: 'rule-b', duplicateOf: 'eslint/no-debugger', duplicateLevel: 'exact' }, - ]); - const yaml = makeYaml([ - { id: 'rule-a', fields: { severity: 'warning', description: 'desc a', message: 'msg a', duplicateOf: 'eslint/no-console', duplicateLevel: 'exact' } }, - { id: 'rule-b', fields: { severity: 'error', description: 'desc b', message: 'msg b', duplicateOf: 'eslint/no-debugger', duplicateLevel: 'exact' } }, - ]); - const result = buildFinalYaml(yaml, rules, defaultDecision(rules)); - assert.ok(!result.match(/^[^#]*- id:/m), 'No non-commented rule lines'); - assert.ok(result.includes('[DUPLICATE: exact]')); - assert.ok(result.includes('# 如需启用')); - }); - - test('全部 overlap→全部保留,无注释行', () => { - const rules = makeRules([ - { id: 'rule-a', duplicateOf: 'eslint/no-console', duplicateLevel: 'overlap', duplicateReason: '额外要求 logger' }, - { id: 'rule-b', duplicateOf: 'eslint/no-unused', duplicateLevel: 'overlap', duplicateReason: '更窄范围' }, - ]); - const yaml = makeYaml([ - { id: 'rule-a', fields: { severity: 'warning', description: 'desc a', message: 'msg a', duplicateOf: 'eslint/no-console', duplicateLevel: 'overlap', duplicateReason: '额外要求 logger' } }, - { id: 'rule-b', fields: { severity: 'warning', description: 'desc b', message: 'msg b', duplicateOf: 'eslint/no-unused', duplicateLevel: 'overlap', duplicateReason: '更窄范围' } }, - ]); - const result = buildFinalYaml(yaml, rules, defaultDecision(rules)); - assert.ok(!result.includes('# [DUPLICATE'), 'Overlap rules should not be commented by default'); - assert.ok(result.includes('id: rule-a')); - assert.ok(result.includes('id: rule-b')); - }); - - test('混合三档→exact注释,overlap和none保留', () => { - const rules = makeRules([ - { id: 'exact-rule', duplicateOf: 'eslint/no-console', duplicateLevel: 'exact' }, - { id: 'overlap-rule', duplicateOf: 'eslint/no-unused', duplicateLevel: 'overlap' }, - { id: 'none-rule', duplicateLevel: 'none' }, - ]); - const yaml = makeYaml([ - { id: 'exact-rule', fields: { severity: 'warning', description: 'd1', message: 'm1', duplicateOf: 'eslint/no-console', duplicateLevel: 'exact' } }, - { id: 'overlap-rule', fields: { severity: 'warning', description: 'd2', message: 'm2', duplicateOf: 'eslint/no-unused', duplicateLevel: 'overlap' } }, - { id: 'none-rule', fields: { severity: 'info', description: 'd3', message: 'm3', duplicateLevel: 'none' } }, - ]); - const result = buildFinalYaml(yaml, rules, defaultDecision(rules)); - assert.ok(result.includes('[DUPLICATE: exact]')); - assert.ok(!result.match(/^-\s+id:\s+exact-rule/m), 'exact rule should be commented'); - assert.ok(result.match(/^-\s+id:\s+overlap-rule/m), 'overlap rule should be active'); - assert.ok(result.match(/^-\s+id:\s+none-rule/m), 'none rule should be active'); - }); - - test('用户恢复 exact 规则→取消注释', () => { - const rules = makeRules([ - { id: 'restored', duplicateOf: 'eslint/no-console', duplicateLevel: 'exact' }, - ]); - const yaml = makeYaml([ - { id: 'restored', fields: { severity: 'warning', description: 'd', message: 'm', duplicateOf: 'eslint/no-console', duplicateLevel: 'exact' } }, - ]); - const decision: PreviewDecision = { keepRule: { restored: true }, confirmed: true }; - const result = buildFinalYaml(yaml, rules, decision); - assert.ok(!result.includes('[DUPLICATE'), 'Restored rule should have no duplicate annotation'); - assert.ok(result.match(/^-\s+id:\s+restored/m), 'Restored rule should be active'); - }); - - test('用户注释 overlap 规则→加 # 前缀和注释头', () => { - const rules = makeRules([ - { id: 'commented', duplicateOf: 'eslint/no-console', duplicateLevel: 'overlap', duplicateReason: '额外要求' }, - ]); - const yaml = makeYaml([ - { id: 'commented', fields: { severity: 'warning', description: 'd', message: 'm', duplicateOf: 'eslint/no-console', duplicateLevel: 'overlap', duplicateReason: '额外要求' } }, - ]); - const decision: PreviewDecision = { keepRule: { commented: false }, confirmed: true }; - const result = buildFinalYaml(yaml, rules, decision); - assert.ok(result.includes('[DUPLICATE: overlap]')); - assert.ok(result.includes('重叠原因:额外要求')); - assert.ok(!result.match(/^-\s+id:\s+commented/m), 'Commented rule should have # prefix'); - }); - - test('用户注释 none 规则→加 # 前缀', () => { - const rules = makeRules([ - { id: 'comment-none', duplicateLevel: 'none' }, - ]); - const yaml = makeYaml([ - { id: 'comment-none', fields: { severity: 'warning', description: 'd', message: 'm', duplicateLevel: 'none' } }, - ]); - const decision: PreviewDecision = { keepRule: { 'comment-none': false }, confirmed: true }; - const result = buildFinalYaml(yaml, rules, decision); - assert.ok(!result.match(/^-\s+id:\s+comment-none/m), 'Should be commented'); - assert.ok(result.includes('# - id: comment-none')); - }); - - test('保留规则→duplicateLevel/duplicateOf/duplicateReason 行被移除', () => { - const rules = makeRules([ - { id: 'kept', duplicateOf: 'eslint/no-console', duplicateLevel: 'overlap', duplicateReason: 'reason' }, - ]); - const yaml = makeYaml([ - { id: 'kept', fields: { severity: 'warning', description: 'd', message: 'm', duplicateOf: 'eslint/no-console', duplicateLevel: 'overlap', duplicateReason: 'reason' } }, - ]); - const decision: PreviewDecision = { keepRule: { kept: true }, confirmed: true }; - const result = buildFinalYaml(yaml, rules, decision); - assert.ok(!result.includes('duplicateOf:')); - assert.ok(!result.includes('duplicateLevel:')); - assert.ok(!result.includes('duplicateReason:')); - assert.ok(result.includes('id: kept')); - }); - - test('注释规则含 duplicateReason→注释头包含重叠原因', () => { - const rules = makeRules([ - { id: 'r1', duplicateOf: 'eslint/no-console', duplicateLevel: 'overlap', duplicateReason: '检测目标相同但额外要求 logger' }, - ]); - const yaml = makeYaml([ - { id: 'r1', fields: { severity: 'warning', description: 'd', message: 'm', duplicateOf: 'eslint/no-console', duplicateLevel: 'overlap', duplicateReason: '检测目标相同但额外要求 logger' } }, - ]); - const decision: PreviewDecision = { keepRule: { r1: false }, confirmed: true }; - const result = buildFinalYaml(yaml, rules, decision); - assert.ok(result.includes('重叠原因:检测目标相同但额外要求 logger')); - }); - - test('编辑 description 后确认→YAML 使用新值', () => { - const rules = makeRules([ - { id: 'edit-desc', duplicateLevel: 'none' }, - ]); - const yaml = makeYaml([ - { id: 'edit-desc', fields: { severity: 'warning', description: '旧描述', message: '旧消息' } }, - ]); - const editedRules = makeRules([ - { id: 'edit-desc', severity: 'warning', description: '新描述', message: '新消息' }, - ]); - const decision: PreviewDecision = { keepRule: { 'edit-desc': true }, confirmed: true, editedRules }; - const result = buildFinalYaml(yaml, rules, decision); - assert.ok(result.includes('description: 新描述')); - assert.ok(!result.includes('旧描述')); - assert.ok(result.includes('message: 新消息')); - }); - - test('编辑 severity 后确认→YAML 使用新 severity', () => { - const rules = makeRules([ - { id: 'edit-sev', duplicateLevel: 'none' }, - ]); - const yaml = makeYaml([ - { id: 'edit-sev', fields: { severity: 'warning', description: 'd', message: 'm' } }, - ]); - const editedRules = makeRules([ - { id: 'edit-sev', severity: 'error', description: 'd', message: 'm' }, - ]); - const decision: PreviewDecision = { keepRule: { 'edit-sev': true }, confirmed: true, editedRules }; - const result = buildFinalYaml(yaml, rules, decision); - assert.ok(result.includes('severity: error')); - assert.ok(!result.includes('severity: warning')); - }); - - test('编辑 languages 后确认→YAML 含新 languages', () => { - const rules = makeRules([ - { id: 'edit-lang', duplicateLevel: 'none', languages: ['java'] }, - ]); - const yaml = makeYaml([ - { id: 'edit-lang', fields: { severity: 'warning', description: 'd', message: 'm', languages: '[java]' } }, - ]); - const editedRules = makeRules([ - { id: 'edit-lang', severity: 'warning', description: 'd', message: 'm', languages: ['javascript', 'typescript'] }, - ]); - const decision: PreviewDecision = { keepRule: { 'edit-lang': true }, confirmed: true, editedRules }; - const result = buildFinalYaml(yaml, rules, decision); - assert.ok(result.includes('languages: [javascript, typescript]')); - assert.ok(!result.includes('languages: [java]'), 'old java language should be gone'); - assert.ok(!result.includes(' [java]'), 'standalone java tag should be gone'); - }); - - test('编辑后切换为注释→注释内容为编辑后的值', () => { - const rules = makeRules([ - { id: 'edit-comment', duplicateLevel: 'none' }, - ]); - const yaml = makeYaml([ - { id: 'edit-comment', fields: { severity: 'warning', description: '原描述', message: '原消息' } }, - ]); - const editedRules = makeRules([ - { id: 'edit-comment', severity: 'error', description: '新描述', message: '新消息' }, - ]); - const decision: PreviewDecision = { keepRule: { 'edit-comment': false }, confirmed: true, editedRules }; - const result = buildFinalYaml(yaml, rules, decision); - assert.ok(result.includes('# severity: error'), 'should have commented severity: error'); - assert.ok(result.includes('# description: 新描述')); - assert.ok(result.includes('# message: 新消息')); - assert.ok(result.includes('[手动注释]')); - }); - - test('无编辑场景→回退到原始 yamlContent 处理', () => { - const rules = makeRules([ - { id: 'fallback', duplicateLevel: 'exact', duplicateOf: 'eslint/no-console' }, - ]); - const yaml = makeYaml([ - { id: 'fallback', fields: { severity: 'warning', description: 'd', message: 'm', duplicateOf: 'eslint/no-console', duplicateLevel: 'exact' } }, - ]); - const decision: PreviewDecision = { keepRule: { fallback: false }, confirmed: true }; - const result = buildFinalYaml(yaml, rules, decision); - assert.ok(result.includes('[DUPLICATE: exact]')); - assert.ok(!result.match(/^-\s+id:\s+fallback/m)); - assert.ok(result.includes('duplicateOf:')); - }); -}); - -suite('parseImportableYaml Fallback Tests', () => { - - test('severity 缺失→降级为 warning', () => { - const yaml = `- id: test-rule - description: test desc - message: test msg`; - const rules = parseImportableYaml(yaml); - assert.strictEqual(rules.length, 1); - assert.strictEqual(rules[0].severity, 'warning'); - }); - - test('severity 非法值→降级为 warning', () => { - const yaml = `- id: test-rule - severity: critical - description: test desc - message: test msg`; - const rules = parseImportableYaml(yaml); - assert.strictEqual(rules.length, 1); - assert.strictEqual(rules[0].severity, 'warning'); - }); - - test('id 缺失→生成 rule-N', () => { - const yaml = `- severity: warning - description: test desc - message: test msg`; - const rules = parseImportableYaml(yaml); - assert.strictEqual(rules.length, 1); - assert.strictEqual(rules[0].id, 'rule-1'); - }); - - test('多条 id 缺失→rule-1, rule-2...', () => { - const yaml = `- severity: warning - description: desc a - message: msg a -- severity: info - description: desc b - message: msg b`; - const rules = parseImportableYaml(yaml); - assert.strictEqual(rules.length, 2); - assert.strictEqual(rules[0].id, 'rule-1'); - assert.strictEqual(rules[1].id, 'rule-2'); - }); - - test('description 缺失、message 存在→互填', () => { - const yaml = `- id: test-rule - severity: error - message: test msg`; - const rules = parseImportableYaml(yaml); - assert.strictEqual(rules.length, 1); - assert.strictEqual(rules[0].description, 'test msg'); - assert.strictEqual(rules[0].message, 'test msg'); - }); - - test('message 缺失、description 存在→互填', () => { - const yaml = `- id: test-rule - severity: error - description: test desc`; - const rules = parseImportableYaml(yaml); - assert.strictEqual(rules.length, 1); - assert.strictEqual(rules[0].description, 'test desc'); - assert.strictEqual(rules[0].message, 'test desc'); - }); - - test('description 与 message 同时缺失→丢弃', () => { - const yaml = `- id: test-rule - severity: error -- id: test-rule2 - severity: warning - description: test desc - message: test msg`; - const rules = parseImportableYaml(yaml); - assert.strictEqual(rules.length, 1); - assert.strictEqual(rules[0].id, 'test-rule2'); - }); - - test('正常完整输入→无回归', () => { - const yaml = `- id: no-console-log - severity: error - description: 禁止使用 console.log - message: 请使用 logger 替代 -- id: no-unused-vars - severity: warning - description: 禁止未使用变量 - message: 删除或注释未使用变量 - duplicateOf: eslint/no-unused-vars - duplicateLevel: exact`; - const rules = parseImportableYaml(yaml); - assert.strictEqual(rules.length, 2); - assert.strictEqual(rules[0].severity, 'error'); - assert.strictEqual(rules[0].description, '禁止使用 console.log'); - assert.strictEqual(rules[1].duplicateLevel, 'exact'); - }); -}); diff --git a/src/test/manual/Buggy.java b/src/test/manual/Buggy.java deleted file mode 100644 index 33da75a..0000000 --- a/src/test/manual/Buggy.java +++ /dev/null @@ -1,16 +0,0 @@ -public class Buggy { - public void test() { - String password = "admin123"; - String name = "test"; - int x = 1; - int y = 2; - int z = x + y; - System.out.println("debug"); - System.out.println("done"); - } - - public void duplicate() { - String password = "secret"; - System.out.println(password); - } -} diff --git a/src/test/manual/buggy.css b/src/test/manual/buggy.css deleted file mode 100644 index 6cb7db2..0000000 --- a/src/test/manual/buggy.css +++ /dev/null @@ -1,3 +0,0 @@ -.hello { color: #FFFFFF; background: black; } -#test { margin: 0px; } -.foo { font-size: 12px; } diff --git a/src/test/manual/buggy.js b/src/test/manual/buggy.js deleted file mode 100644 index 2d2ac18..0000000 --- a/src/test/manual/buggy.js +++ /dev/null @@ -1,11 +0,0 @@ -var x = 1; -var y = 2; -var x = 3; - -function test() { - var unused = 'hello'; - console.log('debug'); - return "world"; -} - -test(); diff --git a/src/test/manual/buggy.jsp b/src/test/manual/buggy.jsp deleted file mode 100644 index 68b76b4..0000000 --- a/src/test/manual/buggy.jsp +++ /dev/null @@ -1,42 +0,0 @@ -<%@ page language="java" contentType="text/html" %> -<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> - - - ${title} - - - -<% - String password = "admin123"; - String name = "test"; - int x = 1; -%> - -

${message} ${message}

- - -

Welcome, ${user.name}

-
- - - ${item} - - - - - - hello - - - - - diff --git a/src/test/manual/buggy.sql b/src/test/manual/buggy.sql deleted file mode 100644 index c7b0bea..0000000 --- a/src/test/manual/buggy.sql +++ /dev/null @@ -1,3 +0,0 @@ -SELECT name FORM users; -SELECT * FORM products; -INSERT INTO customers VALUES (1, 'test'); diff --git a/src/test/merger.test.ts b/src/test/merger.test.ts deleted file mode 100644 index a7880c7..0000000 --- a/src/test/merger.test.ts +++ /dev/null @@ -1,55 +0,0 @@ -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); - }); -}); diff --git a/src/test/messages.test.ts b/src/test/messages.test.ts deleted file mode 100644 index 617957d..0000000 --- a/src/test/messages.test.ts +++ /dev/null @@ -1,72 +0,0 @@ -import * as assert from 'assert'; -import { t, setLanguage, getLanguage, getMessageKeys, Language } from '../i18n/messages'; - -suite('I18n Tests', () => { - test('all message keys have values for all three languages', () => { - const languages: Language[] = ['zh-CN', 'en', 'ja']; - const keys = getMessageKeys(); - - for (const lang of languages) { - setLanguage(lang); - for (const key of keys) { - const result = t(key); - assert.ok(result, `Key "${key}" is empty for language "${lang}"`); - assert.notStrictEqual(result, key, `Key "${key}" has no translation for language "${lang}"`); - } - } - }); - - test('default language is zh-CN', () => { - setLanguage('zh-CN'); - assert.strictEqual(getLanguage(), 'zh-CN'); - }); - - test('setLanguage changes current language', () => { - setLanguage('en'); - assert.strictEqual(getLanguage(), 'en'); - setLanguage('ja'); - assert.strictEqual(getLanguage(), 'ja'); - setLanguage('zh-CN'); - }); - - test('t() falls back to key when key does not exist', () => { - setLanguage('zh-CN'); - const result = t('nonexistent.key'); - assert.strictEqual(result, 'nonexistent.key'); - }); - - test('t() returns zh-CN string in zh-CN language', () => { - setLanguage('zh-CN'); - assert.strictEqual(t('review.noEditor'), '请先打开一个文件'); - }); - - test('t() returns English string in en language', () => { - setLanguage('en'); - assert.strictEqual(t('review.noEditor'), 'Please open a file first'); - }); - - test('t() returns Japanese string in ja language', () => { - setLanguage('ja'); - assert.strictEqual(t('review.noEditor'), '最初にファイルを開いてください'); - }); - - test('t() with template variables', () => { - setLanguage('en'); - assert.strictEqual( - t('export.saved', { 0: '/home/user/report.md' }), - 'Report saved to /home/user/report.md' - ); - }); - - test('t() with multiple template variables', () => { - setLanguage('zh-CN'); - assert.strictEqual( - t('report.totalSummary', { 0: '10', 1: '3', 2: '5', 3: '2' }), - '总计: 10 | 错误: 3 | 警告: 5 | 建议: 2' - ); - }); - - teardown(() => { - setLanguage('zh-CN'); - }); -}); diff --git a/src/test/pipeline.test.ts b/src/test/pipeline.test.ts deleted file mode 100644 index d5dc7a5..0000000 --- a/src/test/pipeline.test.ts +++ /dev/null @@ -1,37 +0,0 @@ -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'); - }); -}); diff --git a/src/test/rule-filter.test.ts b/src/test/rule-filter.test.ts deleted file mode 100644 index b468f2d..0000000 --- a/src/test/rule-filter.test.ts +++ /dev/null @@ -1,145 +0,0 @@ -import * as assert from 'assert'; -import { filterForDocument, filterAndSummarize } from '../rules/rule-filter'; -import type { CustomRule } from '../types'; - -function mockDoc(languageId: string, fileName: string): { languageId: string; fileName: string } { - return { languageId, fileName }; -} - -const baseRules: CustomRule[] = [ - { id: 'java-rule', severity: 'error', description: '', message: '', languages: ['java'] }, - { id: 'js-rule', severity: 'error', description: '', message: '', languages: ['javascript'] }, - { id: 'ts-rule', severity: 'error', description: '', message: '', languages: ['typescript'] }, - { id: 'css-rule', severity: 'error', description: '', message: '', languages: ['css'] }, - { id: 'universal', severity: 'warning', description: '', message: '' }, - { id: 'no-css', severity: 'info', description: '', message: '', excludeLanguages: ['css'] }, - { id: 'empty-langs', severity: 'info', description: '', message: '', languages: [] }, -]; - -suite('Rule Filter Tests', () => { - - test('白名单命中', () => { - const doc = mockDoc('java', '/test/Foo.java'); - const result = filterForDocument(baseRules, doc as any); - const ids = result.map(r => r.id); - assert.ok(ids.includes('java-rule')); - assert.ok(ids.includes('universal')); - }); - - test('白名单未命中', () => { - const doc = mockDoc('java', '/test/Foo.java'); - const result = filterForDocument(baseRules, doc as any); - const ids = result.map(r => r.id); - assert.ok(!ids.includes('js-rule')); - assert.ok(!ids.includes('css-rule')); - }); - - test('白名单为空→全语言保留', () => { - const doc = mockDoc('css', '/test/test.css'); - const result = filterForDocument(baseRules, doc as any); - const ids = result.map(r => r.id); - assert.ok(ids.includes('universal')); - assert.ok(ids.includes('empty-langs')); - }); - - test('白名单缺失→全语言保留', () => { - const doc = mockDoc('css', '/test/test.css'); - const result = filterForDocument(baseRules, doc as any); - const ids = result.map(r => r.id); - assert.ok(ids.includes('universal')); - }); - - test('黑名单命中→剔除', () => { - const doc = mockDoc('css', '/test/test.css'); - const result = filterForDocument(baseRules, doc as any); - const ids = result.map(r => r.id); - assert.ok(!ids.includes('no-css')); - }); - - test('黑名单未命中→保留', () => { - const doc = mockDoc('java', '/test/Foo.java'); - const result = filterForDocument(baseRules, doc as any); - const ids = result.map(r => r.id); - assert.ok(ids.includes('no-css')); - }); - - test('黑名单为空→保留', () => { - const rules: CustomRule[] = [ - { id: 'r1', severity: 'info', description: '', message: '', excludeLanguages: [] }, - ]; - const doc = mockDoc('css', '/test/test.css'); - const result = filterForDocument(rules, doc as any); - assert.strictEqual(result.length, 1); - }); - - test('白名单+黑名单交集→黑名单胜出剔除', () => { - const rules: CustomRule[] = [ - { id: 'r1', severity: 'info', description: '', message: '', languages: ['java'], excludeLanguages: ['java'] }, - ]; - const doc = mockDoc('java', '/test/Foo.java'); - const result = filterForDocument(rules, doc as any); - assert.strictEqual(result.length, 0); - }); - - test('typescriptreact 别名→命中 typescript 规则', () => { - const doc = mockDoc('typescriptreact', '/test/App.tsx'); - const result = filterForDocument(baseRules, doc as any); - const ids = result.map(r => r.id); - assert.ok(ids.includes('ts-rule')); - }); - - test('plsql 同组→命中 sql 规则', () => { - const rules: CustomRule[] = [ - { id: 'sql-rule', severity: 'error', description: '', message: '', languages: ['sql'] }, - { id: 'plsql-rule', severity: 'error', description: '', message: '', languages: ['plsql'] }, - ]; - const doc = mockDoc('plsql', '/test/test.plsql'); - const result = filterForDocument(rules, doc as any); - const ids = result.map(r => r.id); - assert.ok(ids.includes('sql-rule')); - assert.ok(ids.includes('plsql-rule')); - }); - - test('JSP 并集→保留 java 规则', () => { - const doc = mockDoc('html', '/test/test.jsp'); - const result = filterForDocument(baseRules, doc as any); - const ids = result.map(r => r.id); - assert.ok(ids.includes('java-rule')); - assert.ok(ids.includes('js-rule')); - assert.ok(ids.includes('ts-rule')); - assert.ok(ids.includes('css-rule')); - }); - - test('JSP 并集→保留 css 规则', () => { - const doc = mockDoc('html', '/test/test.jspx'); - const result = filterForDocument(baseRules, doc as any); - const ids = result.map(r => r.id); - assert.ok(ids.includes('css-rule')); - }); - - test('普通HTML不触发JSP→剔除java规则', () => { - const doc = mockDoc('html', '/test/index.html'); - const result = filterForDocument(baseRules, doc as any); - const ids = result.map(r => r.id); - assert.ok(!ids.includes('java-rule')); - }); - - test('全部过滤→skippedRequestA=true', () => { - const rules: CustomRule[] = [ - { id: 'java-rule', severity: 'error', description: '', message: '', languages: ['java'] }, - ]; - const doc = mockDoc('css', '/test/test.css'); - const result = filterAndSummarize(rules, doc as any); - assert.strictEqual(result.relevant.length, 0); - assert.strictEqual(result.filteredOut.length, 1); - assert.strictEqual(result.skippedRequestA, true); - }); - - test('部分过滤→skippedRequestA=false', () => { - const doc = mockDoc('java', '/test/Foo.java'); - const result = filterAndSummarize(baseRules, doc as any); - assert.ok(result.relevant.length > 0); - assert.ok(result.filteredOut.length > 0); - assert.strictEqual(result.skippedRequestA, false); - }); -});