Files
deepseek-harness/packages/lsp/lsp-local/tests/built-lib.e2e.ts
2026-07-21 13:29:40 +08:00

74 lines
3.2 KiB
TypeScript

import { spawn } from 'node:child_process'
import { existsSync } from 'node:fs'
import { mkdtemp, mkdir, rm, writeFile, realpath } from 'node:fs/promises'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import { fileURLToPath, pathToFileURL } from 'node:url'
import { afterAll, beforeAll, describe, expect, it } from 'vitest'
/**
* Keyless built-artifact smoke: plain Node imports `@deepseek-ai/dsh-lsp` and
* `@deepseek-ai/dsh-lsp-local` by name through their exports maps, spawns the fixture server, runs
* one query (exercising real `Content-Length` framing over `lib/index.js`), and disposes (exercising
* subprocess cleanup). Unit tests use `src/`; this pins the downstream `lib/` path. Skips when `lib/`
* is absent; CI runs it after the build.
*/
const pkgDir = fileURLToPath(new URL('..', import.meta.url))
const seamLib = join(pkgDir, '../lsp/lib/index.js')
const built = existsSync(join(pkgDir, 'lib/index.js')) && existsSync(seamLib)
const fixtureServer = fileURLToPath(new URL('./fixture-server.ts', import.meta.url))
let root: string
let ws: string
beforeAll(async () => {
root = await realpath(await mkdtemp(join(tmpdir(), 'lsp-built-')))
ws = join(root, 'ws')
await mkdir(ws)
await writeFile(join(ws, 'a.ts'), 'const x = 1\n')
})
afterAll(async () => {
if (root) await rm(root, { recursive: true, force: true })
})
describe.skipIf(!built)('built lib real load path (plain node)', () => {
it('runs a query through lib/index.js and disposes cleanly, framing over the base protocol', async () => {
const location = JSON.stringify({ uri: pathToFileURL(join(ws, 'a.ts')).href, range: { start: { line: 0, character: 0 }, end: { line: 0, character: 3 } } })
const script = `
const { Context } = await import('cordis')
const { default: Lsp } = await import('@deepseek-ai/dsh-lsp')
const LspLocal = await import('@deepseek-ai/dsh-lsp-local')
const ctx = new Context()
await ctx.plugin(Lsp)
await ctx.plugin(LspLocal, {
servers: {
fake: {
command: ${JSON.stringify(process.execPath)},
args: [${JSON.stringify(fixtureServer)}],
env: { LSP_FAKE_DEF: ${JSON.stringify(location)} },
extensionToLanguage: { '.ts': 'typescript' },
},
},
})
const result = await ctx.lsp.query({ operation: 'goToDefinition', filePath: 'a.ts', position: { line: 0, character: 6 }, workspaceRoot: ${JSON.stringify(ws)} })
console.log(JSON.stringify(result))
await ctx.fiber.dispose()
`
const child = spawn(process.execPath, ['--input-type=module', '-e', script], { cwd: pkgDir, stdio: ['ignore', 'pipe', 'pipe'] })
let stdout = ''
let stderr = ''
child.stdout.on('data', (chunk: Buffer) => { stdout += chunk.toString('utf8') })
child.stderr.on('data', (chunk: Buffer) => { stderr += chunk.toString('utf8') })
const exitCode = await new Promise<number | null>(resolve => child.on('close', resolve))
expect(exitCode, `stderr:\n${stderr}`).toBe(0)
const lastLine = stdout.trim().split('\n').at(-1) ?? ''
const result = JSON.parse(lastLine) as { kind: string; locations: unknown[] }
expect(result.kind).toBe('locations')
expect(result.locations).toHaveLength(1)
}, 60_000)
})