import { readdirSync, readFileSync } from "node:fs"; import path from "node:path"; import { fileURLToPath } from "node:url"; import { afterEach, describe, expect, it } from "vitest"; import { createPiSession, type PiSession, } from "../../../test/fixture/create-pi-session.ts"; let session: PiSession | undefined; afterEach(async () => { await session?.dispose(); session = undefined; }); function runtimePath(): string { return fileURLToPath(new URL("../index.ts", import.meta.url)); } describe("runtime contract", () => { it("loads through the owned fixture", async () => { const extensionPath = runtimePath(); session = await createPiSession({ extensionPaths: [extensionPath] }); expect(session.errors).toEqual([]); expect(session.extensions).toHaveLength(1); expect(session.extensions[0]!.path).toBe(extensionPath); }); it("writes its opt-in lifecycle trace inside the sandbox", async () => { session = await createPiSession({ extensionPaths: [runtimePath()], env: { PI_RUNTIME_DEBUG: "1" }, }); const runner = session.session.extensionRunner; await runner.emit({ type: "session_start", reason: "startup" }); await runner.emit({ type: "session_shutdown", reason: "quit" }); const stateHome = session.env["XDG_STATE_HOME"]; if (!stateHome) throw new Error("missing sandbox state home"); const directory = path.join(stateHome, "pi-ext", "debug", "runtime"); const [file] = readdirSync(directory); if (!file) throw new Error("missing debug trace file"); const events = readFileSync(path.join(directory, file), "utf8") .trim() .split("\n") .map((line) => JSON.parse(line).event); expect(events).toEqual( expect.arrayContaining(["session.start", "session.shutdown"]), ); }); });