openmatrix 0.2.23 → 0.2.25

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.
@@ -143,10 +143,25 @@ function generateTestContent(source, config) {
143
143
  // 导入语句
144
144
  const importPath = source.path.replace(/\.(ts|tsx)$/, '').replace(/\.(js|jsx)$/, '');
145
145
  const imports = generateImports(source.exports, importPath, usesTypeScript);
146
+ // 需要生命周期钩子的文件类型
147
+ const needsLifecycle = ['service', 'util', 'api', 'module'].includes(source.fileType);
148
+ // 需要 vi 导入的测试类型(使用 Mock 验证副作用)
149
+ const needsViImport = ['service', 'util', 'api', 'component', 'module'].includes(source.fileType);
146
150
  // 测试块
147
151
  const testBlocks = source.exports.map(exp => generateTestBlock(exp, framework, usesDescribeIt, source.fileType));
148
152
  // 组装内容
149
- const content = `${imports}\n\n${testBlocks.join('\n\n')}\n`;
153
+ const content = `${needsViImport ? `import { vi } from 'vitest';\n` : ''}${imports}
154
+ ${needsLifecycle ? `
155
+ beforeEach(() => {
156
+ vi.clearAllMocks();
157
+ });
158
+
159
+ afterEach(() => {
160
+ vi.restoreAllMocks();
161
+ });
162
+ ` : ''}
163
+ ${testBlocks.join('\n\n')}
164
+ `;
150
165
  return content;
151
166
  }
152
167
  /**
@@ -163,31 +178,16 @@ function generateImports(exports, importPath, usesTypeScript) {
163
178
  : `const { ${exports.join(', ')} } = require('../../${importPath}');`;
164
179
  }
165
180
  /**
166
- * 生成测试块
181
+ * 生成测试块 - 根据文件类型生成有意义的业务测试
167
182
  */
