130 lines
3.4 KiB
TypeScript
130 lines
3.4 KiB
TypeScript
import { it } from "@fast-check/vitest";
|
|
import { describe, expect } from "vitest";
|
|
|
|
import { analyzeJavaScriptSemantics } from "../../../src/domain/javascriptSemanticAnalysis.js";
|
|
|
|
describe("JavaScript semantic analysis: runtime boundaries 1", () => {
|
|
it("recovers EventEmitter candidates and imported timer handle cancellation", () => {
|
|
const ir = analyzeJavaScriptSemantics(`
|
|
import {
|
|
setTimeout as later,
|
|
clearTimeout as cancel,
|
|
} from "node:timers";
|
|
function run(bus, dynamicName) {
|
|
const handle = later(work, 25);
|
|
bus.on("ready", handler);
|
|
bus.once(dynamicName, handler);
|
|
bus.emit("ready");
|
|
bus.off("ready", handler);
|
|
cancel(handle);
|
|
}
|
|
`);
|
|
|
|
expect(
|
|
ir.eventOperations.map(({ kind, method, eventName, resolution }) => ({
|
|
kind,
|
|
method,
|
|
eventName,
|
|
resolution,
|
|
})),
|
|
).toEqual([
|
|
{
|
|
kind: "register",
|
|
method: "on",
|
|
eventName: "ready",
|
|
resolution: "complete",
|
|
},
|
|
{
|
|
kind: "register",
|
|
method: "once",
|
|
eventName: null,
|
|
resolution: "unresolved",
|
|
},
|
|
{
|
|
kind: "dispatch",
|
|
method: "emit",
|
|
eventName: "ready",
|
|
resolution: "complete",
|
|
},
|
|
{
|
|
kind: "remove",
|
|
method: "off",
|
|
eventName: "ready",
|
|
resolution: "complete",
|
|
},
|
|
]);
|
|
expect(ir.timerOperations).toEqual([
|
|
expect.objectContaining({
|
|
kind: "schedule",
|
|
method: "setTimeout",
|
|
delayMilliseconds: 25,
|
|
resolution: "complete",
|
|
}),
|
|
expect.objectContaining({
|
|
kind: "cancel",
|
|
method: "clearTimeout",
|
|
delayMilliseconds: null,
|
|
resolution: "complete",
|
|
linkedTimerId: ir.timerOperations[0]?.timerId,
|
|
}),
|
|
]);
|
|
expect(ir.functionFingerprints[0]?.components.effects).toEqual([
|
|
"event",
|
|
"timer",
|
|
]);
|
|
});
|
|
|
|
it("does not treat shadowed timer globals as Node timers", () => {
|
|
const ir = analyzeJavaScriptSemantics(`
|
|
function run(setTimeout, clearTimeout) {
|
|
const handle = setTimeout(work, 1);
|
|
clearTimeout(handle);
|
|
}
|
|
`);
|
|
|
|
expect(ir.timerOperations).toEqual([]);
|
|
});
|
|
|
|
it("bounds every added semantic effect family independently", () => {
|
|
const ir = analyzeJavaScriptSemantics(
|
|
`
|
|
import { spawn } from "node:child_process";
|
|
import { open } from "node:fs/promises";
|
|
Promise.resolve(1);
|
|
bus.on("ready", handler);
|
|
setTimeout(work, 1);
|
|
spawn("/bin/tool");
|
|
const endpoint = process.env.API_URL;
|
|
fetch("https://example.test");
|
|
JSON.parse(raw);
|
|
open(path);
|
|
`,
|
|
{
|
|
maxPromiseOperations: 0,
|
|
maxEventOperations: 0,
|
|
maxTimerOperations: 0,
|
|
maxChildProcessOperations: 0,
|
|
maxConfigurationOperations: 0,
|
|
maxRequestOperations: 0,
|
|
maxBoundaryOperations: 0,
|
|
maxResourceOperations: 0,
|
|
maxObjectOperations: 0,
|
|
},
|
|
);
|
|
|
|
expect(ir.coverage.limitsReached).toEqual(
|
|
expect.arrayContaining([
|
|
"maxBoundaryOperations",
|
|
"maxChildProcessOperations",
|
|
"maxConfigurationOperations",
|
|
"maxEventOperations",
|
|
"maxPromiseOperations",
|
|
"maxRequestOperations",
|
|
"maxResourceOperations",
|
|
"maxTimerOperations",
|
|
"maxObjectOperations",
|
|
]),
|
|
);
|
|
});
|
|
});
|