claudecode-omc 4.8.0 → 4.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/plugin.json +1 -1
- package/bridge/cli.cjs +303 -7669
- package/dist/cli/index.js +0 -20
- package/dist/cli/index.js.map +1 -1
- package/dist/hud/usage-api.d.ts.map +1 -1
- package/dist/hud/usage-api.js +14 -0
- package/dist/hud/usage-api.js.map +1 -1
- package/dist/testing/cli/commands.d.ts +0 -11
- package/dist/testing/cli/commands.d.ts.map +1 -1
- package/dist/testing/cli/commands.js +0 -22
- package/dist/testing/cli/commands.js.map +1 -1
- package/docs/testing/IMPLEMENTATION.md +804 -0
- package/package.json +1 -1
- package/dist/testing/integrations/promptfoo/config-generator.d.ts +0 -5
- package/dist/testing/integrations/promptfoo/config-generator.d.ts.map +0 -1
- package/dist/testing/integrations/promptfoo/config-generator.js +0 -44
- package/dist/testing/integrations/promptfoo/config-generator.js.map +0 -1
- package/dist/testing/integrations/promptfoo/types.d.ts +0 -36
- package/dist/testing/integrations/promptfoo/types.d.ts.map +0 -1
- package/dist/testing/integrations/promptfoo/types.js +0 -2
- package/dist/testing/integrations/promptfoo/types.js.map +0 -1
|
@@ -0,0 +1,804 @@
|
|
|
1
|
+
# LLM Testing System - 实现文档
|
|
2
|
+
|
|
3
|
+
> 本文档为 oh-my-claudecode LLM 测试系统的完整实现参考,供其他项目(如 oh-my-codex)快速集成使用。
|
|
4
|
+
|
|
5
|
+
## 目录
|
|
6
|
+
|
|
7
|
+
- [系统概述](#系统概述)
|
|
8
|
+
- [架构设计](#架构设计)
|
|
9
|
+
- [核心模块](#核心模块)
|
|
10
|
+
- [集成指南](#集成指南)
|
|
11
|
+
- [API 参考](#api-参考)
|
|
12
|
+
- [CLI 命令](#cli-命令)
|
|
13
|
+
- [扩展开发](#扩展开发)
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## 系统概述
|
|
18
|
+
|
|
19
|
+
### 设计目标
|
|
20
|
+
|
|
21
|
+
1. **零配置测试生成**:自动检测技术栈,无需手动配置即可生成测试
|
|
22
|
+
2. **多语言支持**:React / Node.js / Python / Go / Rust
|
|
23
|
+
3. **智能复杂度路由**:简单代码自动生成,复杂代码委托 AI Agent
|
|
24
|
+
4. **持续迭代集成**:与 Ralph / Autopilot / UltraQA 工作流深度集成
|
|
25
|
+
5. **质量闭环**:覆盖率分析 → 测试生成 → 质量评分 → 持续改进
|
|
26
|
+
|
|
27
|
+
### 核心能力矩阵
|
|
28
|
+
|
|
29
|
+
| 能力 | 模块 | 说明 |
|
|
30
|
+
|------|------|------|
|
|
31
|
+
| 技术栈检测 | `detectors/` | 从配置文件自动识别语言、框架、数据库、API 类型 |
|
|
32
|
+
| 测试生成 | `generators/` | 针对不同语言生成符合惯例的测试代码 |
|
|
33
|
+
| 复杂度分析 | `analyzers/complexity.ts` | 圈复杂度、嵌套深度、外部依赖分析 |
|
|
34
|
+
| 覆盖率分析 | `analyzers/coverage.ts` | 解析 c8/nyc 报告,识别未覆盖代码 |
|
|
35
|
+
| 质量评分 | `analyzers/quality-scorer.ts` | 0-100 分多维度测试质量评估 |
|
|
36
|
+
| 合约测试 | `generators/contract.ts` | 从 OpenAPI 规范生成 Pact/Supertest/MSW 测试 |
|
|
37
|
+
| E2E 测试 | `generators/e2e.ts` | 从用户流程描述生成 Playwright 测试 |
|
|
38
|
+
| 行为测试 | `integrations/giskard/` | 扰动测试和鲁棒性测试 |
|
|
39
|
+
| LLM 提示测试 | `integrations/promptfoo/` | Promptfoo 配置生成 |
|
|
40
|
+
| CI/CD 自动化 | `integrations/cicd.ts` | GitHub Actions 工作流生成 |
|
|
41
|
+
| 性能优化 | `performance/` | TTL 缓存 + 并行生成 |
|
|
42
|
+
|
|
43
|
+
---
|
|
44
|
+
|
|
45
|
+
## 架构设计
|
|
46
|
+
|
|
47
|
+
### 目录结构
|
|
48
|
+
|
|
49
|
+
```
|
|
50
|
+
src/testing/
|
|
51
|
+
├── index.ts # 模块入口,导出 TestingModule
|
|
52
|
+
├── types.ts # 核心类型定义
|
|
53
|
+
│
|
|
54
|
+
├── detectors/ # 技术栈检测层
|
|
55
|
+
│ ├── index.ts # 多语言检测编排器
|
|
56
|
+
│ ├── package-json.ts # Node.js 生态检测
|
|
57
|
+
│ ├── python.ts # Python 生态检测
|
|
58
|
+
│ ├── go.ts # Go 生态检测
|
|
59
|
+
│ └── rust.ts # Rust 生态检测
|
|
60
|
+
│
|
|
61
|
+
├── analyzers/ # 分析层
|
|
62
|
+
│ ├── types.ts # 分析器类型定义
|
|
63
|
+
│ ├── complexity.ts # 代码复杂度分析
|
|
64
|
+
│ ├── coverage.ts # 覆盖率分析
|
|
65
|
+
│ └── quality-scorer.ts # 测试质量评分
|
|
66
|
+
│
|
|
67
|
+
├── generators/ # 测试生成层
|
|
68
|
+
│ ├── react.ts # React 组件测试
|
|
69
|
+
│ ├── nodejs.ts # Node.js 函数测试
|
|
70
|
+
│ ├── python.ts # Python pytest/unittest
|
|
71
|
+
│ ├── go.ts # Go table-driven 测试
|
|
72
|
+
│ ├── rust.ts # Rust #[test] 测试
|
|
73
|
+
│ ├── contract.ts # API 合约测试
|
|
74
|
+
│ └── e2e.ts # Playwright E2E 测试
|
|
75
|
+
│
|
|
76
|
+
├── cli/ # CLI 集成层
|
|
77
|
+
│ ├── commands.ts # CLI 命令实现
|
|
78
|
+
│ ├── agent-integration.ts # test-engineer Agent 集成
|
|
79
|
+
│ └── ultraqa-integration.ts # UltraQA 集成
|
|
80
|
+
│
|
|
81
|
+
├── integrations/ # 外部集成层
|
|
82
|
+
│ ├── autopilot.ts # Autopilot 工作流集成
|
|
83
|
+
│ ├── ralph.ts # Ralph 模式集成
|
|
84
|
+
│ ├── cicd.ts # CI/CD 工作流生成
|
|
85
|
+
│ ├── promptfoo/
|
|
86
|
+
│ │ ├── types.ts # Promptfoo 类型
|
|
87
|
+
│ │ └── config-generator.ts # Promptfoo 配置生成
|
|
88
|
+
│ └── giskard/
|
|
89
|
+
│ ├── types.ts # Giskard 类型
|
|
90
|
+
│ └── behavioral-tests.ts # 行为测试生成
|
|
91
|
+
│
|
|
92
|
+
└── performance/ # 性能优化层
|
|
93
|
+
├── cache-manager.ts # TTL 缓存管理
|
|
94
|
+
└── parallel-generator.ts # 并行测试生成
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### 数据流
|
|
98
|
+
|
|
99
|
+
```
|
|
100
|
+
用户输入 (/test-gen 或 omc test gen)
|
|
101
|
+
│
|
|
102
|
+
▼
|
|
103
|
+
┌─────────────────────┐
|
|
104
|
+
│ CLI Command Layer │ commands.ts
|
|
105
|
+
│ (路由 & 编排) │
|
|
106
|
+
└─────────┬───────────┘
|
|
107
|
+
│
|
|
108
|
+
┌─────┴─────┐
|
|
109
|
+
▼ ▼
|
|
110
|
+
┌────────┐ ┌──────────┐
|
|
111
|
+
│Detector│ │Complexity │
|
|
112
|
+
│(检测) │ │Analyzer │
|
|
113
|
+
└───┬────┘ └─────┬────┘
|
|
114
|
+
│ │
|
|
115
|
+
▼ ▼
|
|
116
|
+
┌─────────────────────┐
|
|
117
|
+
│ Route Decision │
|
|
118
|
+
│ simple → Generator │
|
|
119
|
+
│ complex → Agent │
|
|
120
|
+
└─────────┬───────────┘
|
|
121
|
+
│
|
|
122
|
+
┌─────┴─────┐
|
|
123
|
+
▼ ▼
|
|
124
|
+
┌────────┐ ┌──────────────┐
|
|
125
|
+
│Generator│ │Agent │
|
|
126
|
+
│(生成) │ │Integration │
|
|
127
|
+
└───┬────┘ └──────┬───────┘
|
|
128
|
+
│ │
|
|
129
|
+
▼ ▼
|
|
130
|
+
┌─────────────────────┐
|
|
131
|
+
│ Test File Output │
|
|
132
|
+
│ + Quality Score │
|
|
133
|
+
└──────────────────────┘
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
---
|
|
138
|
+
|
|
139
|
+
## 核心模块
|
|
140
|
+
|
|
141
|
+
### 1. 类型系统
|
|
142
|
+
|
|
143
|
+
#### TechStack(技术栈描述)
|
|
144
|
+
|
|
145
|
+
```typescript
|
|
146
|
+
// src/testing/types.ts
|
|
147
|
+
export interface TechStack {
|
|
148
|
+
frontend?: {
|
|
149
|
+
framework: 'react' | 'vue' | 'svelte' | 'none';
|
|
150
|
+
testFramework?: 'vitest' | 'jest' | 'none';
|
|
151
|
+
};
|
|
152
|
+
backend?: {
|
|
153
|
+
language: 'nodejs' | 'python' | 'go' | 'rust';
|
|
154
|
+
testFramework?: string; // vitest, jest, pytest, unittest, testing, cargo
|
|
155
|
+
};
|
|
156
|
+
databases?: string[]; // postgresql, mysql, mongodb
|
|
157
|
+
apis?: ('rest' | 'graphql' | 'grpc')[];
|
|
158
|
+
}
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
#### 分析器类型
|
|
162
|
+
|
|
163
|
+
```typescript
|
|
164
|
+
// src/testing/analyzers/types.ts
|
|
165
|
+
|
|
166
|
+
// 覆盖率指标
|
|
167
|
+
export interface CoverageMetrics {
|
|
168
|
+
total: number;
|
|
169
|
+
covered: number;
|
|
170
|
+
pct: number;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
export interface CoverageReport {
|
|
174
|
+
lines: CoverageMetrics;
|
|
175
|
+
statements: CoverageMetrics;
|
|
176
|
+
functions: CoverageMetrics;
|
|
177
|
+
branches: CoverageMetrics;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
export interface CoverageAnalysisResult {
|
|
181
|
+
totalCoverage: number;
|
|
182
|
+
lineCoverage: number;
|
|
183
|
+
functionCoverage: number;
|
|
184
|
+
branchCoverage: number;
|
|
185
|
+
statementCoverage: number;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// 覆盖率缺口
|
|
189
|
+
export interface CoverageGap {
|
|
190
|
+
file: string;
|
|
191
|
+
startLine: number;
|
|
192
|
+
endLine: number;
|
|
193
|
+
reason: string;
|
|
194
|
+
codeSnippet?: string;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// 测试质量评分
|
|
198
|
+
export interface TestQualityScore {
|
|
199
|
+
completenessScore: number; // 完整性 0-100
|
|
200
|
+
assertionQuality: number; // 断言质量 0-100
|
|
201
|
+
independenceScore: number; // 独立性 0-100
|
|
202
|
+
namingScore: number; // 命名质量 0-100
|
|
203
|
+
overallScore: number; // 综合评分 0-100
|
|
204
|
+
assertionCount: number;
|
|
205
|
+
hasEdgeCases: boolean;
|
|
206
|
+
hasAssertions: boolean;
|
|
207
|
+
hasSpecificAssertions: boolean;
|
|
208
|
+
hasSharedState: boolean;
|
|
209
|
+
hasDescriptiveNames: boolean;
|
|
210
|
+
metrics: TestQualityMetrics;
|
|
211
|
+
recommendations: string[]; // 改进建议
|
|
212
|
+
}
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
### 2. 技术栈检测器(Detectors)
|
|
216
|
+
|
|
217
|
+
检测器负责从项目配置文件中自动识别技术栈。
|
|
218
|
+
|
|
219
|
+
#### 检测编排器
|
|
220
|
+
|
|
221
|
+
```typescript
|
|
222
|
+
// src/testing/detectors/index.ts
|
|
223
|
+
import { detectFromPackageJson } from './package-json.js';
|
|
224
|
+
|
|
225
|
+
export async function detectTechStack(projectRoot: string): Promise<TechStack> {
|
|
226
|
+
// 按优先级依次尝试:Node.js → Python → Go → Rust
|
|
227
|
+
// 1. 检查 package.json
|
|
228
|
+
// 2. 检查 requirements.txt / pyproject.toml
|
|
229
|
+
// 3. 检查 go.mod
|
|
230
|
+
// 4. 检查 Cargo.toml
|
|
231
|
+
// 返回合并后的 TechStack
|
|
232
|
+
}
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
#### 各语言检测逻辑
|
|
236
|
+
|
|
237
|
+
| 检测器 | 配置文件 | 检测内容 |
|
|
238
|
+
|--------|----------|----------|
|
|
239
|
+
| `package-json.ts` | `package.json` | React/Vue/Svelte, Express/Fastify/Koa, Vitest/Jest, pg/mysql/mongodb, GraphQL/gRPC |
|
|
240
|
+
| `python.ts` | `requirements.txt` | Flask/FastAPI/Django, pytest/unittest, psycopg2/mysql/pymongo, GraphQL |
|
|
241
|
+
| `go.ts` | `go.mod` | Gin/Gorilla/Echo, database drivers, gRPC, GraphQL |
|
|
242
|
+
| `rust.ts` | `Cargo.toml` | Actix/Rocket/Axum, diesel/sqlx, tonic gRPC, async-graphql |
|
|
243
|
+
|
|
244
|
+
**集成要点:** 新项目只需实现对应的检测器函数,签名为:
|
|
245
|
+
|
|
246
|
+
```typescript
|
|
247
|
+
async function detectFromXxx(configContent: any): Promise<TechStack>
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
### 3. 测试生成器(Generators)
|
|
251
|
+
|
|
252
|
+
每个生成器接收源代码和配置,输出测试文件内容。
|
|
253
|
+
|
|
254
|
+
#### 统一接口模式
|
|
255
|
+
|
|
256
|
+
```typescript
|
|
257
|
+
// 所有生成器遵循相同模式
|
|
258
|
+
interface GeneratorOptions {
|
|
259
|
+
filePath: string; // 源文件路径
|
|
260
|
+
code: string; // 源代码内容
|
|
261
|
+
testFramework: string; // 测试框架
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
interface GeneratorResult {
|
|
265
|
+
testFilePath: string; // 生成的测试文件路径
|
|
266
|
+
testCode: string; // 生成的测试代码
|
|
267
|
+
}
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
#### 各语言生成器特性
|
|
271
|
+
|
|
272
|
+
| 生成器 | 输入 | 输出 | 特性 |
|
|
273
|
+
|--------|------|------|------|
|
|
274
|
+
| `react.ts` | `.tsx/.jsx` | `*.test.tsx` | 检测 onClick/children props,生成 RTL 测试 |
|
|
275
|
+
| `nodejs.ts` | `.ts` | `*.test.ts` | 正则提取导出函数,生成 Vitest/Jest 测试 |
|
|
276
|
+
| `python.ts` | `.py` | `test_*.py` | 提取函数/类,生成 pytest 参数化测试 |
|
|
277
|
+
| `go.ts` | `.go` | `*_test.go` | 提取导出函数,生成 table-driven 测试 |
|
|
278
|
+
| `rust.ts` | `.rs` | 内嵌 `#[cfg(test)]` | 提取 fn/struct/impl,生成 `#[test]` 测试 |
|
|
279
|
+
| `contract.ts` | OpenAPI spec | `*.pact.test.ts` | 支持 Pact/Supertest/MSW 三种框架 |
|
|
280
|
+
| `e2e.ts` | 流程描述 | `*.spec.ts` | 解析自然语言步骤,生成 Playwright 测试 |
|
|
281
|
+
|
|
282
|
+
#### React 生成器示例
|
|
283
|
+
|
|
284
|
+
```typescript
|
|
285
|
+
// src/testing/generators/react.ts
|
|
286
|
+
export async function generateReactTest(options: {
|
|
287
|
+
filePath: string;
|
|
288
|
+
code: string;
|
|
289
|
+
testFramework: 'vitest' | 'jest';
|
|
290
|
+
}): Promise<{ testFilePath: string; testCode: string }> {
|
|
291
|
+
// 1. 从文件路径提取组件名
|
|
292
|
+
const fileName = filePath.split('/').pop()?.replace(/\.(tsx?|jsx?)$/, '');
|
|
293
|
+
|
|
294
|
+
// 2. 分析代码特征
|
|
295
|
+
const hasOnClick = code.includes('onClick');
|
|
296
|
+
const hasChildren = code.includes('children');
|
|
297
|
+
|
|
298
|
+
// 3. 生成测试代码
|
|
299
|
+
// - import { render, screen } from '@testing-library/react'
|
|
300
|
+
// - describe('ComponentName', () => { ... })
|
|
301
|
+
// - it('renders children', () => { ... })
|
|
302
|
+
// - it('calls onClick when clicked', () => { ... }) // 如果有 onClick
|
|
303
|
+
|
|
304
|
+
// 4. 返回测试文件路径和代码
|
|
305
|
+
return { testFilePath, testCode };
|
|
306
|
+
}
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
### 4. 分析器(Analyzers)
|
|
310
|
+
|
|
311
|
+
#### 复杂度分析
|
|
312
|
+
|
|
313
|
+
```typescript
|
|
314
|
+
// src/testing/analyzers/complexity.ts
|
|
315
|
+
export interface ComplexityAnalysisResult {
|
|
316
|
+
complexity: 'simple' | 'complex';
|
|
317
|
+
metrics: {
|
|
318
|
+
lines: number;
|
|
319
|
+
cyclomaticComplexity: number;
|
|
320
|
+
nestingLevel: number;
|
|
321
|
+
externalDependencies: number;
|
|
322
|
+
};
|
|
323
|
+
reasons: string[];
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
export async function analyzeComplexity(options: {
|
|
327
|
+
code: string;
|
|
328
|
+
filePath: string;
|
|
329
|
+
}): Promise<ComplexityAnalysisResult>
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
**复杂度判定规则:**
|
|
333
|
+
|
|
334
|
+
| 指标 | 简单阈值 | 复杂标记 |
|
|
335
|
+
|------|----------|----------|
|
|
336
|
+
| 代码行数 | < 50 | ≥ 50 |
|
|
337
|
+
| 圈复杂度 | < 10 | ≥ 10 |
|
|
338
|
+
| 嵌套深度 | < 3 | ≥ 3 |
|
|
339
|
+
| 外部依赖 | 0 | > 0 |
|
|
340
|
+
| 特殊模式 | 无 | payment/auth/transaction/async |
|
|
341
|
+
|
|
342
|
+
#### 覆盖率分析
|
|
343
|
+
|
|
344
|
+
```typescript
|
|
345
|
+
// src/testing/analyzers/coverage.ts
|
|
346
|
+
export async function analyzeCoverage(options: {
|
|
347
|
+
coverageReportPath: string;
|
|
348
|
+
}): Promise<CoverageAnalysisResult>
|
|
349
|
+
|
|
350
|
+
export async function identifyGaps(options: {
|
|
351
|
+
coverageReportPath: string;
|
|
352
|
+
}): Promise<GapAnalysisResult>
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
#### 质量评分
|
|
356
|
+
|
|
357
|
+
```typescript
|
|
358
|
+
// src/testing/analyzers/quality-scorer.ts
|
|
359
|
+
export async function scoreTestQuality(options: {
|
|
360
|
+
testCode: string;
|
|
361
|
+
testType: 'unit' | 'integration' | 'e2e';
|
|
362
|
+
}): Promise<TestQualityScore>
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
**评分维度:**
|
|
366
|
+
|
|
367
|
+
| 维度 | 权重 | 检测内容 |
|
|
368
|
+
|------|------|----------|
|
|
369
|
+
| completenessScore | 25% | 断言数量、边界用例、mock 使用 |
|
|
370
|
+
| assertionQuality | 25% | 具体断言(toBe/toEqual vs toBeTruthy) |
|
|
371
|
+
| independenceScore | 25% | 是否有共享状态、setup/teardown |
|
|
372
|
+
| namingScore | 25% | 描述性测试名称(包含 should/when/returns) |
|
|
373
|
+
|
|
374
|
+
|
|
375
|
+
### 5. CLI 命令层
|
|
376
|
+
|
|
377
|
+
#### 命令路由逻辑
|
|
378
|
+
|
|
379
|
+
```typescript
|
|
380
|
+
// src/testing/cli/commands.ts
|
|
381
|
+
|
|
382
|
+
// 语言检测(文件扩展名 → 语言)
|
|
383
|
+
function detectLanguage(filePath: string): Language | null {
|
|
384
|
+
if (filePath.match(/\.(tsx|jsx)$/)) return 'react';
|
|
385
|
+
if (filePath.match(/\.ts$/)) return 'typescript';
|
|
386
|
+
if (filePath.match(/\.py$/)) return 'python';
|
|
387
|
+
if (filePath.match(/\.go$/)) return 'go';
|
|
388
|
+
if (filePath.match(/\.rs$/)) return 'rust';
|
|
389
|
+
return null;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
// 主命令:测试生成
|
|
393
|
+
export async function testGenCommand(options: {
|
|
394
|
+
filePath: string;
|
|
395
|
+
output?: string;
|
|
396
|
+
language?: Language;
|
|
397
|
+
}): Promise<{ success: boolean; testFilePath?: string; error?: string }>
|
|
398
|
+
|
|
399
|
+
// 技术栈检测
|
|
400
|
+
export async function testDetectStackCommand(options: {
|
|
401
|
+
projectRoot: string;
|
|
402
|
+
}): Promise<{ stack: TechStack }>
|
|
403
|
+
|
|
404
|
+
// 覆盖率分析
|
|
405
|
+
export async function testAnalyzeCoverageCommand(options: {
|
|
406
|
+
projectRoot: string;
|
|
407
|
+
}): Promise<CoverageAnalysisResult>
|
|
408
|
+
|
|
409
|
+
// 复杂度分析
|
|
410
|
+
export async function testComplexityCommand(options: {
|
|
411
|
+
filePath: string;
|
|
412
|
+
}): Promise<ComplexityAnalysisResult & { complexity: string; reasons: string[] }>
|
|
413
|
+
|
|
414
|
+
// 合约测试生成
|
|
415
|
+
export async function testContractCommand(options: {
|
|
416
|
+
specPath: string;
|
|
417
|
+
framework: 'pact' | 'supertest' | 'msw';
|
|
418
|
+
consumer?: string;
|
|
419
|
+
provider?: string;
|
|
420
|
+
}): Promise<{ success: boolean; testFilePath?: string }>
|
|
421
|
+
|
|
422
|
+
// Promptfoo 配置生成
|
|
423
|
+
export async function testPromptfooCommand(options: {
|
|
424
|
+
promptFile: string;
|
|
425
|
+
provider?: string;
|
|
426
|
+
output?: string;
|
|
427
|
+
}): Promise<{ success: boolean; configPath?: string; error?: string }>
|
|
428
|
+
|
|
429
|
+
// E2E 测试生成
|
|
430
|
+
export async function testE2ECommand(options: {
|
|
431
|
+
flowDescription: string;
|
|
432
|
+
baseUrl?: string;
|
|
433
|
+
testName?: string;
|
|
434
|
+
output?: string;
|
|
435
|
+
}): Promise<{ success: boolean; testFilePath?: string; error?: string }>
|
|
436
|
+
|
|
437
|
+
// Giskard 行为测试
|
|
438
|
+
export async function testGiskardCommand(options: {
|
|
439
|
+
filePath: string;
|
|
440
|
+
testType?: 'perturbation' | 'robustness';
|
|
441
|
+
output?: string;
|
|
442
|
+
}): Promise<{ success: boolean; testFilePath?: string; error?: string }>
|
|
443
|
+
|
|
444
|
+
// CI/CD 工作流生成
|
|
445
|
+
export async function testCICDCommand(options: {
|
|
446
|
+
language?: 'nodejs' | 'python' | 'go' | 'rust';
|
|
447
|
+
output?: string;
|
|
448
|
+
}): Promise<{ success: boolean; workflowPath?: string; error?: string }>
|
|
449
|
+
|
|
450
|
+
// 测试质量评分
|
|
451
|
+
export async function testQualityCommand(options: {
|
|
452
|
+
testFilePath: string;
|
|
453
|
+
testType?: 'unit' | 'integration' | 'e2e';
|
|
454
|
+
}): Promise<{ success: boolean; score?: TestQualityScore; error?: string }>
|
|
455
|
+
```
|
|
456
|
+
|
|
457
|
+
### 6. Agent 集成层
|
|
458
|
+
|
|
459
|
+
#### test-engineer Agent 上下文增强
|
|
460
|
+
|
|
461
|
+
```typescript
|
|
462
|
+
// src/testing/cli/agent-integration.ts
|
|
463
|
+
|
|
464
|
+
export interface TestEngineerContext {
|
|
465
|
+
filePath: string;
|
|
466
|
+
code: string;
|
|
467
|
+
techStack: TechStack;
|
|
468
|
+
complexity: 'simple' | 'complex';
|
|
469
|
+
complexityMetrics: {
|
|
470
|
+
lines: number;
|
|
471
|
+
cyclomaticComplexity: number;
|
|
472
|
+
nestingLevel: number;
|
|
473
|
+
externalDependencies: number;
|
|
474
|
+
};
|
|
475
|
+
suggestedApproach: 'auto-generate' | 'guided' | 'manual';
|
|
476
|
+
questionsForUser: string[];
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
// 准备上下文
|
|
480
|
+
export async function prepareTestEngineerContext(options: {
|
|
481
|
+
filePath: string;
|
|
482
|
+
code: string;
|
|
483
|
+
projectRoot: string;
|
|
484
|
+
}): Promise<TestEngineerContext>
|
|
485
|
+
|
|
486
|
+
// 生成 Agent 调用命令
|
|
487
|
+
export function invokeTestEngineerAgent(context: TestEngineerContext): string
|
|
488
|
+
```
|
|
489
|
+
|
|
490
|
+
**路由策略:**
|
|
491
|
+
|
|
492
|
+
| 复杂度 | 方法 | 说明 |
|
|
493
|
+
|--------|------|------|
|
|
494
|
+
| `simple` | `auto-generate` | 直接调用 Generator 生成 |
|
|
495
|
+
| `complex` (圈复杂度 < 15) | `guided` | 生成框架 + 向用户提问 |
|
|
496
|
+
| `complex` (圈复杂度 ≥ 15) | `manual` | 仅提供框架和指导 |
|
|
497
|
+
|
|
498
|
+
#### UltraQA 集成
|
|
499
|
+
|
|
500
|
+
```typescript
|
|
501
|
+
// src/testing/cli/ultraqa-integration.ts
|
|
502
|
+
|
|
503
|
+
export interface UltraQATestResult {
|
|
504
|
+
filesProcessed: number;
|
|
505
|
+
testsGenerated: number;
|
|
506
|
+
coverageBefore?: number;
|
|
507
|
+
coverageAfter?: number;
|
|
508
|
+
errors: string[];
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
// 增强 UltraQA 工作流
|
|
512
|
+
export async function enhanceUltraQAWithTestGen(options: {
|
|
513
|
+
changedFiles: string[];
|
|
514
|
+
projectRoot: string;
|
|
515
|
+
coverageThreshold?: number;
|
|
516
|
+
generateTests?: (filePath: string) => Promise<{ testFilePath: string }>;
|
|
517
|
+
}): Promise<UltraQATestResult>
|
|
518
|
+
```
|
|
519
|
+
|
|
520
|
+
### 7. 工作流集成
|
|
521
|
+
|
|
522
|
+
#### Ralph 模式集成
|
|
523
|
+
|
|
524
|
+
```typescript
|
|
525
|
+
// src/testing/integrations/ralph.ts
|
|
526
|
+
|
|
527
|
+
export interface RalphTestConfig {
|
|
528
|
+
enabled: boolean;
|
|
529
|
+
coverageThreshold: number;
|
|
530
|
+
maxIterations: number;
|
|
531
|
+
testCommand: string;
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
// 集成到 Ralph 验证循环
|
|
535
|
+
export async function integrateWithRalph(config: RalphTestConfig): Promise<{
|
|
536
|
+
shouldContinue: boolean;
|
|
537
|
+
coverageMet: boolean;
|
|
538
|
+
testsPassed: boolean;
|
|
539
|
+
}>
|
|
540
|
+
|
|
541
|
+
// 为修改的文件生成测试
|
|
542
|
+
export async function generateTestsForIteration(options: {
|
|
543
|
+
modifiedFiles: string[];
|
|
544
|
+
projectRoot: string;
|
|
545
|
+
}): Promise<{ generatedTests: string[]; errors: string[] }>
|
|
546
|
+
|
|
547
|
+
// 执行测试
|
|
548
|
+
export async function runTestsInRalphCycle(options: {
|
|
549
|
+
testCommand: string;
|
|
550
|
+
projectRoot: string;
|
|
551
|
+
}): Promise<{ passed: boolean; output: string }>
|
|
552
|
+
|
|
553
|
+
// 检查覆盖率阈值
|
|
554
|
+
export async function checkCoverageThreshold(options: {
|
|
555
|
+
threshold: number;
|
|
556
|
+
projectRoot: string;
|
|
557
|
+
}): Promise<{ met: boolean; current: number }>
|
|
558
|
+
```
|
|
559
|
+
|
|
560
|
+
**Ralph 集成流程:**
|
|
561
|
+
|
|
562
|
+
```
|
|
563
|
+
Ralph 验证循环
|
|
564
|
+
│
|
|
565
|
+
▼
|
|
566
|
+
检测修改的文件
|
|
567
|
+
│
|
|
568
|
+
▼
|
|
569
|
+
为修改文件生成测试 (generateTestsForIteration)
|
|
570
|
+
│
|
|
571
|
+
▼
|
|
572
|
+
执行测试 (runTestsInRalphCycle)
|
|
573
|
+
│
|
|
574
|
+
├── 测试失败 → 返回 Ralph 修复循环
|
|
575
|
+
│
|
|
576
|
+
▼
|
|
577
|
+
检查覆盖率 (checkCoverageThreshold)
|
|
578
|
+
│
|
|
579
|
+
├── 未达标 → 生成补充测试 → 重新执行
|
|
580
|
+
│
|
|
581
|
+
▼
|
|
582
|
+
验证通过 → 继续 Ralph 下一步
|
|
583
|
+
```
|
|
584
|
+
|
|
585
|
+
#### Autopilot 集成
|
|
586
|
+
|
|
587
|
+
```typescript
|
|
588
|
+
// src/testing/integrations/autopilot.ts
|
|
589
|
+
|
|
590
|
+
export type TestPhase = 'unit' | 'integration' | 'e2e';
|
|
591
|
+
|
|
592
|
+
// 集成到 Autopilot 工作流
|
|
593
|
+
export async function integrateWithAutopilot(options: {
|
|
594
|
+
phase: TestPhase;
|
|
595
|
+
files: string[];
|
|
596
|
+
projectRoot: string;
|
|
597
|
+
}): Promise<{
|
|
598
|
+
phase: TestPhase;
|
|
599
|
+
testsGenerated: number;
|
|
600
|
+
testsPassed: boolean;
|
|
601
|
+
}>
|
|
602
|
+
|
|
603
|
+
// 根据文件模式确定测试阶段
|
|
604
|
+
export async function generateTestsForPhase(options: {
|
|
605
|
+
phase: TestPhase;
|
|
606
|
+
files: string[];
|
|
607
|
+
projectRoot: string;
|
|
608
|
+
}): Promise<{ generatedTests: string[]; errors: string[] }>
|
|
609
|
+
```
|
|
610
|
+
|
|
611
|
+
**Autopilot 测试阶段:**
|
|
612
|
+
|
|
613
|
+
| 阶段 | 文件模式 | 生成内容 |
|
|
614
|
+
|------|----------|----------|
|
|
615
|
+
| `unit` | `src/**/*.ts`, `src/**/*.py` | 单元测试 |
|
|
616
|
+
| `integration` | `src/services/**`, `src/api/**` | 集成测试 |
|
|
617
|
+
| `e2e` | `src/pages/**`, `src/routes/**` | Playwright E2E 测试 |
|
|
618
|
+
|
|
619
|
+
|
|
620
|
+
### 8. 性能优化层
|
|
621
|
+
|
|
622
|
+
#### 缓存管理
|
|
623
|
+
|
|
624
|
+
```typescript
|
|
625
|
+
// src/testing/performance/cache-manager.ts
|
|
626
|
+
|
|
627
|
+
export class CacheManager {
|
|
628
|
+
private cache: Map<string, { value: any; expiresAt: number }>;
|
|
629
|
+
private ttl: number; // 默认 300000ms (5分钟)
|
|
630
|
+
|
|
631
|
+
constructor(ttlMs?: number);
|
|
632
|
+
get(key: string): any | undefined;
|
|
633
|
+
set(key: string, value: any): void;
|
|
634
|
+
has(key: string): boolean;
|
|
635
|
+
clear(): void;
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
// 全局缓存实例
|
|
639
|
+
export function getCachedAnalysis(key: string): any | undefined;
|
|
640
|
+
export function setCachedAnalysis(key: string, value: any): void;
|
|
641
|
+
```
|
|
642
|
+
|
|
643
|
+
#### 并行生成
|
|
644
|
+
|
|
645
|
+
```typescript
|
|
646
|
+
// src/testing/performance/parallel-generator.ts
|
|
647
|
+
|
|
648
|
+
export class ParallelGenerator {
|
|
649
|
+
private concurrency: number; // 默认 3
|
|
650
|
+
|
|
651
|
+
constructor(concurrency?: number);
|
|
652
|
+
|
|
653
|
+
// 并行生成多个文件的测试
|
|
654
|
+
async generateTestsInParallel(options: {
|
|
655
|
+
files: string[];
|
|
656
|
+
projectRoot: string;
|
|
657
|
+
generator: (filePath: string) => Promise<{ testFilePath: string }>;
|
|
658
|
+
}): Promise<{
|
|
659
|
+
results: Array<{ file: string; testFilePath: string }>;
|
|
660
|
+
errors: Array<{ file: string; error: string }>;
|
|
661
|
+
}>;
|
|
662
|
+
}
|
|
663
|
+
```
|
|
664
|
+
|
|
665
|
+
---
|
|
666
|
+
|
|
667
|
+
## 集成指南
|
|
668
|
+
|
|
669
|
+
### 快速集成到新项目(如 oh-my-codex)
|
|
670
|
+
|
|
671
|
+
#### 步骤 1:安装依赖
|
|
672
|
+
|
|
673
|
+
```bash
|
|
674
|
+
npm install claudecode-omc@4.8.0
|
|
675
|
+
```
|
|
676
|
+
|
|
677
|
+
#### 步骤 2:引入核心模块
|
|
678
|
+
|
|
679
|
+
```typescript
|
|
680
|
+
import { detectTechStack } from 'claudecode-omc/dist/testing/detectors/index.js';
|
|
681
|
+
import { analyzeComplexity } from 'claudecode-omc/dist/testing/analyzers/complexity.js';
|
|
682
|
+
import { generateReactTest } from 'claudecode-omc/dist/testing/generators/react.js';
|
|
683
|
+
import { generateNodeJsTest } from 'claudecode-omc/dist/testing/generators/nodejs.js';
|
|
684
|
+
import { generatePythonTest } from 'claudecode-omc/dist/testing/generators/python.js';
|
|
685
|
+
import { generateGoTest } from 'claudecode-omc/dist/testing/generators/go.js';
|
|
686
|
+
import { generateRustTest } from 'claudecode-omc/dist/testing/generators/rust.js';
|
|
687
|
+
import { scoreTestQuality } from 'claudecode-omc/dist/testing/analyzers/quality-scorer.js';
|
|
688
|
+
```
|
|
689
|
+
|
|
690
|
+
#### 步骤 3:实现测试生成流程
|
|
691
|
+
|
|
692
|
+
```typescript
|
|
693
|
+
async function generateTestForFile(filePath: string) {
|
|
694
|
+
// 1. 检测技术栈
|
|
695
|
+
const stack = await detectTechStack(process.cwd());
|
|
696
|
+
|
|
697
|
+
// 2. 读取源代码
|
|
698
|
+
const code = await fs.readFile(filePath, 'utf-8');
|
|
699
|
+
|
|
700
|
+
// 3. 分析复杂度
|
|
701
|
+
const complexity = await analyzeComplexity({ code, filePath });
|
|
702
|
+
|
|
703
|
+
// 4. 根据语言选择生成器
|
|
704
|
+
if (filePath.endsWith('.tsx')) {
|
|
705
|
+
return generateReactTest({
|
|
706
|
+
filePath, code,
|
|
707
|
+
testFramework: stack.frontend?.testFramework || 'vitest'
|
|
708
|
+
});
|
|
709
|
+
}
|
|
710
|
+
if (filePath.endsWith('.ts')) {
|
|
711
|
+
return generateNodeJsTest({
|
|
712
|
+
filePath, code,
|
|
713
|
+
testFramework: stack.backend?.testFramework || 'vitest'
|
|
714
|
+
});
|
|
715
|
+
}
|
|
716
|
+
if (filePath.endsWith('.py')) {
|
|
717
|
+
return generatePythonTest({
|
|
718
|
+
filePath, code,
|
|
719
|
+
testFramework: stack.backend?.testFramework || 'pytest'
|
|
720
|
+
});
|
|
721
|
+
}
|
|
722
|
+
// ... 其他语言
|
|
723
|
+
}
|
|
724
|
+
```
|
|
725
|
+
|
|
726
|
+
#### 步骤 4:集成到 CI/CD
|
|
727
|
+
|
|
728
|
+
```typescript
|
|
729
|
+
import { generateGitHubActionsWorkflow } from 'claudecode-omc/dist/testing/integrations/cicd.js';
|
|
730
|
+
|
|
731
|
+
const workflow = await generateGitHubActionsWorkflow({
|
|
732
|
+
language: 'nodejs',
|
|
733
|
+
coverage: true,
|
|
734
|
+
artifacts: true,
|
|
735
|
+
});
|
|
736
|
+
await fs.writeFile('.github/workflows/test.yml', workflow);
|
|
737
|
+
```
|
|
738
|
+
|
|
739
|
+
### 自定义检测器
|
|
740
|
+
|
|
741
|
+
如果需要支持新的语言或框架:
|
|
742
|
+
|
|
743
|
+
```typescript
|
|
744
|
+
// my-detector.ts
|
|
745
|
+
import type { TechStack } from 'claudecode-omc/dist/testing/types.js';
|
|
746
|
+
|
|
747
|
+
export async function detectFromMyConfig(config: any): Promise<TechStack> {
|
|
748
|
+
const stack: TechStack = {};
|
|
749
|
+
|
|
750
|
+
// 检测前端框架
|
|
751
|
+
if (config.dependencies?.['my-framework']) {
|
|
752
|
+
stack.frontend = {
|
|
753
|
+
framework: 'react', // 映射到已知框架
|
|
754
|
+
testFramework: config.devDependencies?.vitest ? 'vitest' : 'jest',
|
|
755
|
+
};
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
// 检测后端
|
|
759
|
+
if (config.dependencies?.['my-server']) {
|
|
760
|
+
stack.backend = {
|
|
761
|
+
language: 'nodejs',
|
|
762
|
+
testFramework: 'vitest',
|
|
763
|
+
};
|
|
764
|
+
}
|
|
765
|
+
|
|
766
|
+
return stack;
|
|
767
|
+
}
|
|
768
|
+
```
|
|
769
|
+
|
|
770
|
+
### 自定义生成器
|
|
771
|
+
|
|
772
|
+
```typescript
|
|
773
|
+
// my-generator.ts
|
|
774
|
+
export async function generateMyFrameworkTest(options: {
|
|
775
|
+
filePath: string;
|
|
776
|
+
code: string;
|
|
777
|
+
testFramework: string;
|
|
778
|
+
}): Promise<{ testFilePath: string; testCode: string }> {
|
|
779
|
+
const { filePath, code, testFramework } = options;
|
|
780
|
+
|
|
781
|
+
// 1. 解析源代码,提取函数/类
|
|
782
|
+
const functions = extractFunctions(code);
|
|
783
|
+
|
|
784
|
+
// 2. 生成测试文件路径
|
|
785
|
+
const testFilePath = filePath.replace(/\.ts$/, '.test.ts');
|
|
786
|
+
|
|
787
|
+
// 3. 生成测试代码
|
|
788
|
+
const testCode = `
|
|
789
|
+
import { describe, it, expect } from '${testFramework}';
|
|
790
|
+
import { ${functions.join(', ')} } from './${path.basename(filePath, '.ts')}';
|
|
791
|
+
|
|
792
|
+
describe('${path.basename(filePath, '.ts')}', () => {
|
|
793
|
+
${functions.map(fn => `
|
|
794
|
+
it('${fn} works correctly', () => {
|
|
795
|
+
// TODO: Add test cases
|
|
796
|
+
expect(${fn}).toBeDefined();
|
|
797
|
+
});`).join('\n')}
|
|
798
|
+
});
|
|
799
|
+
`;
|
|
800
|
+
|
|
801
|
+
return { testFilePath, testCode };
|
|
802
|
+
}
|
|
803
|
+
```
|
|
804
|
+
|