11import * as http from 'http' ;
22import { afterEach , beforeEach , describe , expect , it , vi } from 'vitest' ;
33import type { McpTool , McpToolSpec } from '../types' ;
4+ import type { OutputChannelWithHistory } from '../util' ;
5+
6+ /* eslint-disable @typescript-eslint/naming-convention */
7+ // HTTP headers and MCP protocol headers must use their spec-defined names
48
59vi . mock ( 'vscode' ) ;
610
711// Mock all tool creators to return minimal stub tools
812vi . mock ( './tools' , ( ) => ( {
9- createAddRemoteFileSourcesTool : vi . fn ( ( ) => makeStubTool ( 'addRemoteFileSources' ) ) ,
13+ createAddRemoteFileSourcesTool : vi . fn ( ( ) =>
14+ makeStubTool ( 'addRemoteFileSources' )
15+ ) ,
1016 createConnectToServerTool : vi . fn ( ( ) => makeStubTool ( 'connectToServer' ) ) ,
1117 createGetColumnStatsTool : vi . fn ( ( ) => makeStubTool ( 'getColumnStats' ) ) ,
1218 createGetLogsTool : vi . fn ( ( ) => makeStubTool ( 'getLogs' ) ) ,
1319 createGetTableDataTool : vi . fn ( ( ) => makeStubTool ( 'getTableData' ) ) ,
1420 createGetTableStatsTool : vi . fn ( ( ) => makeStubTool ( 'getTableStats' ) ) ,
1521 createListConnectionsTool : vi . fn ( ( ) => makeStubTool ( 'listConnections' ) ) ,
1622 createListVariablesTool : vi . fn ( ( ) => makeStubTool ( 'listVariables' ) ) ,
17- createListRemoteFileSourcesTool : vi . fn ( ( ) => makeStubTool ( 'listRemoteFileSources' ) ) ,
23+ createListRemoteFileSourcesTool : vi . fn ( ( ) =>
24+ makeStubTool ( 'listRemoteFileSources' )
25+ ) ,
1826 createListServersTool : vi . fn ( ( ) => makeStubTool ( 'listServers' ) ) ,
1927 createOpenFilesInEditorTool : vi . fn ( ( ) => makeStubTool ( 'openFilesInEditor' ) ) ,
2028 createOpenVariablePanelsTool : vi . fn ( ( ) => makeStubTool ( 'openVariablePanels' ) ) ,
21- createRemoveRemoteFileSourcesTool : vi . fn ( ( ) => makeStubTool ( 'removeRemoteFileSources' ) ) ,
29+ createRemoveRemoteFileSourcesTool : vi . fn ( ( ) =>
30+ makeStubTool ( 'removeRemoteFileSources' )
31+ ) ,
2232 createRunCodeFromUriTool : vi . fn ( ( ) => makeStubTool ( 'runCodeFromUri' ) ) ,
2333 createRunCodeTool : vi . fn ( ( ) => makeStubTool ( 'runCode' ) ) ,
24- createSetEditorConnectionTool : vi . fn ( ( ) => makeStubTool ( 'setEditorConnection' ) ) ,
34+ createSetEditorConnectionTool : vi . fn ( ( ) =>
35+ makeStubTool ( 'setEditorConnection' )
36+ ) ,
2537 createShowOutputPanelTool : vi . fn ( ( ) => makeStubTool ( 'showOutputPanel' ) ) ,
2638} ) ) ;
2739
@@ -37,6 +49,7 @@ function makeStubTool(name: string): McpTool<McpToolSpec> {
3749 title : name ,
3850 description : `Stub tool: ${ name } ` ,
3951 inputSchema : { } ,
52+ outputSchema : { } ,
4053 } ,
4154 handler : vi . fn ( ) . mockResolvedValue ( {
4255 content : [ { type : 'text' , text : `stub result for ${ name } ` } ] ,
@@ -45,7 +58,7 @@ function makeStubTool(name: string): McpTool<McpToolSpec> {
4558}
4659
4760/** Create a minimal mock for OutputChannelWithHistory */
48- function makeMockOutputChannel ( ) {
61+ function makeMockOutputChannel ( ) : OutputChannelWithHistory {
4962 return {
5063 appendLine : vi . fn ( ) ,
5164 append : vi . fn ( ) ,
@@ -55,15 +68,19 @@ function makeMockOutputChannel() {
5568 hide : vi . fn ( ) ,
5669 replace : vi . fn ( ) ,
5770 name : 'mock' ,
58- } ;
71+ } as unknown as OutputChannelWithHistory ;
5972}
6073
6174// Helper: send an HTTP POST to the MCP server and collect the full response
6275function postToMcp (
6376 port : number ,
6477 body : unknown ,
6578 headers : Record < string , string > = { }
66- ) : Promise < { status : number ; headers : http . IncomingMessage [ 'headers' ] ; body : string } > {
79+ ) : Promise < {
80+ status : number ;
81+ headers : http . IncomingMessage [ 'headers' ] ;
82+ body : string ;
83+ } > {
6784 return new Promise ( ( resolve , reject ) => {
6885 const payload = JSON . stringify ( body ) ;
6986 const req = http . request (
@@ -85,7 +102,11 @@ function postToMcp(
85102 let data = '' ;
86103 res . on ( 'data' , chunk => ( data += chunk ) ) ;
87104 res . on ( 'end' , ( ) =>
88- resolve ( { status : res . statusCode ?? 0 , headers : res . headers , body : data } )
105+ resolve ( {
106+ status : res . statusCode ?? 0 ,
107+ headers : res . headers ,
108+ body : data ,
109+ } )
89110 ) ;
90111 }
91112 ) ;
@@ -129,7 +150,7 @@ describe('McpServer', () => {
129150 { } as any , // panelService
130151 { } as any , // pythonDiagnostics
131152 { } as any , // pythonWorkspace
132- { } as any // serverManager
153+ { } as any // serverManager
133154 ) ;
134155
135156 port = await server . start ( ) ;
@@ -149,8 +170,8 @@ describe('McpServer', () => {
149170 expect ( res . status ) . toBe ( 200 ) ;
150171 // The session ID is returned either in the response header or body
151172 const sessionId =
152- res . headers [ 'mcp-session-id' ] as string | undefined ??
153- ( ( ) => {
173+ ( res . headers [ 'mcp-session-id' ] as string | undefined ) ??
174+ ( ( ) : string | undefined => {
154175 try {
155176 return JSON . parse ( res . body ) ?. sessionId as string | undefined ;
156177 } catch {
@@ -172,10 +193,10 @@ describe('McpServer', () => {
172193 expect ( body . error . code ) . toBe ( - 32600 ) ;
173194 } ) ;
174195
175- it ( 'should reject GET requests with 405' , async ( ) => {
196+ it ( 'should reject unsupported methods (PUT, DELETE, etc.) with 405' , async ( ) => {
176197 const res = await new Promise < { status : number } > ( ( resolve , reject ) => {
177198 const req = http . request (
178- { hostname : '127.0.0.1' , port, path : '/mcp' , method : 'GET ' } ,
199+ { hostname : '127.0.0.1' , port, path : '/mcp' , method : 'PUT ' } ,
179200 res => resolve ( { status : res . statusCode ?? 0 } )
180201 ) ;
181202 req . on ( 'error' , reject ) ;
@@ -301,10 +322,26 @@ describe('McpServer', () => {
301322
302323 // Then fire requests on both sessions in parallel
303324 const [ res1a , res1b , res2a , res2b ] = await Promise . all ( [
304- postToMcp ( port , { ...LIST_TOOLS_REQUEST , id : 21 } , { 'mcp-session-id' : session1 } ) ,
305- postToMcp ( port , { ...LIST_TOOLS_REQUEST , id : 22 } , { 'mcp-session-id' : session1 } ) ,
306- postToMcp ( port , { ...LIST_TOOLS_REQUEST , id : 23 } , { 'mcp-session-id' : session2 } ) ,
307- postToMcp ( port , { ...LIST_TOOLS_REQUEST , id : 24 } , { 'mcp-session-id' : session2 } ) ,
325+ postToMcp (
326+ port ,
327+ { ...LIST_TOOLS_REQUEST , id : 21 } ,
328+ { 'mcp-session-id' : session1 }
329+ ) ,
330+ postToMcp (
331+ port ,
332+ { ...LIST_TOOLS_REQUEST , id : 22 } ,
333+ { 'mcp-session-id' : session1 }
334+ ) ,
335+ postToMcp (
336+ port ,
337+ { ...LIST_TOOLS_REQUEST , id : 23 } ,
338+ { 'mcp-session-id' : session2 }
339+ ) ,
340+ postToMcp (
341+ port ,
342+ { ...LIST_TOOLS_REQUEST , id : 24 } ,
343+ { 'mcp-session-id' : session2 }
344+ ) ,
308345 ] ) ;
309346
310347 for ( const res of [ res1a , res1b , res2a , res2b ] ) {
@@ -327,7 +364,8 @@ describe('McpServer', () => {
327364 const toolsRes = await postToMcp ( port , LIST_TOOLS_REQUEST , {
328365 'mcp-session-id' : sessionId ,
329366 } ) ;
330- const tools : { name : string } [ ] = JSON . parse ( toolsRes . body ) . result ?. tools ?? [ ] ;
367+ const tools : { name : string } [ ] =
368+ JSON . parse ( toolsRes . body ) . result ?. tools ?? [ ] ;
331369 expect ( tools . some ( t => t . name === 'addRemoteFileSources' ) ) . toBe ( true ) ;
332370 } ) ;
333371
@@ -338,7 +376,8 @@ describe('McpServer', () => {
338376 const toolsRes = await postToMcp ( port , LIST_TOOLS_REQUEST , {
339377 'mcp-session-id' : sessionId ,
340378 } ) ;
341- const tools : { name : string } [ ] = JSON . parse ( toolsRes . body ) . result ?. tools ?? [ ] ;
379+ const tools : { name : string } [ ] =
380+ JSON . parse ( toolsRes . body ) . result ?. tools ?? [ ] ;
342381 expect ( tools . some ( t => t . name === 'listServers' ) ) . toBe ( true ) ;
343382 } ) ;
344383
@@ -349,7 +388,8 @@ describe('McpServer', () => {
349388 const toolsRes = await postToMcp ( port , LIST_TOOLS_REQUEST , {
350389 'mcp-session-id' : sessionId ,
351390 } ) ;
352- const tools : { name : string } [ ] = JSON . parse ( toolsRes . body ) . result ?. tools ?? [ ] ;
391+ const tools : { name : string } [ ] =
392+ JSON . parse ( toolsRes . body ) . result ?. tools ?? [ ] ;
353393 expect ( tools . some ( t => t . name === 'runCode' ) ) . toBe ( true ) ;
354394 } ) ;
355395 } ) ;
0 commit comments