|
1 | 1 | import type { ChildProcess } from 'node:child_process'; |
| 2 | +import { EventEmitter } from 'node:events'; |
2 | 3 |
|
3 | 4 | import type { JSONRPCMessage } from '@modelcontextprotocol/core'; |
4 | 5 | import spawn from 'cross-spawn'; |
@@ -174,6 +175,42 @@ describe('StdioClientTransport using cross-spawn', () => { |
174 | 175 | expect(mockProcess.stdin.write).toHaveBeenCalled(); |
175 | 176 | }); |
176 | 177 |
|
| 178 | + // Regression test for #780: listeners must be detached from the child's |
| 179 | + // stdio streams on close(), not left attached to a process we no longer track. |
| 180 | + test('should detach stdout/stdin listeners on close', async () => { |
| 181 | + const stdout = new EventEmitter(); |
| 182 | + const stdin = Object.assign(new EventEmitter(), { |
| 183 | + write: vi.fn().mockReturnValue(true), |
| 184 | + end: vi.fn() |
| 185 | + }); |
| 186 | + const proc = Object.assign(new EventEmitter(), { |
| 187 | + stdin, |
| 188 | + stdout, |
| 189 | + stderr: null, |
| 190 | + exitCode: null as number | null, |
| 191 | + kill: vi.fn() |
| 192 | + }); |
| 193 | + mockSpawn.mockReturnValue(proc as unknown as ChildProcess); |
| 194 | + |
| 195 | + const transport = new StdioClientTransport({ command: 'test-command' }); |
| 196 | + const started = transport.start(); |
| 197 | + proc.emit('spawn'); |
| 198 | + await started; |
| 199 | + |
| 200 | + expect(stdout.listenerCount('data')).toBe(1); |
| 201 | + expect(stdout.listenerCount('error')).toBe(1); |
| 202 | + expect(stdin.listenerCount('error')).toBe(1); |
| 203 | + |
| 204 | + const closed = transport.close(); |
| 205 | + proc.exitCode = 0; |
| 206 | + proc.emit('close'); |
| 207 | + await closed; |
| 208 | + |
| 209 | + expect(stdout.listenerCount('data')).toBe(0); |
| 210 | + expect(stdout.listenerCount('error')).toBe(0); |
| 211 | + expect(stdin.listenerCount('error')).toBe(0); |
| 212 | + }); |
| 213 | + |
177 | 214 | describe('windowsHide', () => { |
178 | 215 | const originalPlatform = process.platform; |
179 | 216 |
|
|
0 commit comments