Files
deepseek-harness/vitest.snapshot.config.ts
T
Yichen Jiang 03d2c54db1 test: start every suite from an environment with no proxy
A developer's Clash and a CI runner's squid both export HTTP_PROXY and its
siblings. Now that the harness honors them, an ambient value decides test
outcomes: this PR has already recorded a proxy's 502 page as a snapshot's
expected output, and let a runner's own export stand in for "what the user
exported" in an assertion about inherited names.

A Vitest setup file clears the proxy names before any suite runs, wired into
every configuration that declares a setup. Real-API e2e is cleared too: before
proxy support existed every request connected directly and that suite passed, so
direct is the environment it is known to work in.

`NODE_USE_ENV_PROXY` cannot be cleared this way — Node samples the proxy
environment at process start — and the module says so. A proxy application never
exports it, and the eight names one does export are fully handled: with all of
them set, the affected suites pass.

The wiring is what regresses, so that is what the test pins. Configurations are
discovered rather than listed, because the web suites carry no setup today and a
hand-written list would let one of them gain a setup without gaining this one.

`plugin.spec.ts` kept its own copy of the eight names to guard against the
machine; it never sets a proxy variable itself, so the setup replaces that
entirely. `install.spec.ts` had one assertion waiting on a DNS miss with no
deadline, which timed out once under load.
2026-08-29 13:17:12 +08:00

68 lines
3.2 KiB
TypeScript

import { availableParallelism } from 'node:os'
import tsconfigPaths from 'vite-tsconfig-paths'
import { defineConfig } from 'vitest/config'
import { standardDecoratorPlugin, vitestExecArgv } from './vitest.shared.ts'
const DEFAULT_SNAPSHOT_MAX_CONCURRENCY = 5
function positiveIntFromEnv(name: string, fallback: number): number {
const raw = process.env[name]
if (raw === undefined || raw === '') return fallback
const value = Number(raw)
if (!Number.isInteger(value) || value < 1) {
throw new Error(`${name} must be a positive integer, got ${JSON.stringify(raw)}`)
}
return value
}
const snapshotMaxConcurrency = positiveIntFromEnv(
'DSH_SNAPSHOT_MAX_CONCURRENCY',
Math.min(DEFAULT_SNAPSHOT_MAX_CONCURRENCY, availableParallelism()),
)
// Replay is the keyless default: boot real subprocess paths from recorded model responses and diff
// assembled requests, normalized protocol or transcript output, and persisted-log expected outputs.
// `record` calls the real API and updates fixtures and expected outputs; `refresh` replays committed scripts
// and updates current expected outputs. Replay/refresh never load `.env`; only record reads a key from the
// environment or root `.env`.
if (process.env.DSH_SNAPSHOT === 'record') {
try {
process.loadEnvFile(new URL('.env', import.meta.url).pathname)
} catch (error) {
// ENOENT (no .env) is fine — the key may already be in the environment.
// Surface any other failure rather than silently recording with wrong env.
if ((error as NodeJS.ErrnoException | null)?.code !== 'ENOENT') throw error
}
}
export default defineConfig({
// Same resolution note as vitest.config.ts: bare workspace names resolve
// through the tsconfig.base.json paths facade; the native option cannot do
// this (the root tsconfig is a solution file with no paths).
plugins: [tsconfigPaths({ projects: ['./tsconfig.base.json'] }), standardDecoratorPlugin()],
test: {
execArgv: vitestExecArgv,
setupFiles: ['./scripts/test-proxy-environment.ts', './scripts/test-invariants.ts'],
include: [
'scripts/session-snapshot-corpus.corpus.ts',
// The assembled Web snapshot executes generated client bundles; source
// mode remains the zero-build path, while lib mode requires a prior build.
...(process.env.DSH_EXAMPLE_MODE === 'lib' ? ['apps/web/tests/**/*.snapshot.ts'] : []),
'snapshots/**/*.snapshot.ts',
],
// Replay never writes committed outputs and every scenario owns its
// mutable runtime state (the subprocess suites use a unique temp dir and
// fixture set per scenario), so replay runs the snapshot files in
// parallel and bounds in-file concurrency with the environment knob
// (value 1 restores fully serial replay on constrained machines). Record
// and refresh stay serial: record spends real API quota per scenario, and
// refresh write-back harvests volatile values from fixtures already on
// disk, so concurrent writers would corrupt expected outputs.
testTimeout: 120_000,
hookTimeout: 30_000,
fileParallelism: (process.env.DSH_SNAPSHOT || 'replay') === 'replay' && snapshotMaxConcurrency > 1,
maxConcurrency: snapshotMaxConcurrency,
},
})