Files
rea/tests/boundary/evm/evmLimiterAvailability.test.ts
T
morluto a74d859a19 fix(evm): probe the selected limiter before advertising availability
Share asynchronous executable selection between discovery and execution. Recheck it for default Linux x64 sessions, preserve caller-injected service policy and propagate cancellation through pending probes.

Refs #5086. Focused owner, inventory and MCP controls passed; Linux-specific discovery and execution cases were skipped on Darwin arm64 and remain unverified.
2026-10-20 22:18:52 +08:00

266 lines
7.4 KiB
TypeScript

import { expect, it } from "vitest";
import { buildCapabilityInventory } from "../../../src/application/CapabilityInventory.js";
import { AnalysisCancelledError } from "../../../src/domain/analysisErrorCore.js";
import {
EVM_PRLIMIT_COMMAND_DEFAULT,
evmLimiterAvailability,
} from "../../../src/evm/EvmoleInterfaceProvider.js";
const linux = { platform: "linux" as const, arch: "x64" };
const status = { open: false, capabilities: [] };
const basePolicy = { processCaptureEnabled: true, evmInterfaceEnabled: true };
it("probes the default limiter with executable and regular-file checks", async () => {
const calls: unknown[][] = [];
const availability = await evmLimiterAvailability(
{},
{
...linux,
fs: {
access: async (path, mode) => {
calls.push(["access", path, mode]);
},
stat: async (path) => {
calls.push(["stat", path]);
return { isFile: () => true };
},
},
},
);
expect(availability).toMatchObject({
status: "available",
diagnostics: { limiter_path: EVM_PRLIMIT_COMMAND_DEFAULT },
});
expect(calls).toEqual([
["access", EVM_PRLIMIT_COMMAND_DEFAULT, 1],
["stat", EVM_PRLIMIT_COMMAND_DEFAULT],
]);
});
it("uses the exact absolute override and reports a failed executable check", async () => {
const limiter = "/fixture/util-linux/prlimit";
const availability = await evmLimiterAvailability(
{ REA_EVM_PRLIMIT_COMMAND: limiter },
{
...linux,
fs: {
access: async (path) => {
expect(path).toBe(limiter);
throw Object.assign(new Error("permission denied"), {
code: "EACCES",
});
},
stat: async () => {
throw new Error("stat must not run after access failure");
},
},
},
);
expect(availability).toMatchObject({
status: "unavailable",
code: "executable_missing",
reason: expect.stringContaining(limiter),
diagnostics: { limiter_path: limiter },
});
});
it("rejects a relative override and non-file without probing an alternate command", async () => {
const relative = await evmLimiterAvailability(
{ REA_EVM_PRLIMIT_COMMAND: "prlimit" },
{
...linux,
fs: {
access: async () => {
throw new Error("relative paths must be rejected first");
},
stat: async () => ({ isFile: () => true }),
},
},
);
expect(relative).toMatchObject({
status: "unavailable",
diagnostics: { limiter_path: "prlimit" },
reason: expect.stringContaining("absolute executable path"),
});
const directory = await evmLimiterAvailability(
{},
{
...linux,
fs: {
access: async () => undefined,
stat: async () => ({ isFile: () => false }),
},
},
);
expect(directory).toMatchObject({
status: "unavailable",
reason: expect.stringContaining("regular executable file"),
});
});
it("rechecks on each observation so inventory follows the selected executable's current state", async () => {
let accessible = true;
const fs = {
access: async () => {
if (!accessible) throw new Error("ENOENT");
},
stat: async () => ({ isFile: () => true }),
};
const first = await evmLimiterAvailability({}, { ...linux, fs });
accessible = false;
const second = await evmLimiterAvailability({}, { ...linux, fs });
expect(first?.status).toBe("available");
expect(second).toMatchObject({
status: "unavailable",
reason: expect.stringContaining("ENOENT"),
});
});
it("yields the event loop while the filesystem owner probe is pending", async () => {
let release!: () => void;
const pendingAccess = new Promise<void>((resolve) => {
release = resolve;
});
let accessStarted!: () => void;
const started = new Promise<void>((resolve) => {
accessStarted = resolve;
});
const probe = evmLimiterAvailability(
{},
{
...linux,
fs: {
access: async () => {
accessStarted();
await pendingAccess;
},
stat: async () => ({ isFile: () => true }),
},
},
);
await started;
let timerRan = false;
setImmediate(() => {
timerRan = true;
});
await new Promise<void>((resolve) => setImmediate(resolve));
expect(timerRan).toBe(true);
release();
expect(await probe).toMatchObject({ status: "available" });
});
it.each(["access", "stat"] as const)(
"preserves cancellation while the %s filesystem probe is pending",
async (stage) => {
let release!: () => void;
const pendingProbe = new Promise<void>((resolve) => {
release = resolve;
});
let probeStarted!: () => void;
const started = new Promise<void>((resolve) => {
probeStarted = resolve;
});
const controller = new AbortController();
const abortReason = { source: "caller-stop" };
const probe = evmLimiterAvailability(
{},
{
...linux,
signal: controller.signal,
fs: {
access: async () => {
if (stage !== "access") return;
probeStarted();
await pendingProbe;
},
stat: async () => {
if (stage === "access")
throw new Error("stat must not run after access cancellation");
probeStarted();
await pendingProbe;
return { isFile: () => true };
},
},
},
);
try {
await started;
controller.abort(abortReason);
release();
const error = await probe.catch((cause: unknown) => cause);
expect(error).toBeInstanceOf(AnalysisCancelledError);
expect(error).toMatchObject({ operation: "binary_session" });
expect((error as AnalysisCancelledError).cause).toBe(abortReason);
} finally {
release();
}
},
);
it("leaves non-Linux-x64 host gates unchanged", async () => {
const fs = {
access: async () => {
throw new Error("unsupported hosts must not probe prlimit");
},
stat: async () => ({ isFile: () => true }),
};
expect(
await evmLimiterAvailability({}, { platform: "darwin", arch: "arm64", fs }),
).toBeUndefined();
expect(
await evmLimiterAvailability({}, { platform: "linux", arch: "arm64", fs }),
).toBeUndefined();
});
it("projects owner prerequisite facts into capability availability", async () => {
const unavailable = await evmLimiterAvailability(
{},
{
...linux,
fs: {
access: async () => {
throw new Error("ENOENT");
},
stat: async () => ({ isFile: () => true }),
},
},
);
const unavailableInventory = buildCapabilityInventory(status, {
...basePolicy,
...(unavailable === undefined
? {}
: { evmLimiterAvailability: unavailable }),
});
expect(
unavailableInventory.find(({ name }) => name === "inspect_evm_interface"),
).toMatchObject({
available: false,
reason: "provider_missing",
remediation: expect.stringContaining("caller-supplied util-linux prlimit"),
});
const available = await evmLimiterAvailability(
{},
{
...linux,
fs: {
access: async () => undefined,
stat: async () => ({ isFile: () => true }),
},
},
);
const availableInventory = buildCapabilityInventory(status, {
...basePolicy,
...(available === undefined ? {} : { evmLimiterAvailability: available }),
});
expect(
availableInventory.find(({ name }) => name === "inspect_evm_interface"),
).toMatchObject({ available: true, reason: "available", remediation: null });
});