mirror of
https://fastgit.cc/https://github.com/anomalyco/opencode
synced 2026-04-20 21:00:29 +08:00
test(lsp): cover diagnostics wait and spawn dedupe
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import { describe, expect, test, beforeEach } from "bun:test"
|
||||
import path from "path"
|
||||
import { Effect } from "effect"
|
||||
import { Bus } from "../../src/bus"
|
||||
import { LSPClient } from "../../src/lsp"
|
||||
import { LSPServer } from "../../src/lsp"
|
||||
import { Log } from "../../src/util"
|
||||
@@ -17,21 +18,27 @@ function spawnFakeServer() {
|
||||
}
|
||||
}
|
||||
|
||||
async function createClient() {
|
||||
const handle = spawnFakeServer() as any
|
||||
const cwd = process.cwd()
|
||||
const client = await Effect.runPromise(
|
||||
LSPClient.create({
|
||||
serverID: "fake",
|
||||
server: handle as unknown as LSPServer.Handle,
|
||||
root: cwd,
|
||||
}).pipe(provideInstance(cwd)),
|
||||
)
|
||||
|
||||
return { client, cwd }
|
||||
}
|
||||
|
||||
describe("LSPClient interop", () => {
|
||||
beforeEach(async () => {
|
||||
await Log.init({ print: true })
|
||||
})
|
||||
|
||||
test("handles workspace/workspaceFolders request", async () => {
|
||||
const handle = spawnFakeServer() as any
|
||||
|
||||
const client = await Effect.runPromise(
|
||||
LSPClient.create({
|
||||
serverID: "fake",
|
||||
server: handle as unknown as LSPServer.Handle,
|
||||
root: process.cwd(),
|
||||
}).pipe(provideInstance(process.cwd())),
|
||||
)
|
||||
const { client } = await createClient()
|
||||
|
||||
await client.connection.sendNotification("test/trigger", {
|
||||
method: "workspace/workspaceFolders",
|
||||
@@ -45,15 +52,7 @@ describe("LSPClient interop", () => {
|
||||
})
|
||||
|
||||
test("handles client/registerCapability request", async () => {
|
||||
const handle = spawnFakeServer() as any
|
||||
|
||||
const client = await Effect.runPromise(
|
||||
LSPClient.create({
|
||||
serverID: "fake",
|
||||
server: handle as unknown as LSPServer.Handle,
|
||||
root: process.cwd(),
|
||||
}).pipe(provideInstance(process.cwd())),
|
||||
)
|
||||
const { client } = await createClient()
|
||||
|
||||
await client.connection.sendNotification("test/trigger", {
|
||||
method: "client/registerCapability",
|
||||
@@ -67,15 +66,7 @@ describe("LSPClient interop", () => {
|
||||
})
|
||||
|
||||
test("handles client/unregisterCapability request", async () => {
|
||||
const handle = spawnFakeServer() as any
|
||||
|
||||
const client = await Effect.runPromise(
|
||||
LSPClient.create({
|
||||
serverID: "fake",
|
||||
server: handle as unknown as LSPServer.Handle,
|
||||
root: process.cwd(),
|
||||
}).pipe(provideInstance(process.cwd())),
|
||||
)
|
||||
const { client } = await createClient()
|
||||
|
||||
await client.connection.sendNotification("test/trigger", {
|
||||
method: "client/unregisterCapability",
|
||||
@@ -87,4 +78,30 @@ describe("LSPClient interop", () => {
|
||||
|
||||
await Effect.runPromise(client.shutdown())
|
||||
})
|
||||
|
||||
test("waitForDiagnostics() resolves when a matching diagnostic event is published", async () => {
|
||||
const { client, cwd } = await createClient()
|
||||
const file = path.join(cwd, "fixture.ts")
|
||||
|
||||
const waiting = Effect.runPromise(client.waitForDiagnostics({ path: file }).pipe(provideInstance(cwd)))
|
||||
|
||||
await Effect.runPromise(Effect.sleep(20))
|
||||
await Effect.runPromise(Effect.promise(() => Bus.publish(LSPClient.Event.Diagnostics, { path: file, serverID: "fake" })).pipe(provideInstance(cwd)))
|
||||
await waiting
|
||||
|
||||
await Effect.runPromise(client.shutdown())
|
||||
})
|
||||
|
||||
test("waitForDiagnostics() times out without throwing when no event arrives", async () => {
|
||||
const { client, cwd } = await createClient()
|
||||
const started = Date.now()
|
||||
|
||||
await Effect.runPromise(client.waitForDiagnostics({ path: path.join(cwd, "never.ts") }).pipe(provideInstance(cwd)))
|
||||
|
||||
const elapsed = Date.now() - started
|
||||
expect(elapsed).toBeGreaterThanOrEqual(2900)
|
||||
expect(elapsed).toBeLessThan(5000)
|
||||
|
||||
await Effect.runPromise(client.shutdown())
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { afterEach, beforeEach, describe, expect, spyOn, test } from "bun:test"
|
||||
import path from "path"
|
||||
import { Effect, Layer } from "effect"
|
||||
import { Effect, Fiber, Layer, Scope } from "effect"
|
||||
import { LSP } from "../../src/lsp"
|
||||
import { LSPServer } from "../../src/lsp"
|
||||
import * as CrossSpawnSpawner from "../../src/effect/cross-spawn-spawner"
|
||||
@@ -153,6 +153,35 @@ describe("LSP service lifecycle", () => {
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
it.live("touchFile() dedupes concurrent spawn attempts for the same file", () =>
|
||||
provideTmpdirInstance(
|
||||
(dir) =>
|
||||
LSP.Service.use((lsp) =>
|
||||
Effect.gen(function* () {
|
||||
const gate = Promise.withResolvers<void>()
|
||||
const scope = yield* Scope.Scope
|
||||
const file = path.join(dir, "src", "inside.ts")
|
||||
|
||||
spawnSpy.mockImplementation(async () => {
|
||||
await gate.promise
|
||||
return undefined
|
||||
})
|
||||
|
||||
const fiber = yield* Effect.all([lsp.touchFile(file, false), lsp.touchFile(file, false)], {
|
||||
concurrency: "unbounded",
|
||||
}).pipe(Effect.forkIn(scope))
|
||||
|
||||
yield* Effect.sleep(20)
|
||||
expect(spawnSpy).toHaveBeenCalledTimes(1)
|
||||
|
||||
gate.resolve()
|
||||
yield* Fiber.join(fiber)
|
||||
}),
|
||||
),
|
||||
{ config: { lsp: true } },
|
||||
),
|
||||
)
|
||||
})
|
||||
|
||||
describe("LSP.Diagnostic", () => {
|
||||
|
||||
Reference in New Issue
Block a user