Path: blob/main/src/vs/workbench/contrib/mcp/test/node/mcpStdioStateHandler.test.ts
5260 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`);4748await new Promise<void>(r => child.stdin.write('Hello MCP!', () => r()));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', () => {62process.stdout.end(() => process.exit(0));63});64});65`);6667const before = Date.now();68handler.stop();69const result = await output;70const delay = Date.now() - before;71assert.strictEqual(result.trim(), 'stdin ended\nSIGTERM received');72assert.ok(delay >= GRACE_TIME, `Expected at least ${GRACE_TIME}ms delay, got ${delay}ms`);73});74}7576test('sigkill after grace', async () => {77const { handler, output } = run(`78setInterval(() => {}, 1000);79process.stdin.on('end', () => process.stdout.write('stdin ended\\n'));80process.stdin.resume();81process.on('SIGTERM', () => {82process.stdout.write('SIGTERM received');83});84`);8586const before = Date.now();87handler.stop();88const result = await output;89const delay = Date.now() - before;90if (!isWindows) {91assert.strictEqual(result.trim(), 'stdin ended\nSIGTERM received');92} else {93assert.strictEqual(result.trim(), 'stdin ended');94}95assert.ok(delay >= GRACE_TIME * 2, `Expected at least ${GRACE_TIME * 2}ms delay, got ${delay}ms`);96});97});9899100