168
183
  function generateTestBlock(exportName, framework, usesDescribeIt, fileType) {
169
184
  const useDescribe = usesDescribeIt ?? true;
170
- const describeOrTest = usesDescribeIt ? 'describe' : 'test';
171
185
  if (useDescribe) {
186
+ // 根据文件类型生成不同的测试场景
187
+ const testCases = generateBusinessTestCases(exportName, fileType);
188
+ const testBlocks = testCases.map(tc => generateTestCaseBlock(tc, exportName));
172
189
  return `describe('${getDescribeTitle(exportName, fileType)}', () => {
173
- it('should work correctly', () => {
174
- // Arrange
175
- const input = {};
176
-
177
- // Act
178
- const result = ${exportName}(input);
179
-
180
- // Assert
181
- expect(result).toBeDefined();
182
- });
183
-
184
- it('should handle edge cases', () => {
185
- // Arrange
186
- const input = null;
187
-
188
- // Act & Assert
189
- expect(() => ${exportName}(input)).not.toThrow();
190
- });
190
+ ${testBlocks.join('\n\n')}
191
191
  });`;
192
192
  }
193
193
  else {
@@ -197,6 +197,258 @@ function generateTestBlock(exportName, framework, usesDescribeIt, fileType) {
197
197
  });`;
198
198
  }
199
199
  }
200
+ /**
201
+ * 根据文件类型生成业务测试用例
202
+ */
203
+ function generateBusinessTestCases(exportName, fileType) {
204
+ switch (fileType) {
205
+ case 'service':
206
+ return [
207
+ {
208
+ name: 'should return valid result on successful execution',
209
+ arrange: `// Arrange - 准备有效的输入参数
210
+ const params = { id: 'test-id', data: {} };
211
+ const spy = vi.spyOn({ ${exportName} }, '${exportName}');`,
212
+ act: `// Act - 执行服务方法
213
+ const result = await ${exportName}(params);`,
214
+ assert: [
215
+ 'expect(result).toBeDefined();',
216
+ 'expect(result).toHaveProperty(\'data\');',
217
+ 'expect(result.error).toBeUndefined();',
218
+ `expect(spy).toHaveBeenCalledWith(params);`,
219
+ 'expect(spy).toHaveBeenCalledTimes(1);'
220
+ ]
221
+ },
222
+ {
223
+ name: 'should handle invalid input gracefully',
224
+ arrange: `// Arrange - 准备无效输入
225
+ const invalidParams = { id: null, data: undefined };`,
226
+ act: `// Act - 使用无效参数调用
227
+ const result = await ${exportName}(invalidParams);`,
228
+ assert: [
229
+ 'expect(result).toBeDefined();',
230
+ 'expect(result).toHaveProperty(\'error\');'
231
+ ]
232
+ },
233
+ {
234
+ name: 'should handle empty parameters',
235
+ arrange: `// Arrange - 空参数测试`,
236
+ act: `// Act & Assert - 验证空参数处理
237
+ const result = await ${exportName}({});`,
238
+ assert: [
239
+ 'expect(result).toBeDefined();',
240
+ '// 应该返回默认值或错误信息,而非崩溃'
241
+ ]
242
+ },
243
+ {
244
+ name: 'should handle missing required fields',
245
+ arrange: `// Arrange - 缺少必填字段`,
246
+ act: `// Act`,
247
+ assert: [
248
+ `expect(() => ${exportName}(null)).not.toThrow();`,
249
+ 'expect(() => ${exportName}(undefined)).not.toThrow();'
250
+ ]
251
+ }
252
+ ];
253
+ case 'util':
254
+ return [
255
+ {
256
+ name: 'should process valid input correctly',
257
+ arrange: `// Arrange - 准备有效输入
258
+ const input = 'test-value';
259
+ const expected = 'expected-result';
260
+ const spy = vi.spyOn({ ${exportName} }, '${exportName}');`,
261
+ act: `// Act
262
+ const result = ${exportName}(input);`,
263
+ assert: [
264
+ 'expect(result).toBeDefined();',
265
+ 'expect(typeof result).toBe(\'string\');',
266
+ `expect(spy).toHaveBeenCalledWith(input);`,
267
+ 'expect(spy).toHaveBeenCalledTimes(1);'
268
+ ]
269
+ },
270
+ {
271
+ name: 'should handle null input',
272
+ arrange: `// Arrange - null 输入`,
273
+ act: `// Act & Assert`,
274
+ assert: [
275
+ `expect(() => ${exportName}(null)).not.toThrow();`,
276
+ 'const result = ${exportName}(null);',
277
+ 'expect(result).toBeDefined();'
278
+ ]
279
+ },
280
+ {
281
+ name: 'should handle empty string input',
282
+ arrange: `// Arrange - 空字符串输入`,
283
+ act: `// Act
284
+ const result = ${exportName}('');`,
285
+ assert: [
286
+ 'expect(result).toBeDefined();',
287
+ '// 验证空字符串的预期行为'
288
+ ]
289
+ },
290
+ {
291
+ name: 'should handle various input types',
292
+ arrange: `// Arrange - 多种输入类型测试`,
293
+ act: `// Act - 测试不同类型`,
294
+ assert: [
295
+ 'const numResult = ${exportName}(123);',
296
+ 'const boolResult = ${exportName}(true);',
297
+ 'const objResult = ${exportName}({ key: \'value\' });',
298
+ 'expect(numResult).toBeDefined();',
299
+ 'expect(boolResult).toBeDefined();',
300
+ 'expect(objResult).toBeDefined();'
301
+ ]
302
+ }
303
+ ];
304
+ case 'component':
305
+ return [
306
+ {
307
+ name: 'should render without crashing',
308
+ arrange: `// Arrange - 准备组件 props
309
+ const props = { title: \'Test Title\', onClick: vi.fn() };`,
310
+ act: `// Act - 渲染组件
311
+ const { container } = render(<${exportName} {...props} />);`,
312
+ assert: [
313
+ 'expect(container).toBeDefined();',
314
+ 'expect(container.firstChild).toBeInTheDocument();'
315
+ ]
316
+ },
317
+ {
318
+ name: 'should handle user interaction',
319
+ arrange: `// Arrange - 准备交互测试
320
+ const handleClick = vi.fn();
321
+ const props = { onClick: handleClick };`,
322
+ act: `// Act - 模拟用户点击
323
+ const { getByRole } = render(<${exportName} {...props} />);
324
+ fireEvent.click(getByRole(\'button\'));`,
325
+ assert: [
326
+ 'expect(handleClick).toHaveBeenCalledTimes(1);'
327
+ ]
328
+ },
329
+ {
330
+ name: 'should display correct content with props',
331
+ arrange: `// Arrange - 准备显示内容测试
332
+ const testContent = \'Hello World\';
333
+ const props = { children: testContent };`,
334
+ act: `// Act - 渲染并检查内容
335
+ const { getByText } = render(<${exportName} {...props} />);`,
336
+ assert: [
337
+ 'expect(getByText(testContent)).toBeInTheDocument();'
338
+ ]
339
+ },
340
+ {
341
+ name: 'should handle missing optional props',
342
+ arrange: `// Arrange - 无可选 props`,
343
+ act: `// Act - 仅传递必需 props
344
+ const { container } = render(<${exportName} />);`,
345
+ assert: [
346
+ 'expect(container).toBeDefined();',
347
+ '// 组件应使用默认值正常渲染'
348
+ ]
349
+ }
350
+ ];
351
+ case 'api':
352
+ return [
353
+ {
354
+ name: 'should return success response for valid request',
355
+ arrange: `// Arrange - 准备有效的 API 请求参数
356
+ const request = { method: \'GET\', path: \'/api/test\' };
357
+ const mockResponse = { status: 200, data: { id: 1 } };
358
+ const spy = vi.spyOn({ ${exportName} }, '${exportName}').mockResolvedValue(mockResponse);`,
359
+ act: `// Act - 执行 API 调用
360
+ const response = await ${exportName}(request);`,
361
+ assert: [
362
+ 'expect(response).toBeDefined();',
363
+ 'expect(response.status).toBe(200);',
364
+ 'expect(response.data).toBeDefined();',
365
+ `expect(spy).toHaveBeenCalledWith(request);`,
366
+ 'expect(spy).toHaveBeenCalledTimes(1);'
367
+ ]
368
+ },
369
+ {
370
+ name: 'should handle 404 not found',
371
+ arrange: `// Arrange - 准备不存在的资源请求
372
+ const request = { method: \'GET\', path: \'/api/nonexistent\' };
373
+ const mockResponse = { status: 404, error: \'Not Found\' };
374
+ vi.spyOn({ ${exportName} }, '${exportName}').mockResolvedValue(mockResponse);`,
375
+ act: `// Act - 请求不存在的资源
376
+ const response = await ${exportName}(request);`,
377
+ assert: [
378
+ 'expect(response.status).toBe(404);',
379
+ 'expect(response.error).toBeDefined();'
380
+ ]
381
+ },
382
+ {
383
+ name: 'should handle invalid request body',
384
+ arrange: `// Arrange - 准备无效的请求体
385
+ const request = { method: \'POST\', path: \'/api/test\', body: null };
386
+ const mockResponse = { status: 400, error: \'invalid request\' };
387
+ vi.spyOn({ ${exportName} }, '${exportName}').mockResolvedValue(mockResponse);`,
388
+ act: `// Act - 发送无效请求
389
+ const response = await ${exportName}(request);`,
390
+ assert: [
391
+ 'expect(response.status).toBe(400);',
392
+ 'expect(response.error).toContain(\'invalid\');'
393
+ ]
394
+ },
395
+ {
396
+ name: 'should handle network errors gracefully',
397
+ arrange: `// Arrange - 模拟网络错误
398
+ const request = { method: \'GET\', path: \'/api/error\' };
399
+ vi.spyOn({ ${exportName} }, '${exportName}').mockRejectedValue(new Error('Network Error'));`,
400
+ act: `// Act - 处理错误响应`,
401
+ assert: [
402
+ `await expect(${exportName}(request)).rejects.toThrow('Network Error');`,
403
+ '// 应该返回错误对象而非抛出异常'
404
+ ]
405
+ }
406
+ ];
407
+ default:
408
+ // 通用模块测试
409
+ return [
410
+ {
411
+ name: 'should return defined result for valid input',
412
+ arrange: `// Arrange - 准备有效输入
413
+ const input = {};`,
414
+ act: `// Act - 执行函数
415
+ const result = ${exportName}(input);`,
416
+ assert: [
417
+ 'expect(result).toBeDefined();'
418
+ ]
419
+ },
420
+ {
421
+ name: 'should handle null input',
422
+ arrange: `// Arrange - null 输入`,
423
+ act: `// Act`,
424
+ assert: [
425
+ `expect(() => ${exportName}(null)).not.toThrow();`
426
+ ]
427
+ },
428
+ {
429
+ name: 'should handle undefined input',
430
+ arrange: `// Arrange - undefined 输入`,
431
+ act: `// Act`,
432
+ assert: [
433
+ `expect(() => ${exportName}(undefined)).not.toThrow();`
434
+ ]
435
+ }
436
+ ];
437
+ }
438
+ }
439
+ /**
440
+ * 生成单个测试用例块
441
+ */
442
+ function generateTestCaseBlock(tc, exportName) {
443
+ const indent = ' ';
444
+ const actIndent = tc.act.includes('\n') ? indent : indent + ' ';
445
+ const assertLines = tc.assert.map(a => `${indent} ${a}`).join('\n');
446
+ return `${indent}it('${tc.name}', () => {
447
+ ${indent} ${tc.arrange}
448
+ ${actIndent}${tc.act}
449
+ ${assertLines}
450
+ ${indent}});`;
451
+ }
200
452
  /**
201
453
  * 获取 describe 标题
202
454
  */
