`startProdWebServer` spread `process.env` without setting `MCP_CATALOG_PATH`, so `smoke:web`, `smoke:web:browser`, and `smoke:web:app` all ran against the developer's real `~/.mcp-inspector/mcp.json` — unlike `smoke:cli` / `smoke:tui`, which have always driven a temp `--catalog`. That made the web smokes both destructive and non-deterministic. `smoke:web:app`'s deep link persists a `deep-link` server row, so a second run finds it already on disk, racing hydration: the deep-link effect misses it in `servers`, POSTs `addServer` anyway, and the backend answers 409. The app swallows that 409 by design (App.tsx:2842) so the smoke still passed, but it reported a spurious non-fatal console error that was really just residue from the previous run. Give the helper its own temp catalog dir and clean it up in `stop()` via `removeSafe`, so cleanup can never turn a passing smoke red. Isolation lives in the shared helper rather than per script, since every web smoke needs it. Only the catalog is redirected — other per-user state under `~/.mcp-inspector` stays shared, since isolating it means redirecting HOME wholesale, which would also move the npx and Playwright caches these smokes depend on. Verified: `smoke:web:app` twice in a row is now clean both times (previously the second run reported the 409), the real catalog's mtime is unchanged, and no temp dirs leak. CI never saw this — a fresh HOME per run made every CI run look like a first run. Closes #1977 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HDdo1rNRVQnVRRdsqSrvG3 Signed-off-by: cliffhall <cliff@futurescale.com>
67 lines
2.4 KiB
JavaScript
67 lines
2.4 KiB
JavaScript
#!/usr/bin/env node
|
|
/**
|
|
* End-to-end smoke test for the prod web launcher path (#1486).
|
|
*
|
|
* `npm run smoke:launcher` only checks `--help` for each mode; it never starts
|
|
* the prod web server, so the static-asset / injected-token path went unverified
|
|
* in CI. This script launches `mcp-inspector --web` (prod, no `--dev`) against
|
|
* the built `clients/web/dist`, waits for it to listen, and asserts that `/`
|
|
* returns HTTP 200 with the injected auth-token global. It exits non-zero
|
|
* (failing CI / `npm run validate`) on any failure, then shuts the server down.
|
|
*
|
|
* This checks the served HTML only — it does not execute the bundle. The
|
|
* companion `smoke:web:browser` (scripts/smoke-web-browser.mjs, #1615) boots the
|
|
* same server and actually runs the app in headless Chromium; both share the
|
|
* spawn/readiness helper in ./lib/prod-web-server.mjs.
|
|
*
|
|
* Expects `clients/web/dist` and `clients/launcher/build` to be built first —
|
|
* the validate / CI ordering guarantees this, so the readiness wait is sized for
|
|
* an already-built `dist`. (If `dist` happened to be missing, the launcher's
|
|
* build-on-demand path would build it on startup, which a cold `vite build`
|
|
* could push past that wait — not a scenario this script targets.)
|
|
*/
|
|
|
|
import { startProdWebServer } from "./lib/prod-web-server.mjs";
|
|
|
|
const HOST = "127.0.0.1";
|
|
const PORT = process.env.SMOKE_WEB_PORT ?? "6299";
|
|
const TOKEN = "smoke-web-token";
|
|
// Mirrors INSPECTOR_API_TOKEN_GLOBAL in core/mcp/remote/constants.ts; kept as a
|
|
// literal because this plain .mjs script can't import the TS source.
|
|
const TOKEN_GLOBAL = "__INSPECTOR_API_TOKEN__";
|
|
|
|
const server = startProdWebServer({
|
|
host: HOST,
|
|
port: PORT,
|
|
token: TOKEN,
|
|
label: "smoke:web",
|
|
});
|
|
|
|
function fail(message) {
|
|
console.error(`smoke:web FAILED — ${message}`);
|
|
server.stop();
|
|
process.exit(1);
|
|
}
|
|
|
|
try {
|
|
const res = await server.waitForReady();
|
|
if (res.status !== 200) {
|
|
fail(`GET / returned HTTP ${res.status}, expected 200`);
|
|
}
|
|
const body = await res.text();
|
|
if (!body.includes(TOKEN_GLOBAL)) {
|
|
fail(
|
|
`served HTML is missing the ${TOKEN_GLOBAL} global (token not injected)`,
|
|
);
|
|
}
|
|
if (!body.includes(TOKEN)) {
|
|
fail("served HTML is missing the injected auth-token value");
|
|
}
|
|
console.log(
|
|
`smoke:web OK — GET / => 200 with injected ${TOKEN_GLOBAL} at ${server.baseUrl}`,
|
|
);
|
|
server.stop();
|
|
process.exit(0);
|
|
} catch (err) {
|
|
fail(err instanceof Error ? err.message : String(err));
|
|
}
|