import { describe, expect, it } from "vitest"; import { analyzeJavaScriptSemantics } from "./javascriptSemanticAnalysis.js"; describe("JavaScript semantic analysis: native bindings 1", () => { it("recovers child-process argv, env, stdio, listeners, and signals", () => { const ir = analyzeJavaScriptSemantics(` import { spawn as launch } from "node:child_process"; function run() { const child = launch( "/bin/tool", ["--mode", "fast"], { env: { MODE: "test" }, stdio: ["ignore", "pipe", "pipe"] }, ); child.on("exit", onExit); child.once("error", onError); child.kill("SIGINT"); } `); expect(ir.childProcessSpawns).toEqual([ expect.objectContaining({ method: "spawn", command: "/bin/tool", argvCount: 2, environmentSupplied: true, stdioMode: "array", resolution: "complete", }), ]); expect( ir.childProcessInteractions.map( ({ kind, eventName, signalName, resolution }) => ({ kind, eventName, signalName, resolution, }), ), ).toEqual([ { kind: "listener", eventName: "exit", signalName: null, resolution: "complete", }, { kind: "listener", eventName: "error", signalName: null, resolution: "complete", }, { kind: "signal", eventName: null, signalName: "SIGINT", resolution: "complete", }, ]); expect(ir.functionFingerprints[0]?.components.effects).toContain( "child-process", ); }); it("recovers configuration, requests, response consumers, and boundaries", () => { const ir = analyzeJavaScriptSemantics(` import { readFileSync } from "node:fs"; import * as http from "node:http"; async function run(schema) { const endpoint = process.env.API_URL ?? "https://fallback.test"; const mode = process.argv[2]; const raw = readFileSync("./config.json", "utf8"); const parsed = JSON.parse(raw); const port = Number(process.env.PORT); const validated = schema.parse(parsed); const response = await fetch(endpoint, { method: "POST", body: validated, }); const body = await response.json(); http.request("https://audit.test", { body }); return { mode, port, body }; } `); expect( ir.configurationOperations.map(({ kind, key }) => ({ kind, key })), ).toEqual( expect.arrayContaining([ { kind: "environment", key: "API_URL" }, { kind: "default", key: "API_URL" }, { kind: "argv", key: "2" }, { kind: "file", key: "./config.json" }, { kind: "environment", key: "PORT" }, ]), ); expect( ir.requestOperations.map(({ kind, method, endpoint, resolution }) => ({ kind, method, endpoint, resolution, })), ).toEqual( expect.arrayContaining([ { kind: "request", method: "fetch", endpoint: null, resolution: "partial", }, { kind: "response-consumer", method: "json", endpoint: null, resolution: "complete", }, { kind: "request", method: "request", endpoint: "https://audit.test", resolution: "complete", }, ]), ); expect( ir.boundaryOperations.map(({ kind, method, resolution }) => ({ kind, method, resolution, })), ).toEqual( expect.arrayContaining([ { kind: "parse", method: "JSON.parse", resolution: "complete" }, { kind: "coerce", method: "Number", resolution: "complete" }, { kind: "parse", method: "parse", resolution: "partial" }, ]), ); expect(ir.functionFingerprints[0]?.components.effects).toContain("network"); }); }); describe("JavaScript semantic analysis: native bindings 2", () => { it("recovers built-in resource acquisition and exact local release", () => { const ir = analyzeJavaScriptSemantics(` import { open } from "node:fs/promises"; import { connect as dial } from "node:net"; async function run(path) { const file = await open(path); const socket = dial({ port: 9000 }); await file.close(); socket.destroy(); } `); expect( ir.resourceOperations.map(({ kind, method, resolution }) => ({ kind, method, resolution, })), ).toEqual([ { kind: "acquire", method: "open", resolution: "complete" }, { kind: "acquire", method: "connect", resolution: "complete" }, { kind: "release", method: "close", resolution: "complete" }, { kind: "release", method: "destroy", resolution: "complete" }, ]); expect(ir.functionFingerprints[0]?.components.effects).toContain( "resource", ); }); });