Path: blob/main/src/vs/workbench/contrib/mcp/test/node/mcpStdioStateHandler.test.ts
3296 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import { spawn } from 'child_process';6import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js';7import * as assert from 'assert';8import { McpStdioStateHandler } from '../../node/mcpStdioStateHandler.js';9import { isWindows } from '../../../../../base/common/platform.js';1011const GRACE_TIME = 100;1213suite('McpStdioStateHandler', () => {14const store = ensureNoDisposablesAreLeakedInTestSuite();1516function run(code: string) {17const child = spawn('node', ['-e', code], {18stdio: 'pipe',19env: { ...process.env, ELECTRON_RUN_AS_NODE: '1' },20});2122return {23child,24handler: store.add(new McpStdioStateHandler(child, GRACE_TIME)),25processId: new Promise<number>((resolve) => {26child.on('spawn', () => resolve(child.pid!));27}),28output: new Promise<string>((resolve) => {29let output = '';30child.stderr.setEncoding('utf-8').on('data', (data) => {31output += data.toString();32});33child.stdout.setEncoding('utf-8').on('data', (data) => {34output += data.toString();35});36child.on('close', () => resolve(output));37}),38};39}4041test('stdin ends process', async () => {42const { child, handler, output } = run(`43const data = require('fs').readFileSync(0, 'utf-8');44process.stdout.write('Data received: ' + data);45process.on('SIGTERM', () => process.stdout.write('SIGTERM received'));46`);4748child.stdin.write('Hello MCP!');49handler.stop();50const result = await output;51assert.strictEqual(result.trim(), 'Data received: Hello MCP!');52});5354if (!isWindows) {55test('sigterm after grace', async () => {56const { handler, output } = run(`57setInterval(() => {}, 1000);58process.stdin.on('end', () => process.stdout.write('stdin ended\\n'));59process.stdin.resume();60process.on('SIGTERM', () => {61process.stdout.write('SIGTERM received', () => process.exit(0));62});63`);6465const before = Date.now();66handler.stop();67const result = await output;68const delay = Date.now() - before;69assert.strictEqual(result.trim(), 'stdin ended\nSIGTERM received');70assert.ok(delay >= GRACE_TIME, `Expected at least ${GRACE_TIME}ms delay, got ${delay}ms`);71});72}7374test('sigkill after grace', async () => {75const { handler, output } = run(`76setInterval(() => {}, 1000);77process.stdin.on('end', () => process.stdout.write('stdin ended\\n'));78process.stdin.resume();79process.on('SIGTERM', () => {80process.stdout.write('SIGTERM received');81});82`);8384const before = Date.now();85handler.stop();86const result = await output;87const delay = Date.now() - before;88if (!isWindows) {89assert.strictEqual(result.trim(), 'stdin ended\nSIGTERM received');90} else {91assert.strictEqual(result.trim(), 'stdin ended');92}93assert.ok(delay >= GRACE_TIME * 2, `Expected at least ${GRACE_TIME * 2}ms delay, got ${delay}ms`);94});95});969798