Skip to content

Commit acb1f62

Browse files
committed
Add http transport tests.
1 parent 02140e2 commit acb1f62

File tree

1 file changed

+247
-0
lines changed

1 file changed

+247
-0
lines changed

test/test-http-transport.js

Lines changed: 247 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,247 @@
1+
/**
2+
* Test script for HTTP transport functionality
3+
*
4+
* This script tests how the HTTP transport works:
5+
* 1. Testing HTTP server startup and connectivity
6+
* 2. Testing MCP protocol implementation over HTTP
7+
* 3. Testing error handling for invalid requests
8+
*/
9+
10+
import { runHttpServer } from '../dist/http-transport.js';
11+
import { configManager } from '../dist/config-manager.js';
12+
import assert from 'assert';
13+
import net from 'net';
14+
15+
/**
16+
* Helper function to find an available port
17+
*/
18+
async function findAvailablePort() {
19+
// Use Node.js net module to let the OS assign an available port
20+
21+
return new Promise((resolve, reject) => {
22+
const server = net.createServer();
23+
24+
// Listen on port 0 to let the OS assign an available port
25+
server.listen(0, () => {
26+
const { port } = server.address();
27+
server.close(() => resolve(port));
28+
});
29+
30+
server.on('error', reject);
31+
});
32+
}
33+
34+
/**
35+
* Helper function to parse Server-Sent Events format
36+
*/
37+
function parseSSE(sseText) {
38+
const lines = sseText.trim().split('\n');
39+
let data = '';
40+
41+
for (const line of lines) {
42+
if (line.startsWith('data: ')) {
43+
data = line.substring(6);
44+
}
45+
}
46+
47+
if (data) {
48+
return JSON.parse(data);
49+
} else {
50+
throw new Error(`No data found in SSE response: ${sseText}`);
51+
}
52+
}
53+
54+
/**
55+
* Helper function to make HTTP request
56+
*/
57+
async function makeHttpRequest(port, data) {
58+
const response = await fetch(`http://localhost:${port}/`, {
59+
method: 'POST',
60+
headers: {
61+
'Content-Type': 'application/json',
62+
'Accept': 'application/json, text/event-stream',
63+
},
64+
body: JSON.stringify(data),
65+
});
66+
67+
// Verify expected response content type and parse result.
68+
assert.strictEqual(
69+
response.headers.get('content-type'),
70+
'text/event-stream',
71+
'Expected text/event-stream content type.'
72+
);
73+
const responseData = parseSSE(await response.text());
74+
75+
return {
76+
status: response.status,
77+
data: responseData
78+
};
79+
}
80+
81+
/**
82+
* Teardown function to clean up after tests
83+
*/
84+
async function teardown(originalConfig, httpServer) {
85+
// Close HTTP server if it exists
86+
if (httpServer) {
87+
try {
88+
httpServer.close();
89+
} catch (error) {
90+
// Ignore errors when closing server
91+
}
92+
}
93+
94+
// Reset configuration to original
95+
if (originalConfig) {
96+
await configManager.updateConfig(originalConfig);
97+
}
98+
}
99+
100+
/**
101+
* Test HTTP server startup and basic MCP communication
102+
*/
103+
async function testHttpServerStartup() {
104+
console.log('\nTest 1: HTTP server startup and MCP initialize');
105+
106+
// Find an available port
107+
const port = await findAvailablePort();
108+
console.log(`Using port ${port} for HTTP transport test`);
109+
110+
// Start HTTP server in background
111+
const serverPromise = runHttpServer(port);
112+
113+
// Give server time to start
114+
await new Promise(resolve => setTimeout(resolve, 1000));
115+
116+
// Test MCP initialize request
117+
console.log('Testing MCP initialize request...');
118+
const initializeRequest = {
119+
jsonrpc: '2.0',
120+
id: 1,
121+
method: 'initialize',
122+
params: {
123+
protocolVersion: '2025-03-26',
124+
capabilities: {},
125+
clientInfo: {
126+
name: 'test-client',
127+
version: '1.0.0'
128+
}
129+
}
130+
};
131+
132+
const initResponse = await makeHttpRequest(port, initializeRequest);
133+
134+
// Verify response
135+
assert.strictEqual(initResponse.status, 200, 'Initialize request should return 200 status');
136+
assert.ok(initResponse.data.result, 'Initialize response should have result');
137+
assert.ok(initResponse.data.result.capabilities, 'Initialize response should have capabilities');
138+
139+
console.log('✓ HTTP server startup and MCP initialize test passed');
140+
return { port, serverPromise };
141+
}
142+
143+
/**
144+
* Test MCP tools listing functionality
145+
*/
146+
async function testToolsListing(port) {
147+
console.log('\nTest 2: MCP tools listing');
148+
149+
const listToolsRequest = {
150+
jsonrpc: '2.0',
151+
id: 2,
152+
method: 'tools/list',
153+
params: {}
154+
};
155+
156+
const toolsResponse = await makeHttpRequest(port, listToolsRequest);
157+
158+
// Verify response
159+
assert.strictEqual(toolsResponse.status, 200, 'Tools list request should return 200 status');
160+
assert.ok(toolsResponse.data.result, 'Tools list response should have result');
161+
assert.ok(Array.isArray(toolsResponse.data.result.tools), 'Tools list should be an array');
162+
assert.ok(toolsResponse.data.result.tools.length > 0, 'Should have at least one tool available');
163+
164+
console.log(`✓ Tools list test passed (found ${toolsResponse.data.result.tools.length} tools)`);
165+
}
166+
167+
/**
168+
* Test error handling for invalid requests
169+
*/
170+
async function testInvalidRequestHandling(port) {
171+
console.log('\nTest 3: Invalid request handling');
172+
173+
const invalidRequest = {
174+
jsonrpc: '2.0',
175+
id: 3,
176+
method: 'nonexistent/method',
177+
params: {}
178+
};
179+
180+
const invalidResponse = await makeHttpRequest(port, invalidRequest);
181+
182+
// Verify error response
183+
assert.strictEqual(invalidResponse.status, 200, 'Invalid request should still return 200 (error in body)');
184+
assert.ok(invalidResponse.data.error, 'Invalid request should return error in response body');
185+
186+
console.log('✓ Invalid request handling test passed');
187+
}
188+
189+
/**
190+
* Main test function for HTTP transport
191+
*/
192+
async function runHttpTransportTests() {
193+
console.log('=== HTTP Transport Tests ===');
194+
195+
let serverInfo;
196+
197+
try {
198+
// Test 1: HTTP server startup and MCP initialize
199+
serverInfo = await testHttpServerStartup();
200+
201+
// Test 2: MCP tools listing
202+
await testToolsListing(serverInfo.port);
203+
204+
// Test 3: Invalid request handling
205+
await testInvalidRequestHandling(serverInfo.port);
206+
207+
console.log('\n✅ All HTTP transport tests passed!');
208+
return { success: true, serverInfo };
209+
210+
} catch (error) {
211+
console.error('❌ HTTP transport test failed:', error.message);
212+
return { success: false, serverInfo };
213+
}
214+
}
215+
216+
/**
217+
* Export the main test function
218+
*/
219+
export default async function runTests() {
220+
let originalConfig;
221+
let testResult;
222+
223+
try {
224+
originalConfig = await configManager.getConfig();
225+
testResult = await runHttpTransportTests();
226+
} catch (error) {
227+
console.error('❌ Test failed:', error.message);
228+
return false;
229+
} finally {
230+
// Clean up server and config
231+
if (testResult && testResult.serverInfo) {
232+
await teardown(originalConfig, testResult.serverInfo.serverPromise);
233+
} else if (originalConfig) {
234+
await teardown(originalConfig);
235+
}
236+
}
237+
238+
return testResult ? testResult.success : false;
239+
}
240+
241+
// If this file is run directly (not imported), execute the test
242+
if (import.meta.url === `file://${process.argv[1]}`) {
243+
runTests().catch(error => {
244+
console.error('❌ Unhandled error:', error);
245+
process.exit(1);
246+
});
247+
}

0 commit comments

Comments
 (0)