import { describe, expect, it } from "vitest"; import { mkdtempSync, rmSync } from "node:fs"; import { tmpdir } from "node:os"; import path from "node:path"; import plugin from "../extensions/pi-lsp.js"; function fakeAPI() { const tools: Array<{ name: string }> = []; const commands: string[] = []; const handlers: Record unknown> = {}; return { tools, commands, handlers, registerTool: (t: { name: string }) => { tools.push(t); }, registerCommand: (name: string, opts: { handler: (args: string, ctx: any) => unknown }) => { commands.push(name); opts.handler("", fakeCtx()); }, on: (event: string, handler: (e: any, c: any) => unknown) => { handlers[event] = handler; }, sendMessage: () => {}, } as any; } function fakeCtx() { return { cwd: mkdtempSync(path.join(tmpdir(), "pi-lsp-ext-")), isProjectTrusted: () => true, ui: { setStatus: () => {}, notify: () => {} }, signal: undefined, } as any; } describe("pi-lsp extension", () => { it("registers the full tool surface", () => { const api = fakeAPI(); plugin(api); const names = api.tools.map((t: { name: string }) => t.name).sort(); expect(names).toEqual([ "lsp_call_hierarchy", "lsp_definition", "lsp_diagnostics", "lsp_fix", "lsp_hover", "lsp_implementation", "lsp_references", "lsp_rename", "lsp_status", "lsp_symbols", "lsp_workspace_symbol", ]); }); it("registers the /lsp command", () => { const api = fakeAPI(); plugin(api); expect(api.commands).toContain("lsp"); }); it("starts a manager on session_start and shuts it down idempotently", async () => { const api = fakeAPI(); plugin(api); const ctx = fakeCtx(); try { await api.handlers.session_start({}, ctx); await api.handlers.session_shutdown({}, ctx); await api.handlers.session_shutdown({}, ctx); } finally { rmSync(ctx.cwd, { recursive: true, force: true }); } }); });