|
|
@@ -195,6 +195,50 @@ describe('lsp-local provider resolution', () => {
|
|
|
await ctx.fiber.dispose()
|
|
|
})
|
|
|
|
|
|
+ it('waits for aborted sibling executable lookups before setup rejects', async () => {
|
|
|
+ const ctx = new Context()
|
|
|
+ await ctx.plugin(Lsp)
|
|
|
+ await ctx.plugin(LocalSubprocessService)
|
|
|
+ await ctx.plugin(LocalFileSystem, { cwd: process.cwd() })
|
|
|
+ const slowStarted = Promise.withResolvers<undefined>()
|
|
|
+ const slowAborted = Promise.withResolvers<undefined>()
|
|
|
+ const releaseCleanup = Promise.withResolvers<undefined>()
|
|
|
+ vi.spyOn(ctx.subprocess, 'resolveExecutable').mockImplementation(async (command, _env, signal) => {
|
|
|
+ if (signal === undefined) throw new Error('missing setup signal')
|
|
|
+ if (command === 'slow-lsp') {
|
|
|
+ return await new Promise<string>((_resolve, reject) => {
|
|
|
+ const onAbort = (): void => {
|
|
|
+ slowAborted.resolve(undefined)
|
|
|
+ void releaseCleanup.promise.then(() => {
|
|
|
+ reject(signal.reason instanceof Error ? signal.reason : new Error(String(signal.reason)))
|
|
|
+ })
|
|
|
+ }
|
|
|
+ signal.addEventListener('abort', onAbort, { once: true })
|
|
|
+ slowStarted.resolve(undefined)
|
|
|
+ if (signal.aborted) onAbort()
|
|
|
+ })
|
|
|
+ }
|
|
|
+ await slowStarted.promise
|
|
|
+ throw new Error('lookup failed')
|
|
|
+ })
|
|
|
+
|
|
|
+ const loading = ctx.plugin(LspLocal, {
|
|
|
+ servers: {
|
|
|
+ slow: { command: 'slow-lsp', extensionToLanguage: { '.ts': 'typescript' } },
|
|
|
+ failing: { command: 'failing-lsp', extensionToLanguage: { '.js': 'javascript' } },
|
|
|
+ },
|
|
|
+ })
|
|
|
+ await slowAborted.promise
|
|
|
+ let settled = false
|
|
|
+ void loading.then(() => { settled = true }, () => { settled = true })
|
|
|
+ await new Promise<void>((resolve) => { setImmediate(resolve) })
|
|
|
+ expect(settled).toBe(false)
|
|
|
+
|
|
|
+ releaseCleanup.resolve(undefined)
|
|
|
+ await expect(loading).rejects.toThrow('lookup failed')
|
|
|
+ await ctx.fiber.dispose()
|
|
|
+ })
|
|
|
+
|
|
|
it('aborts executable resolution when disposed during setup', async () => {
|
|
|
const ctx = new Context()
|
|
|
await ctx.plugin(Lsp)
|