mirror of
https://github.com/morluto/rea
synced 2026-10-27 06:33:32 -04:00
132 lines
5.0 KiB
TypeScript
132 lines
5.0 KiB
TypeScript
import { spawn } from "node:child_process";
|
|
import { once } from "node:events";
|
|
import { readFile, writeFile } from "node:fs/promises";
|
|
import { join } from "node:path";
|
|
import { pathToFileURL } from "node:url";
|
|
import { expect, it, onTestFinished } from "vitest";
|
|
|
|
import { toolContract } from "../../../src/contracts/toolContracts.js";
|
|
import { connectLocalToolsMcp } from "../../fixtures/localToolsMcp.js";
|
|
import { createTestTempDirectory } from "../../fixtures/temporaryDirectory.js";
|
|
|
|
it("preserves file URL scheme spelling and script identity after real Inspector state changes", async () => {
|
|
const root = await createTestTempDirectory("rea-inspector-file-url-");
|
|
const source = join(root, "source #参数%.cjs");
|
|
const runner = join(root, "runner.cjs");
|
|
const sourceText = "globalThis.fileUrlProbe = true;\n";
|
|
await writeFile(source, sourceText);
|
|
await writeFile(
|
|
runner,
|
|
`
|
|
const { readFileSync } = require("node:fs");
|
|
const vm = require("node:vm");
|
|
const scripts = [];
|
|
process.on("message", (urls) => {
|
|
for (const filename of urls) {
|
|
const script = new vm.Script(readFileSync(process.argv[2], "utf8"), { filename });
|
|
scripts.push(script);
|
|
script.runInThisContext();
|
|
}
|
|
process.send({ loaded: scripts.length });
|
|
});
|
|
setInterval(() => {}, 1000);
|
|
`,
|
|
);
|
|
const child = spawn(
|
|
process.execPath,
|
|
["--inspect=127.0.0.1:0", runner, source],
|
|
{
|
|
stdio: ["ignore", "ignore", "pipe", "ipc"],
|
|
},
|
|
);
|
|
onTestFinished(async () => {
|
|
if (child.exitCode === null && child.signalCode === null) {
|
|
const exited = once(child, "exit");
|
|
child.kill("SIGTERM");
|
|
await exited;
|
|
}
|
|
});
|
|
let stderr = "";
|
|
if (child.stderr === null) throw new Error("Expected piped Inspector stderr");
|
|
child.stderr.on("data", (chunk: Buffer) => {
|
|
stderr += chunk.toString();
|
|
});
|
|
const address = /ws:\/\/127\.0\.0\.1:\d+\/[^\s]+/u;
|
|
await expect.poll(() => stderr, { timeout: 5000 }).toMatch(address);
|
|
const socket = address.exec(stderr)?.[0];
|
|
if (socket === undefined) throw new Error("Node omitted its Inspector URL");
|
|
const endpoint = `http://127.0.0.1:${new URL(socket).port}`;
|
|
const load = async (urls: readonly string[]) => {
|
|
const loaded = once(child, "message");
|
|
child.send(urls);
|
|
await loaded;
|
|
};
|
|
const originalUrl = pathToFileURL(source).href;
|
|
await load([originalUrl]);
|
|
const { call } = await connectLocalToolsMcp();
|
|
const listed = await call("list_javascript_runtime_targets", {
|
|
inspector_endpoint: endpoint,
|
|
});
|
|
expect(listed.isError, JSON.stringify(listed.content)).not.toBe(true);
|
|
const discovery = toolContract(
|
|
"list_javascript_runtime_targets",
|
|
).outputSchema.parse(listed.structuredContent);
|
|
const targetId = discovery.normalized_result.targets[0]?.target_id;
|
|
if (targetId === undefined) throw new Error("Node target was not listed");
|
|
const request = {
|
|
inspector_endpoint: endpoint,
|
|
target_id: targetId,
|
|
observation_ms: 25,
|
|
};
|
|
const observe = async () => {
|
|
const response = await call("observe_javascript_runtime", request);
|
|
expect(response.isError, JSON.stringify(response.content)).not.toBe(true);
|
|
return toolContract("observe_javascript_runtime").outputSchema.parse(
|
|
response.structuredContent,
|
|
);
|
|
};
|
|
const first = await observe();
|
|
const initialScript = first.normalized_result.scripts.items.find(
|
|
(script) => script.reported_url === originalUrl,
|
|
);
|
|
expect(initialScript).toMatchObject({
|
|
location: { kind: "file", file_path: source },
|
|
});
|
|
|
|
const addedUrls = [
|
|
originalUrl.replace(/^file:/u, "FILE:"),
|
|
`${originalUrl.replace(/^file:/u, "FiLe:")}?instance=2#selected`,
|
|
` \t${originalUrl}\t `,
|
|
];
|
|
await load(addedUrls);
|
|
const second = await observe();
|
|
const expectedUrls = [originalUrl, ...addedUrls];
|
|
const scripts = second.normalized_result.scripts.items.filter((script) =>
|
|
expectedUrls.includes(script.reported_url ?? ""),
|
|
);
|
|
expect(scripts.map((script) => script.reported_url)).toEqual(
|
|
expect.arrayContaining(expectedUrls),
|
|
);
|
|
expect(scripts).toHaveLength(4);
|
|
expect(new Set(scripts.map((script) => script.script_key)).size).toBe(4);
|
|
for (const script of scripts)
|
|
expect(script.location).toEqual({ kind: "file", file_path: source });
|
|
expect(
|
|
scripts.find((script) => script.reported_url === originalUrl)?.script_key,
|
|
).toBe(initialScript?.script_key);
|
|
|
|
const retained = await call("get_evidence_bundle", {});
|
|
expect(retained.isError, JSON.stringify(retained.content)).not.toBe(true);
|
|
const records = toolContract("get_evidence_bundle").outputSchema.parse(
|
|
retained.structuredContent,
|
|
).result.records;
|
|
const original = records.find(
|
|
(record) => record.evidence_id === first.evidence_id,
|
|
);
|
|
if (original === undefined || !("normalized_result" in original))
|
|
throw new Error("Original Inspector Evidence was not retained");
|
|
expect(original.predicate_type).toBe(first.predicate_type);
|
|
expect(original.normalized_result).toEqual(first.normalized_result);
|
|
expect(await readFile(source, "utf8")).toBe(sourceText);
|
|
}, 30_000);
|