* perf(test): reuse one compiled CLI process per JavaScript application file analyzeJavaScriptDirectory CLI cases were still starting a fresh process after stdio MCP reuse. Keep one compiled process for the same child environment so later cases skip Node and CLI startup. Co-authored-by: morluto <morluto@users.noreply.github.com> * fix(test): bound shared CLI worker lifetime Retire a worker that reports a fatal failure before resolving that command, and wait for it to exit before the next command starts. Join a session frame once its bytes are complete instead of copying the unfinished buffer on every chunk. Restart the shared process after 32 commands or a reported 1 GiB resident set so one file does not keep a single heap high-water mark. Co-authored-by: morluto <morluto@users.noreply.github.com> * fix(test): retire a shared CLI worker after a transport failure A malformed session frame rejected the command and sent SIGTERM without clearing the worker. signalCode stays unset until the process exits, so the next queued command could write to that same process. Transport failures now use the fatal-reply retirement path and the queue waits for that exit. Co-authored-by: morluto <morluto@users.noreply.github.com> --------- Co-authored-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: morluto <morluto@users.noreply.github.com>
84 lines
2.6 KiB
TypeScript
84 lines
2.6 KiB
TypeScript
import { execFile } from "node:child_process";
|
|
import { promisify } from "node:util";
|
|
|
|
import { expect } from "vitest";
|
|
import { z } from "zod";
|
|
|
|
import { javascriptApplicationAnalysisResultSchema } from "../../../src/domain/javascript/javascriptApplicationAnalysis.js";
|
|
import { connectLocalToolsMcp } from "../../fixtures/localToolsMcp.js";
|
|
import type { SharedMcpCall, TestCli } from "./cliFixture.js";
|
|
|
|
const execute = promisify(execFile);
|
|
|
|
export const runNodeJsonControl = async (
|
|
root: string,
|
|
filename: string,
|
|
): Promise<unknown> => {
|
|
const { stdout } = await execute(process.execPath, [filename], {
|
|
cwd: root,
|
|
timeout: 5_000,
|
|
});
|
|
return JSON.parse(stdout) as unknown;
|
|
};
|
|
|
|
/**
|
|
* Use an explicit caller when the test owns the server. Otherwise reuse the
|
|
* file-scoped session from `cliTest`, and start a fresh server only for
|
|
* drivers created outside that fixture.
|
|
*/
|
|
export const resolveJavaScriptApplicationMcpCall = async (
|
|
cli: TestCli,
|
|
mcpCall: SharedMcpCall | undefined,
|
|
): Promise<SharedMcpCall> => {
|
|
if (mcpCall !== undefined) return mcpCall;
|
|
if (cli.sharedMcpCall !== undefined) return cli.sharedMcpCall();
|
|
return (await connectLocalToolsMcp()).call;
|
|
};
|
|
|
|
/**
|
|
* Analyze a directory. A supplied MCP caller reuses its test-owned connection.
|
|
* CLI uses the driver's file-scoped process when present, and a fresh process
|
|
* otherwise.
|
|
*/
|
|
export const analyzeJavaScriptDirectory = async (
|
|
surface: "CLI" | "stdio MCP",
|
|
cli: TestCli,
|
|
root: string,
|
|
{
|
|
sourcePaths = [],
|
|
mcpCall,
|
|
}: {
|
|
readonly sourcePaths?: readonly string[];
|
|
readonly mcpCall?: SharedMcpCall | undefined;
|
|
} = {},
|
|
) => {
|
|
let document: unknown;
|
|
if (surface === "CLI") {
|
|
const run = cli.sharedCliRun ?? ((invocation) => cli.run(invocation));
|
|
const response = await run({
|
|
arguments: [
|
|
"analyze-javascript-application",
|
|
root,
|
|
...sourcePaths.flatMap((path) => ["--source-path", path]),
|
|
"--format",
|
|
"json",
|
|
],
|
|
environment: { REA_LOG_LEVEL: "silent" },
|
|
});
|
|
expect(response.exitCode, response.stderr).toBe(0);
|
|
document = response.json;
|
|
} else {
|
|
const call = await resolveJavaScriptApplicationMcpCall(cli, mcpCall);
|
|
const response = await call("analyze_javascript_application", {
|
|
input_path: root,
|
|
format: "directory",
|
|
...(sourcePaths.length === 0 ? {} : { source_paths: sourcePaths }),
|
|
});
|
|
expect(response.isError, JSON.stringify(response)).not.toBe(true);
|
|
document = response.structuredContent;
|
|
}
|
|
return z
|
|
.object({ normalized_result: javascriptApplicationAnalysisResultSchema })
|
|
.parse(document).normalized_result;
|
|
};
|