Files
msd-core/tests/gsd-mcp-server-bin.test.cjs
Tom Boucher d6788a6805 test(#4523): migrate config/env/locking/perf batch to named timeout constants (#4673)
Batch 12 of the ad hoc timeout literal migration (epic #4445). Replaces
every bare numeric timeout/timeoutMs object-literal property in
tests/check-env.test.cjs, tests/config-get-default.test.cjs,
tests/federated-config.test.cjs,
tests/gsd-check-update-worker-platform-gate.test.cjs,
tests/gsd-mcp-server-bin.test.cjs, tests/health-validation.test.cjs,
tests/locking-bugs-1909-1916-1925-1927.test.cjs,
tests/perf-316-state-lock-buffer-alloc.test.cjs,
tests/perf-317-context-monitor-fs.test.cjs, and
tests/pi-config-dir-env-override.test.cjs with a named constant, per
eslint-rules/no-adhoc-timeout-literal.cjs. Removes these 10 files from the
rule's allowlist.

Ground truth via eslint matched the issue's stated 26 sites across 10 files
exactly. Reuses PROBE_TIMEOUT_MS, GENERATOR_SCRIPT_TIMEOUT_MS,
GSD_TOOLS_CLI_MODERATE_TIMEOUT_MS, and INSTALL_TIMEOUT_MS across 4 files.
No new shared constants needed -- every recurring value across files was
independently verified to be a genuinely different operation class, per
this migration's standing rule that numeric coincidence is never identity.
Adds 11 new file-local constants, three of which are not real subprocess
timeouts at all (a config-merge fixture value, and two node:test per-test
timeout options bounding ReDoS/lock-retry regression backstops).

Per the issue's explicit mandate, gsd-check-update-worker-platform-gate.test.cjs
now imports (read-only) NPM_VIEW_TIMEOUT_MS from
gsd-core/bin/check-latest-version.cjs for disclosure -- this file and that
production module once independently guessed the same 15000ms value,
causing the PR #4428 Windows double-SIGKILL collision. No site in this
file's current bare literals actually wraps a live npm-view call needing
margin arithmetic, so the import documents the historical relationship
honestly rather than fabricating a computation. No src/bin file edited
(only a read-only import added), no numeric value changed anywhere.

Co-authored-by: sim <sim@local>
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-12 21:33:26 -04:00

58 lines
2.5 KiB
JavaScript

'use strict';
/**
* Process-lifecycle test for the gsd-mcp-server bin entry (ADR-1239 Phase C-2,
* #1681 slice 3b / AC4). Spawns the shim, feeds line-delimited JSON-RPC over
* stdin, asserts stdout responses + clean exit on stdin EOF. Synchronous +
* bounded (the server exits when stdin closes — no orphan process).
*/
const { test } = require('node:test');
const assert = require('node:assert/strict');
const { spawnSync } = require('node:child_process');
const path = require('node:path');
const { PROTOCOL_VERSION } = require('../gsd-core/bin/lib/mcp-server.cjs');
const { PROBE_TIMEOUT_MS } = require('./helpers/timeouts.cjs');
const SHIM = path.join(__dirname, '..', 'bin', 'gsd-mcp-server.js');
function run(stdin) {
return spawnSync(process.execPath, [SHIM], {
input: stdin,
encoding: 'utf-8',
timeout: PROBE_TIMEOUT_MS,
env: { ...process.env, GSD_TEST_MODE: '1' },
});
}
test('gsd-mcp-server bin: initialize handshake + tools/list over stdio, then clean exit', () => {
const stdin = [
JSON.stringify({ jsonrpc: '2.0', id: 1, method: 'initialize' }),
JSON.stringify({ jsonrpc: '2.0', id: 2, method: 'tools/list' }),
].join('\n') + '\n';
const res = run(stdin);
assert.strictEqual(res.status, 0, `clean exit; stderr: ${res.stderr}`);
const lines = res.stdout.trim().split('\n').map((l) => JSON.parse(l));
assert.strictEqual(lines.length, 2, 'one response per request');
assert.strictEqual(lines[0].id, 1);
assert.strictEqual(lines[0].result.protocolVersion, PROTOCOL_VERSION, 'initialize returns the protocol version');
assert.ok(Array.isArray(lines[1].result.tools) && lines[1].result.tools.length === 3, 'tools/list advertises the 3 tools');
});
test('gsd-mcp-server bin: a malformed line surfaces a JSON-RPC parse error; the server keeps running', () => {
const stdin = [
'this is not json',
JSON.stringify({ jsonrpc: '2.0', id: 9, method: 'initialize' }),
].join('\n') + '\n';
const res = run(stdin);
assert.strictEqual(res.status, 0, `server survives the bad line; stderr: ${res.stderr}`);
const lines = res.stdout.trim().split('\n').map((l) => JSON.parse(l));
assert.strictEqual(lines[0].error.code, -32700, 'bad line → JSON-RPC parse error');
assert.strictEqual(lines[1].result.protocolVersion, PROTOCOL_VERSION, 'subsequent valid request still handled');
});
test('gsd-mcp-server bin: empty/whitespace-only stdin → clean exit, no output', () => {
const res = run('\n \n');
assert.strictEqual(res.status, 0);
assert.strictEqual(res.stdout.trim(), '', 'no requests → no responses');
});