import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs"; import { stat } from "node:fs/promises"; import { tmpdir } from "node:os"; import { join } from "node:path"; import type { ExtensionAPI } from "@earendil-works/pi-coding-agent"; import { afterEach, expect, it, vi } from "vitest"; import { createPiSession, type PiSession, } from "../../../test/fixture/create-pi-session.ts"; import intellijExtension, { findIdeaProjectRoot } from "../index.ts"; vi.mock("node:fs/promises", async (importOriginal) => { const actual = await importOriginal(); return { ...actual, stat: vi.fn(actual.stat) }; }); let session: PiSession | undefined; let root: string | undefined; afterEach(async () => { await session?.dispose(); session = undefined; if (root) rmSync(root, { recursive: true, force: true }); root = undefined; vi.mocked(stat).mockReset(); const actual = await vi.importActual( "node:fs/promises", ); vi.mocked(stat).mockImplementation(actual.stat); }); it("does not treat an .idea file as project metadata", async () => { root = mkdtempSync(join(tmpdir(), "intellij-idea-file-")); writeFileSync(join(root, ".idea"), "not a directory"); expect(await findIdeaProjectRoot(root)).toBeUndefined(); }); it("does not register an MCP server after shutdown interrupts project detection", async () => { root = mkdtempSync(join(tmpdir(), "intellij-shutdown-")); mkdirSync(join(root, ".idea")); const actual = await vi.importActual( "node:fs/promises", ); const metadata = await actual.stat(join(root, ".idea")); let release!: (value: typeof metadata) => void; const blocked = new Promise((resolve) => { release = resolve; }); vi.mocked(stat).mockImplementation(((path, options) => String(path).endsWith(".idea") ? blocked : actual.stat(path, options)) as typeof stat); let serverNames = (): string[] => []; const probe = (pi: ExtensionAPI) => { serverNames = () => pi.getMcpServers().map((server) => server.name); }; session = await createPiSession({ cwd: root, userSettings: { intellij: { url: "http://127.0.0.1:63342/mcp" } }, extensionFactories: [intellijExtension, probe], }); await session.session.extensionRunner.emit({ type: "session_shutdown", reason: "quit", }); release(metadata); await new Promise((resolve) => setImmediate(resolve)); expect(serverNames()).toEqual([]); expect(session.errors).toEqual([]); });