mirror of
https://github.com/morluto/rea.git
synced 2026-10-26 17:35:11 +00:00
test(mcp): validate selected tools without compiling the entire catalog
This commit is contained in:
1 parent
dec4cf50cb
commit
cc5a96933a
5 files changed
+37
-64
No files matched your search
@@ -1,4 +1,3 @@
|
||||
import type { Client } from "@modelcontextprotocol/client";
|
||||
import { readFile, writeFile } from "node:fs/promises";
|
||||
import { join } from "node:path";
|
||||
import { expect, it } from "vitest";
|
||||
@@ -10,7 +9,7 @@ import { javascriptApplicationAnalysisResultSchema } from "../../../src/domain/j
|
||||
import { parseMcpToolError } from "../../fixtures/mcpToolError.js";
|
||||
import { connectLocalToolsMcp } from "../../fixtures/localToolsMcp.js";
|
||||
import { createTestTempDirectory } from "../../fixtures/temporaryDirectory.js";
|
||||
import { cliTest } from "../../support/cli/cliFixture.js";
|
||||
import { cliTest, type SharedMcpCall } from "../../support/cli/cliFixture.js";
|
||||
|
||||
const cases = [
|
||||
{
|
||||
@@ -99,14 +98,15 @@ it("preserves MCP analysis limits in retained Evidence and an exported bundle ro
|
||||
await writeFile(join(root, "main.js"), "export const observed = 7;\n");
|
||||
const bundleRoot = await createTestTempDirectory("rea-js-limit-bundle-");
|
||||
const path = join(bundleRoot, "evidence.json");
|
||||
const { client } = await connectLocalToolsMcp({
|
||||
const { call } = await connectLocalToolsMcp({
|
||||
NODE_OPTIONS: "--max-old-space-size=512",
|
||||
});
|
||||
const expected = new Map<string, unknown>();
|
||||
for (const { controls } of cases) {
|
||||
const response = await client.callTool({
|
||||
name: "analyze_javascript_application",
|
||||
arguments: { input_path: root, ...controls, detail: "summary" },
|
||||
const response = await call("analyze_javascript_application", {
|
||||
input_path: root,
|
||||
...controls,
|
||||
detail: "summary",
|
||||
});
|
||||
expect(response.isError, JSON.stringify(response.content)).not.toBe(true);
|
||||
const { parent_evidence_id } = z
|
||||
@@ -122,32 +122,23 @@ it("preserves MCP analysis limits in retained Evidence and an exported bundle ro
|
||||
});
|
||||
}
|
||||
expect(expected.size).toBe(cases.length);
|
||||
const retained = await getBundle(client);
|
||||
const retained = await getBundle(call);
|
||||
for (const [evidence_id, parameters] of expected)
|
||||
expect(
|
||||
retained.records.find((record) => record.evidence_id === evidence_id)
|
||||
?.parameters,
|
||||
).toEqual(parameters);
|
||||
const exported = await client.callTool({
|
||||
name: "export_evidence_bundle",
|
||||
arguments: { path },
|
||||
});
|
||||
const exported = await call("export_evidence_bundle", { path });
|
||||
expect(exported.isError).not.toBe(true);
|
||||
expect(parseEvidenceBundle(JSON.parse(await readFile(path, "utf8")))).toEqual(
|
||||
retained,
|
||||
);
|
||||
const closed = await client.callTool({
|
||||
name: "close_binary",
|
||||
arguments: {},
|
||||
});
|
||||
const closed = await call("close_binary", {});
|
||||
expect(closed.isError).not.toBe(true);
|
||||
expect((await getBundle(client)).records).toEqual([]);
|
||||
const imported = await client.callTool({
|
||||
name: "import_evidence_bundle",
|
||||
arguments: { path },
|
||||
});
|
||||
expect((await getBundle(call)).records).toEqual([]);
|
||||
const imported = await call("import_evidence_bundle", { path });
|
||||
expect(imported.isError, JSON.stringify(imported.content)).not.toBe(true);
|
||||
expect(await getBundle(client)).toEqual(retained);
|
||||
expect(await getBundle(call)).toEqual(retained);
|
||||
}, 60_000);
|
||||
|
||||
cliTest(
|
||||
@@ -184,17 +175,14 @@ cliTest(
|
||||
] as const
|
||||
).map(([path, content]) => writeFile(join(root, path), content)),
|
||||
);
|
||||
const { client } = await connectLocalToolsMcp({
|
||||
const { client, call } = await connectLocalToolsMcp({
|
||||
NODE_OPTIONS: "--max-old-space-size=512",
|
||||
});
|
||||
const response = await client.callTool({
|
||||
name: "analyze_javascript_application",
|
||||
arguments: {
|
||||
input_path: root,
|
||||
max_heap_mb: 128,
|
||||
analysis_timeout_ms: 1,
|
||||
source_paths: sourcePaths,
|
||||
},
|
||||
const response = await call("analyze_javascript_application", {
|
||||
input_path: root,
|
||||
max_heap_mb: 128,
|
||||
analysis_timeout_ms: 1,
|
||||
source_paths: sourcePaths,
|
||||
});
|
||||
expect(parseMcpToolError(response)).toMatchObject({
|
||||
error: {
|
||||
@@ -210,7 +198,7 @@ cliTest(
|
||||
},
|
||||
},
|
||||
});
|
||||
const bundle = await getBundle(client);
|
||||
const bundle = await getBundle(call);
|
||||
const partial = bundle.records.find(
|
||||
(record) => record.operation === "analyze_javascript_application",
|
||||
);
|
||||
@@ -244,13 +232,10 @@ cliTest(
|
||||
})
|
||||
.parse(cliTimeout.json);
|
||||
const cliPartial = parseEvidence(cliFailure.details.partial_observation);
|
||||
const control = await client.callTool({
|
||||
name: "analyze_javascript_application",
|
||||
arguments: {
|
||||
input_path: root,
|
||||
max_heap_mb: 128,
|
||||
source_paths: sourcePaths,
|
||||
},
|
||||
const control = await call("analyze_javascript_application", {
|
||||
input_path: root,
|
||||
max_heap_mb: 128,
|
||||
source_paths: sourcePaths,
|
||||
});
|
||||
expect(control.isError).not.toBe(true);
|
||||
const controlEvidence = parseEvidence(control.structuredContent);
|
||||
@@ -271,11 +256,8 @@ cliTest(
|
||||
60_000,
|
||||
);
|
||||
|
||||
async function getBundle(client: Client) {
|
||||
const response = await client.callTool({
|
||||
name: "get_evidence_bundle",
|
||||
arguments: { detail: "complete" },
|
||||
});
|
||||
async function getBundle(call: SharedMcpCall) {
|
||||
const response = await call("get_evidence_bundle", { detail: "complete" });
|
||||
expect(response.isError).not.toBe(true);
|
||||
return parseEvidenceBundle(
|
||||
z.object({ result: z.unknown() }).parse(response.structuredContent).result,
|
||||
|
||||
@@ -92,11 +92,9 @@ it("observes a real Node Inspector and releases attachments after cancellation a
|
||||
const before = attachments();
|
||||
const controller = new AbortController();
|
||||
const cancelled = expect(
|
||||
client.callTool(
|
||||
{
|
||||
name: "observe_javascript_runtime",
|
||||
arguments: { ...request, observation_ms: 10_000 },
|
||||
},
|
||||
call(
|
||||
"observe_javascript_runtime",
|
||||
{ ...request, observation_ms: 10_000 },
|
||||
{ signal: controller.signal },
|
||||
),
|
||||
).rejects.toThrow("cancel live observation");
|
||||
|
||||
@@ -485,10 +485,7 @@ it("inspects native SAZ through the public CLI and MCP with equal evidence, byte
|
||||
expect(response.isError, JSON.stringify(response)).not.toBe(true);
|
||||
const fromMcp = parseEvidence(response.structuredContent);
|
||||
expect(fromMcp.normalized_result).toEqual(evidence.normalized_result);
|
||||
const bundle = await client.callTool({
|
||||
name: "get_evidence_bundle",
|
||||
arguments: { detail: "complete" },
|
||||
});
|
||||
const bundle = await call("get_evidence_bundle", { detail: "complete" });
|
||||
const records = z
|
||||
.object({ result: z.object({ records: z.array(z.unknown()) }) })
|
||||
.parse(bundle.structuredContent)
|
||||
|
||||
@@ -119,14 +119,11 @@ ${body}
|
||||
},
|
||||
});
|
||||
|
||||
const { client } = await connectLocalToolsMcp(environment);
|
||||
const response = await client.callTool({
|
||||
name: "inspect_wasm_artifact",
|
||||
arguments: {
|
||||
path: selected,
|
||||
candidate_paths: [candidate],
|
||||
glue_paths: [glue],
|
||||
},
|
||||
const { client, call } = await connectLocalToolsMcp(environment);
|
||||
const response = await call("inspect_wasm_artifact", {
|
||||
path: selected,
|
||||
candidate_paths: [candidate],
|
||||
glue_paths: [glue],
|
||||
});
|
||||
const mcpError = parseMcpToolError(response).error;
|
||||
expect(mcpError.code).toBe("invalid_request");
|
||||
@@ -141,9 +138,8 @@ ${body}
|
||||
.passthrough()
|
||||
.parse(mcpError.details).partial_observation,
|
||||
);
|
||||
const retainedReply = await client.callTool({
|
||||
name: "get_evidence_bundle",
|
||||
arguments: { detail: "complete" },
|
||||
const retainedReply = await call("get_evidence_bundle", {
|
||||
detail: "complete",
|
||||
});
|
||||
expect(retainedReply.isError).not.toBe(true);
|
||||
const retainedBundle = toolContract(
|
||||
|
||||
@@ -29,7 +29,7 @@ it("delivers typed tool failures without violating the advertised success schema
|
||||
const tool = tools.find(({ name }) => name === request.name);
|
||||
if (tool?.outputSchema === undefined)
|
||||
throw new Error(`${request.name} omitted its output schema`);
|
||||
const reply = await client.callTool(request);
|
||||
const reply = await client.callTool(request, { toolDefinition: tool });
|
||||
const diagnostic = parseMcpToolError(reply);
|
||||
expect(diagnostic.error.message, request.name).not.toBe("");
|
||||
if ("path" in request.arguments || "input_path" in request.arguments)
|
||||
|
||||
Reference in new issue
Block a user