| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112 |
- /**
- * Node 22 startup-output smoke for the shipped Web CLI composition.
- *
- * Only the dedicated Node compatibility gate opts this test in after building
- * both artifacts; ordinary Vitest inventory deterministically skips it.
- * The child runs built artifacts under plain Node with the real shipped
- * config (base.cordis.yml + the web.cordis.yml overlay).
- * Its URL line follows AppCLIEntry's settled boot; SIGTERM then exercises the
- * shipped quiescent disposer.
- */
- import { spawn } from 'node:child_process'
- import { existsSync } from 'node:fs'
- import { mkdtemp, readFile, rm } from 'node:fs/promises'
- import { tmpdir } from 'node:os'
- import { join, resolve } from 'node:path'
- import { fileURLToPath } from 'node:url'
- import yaml from 'js-yaml'
- import { describe, expect, it } from 'vitest'
- const repoRoot = fileURLToPath(new URL('../../../', import.meta.url))
- const builtBin = join(repoRoot, 'apps/cli/lib/bin.js')
- const webDist = join(repoRoot, 'apps/web/dist/index.html')
- // The web overlay owns the session-query-sqlite lazy-open patch row.
- const configPath = join(repoRoot, 'apps/cli/config/web.cordis.yml')
- const requireBuiltArtifacts = process.env.DSH_REQUIRE_BUILT_CLI_SMOKE === '1'
- interface ConfigRow {
- id?: string
- config?: { openAt?: unknown }
- }
- const jsExprType = new yaml.Type('tag:yaml.org,2002:js', {
- kind: 'scalar',
- construct: value => String(value),
- })
- const configSchema = yaml.JSON_SCHEMA.extend(jsExprType)
- /** Boot the built Web CLI, wait for its settled URL, then dispose through SIGTERM. */
- function runBuiltWeb(cwd: string): Promise<{ stdout: string; stderr: string; code: number }> {
- return new Promise((resolveRun, rejectRun) => {
- const env: NodeJS.ProcessEnv = {
- ...process.env,
- DEEPSEEK_API_KEY: 'dsh-cli-smoke-dummy-key',
- DSH_HOME: join(cwd, '.dsh'),
- }
- delete env.DEEPSEEK_BASE_URL
- delete env.NODE_OPTIONS
- delete env.NODE_NO_WARNINGS
- const child = spawn(process.execPath, [
- builtBin,
- 'web',
- '--host',
- '127.0.0.1',
- '--port',
- '0',
- ], {
- cwd,
- env,
- stdio: ['ignore', 'pipe', 'pipe'],
- })
- let stdout = ''
- let stderr = ''
- let settled = false
- child.stdout.setEncoding('utf8')
- child.stderr.setEncoding('utf8')
- child.stdout.on('data', (chunk: string) => {
- stdout += chunk
- if (!settled && /dsh web: http:\/\/127\.0\.0\.1:\d+/u.test(stdout)) {
- settled = true
- child.kill('SIGTERM')
- }
- })
- child.stderr.on('data', (chunk: string) => { stderr += chunk })
- const timer = setTimeout(() => {
- child.kill('SIGKILL')
- rejectRun(new Error(`built Web CLI did not settle and dispose within 60s\nstdout:\n${stdout}\nstderr:\n${stderr}`))
- }, 60_000)
- child.on('error', (error) => {
- clearTimeout(timer)
- rejectRun(error)
- })
- child.on('close', (code) => {
- clearTimeout(timer)
- if (!settled) {
- rejectRun(new Error(`built Web CLI exited before settled startup (code ${String(code)})\nstdout:\n${stdout}\nstderr:\n${stderr}`))
- return
- }
- resolveRun({ stdout, stderr, code: code ?? -1 })
- })
- })
- }
- describe.skipIf(!requireBuiltArtifacts)('built CLI lazy-search startup', () => {
- it('boots and disposes the shipped composition without a SQLite startup warning', async () => {
- expect(existsSync(builtBin), `missing built CLI ${resolve(builtBin)}; run pnpm build`).toBe(true)
- expect(existsSync(webDist), `missing Web dist ${resolve(webDist)}; run pnpm run build:web`).toBe(true)
- const rows = yaml.load(await readFile(configPath, 'utf8'), { schema: configSchema }) as ConfigRow[]
- const searchRow = rows.find(row => row.id === 'session-query-sqlite')
- expect(searchRow?.config?.openAt).toBe('first-search')
- const cwd = await mkdtemp(join(tmpdir(), 'dsh-cli-lazy-search-'))
- try {
- const result = await runBuiltWeb(cwd)
- expect(result.stdout).toMatch(/dsh web: http:\/\/127\.0\.0\.1:\d+/u)
- expect(result.code).toBe(0)
- expect(result.stderr).not.toMatch(/ExperimentalWarning: SQLite/u)
- } finally {
- await rm(cwd, { recursive: true, force: true })
- }
- }, 70_000)
- })
|