import { exec } from "child_process"; import { promisify } from "util"; import { existsSync } from "fs"; import { join } from "path"; const execAsync = promisify(exec); export const runTestsTool = { name: "run_tests", description: "运行项目测试。自动检测测试框架(Jest、Mocha、Pytest 等),执行测试并返回结构化的结果(通过/失败/跳过数量及失败详情)。", inputSchema: { type: "object", properties: { project_path: { type: "string", description: "项目根目录(绝对路径)", }, test_file: { type: "string", description: "指定测试文件(可选,默认运行全部测试)", }, test_name: { type: "string", description: "指定测试名称或模式(可选)", }, }, required: ["project_path"], }, }; async function runCommand(cmd, cwd) { try { const { stdout, stderr } = await execAsync(cmd, { cwd, timeout: 120000, maxBuffer: 1024 * 1024 * 10, shell: process.platform === "win32" ? "powershell.exe" : "/bin/bash", }); return { stdout, stderr, code: 0 }; } catch (error) { return { stdout: error.stdout || "", stderr: error.stderr || "", code: error.code ?? 1, }; } } export async function executeRunTests(args) { const { project_path, test_file, test_name } = args; const hasFile = (name) => existsSync(join(project_path, name)); let testCmd = ""; let framework = ""; // 检测测试框架 if (hasFile("jest.config.js") || hasFile("jest.config.ts") || hasFile("jest.config.mjs")) { framework = "Jest"; testCmd = "npx jest --verbose --no-coverage"; if (test_file) testCmd += ` "${test_file}"`; if (test_name) testCmd += ` -t "${test_name}"`; } else if (hasFile("vitest.config.ts") || hasFile("vitest.config.js")) { framework = "Vitest"; testCmd = "npx vitest run --reporter verbose"; if (test_file) testCmd += ` "${test_file}"`; } else if (hasFile("pytest.ini") || hasFile("pyproject.toml") || hasFile("setup.cfg")) { framework = "Pytest"; testCmd = "python -m pytest -v"; if (test_file) testCmd += ` "${test_file}"`; if (test_name) testCmd += ` -k "${test_name}"`; } else if (hasFile("package.json")) { // 尝试 package.json 中的 test 脚本 framework = "npm test"; testCmd = "npm test 2>&1"; } else if (hasFile("requirements.txt")) { framework = "Pytest (default)"; testCmd = "python -m pytest -v"; if (test_file) testCmd += ` "${test_file}"`; } if (!testCmd) { return `# 测试结果\n\n⚠️ 未检测到测试框架\n项目路径: ${project_path}\n\n建议:\n- JS/TS: npm install -D jest 或 vitest\n- Python: pip install pytest`; } const result = await runCommand(testCmd, project_path); const fullOutput = (result.stdout + "\n" + result.stderr).trim(); // 解析测试结果 let passed = 0, failed = 0, skipped = 0; // Jest / Vitest 格式 const jestMatch = fullOutput.match(/Tests:\s+(?:(\d+) failed,?\s*)?(?:(\d+) skipped,?\s*)?(?:(\d+) passed)?/); if (jestMatch) { failed = parseInt(jestMatch[1] || "0"); skipped = parseInt(jestMatch[2] || "0"); passed = parseInt(jestMatch[3] || "0"); } // Pytest 格式 const pytestMatch = fullOutput.match(/(\d+) passed(?:.*?(\d+) failed)?(?:.*?(\d+) skipped)?/); if (pytestMatch) { passed = parseInt(pytestMatch[1] || "0"); failed = parseInt(pytestMatch[2] || "0"); skipped = parseInt(pytestMatch[3] || "0"); } const total = passed + failed + skipped; const allPassed = failed === 0 && result.code === 0; const output = [ `# 测试报告`, ``, `📂 项目: ${project_path}`, `🔧 框架: ${framework}`, ``, `## 结果`, allPassed ? "✅ **全部通过**" : "❌ **存在失败**", ``, `| 状态 | 数量 |`, `|------|------|`, `| ✅ 通过 | ${passed} |`, `| ❌ 失败 | ${failed} |`, `| ⏭️ 跳过 | ${skipped} |`, `| 📊 总计 | ${total} |`, ``, `## 命令`, `\`${testCmd}\``, ``, `## 完整输出`, "```", fullOutput.slice(0, 5000), "```", ]; if (failed > 0) { output.push(``, `## 建议`, `1. 查看上方失败的测试用例详情`, `2. 使用 code_debug 工具分析失败原因`, `3. 修复后重新运行 run_tests 验证`); } return output.join("\n"); } //# sourceMappingURL=runTests.js.map