import { describe, it, before, after } from "node:test"; import * as assert from "node:assert/strict"; import * as fs from "node:fs"; import * as os from "node:os"; import * as path from "node:path"; import { setTestSocket, stopTestDaemon, runCliJson, requireServer, } from "../helpers.ts"; const skip = requireServer("gopls"); async function pollUntil( fn: () => Promise, predicate: (v: T) => boolean, timeoutMs: number, intervalMs = 250, ): Promise { const deadline = Date.now() + timeoutMs; let last: T = await fn(); while (Date.now() < deadline) { if (predicate(last)) return last; await new Promise((r) => setTimeout(r, intervalMs)); last = await fn(); } return last; } interface DiagResult { [serverId: string]: { diagnostics?: { message: string }[] }; } describe("watcher: gopls picks up externally-created files", { skip: skip ?? undefined }, () => { let tmpDir: string; let mainFile: string; let helperFile: string; const env = { ...process.env }; let cleanup: () => void; before(async () => { cleanup = setTestSocket(env); await stopTestDaemon(env); tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "pi-lsp-gopls-watch-")); fs.writeFileSync(path.join(tmpDir, "go.mod"), "module example.com/wtest\n\ngo 1.21\n"); mainFile = path.join(tmpDir, "main.go"); helperFile = path.join(tmpDir, "helper.go"); fs.writeFileSync( mainFile, "package main\n\nfunc main() {\n\tHelper()\n}\n", ); }); after(async () => { await stopTestDaemon(env); cleanup(); fs.rmSync(tmpDir, { recursive: true, force: true }); }); it("initially reports undefined symbol", async () => { const result = await pollUntil( async () => (await runCliJson( [mainFile, "diagnostics", '{"timeoutMs":3000}'], env, )) as DiagResult, (r) => { const diags = r["gopls"]?.diagnostics ?? []; return diags.some( (d) => d.message.toLowerCase().includes("undefined") || d.message.includes("Helper"), ); }, 15000, 500, ); const diags = result["gopls"]?.diagnostics ?? []; const hasUndefined = diags.some((d) => d.message.toLowerCase().includes("undefined") || d.message.includes("Helper"), ); assert.ok( hasUndefined, `Expected undefined-symbol diagnostic, got: ${JSON.stringify(diags)}`, ); }); it("clears the diagnostic after helper.go is created externally", async () => { fs.writeFileSync( helperFile, "package main\n\nfunc Helper() {}\n", ); const result = await pollUntil( async () => (await runCliJson( [mainFile, "diagnostics", '{"timeoutMs":3000}'], env, )) as DiagResult, (r) => { const diags = r["gopls"]?.diagnostics ?? []; return !diags.some( (d) => d.message.toLowerCase().includes("undefined") || d.message.includes("Helper"), ); }, 15000, 500, ); const finalDiags = result["gopls"]?.diagnostics ?? []; const stillUndefined = finalDiags.some( (d) => d.message.toLowerCase().includes("undefined") || d.message.includes("Helper"), ); assert.ok( !stillUndefined, `Expected diagnostic to clear after creating helper.go, still got: ${JSON.stringify(finalDiags)}`, ); }); });