@@ -228,8 +228,10 @@ export interface ParsedTask {
228
228
  title: string;
229
229
  description: string;
230
230
  goals: string[];
231
- /** 每个 goal 的类型标注 (AI 在提取时标注),与 goals 数组一一对应 */
231
+ /** 每个 goal 的类型标注 (AI 必填),与 goals 数组一一对应 */
232
232
  goalTypes?: GoalType[];
233
+ /** 每个 goal 的复杂度标注 (AI 必填),与 goals 数组一一对应 */
234
+ goalComplexity?: ('low' | 'medium' | 'high')[];
233
235
  constraints: string[];
234
236
  deliverables: string[];
235
237
  rawContent: string;
package/package.json CHANGED
@@ -1,61 +1,61 @@
1
- {
2
- "name": "openmatrix",
3
- "version": "0.2.23",
4
- "description": "AI Agent task orchestration system with Claude Code Skills integration",
5
- "main": "dist/index.js",
6
- "types": "dist/index.d.ts",
7
- "bin": {
8
- "openmatrix": "dist/cli/index.js"
9
- },
10
- "files": [
11
- "dist",
12
- "skills",
13
- "scripts",
14
- "README.md"
15
- ],
16
- "scripts": {
17
- "build": "tsc",
18
- "dev": "tsx src/cli/index.ts",
19
- "test": "vitest",
20
- "lint": "eslint src --ext .ts",
21
- "typecheck": "tsc --noEmit",
22
- "postinstall": "node scripts/install-skills.js"
23
- },
24
- "keywords": [
25
- "claude",
26
- "claude-code",
27
- "ai-agent",
28
- "task-orchestration",
29
- "automation",
30
- "multi-agent"
31
- ],
32
- "author": "",
33
- "license": "MIT",
34
- "type": "commonjs",
35
- "repository": {
36
- "type": "git",
37
- "url": "git+https://github.com/bigfish1913/openmatrix.git"
38
- },
39
- "homepage": "https://github.com/bigfish1913/openmatrix#readme",
40
- "bugs": {
41
- "url": "https://github.com/bigfish1913/openmatrix/issues"
42
- },
43
- "dependencies": {
44
- "chalk": "^5.6.2",
45
- "chokidar": "^5.0.0",
46
- "commander": "^14.0.3",
47
- "winston": "^3.19.0"
48
- },
49
- "devDependencies": {
50
- "@types/node": "^22.0.0",
51
- "@typescript-eslint/eslint-plugin": "^8.58.1",
52
- "@typescript-eslint/parser": "^8.58.1",
53
- "@vitest/coverage-v8": "^1.6.1",
54
- "eslint": "^10.2.0",
55
- "typescript": "^5.3.3",
56
- "vitest": "^1.6.0"
57
- },
58
- "engines": {
59
- "node": ">=18.0.0"
60
- }
61
- }
1
+ {
2
+ "name": "openmatrix",
3
+ "version": "0.2.25",
4
+ "description": "AI Agent task orchestration system with Claude Code Skills integration",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "bin": {
8
+ "openmatrix": "dist/cli/index.js"
9
+ },
10
+ "files": [
11
+ "dist",
12
+ "skills",
13
+ "scripts",
14
+ "README.md"
15
+ ],
16
+ "scripts": {
17
+ "build": "tsc",
18
+ "dev": "tsx src/cli/index.ts",
19
+ "test": "vitest",
20
+ "lint": "eslint src --ext .ts",
21
+ "typecheck": "tsc --noEmit",
22
+ "postinstall": "node scripts/install-skills.js"
23
+ },
24
+ "keywords": [
25
+ "claude",
26
+ "claude-code",
27
+ "ai-agent",
28
+ "task-orchestration",
29
+ "automation",
30
+ "multi-agent"
31
+ ],
32
+ "author": "",
33
+ "license": "MIT",
34
+ "type": "commonjs",
35
+ "repository": {
36
+ "type": "git",
37
+ "url": "git+https://github.com/bigfish1913/openmatrix.git"
38
+ },
39
+ "homepage": "https://github.com/bigfish1913/openmatrix#readme",
40
+ "bugs": {
41
+ "url": "https://github.com/bigfish1913/openmatrix/issues"
42
+ },
43
+ "dependencies": {
44
+ "chalk": "^5.6.2",
45
+ "chokidar": "^5.0.0",
46
+ "commander": "^14.0.3",
47
+ "winston": "^3.19.0"
48
+ },
49
+ "devDependencies": {
50
+ "@types/node": "^22.0.0",
51
+ "@typescript-eslint/eslint-plugin": "^8.58.1",
52
+ "@typescript-eslint/parser": "^8.58.1",
53
+ "@vitest/coverage-v8": "^1.6.1",
54
+ "eslint": "^10.2.0",
55
+ "typescript": "^5.3.3",
56
+ "vitest": "^1.6.0"
57
+ },
58
+ "engines": {
59
+ "node": ">=18.0.0"
60
+ }
61
+ }