Skip to content

Commit 999f549

Browse files
fix: use proper initialization for servers in tests
- Make test servers use ts and init them with tsx - Add test to check if hanging server actually gets killed
1 parent ca6fe90 commit 999f549

File tree

5 files changed

+102
-34
lines changed

5 files changed

+102
-34
lines changed

src/integration-tests/process-cleanup.test.ts

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { Client } from '../client/index.js';
33
import { StdioClientTransport } from '../client/stdio.js';
44
import { Server } from '../server/index.js';
55
import { StdioServerTransport } from '../server/stdio.js';
6+
import { LoggingMessageNotificationSchema } from '../types.js';
67

78
describe('Process cleanup', () => {
89
vi.setConfig({ testTimeout: 5000 }); // 5 second timeout
@@ -52,8 +53,8 @@ describe('Process cleanup', () => {
5253
});
5354

5455
const transport = new StdioClientTransport({
55-
command: process.argv0,
56-
args: ['test-server.js'],
56+
command: 'npm',
57+
args: ['exec', 'tsx', 'test-server.ts'],
5758
cwd: __dirname
5859
});
5960

@@ -71,4 +72,39 @@ describe('Process cleanup', () => {
7172

7273
expect(onCloseWasCalled).toBe(1);
7374
});
75+
76+
it('should exit cleanly for a server that hangs', async () => {
77+
const client = new Client({
78+
name: 'test-client',
79+
version: '1.0.0'
80+
});
81+
82+
const transport = new StdioClientTransport({
83+
command: 'npm',
84+
args: ['exec', 'tsx', 'server-that-hangs.ts'],
85+
cwd: __dirname
86+
});
87+
88+
await client.connect(transport);
89+
await client.setLoggingLevel('debug');
90+
client.setNotificationHandler(LoggingMessageNotificationSchema, notification => {
91+
console.debug('server log: ' + notification.params.data);
92+
});
93+
const serverPid = transport.pid!;
94+
95+
await client.close();
96+
97+
// A short delay to allow the close event to propagate
98+
await new Promise(resolve => setTimeout(resolve, 50));
99+
100+
try {
101+
process.kill(serverPid, 9);
102+
throw new Error('Expected server to be dead but it is alive');
103+
} catch (err: unknown) {
104+
// 'ESRCH' the process doesn't exist
105+
if (err && typeof err === 'object' && 'code' in err && err.code === 'ESRCH') {
106+
// success
107+
} else throw err;
108+
}
109+
});
74110
});

src/integration-tests/server-that-hangs.js

Lines changed: 0 additions & 21 deletions
This file was deleted.
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import { setTimeout } from 'node:timers';
2+
import process from 'node:process';
3+
import { McpServer } from '../server/mcp.js';
4+
import { StdioServerTransport } from '../server/stdio.js';
5+
6+
const transport = new StdioServerTransport();
7+
8+
const server = new McpServer(
9+
{
10+
name: 'server-that-hangs',
11+
title: 'Test Server that hangs',
12+
version: '1.0.0'
13+
},
14+
{
15+
capabilities: {
16+
logging: {}
17+
}
18+
}
19+
);
20+
21+
await server.connect(transport);
22+
23+
const doNotExitImmediately = async (signal: NodeJS.Signals) => {
24+
await server.sendLoggingMessage({
25+
level: 'debug',
26+
data: `received signal ${signal}`
27+
});
28+
setTimeout(() => process.exit(0), 30 * 1000);
29+
};
30+
31+
transport.onclose = () => {
32+
server.sendLoggingMessage({
33+
level: 'debug',
34+
data: 'transport: onclose called. This should never happen'
35+
});
36+
};
37+
38+
process.stdin.on('close', hadErr => {
39+
server.sendLoggingMessage({
40+
level: 'debug',
41+
data: 'stdin closed. Error: ' + hadErr
42+
});
43+
});
44+
process.on('SIGINT', doNotExitImmediately);
45+
process.on('SIGTERM', doNotExitImmediately);

src/integration-tests/test-server.js

Lines changed: 0 additions & 11 deletions
This file was deleted.
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { McpServer } from '../server/mcp.js';
2+
import { StdioServerTransport } from '../server/stdio.js';
3+
4+
const transport = new StdioServerTransport();
5+
6+
const server = new McpServer({
7+
name: 'test-server',
8+
version: '1.0.0'
9+
});
10+
11+
await server.connect(transport);
12+
13+
const exit = async () => {
14+
await server.close();
15+
process.exit(0);
16+
};
17+
18+
process.on('SIGINT', exit);
19+
process.on('SIGTERM', exit);

0 commit comments

Comments
 (0)