import { type ChildProcess, spawn } from "node:child_process"; import { once } from "node:events"; import { access } from "node:fs/promises"; import { join } from "node:path"; import { fauxAssistantMessage, InMemoryCredentialStore, } from "@earendil-works/pi-ai"; import { afterEach, describe, expect, it } from "vitest"; import { createPiSession, type PiSession } from "./create-pi-session.ts"; let session: PiSession | undefined; afterEach(async () => { await session?.dispose(); session = undefined; }); describe("owned Pi fixture", () => { it("waits for a briefly retained child cwd before deleting owned fixtures", async () => { let child: ChildProcess | undefined; session = await createPiSession({ extensionFactories: [ (pi) => { pi.on("session_shutdown", () => { setTimeout(() => child?.kill(), 150); }); }, ], }); const cwd = session.cwd; child = spawn( process.execPath, ["-e", "process.stdout.write('ready'); setInterval(() => {}, 1000);"], { cwd, stdio: ["ignore", "pipe", "ignore"] }, ); const closed = once(child, "close"); try { await once(child.stdout!, "data"); await session.dispose(); await closed; expect(session.errors).toEqual([]); await expect(access(cwd)).rejects.toMatchObject({ code: "ENOENT" }); } finally { child.kill(); await closed; } }); it("surfaces shutdown errors after restoring environment and deleting its owned cwd", async () => { const originalHome = process.env["HOME"]; session = await createPiSession({ extensionFactories: [ (pi) => { pi.on("session_shutdown", () => { throw new Error("shutdown regression evidence"); }); }, ], }); const cwd = session.cwd; await expect(session.dispose()).rejects.toThrow( "shutdown regression evidence", ); expect(process.env["HOME"]).toBe(originalHome); await expect(access(cwd)).rejects.toMatchObject({ code: "ENOENT" }); }); it("keeps injected credentials and catalog cache off disk", async () => { session = await createPiSession({ credentials: new InMemoryCredentialStore(), extensionPaths: [], responses: [fauxAssistantMessage("in-memory credentials")], }); await session.session.prompt("hi"); await session.session.modelRuntime.refresh({ allowNetwork: false }); expect(session.errors).toEqual([]); expect(session.providerState.callCount).toBe(1); for (const file of ["auth.json", "models-store.json"]) { await expect(access(join(session.agentDir, file))).rejects.toMatchObject({ code: "ENOENT", }); } }); it("drives a scripted model turn through the public provider", async () => { session = await createPiSession({ extensionPaths: [], responses: [fauxAssistantMessage("hello from the owned fixture")], }); await session.session.prompt("hi"); expect(session.errors).toEqual([]); expect(session.providerState.callCount).toBe(1); }); });