From ea0d24b05a1ff4a81d97bb07080ed9f345568bf6 Mon Sep 17 00:00:00 2001 From: Yichen Jiang Date: Thu, 22 Oct 2026 12:06:36 +0800 Subject: [PATCH] test: keep unit suites off the official model listing Suites that boot the DeepSeek routes with a dummy or inherited key now request `GET https://api.deepseek.com/models` on the official endpoint. A unit-lane setup file rejects that request at once, so such a suite serves the built-in catalog instead of reaching production or waiting out the listing deadline on a network that never answers; suites that exercise the listing answer `fetch` themselves. Other requests pass through unchanged. --- scripts/test-official-listing-guard.spec.ts | 26 +++++++++++++++++++++ scripts/test-official-listing-guard.ts | 22 +++++++++++++++++ vitest.config.ts | 12 +++++++--- 3 files changed, 57 insertions(+), 3 deletions(-) create mode 100644 scripts/test-official-listing-guard.spec.ts create mode 100644 scripts/test-official-listing-guard.ts diff --git a/scripts/test-official-listing-guard.spec.ts b/scripts/test-official-listing-guard.spec.ts new file mode 100644 index 0000000000..7345d3b5d8 --- /dev/null +++ b/scripts/test-official-listing-guard.spec.ts @@ -0,0 +1,26 @@ +/** Unit suites cannot reach the official DeepSeek model listing unless they answer `fetch` themselves. */ +import { createServer } from 'node:http' +import { afterEach, expect, it } from 'vitest' +import type { Server } from 'node:http' + +const servers: Server[] = [] +afterEach(async () => { + await Promise.all(servers.splice(0).map(server => new Promise(resolve => server.close(resolve)))) +}) + +it.each([ + ['string', () => fetch('https://api.deepseek.com/models')], + ['URL', () => fetch(new URL('https://api.deepseek.com/models'))], + ['Request', () => fetch(new Request('https://api.deepseek.com/models', { headers: { 'x-api-key': 'fixture-key' } }))], +])('rejects the official model listing given as a %s', async (_kind, request) => { + await expect(request()).rejects.toThrow('unit suites do not reach https://api.deepseek.com/models; answer it with a fetch stub') +}) + +it('passes every other request to the ambient fetch', async () => { + const server = createServer((_request, response) => { response.end('local') }) + servers.push(server) + await new Promise(resolve => server.listen(0, '127.0.0.1', resolve)) + const address = server.address() + if (address === null || typeof address === 'string') throw new Error('no port') + await expect((await fetch(`http://127.0.0.1:${address.port}/models`)).text()).resolves.toBe('local') +}) diff --git a/scripts/test-official-listing-guard.ts b/scripts/test-official-listing-guard.ts new file mode 100644 index 0000000000..bd8bc82f61 --- /dev/null +++ b/scripts/test-official-listing-guard.ts @@ -0,0 +1,22 @@ +/** + * Keep unit suites from requesting the official DeepSeek model listing. + * + * The API-key and account routes request `GET https://api.deepseek.com/models` whenever they hold a + * credential for the official endpoint, and suites routinely boot those routes with a dummy key or + * inherit a developer's real one. Such a request would reach production and, on a network that never + * answers, hold the route's first catalog access for its whole listing deadline. Here it fails at once, + * so the route serves its built-in catalog; a suite that exercises the listing replaces `fetch` itself. + * @module + */ + +/** The listing endpoint of the official DeepSeek API. */ +const OFFICIAL_LISTING = 'https://api.deepseek.com/models' + +const ambientFetch = globalThis.fetch +globalThis.fetch = (input, init) => { + const url = input instanceof Request ? input.url : input instanceof URL ? input.href : input + if (url === OFFICIAL_LISTING) { + return Promise.reject(new TypeError(`unit suites do not reach ${OFFICIAL_LISTING}; answer it with a fetch stub`)) + } + return ambientFetch(input, init) +} diff --git a/vitest.config.ts b/vitest.config.ts index 56d206808f..0885f31d14 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -182,7 +182,9 @@ export default defineConfig({ resolve: { alias: claudeCodeTestingAliases }, test: { ...localWorkerBudget, - setupFiles: ['./scripts/test-proxy-environment.ts', './scripts/test-dom-environment.ts'], + setupFiles: [ + './scripts/test-proxy-environment.ts', './scripts/test-dom-environment.ts', './scripts/test-official-listing-guard.ts', + ], // .tsx: client component specs (jsdom via per-file @vitest-environment pragma). include: testIncludes, exclude: platformUnsupportedTests, @@ -200,7 +202,9 @@ export default defineConfig({ // Linux, and Windows. Forked workers avoid that shared thread path. pool: 'forks', ...laneTestBudget, - setupFiles: ['./scripts/test-proxy-environment.ts', './scripts/test-dom-environment.ts'], + setupFiles: [ + './scripts/test-proxy-environment.ts', './scripts/test-dom-environment.ts', './scripts/test-official-listing-guard.ts', + ], include: testIncludes, exclude: [ ...platformUnsupportedTests, @@ -217,7 +221,9 @@ export default defineConfig({ execArgv: vitestExecArgv, pool: 'forks', ...laneTestBudget, - setupFiles: ['./scripts/test-proxy-environment.ts', './scripts/test-dom-environment.ts'], + setupFiles: [ + './scripts/test-proxy-environment.ts', './scripts/test-dom-environment.ts', './scripts/test-official-listing-guard.ts', + ], include: processBoundTests, exclude: [ ...platformUnsupportedTests,