Skip to content

Commit 4e786cd

Browse files
claude[bot]zhuangjianguoclaude
authored
test(cli): three e2e spawners name the entrypoint they actually reach (#11462)
The identical `bin/run.js` — the SHIPPED entrypoint` comment in serve-mcp-stdio-answers, serve-mcp-capability-collision and serve-stdio-stdout-purity was false: all three pin NODE_ENV=development on the child for the --dev admin seed, and @oclif/core 4.13.3 reroutes command resolution from ./dist/commands to ./src/commands for exactly that value, so packages/cli/dist was never consulted. Spawn bin/run-dev.js through tsx like the ~20 sibling e2e files here, and replace the comment with the mechanism. Claude-Session: https://claude.ai/code/session_019siH5jDmk5hrayvfyojUqR Co-authored-by: Claude <zhuangjianguo@steedos.com> Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
1 parent acd920d commit 4e786cd

3 files changed

Lines changed: 69 additions & 12 deletions

File tree

packages/cli/test/serve-mcp-capability-collision.e2e.test.ts

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -48,11 +48,27 @@ import { mkdtempSync, rmSync, writeFileSync } from 'node:fs';
4848
import { tmpdir } from 'node:os';
4949
import { join, resolve } from 'node:path';
5050
import { fileURLToPath } from 'node:url';
51-
import { E2E_SECRET_KEY, childEnv, randomPort } from './helpers/serve-process.js';
51+
import { E2E_SECRET_KEY, TSX, childEnv, randomPort } from './helpers/serve-process.js';
5252

5353
const HERE = resolve(fileURLToPath(import.meta.url), '..');
54-
/** `bin/run.js` — the SHIPPED entrypoint, i.e. the one the card's repro names. */
55-
const CLI = resolve(HERE, '../bin/run.js');
54+
/**
55+
* `bin/run-dev.js` through `tsx` — the SOURCE entrypoint, like the ~20 sibling
56+
* e2e files in this directory.
57+
*
58+
* ⛔ NOT `bin/run.js`. This file used to spell that one and call it "the SHIPPED
59+
* entrypoint"; the claim was never true here (#11317). The boot below pins
60+
* `NODE_ENV=development` on the child for the `--dev` admin seed, and
61+
* @oclif/core 4.13.3 skips its TypeScript path lookup only when `isProd()` —
62+
* `!['development', 'test'].includes(process.env.NODE_ENV ?? '')`. Under that
63+
* value oclif rewrites the command target from the declared `./dist/commands`
64+
* to `./src/commands` and transpiles, so `packages/cli/dist` is never consulted
65+
* whichever stub is named. `serve-node-env-production-default.e2e.test.ts` is
66+
* the file that genuinely reaches the built artifact, and it gets there by
67+
* leaving `NODE_ENV` UNSET — the value that disables the reroute. Restoring
68+
* `bin/run.js` here without dropping the `NODE_ENV` pin below is a no-op with a
69+
* false comment attached.
70+
*/
71+
const CLI = resolve(HERE, '../bin/run-dev.js');
5672

5773
/** The consumer's real identity — see `serve-capability-identity.test.ts`. */
5874
const CONSUMER_PLUGIN_ID = 'com.objectstack.connector.mcp';
@@ -106,7 +122,7 @@ const children: ChildProcessWithoutNullStreams[] = [];
106122

107123
function boot(env: Record<string, string | undefined>, waitFor: RegExp): Promise<ChildProcessWithoutNullStreams> {
108124
return new Promise((resolveBoot, rejectBoot) => {
109-
const child = spawn(process.execPath, [CLI, 'serve', '-p', port, '--dev'], {
125+
const child = spawn(TSX, [CLI, 'serve', '-p', port, '--dev'], {
110126
cwd: dir,
111127
stdio: ['pipe', 'pipe', 'pipe'],
112128
// `childEnv`, not a bare `...process.env`: the vitest worker exports
@@ -127,6 +143,9 @@ function boot(env: Record<string, string | undefined>, waitFor: RegExp): Promise
127143
OS_SECRET_KEY: E2E_SECRET_KEY,
128144
// The dev-admin seed the key mint signs in as is gated on this, and
129145
// vitest exports `test`.
146+
// Still passed explicitly although `bin/run-dev.js` assigns it too: the
147+
// shim's assignment runs after its own static imports have evaluated,
148+
// so only the child env pins the value for the whole process lifetime.
130149
NODE_ENV: 'development',
131150
...env,
132151
}),

packages/cli/test/serve-mcp-stdio-answers.e2e.test.ts

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -47,11 +47,27 @@ import { mkdtempSync, rmSync, writeFileSync } from 'node:fs';
4747
import { tmpdir } from 'node:os';
4848
import { join, resolve } from 'node:path';
4949
import { fileURLToPath } from 'node:url';
50-
import { E2E_SECRET_KEY, childEnv, randomPort } from './helpers/serve-process.js';
50+
import { E2E_SECRET_KEY, TSX, childEnv, randomPort } from './helpers/serve-process.js';
5151

5252
const HERE = resolve(fileURLToPath(import.meta.url), '..');
53-
/** `bin/run.js` — the SHIPPED entrypoint, i.e. the one the card's repro names. */
54-
const CLI = resolve(HERE, '../bin/run.js');
53+
/**
54+
* `bin/run-dev.js` through `tsx` — the SOURCE entrypoint, like the ~20 sibling
55+
* e2e files in this directory.
56+
*
57+
* ⛔ NOT `bin/run.js`. This file used to spell that one and call it "the SHIPPED
58+
* entrypoint"; the claim was never true here (#11317). The boot below pins
59+
* `NODE_ENV=development` on the child for the `--dev` admin seed, and
60+
* @oclif/core 4.13.3 skips its TypeScript path lookup only when `isProd()` —
61+
* `!['development', 'test'].includes(process.env.NODE_ENV ?? '')`. Under that
62+
* value oclif rewrites the command target from the declared `./dist/commands`
63+
* to `./src/commands` and transpiles, so `packages/cli/dist` is never consulted
64+
* whichever stub is named. `serve-node-env-production-default.e2e.test.ts` is
65+
* the file that genuinely reaches the built artifact, and it gets there by
66+
* leaving `NODE_ENV` UNSET — the value that disables the reroute. Restoring
67+
* `bin/run.js` here without dropping the `NODE_ENV` pin below is a no-op with a
68+
* false comment attached.
69+
*/
70+
const CLI = resolve(HERE, '../bin/run-dev.js');
5571

5672
const CONFIG = `
5773
export default {
@@ -101,7 +117,7 @@ interface Booted {
101117
*/
102118
function boot(env: Record<string, string | undefined>, waitFor: RegExp): Promise<Booted> {
103119
return new Promise((resolveBoot, rejectBoot) => {
104-
const child = spawn(process.execPath, [CLI, 'serve', '-p', port, '--dev'], {
120+
const child = spawn(TSX, [CLI, 'serve', '-p', port, '--dev'], {
105121
cwd: dir,
106122
stdio: ['pipe', 'pipe', 'pipe'],
107123
// `childEnv`, not a bare `...process.env`: the vitest worker exports
@@ -123,6 +139,9 @@ function boot(env: Record<string, string | undefined>, waitFor: RegExp): Promise
123139
// Explicit, not inherited: the dev-admin seed this fixture signs in as
124140
// is hard-gated on `NODE_ENV === 'development'`, and vitest exports
125141
// `test`, which would leave the DB user-less and the mint unauthorized.
142+
// Still passed explicitly although `bin/run-dev.js` assigns it too: the
143+
// shim's assignment runs after its own static imports have evaluated,
144+
// so only the child env pins the value for the whole process lifetime.
126145
NODE_ENV: 'development',
127146
...env,
128147
}),

packages/cli/test/serve-stdio-stdout-purity.e2e.test.ts

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,11 +44,27 @@ import { mkdtempSync, rmSync, writeFileSync } from 'node:fs';
4444
import { tmpdir } from 'node:os';
4545
import { join, resolve } from 'node:path';
4646
import { fileURLToPath } from 'node:url';
47-
import { E2E_SECRET_KEY, childEnv, randomPort } from './helpers/serve-process.js';
47+
import { E2E_SECRET_KEY, TSX, childEnv, randomPort } from './helpers/serve-process.js';
4848

4949
const HERE = resolve(fileURLToPath(import.meta.url), '..');
50-
/** `bin/run.js` — the SHIPPED entrypoint, i.e. the one the card's repro names. */
51-
const CLI = resolve(HERE, '../bin/run.js');
50+
/**
51+
* `bin/run-dev.js` through `tsx` — the SOURCE entrypoint, like the ~20 sibling
52+
* e2e files in this directory.
53+
*
54+
* ⛔ NOT `bin/run.js`. This file used to spell that one and call it "the SHIPPED
55+
* entrypoint"; the claim was never true here (#11317). The boot below pins
56+
* `NODE_ENV=development` on the child for the `--dev` admin seed, and
57+
* @oclif/core 4.13.3 skips its TypeScript path lookup only when `isProd()` —
58+
* `!['development', 'test'].includes(process.env.NODE_ENV ?? '')`. Under that
59+
* value oclif rewrites the command target from the declared `./dist/commands`
60+
* to `./src/commands` and transpiles, so `packages/cli/dist` is never consulted
61+
* whichever stub is named. `serve-node-env-production-default.e2e.test.ts` is
62+
* the file that genuinely reaches the built artifact, and it gets there by
63+
* leaving `NODE_ENV` UNSET — the value that disables the reroute. Restoring
64+
* `bin/run.js` here without dropping the `NODE_ENV` pin below is a no-op with a
65+
* false comment attached.
66+
*/
67+
const CLI = resolve(HERE, '../bin/run-dev.js');
5268

5369
const CONFIG = `
5470
export default {
@@ -93,7 +109,7 @@ interface Booted {
93109
*/
94110
function boot(env: Record<string, string | undefined>, waitFor: RegExp): Promise<Booted> {
95111
return new Promise((resolveBoot, rejectBoot) => {
96-
const child = spawn(process.execPath, [CLI, 'serve', '-p', port, '--dev'], {
112+
const child = spawn(TSX, [CLI, 'serve', '-p', port, '--dev'], {
97113
cwd: dir,
98114
stdio: ['pipe', 'pipe', 'pipe'],
99115
// `childEnv`, not a bare `...process.env`: the vitest worker exports
@@ -117,6 +133,9 @@ function boot(env: Record<string, string | undefined>, waitFor: RegExp): Promise
117133
OS_SECRET_KEY: E2E_SECRET_KEY,
118134
// Explicit, not inherited: the dev-admin seed the mint signs in as is
119135
// hard-gated on `NODE_ENV === 'development'`, and vitest exports `test`.
136+
// Still passed explicitly although `bin/run-dev.js` assigns it too: the
137+
// shim's assignment runs after its own static imports have evaluated,
138+
// so only the child env pins the value for the whole process lifetime.
120139
NODE_ENV: 'development',
121140
...env,
122141
}),

0 commit comments

Comments
 (0)