69 lines
2.9 KiB
TypeScript
69 lines
2.9 KiB
TypeScript
import { parametersSchema as z, defineCustomTool } from "@roo-code/types"
|
|
import { spawnSync } from "child_process"
|
|
import { readdirSync, readFileSync, existsSync } from "fs"
|
|
import { join } from "path"
|
|
|
|
export default defineCustomTool({
|
|
name: "mvn_test",
|
|
description: "Run Maven tests for a module in a multi-module project. Parses surefire XML reports and returns structured pass/fail results.",
|
|
parameters: z.object({
|
|
worktreePath: z.string().describe("Path to the project root or worktree (e.g. /path/to/my-project)"),
|
|
module: z.string().describe("Module path relative to java/, e.g. modules/cs-modules/my-module"),
|
|
testClass: z.string().optional().describe("Specific test class to run, e.g. MyServiceTest"),
|
|
}),
|
|
async execute({ worktreePath, module, testClass }) {
|
|
const pomPath = join(worktreePath, "java", "pom.xml")
|
|
if (!existsSync(pomPath)) {
|
|
return `Error: pom.xml not found at ${pomPath}`
|
|
}
|
|
|
|
const args = ["test", "-pl", `java/${module}`, "-f", pomPath, "--batch-mode", "-q"]
|
|
if (testClass) {
|
|
args.push(`-Dtest=${testClass}`)
|
|
}
|
|
|
|
const result = spawnSync("mvn", args, {
|
|
encoding: "utf-8",
|
|
timeout: 300000,
|
|
cwd: worktreePath,
|
|
maxBuffer: 10 * 1024 * 1024,
|
|
})
|
|
|
|
const surefireDir = join(worktreePath, "java", module, "target", "surefire-reports")
|
|
const summary = { total: 0, passed: 0, failed: 0, errors: 0, skipped: 0, failures: [] as string[] }
|
|
|
|
if (existsSync(surefireDir)) {
|
|
try {
|
|
const xmlFiles = readdirSync(surefireDir).filter(f => f.startsWith("TEST-") && f.endsWith(".xml"))
|
|
for (const xmlFile of xmlFiles) {
|
|
const content = readFileSync(join(surefireDir, xmlFile), "utf-8")
|
|
const testsMatch = content.match(/tests="(\d+)"/)
|
|
const failuresMatch = content.match(/failures="(\d+)"/)
|
|
const errorsMatch = content.match(/errors="(\d+)"/)
|
|
const skippedMatch = content.match(/skipped="(\d+)"/)
|
|
|
|
if (testsMatch) summary.total += parseInt(testsMatch[1])
|
|
if (failuresMatch) summary.failed += parseInt(failuresMatch[1])
|
|
if (errorsMatch) summary.errors += parseInt(errorsMatch[1])
|
|
if (skippedMatch) summary.skipped += parseInt(skippedMatch[1])
|
|
|
|
const failureMatches = content.matchAll(/<failure[^>]*message="([^"]*)"[^>]*>/g)
|
|
for (const m of failureMatches) {
|
|
summary.failures.push(`${xmlFile.replace("TEST-", "").replace(".xml", "")}: ${m[1]}`)
|
|
}
|
|
}
|
|
summary.passed = summary.total - summary.failed - summary.errors - summary.skipped
|
|
} catch (e) {
|
|
// surefire parsing failed, continue with what we have
|
|
}
|
|
}
|
|
|
|
return JSON.stringify({
|
|
buildStatus: result.status === 0 ? "SUCCESS" : "FAILURE",
|
|
exitCode: result.status,
|
|
summary,
|
|
lastOutput: (result.status !== 0 ? (result.stderr || result.stdout || "") : "").split("\n").slice(-15).join("\n"),
|
|
}, null, 2)
|
|
},
|
|
})
|