@@ -18,6 +18,36 @@ const createMockResponse = () => {
1818 return res as unknown as jest . Mocked < http . ServerResponse > ;
1919} ;
2020
21+ const createMockRequest = ( { headers = { } , body } : { headers ?: Record < string , string > , body ?: string } = { } ) => {
22+ const mockReq = {
23+ headers,
24+ body : body ? body : undefined ,
25+ auth : {
26+ token : 'test-token' ,
27+ } ,
28+ on : jest . fn < http . IncomingMessage [ 'on' ] > ( ) . mockImplementation ( ( event , listener ) => {
29+ const mockListener = listener as unknown as ( ...args : unknown [ ] ) => void ;
30+ if ( event === 'data' ) {
31+ mockListener ( Buffer . from ( body || '' ) as unknown as Error ) ;
32+ }
33+ if ( event === 'error' ) {
34+ mockListener ( new Error ( 'test' ) ) ;
35+ }
36+ if ( event === 'end' ) {
37+ mockListener ( ) ;
38+ }
39+ if ( event === 'close' ) {
40+ setTimeout ( listener , 100 ) ;
41+ }
42+ return mockReq ;
43+ } ) ,
44+ listeners : jest . fn < http . IncomingMessage [ 'listeners' ] > ( ) ,
45+ removeListener : jest . fn < http . IncomingMessage [ 'removeListener' ] > ( ) ,
46+ } as unknown as http . IncomingMessage ;
47+
48+ return mockReq ;
49+ } ;
50+
2151/**
2252 * Helper to create and start test HTTP server with MCP setup
2353 */
@@ -298,4 +328,129 @@ describe('SSEServerTransport', () => {
298328 expect ( mockRes . write ) . toHaveBeenCalledWith ( `event: message\ndata: ${ JSON . stringify ( expectedMessage ) } \n\n` ) ;
299329 } ) ;
300330 } ) ;
301- } ) ;
331+
332+ describe ( 'handlePostMessage method' , ( ) => {
333+ it ( 'should return 500 if server has not started' , async ( ) => {
334+ const mockReq = createMockRequest ( ) ;
335+ const mockRes = createMockResponse ( ) ;
336+ const endpoint = '/messages' ;
337+ const transport = new SSEServerTransport ( endpoint , mockRes ) ;
338+
339+ const error = 'SSE connection not established' ;
340+ await expect ( transport . handlePostMessage ( mockReq , mockRes ) )
341+ . rejects . toThrow ( error ) ;
342+ expect ( mockRes . writeHead ) . toHaveBeenCalledWith ( 500 ) ;
343+ expect ( mockRes . end ) . toHaveBeenCalledWith ( error ) ;
344+ } ) ;
345+
346+ it ( 'should return 400 if content-type is not application/json' , async ( ) => {
347+ const mockReq = createMockRequest ( { headers : { 'content-type' : 'text/plain' } } ) ;
348+ const mockRes = createMockResponse ( ) ;
349+ const endpoint = '/messages' ;
350+ const transport = new SSEServerTransport ( endpoint , mockRes ) ;
351+ await transport . start ( ) ;
352+
353+ transport . onerror = jest . fn ( ) ;
354+ const error = 'Unsupported content-type: text/plain' ;
355+ await expect ( transport . handlePostMessage ( mockReq , mockRes ) )
356+ . resolves . toBe ( undefined ) ;
357+ expect ( mockRes . writeHead ) . toHaveBeenCalledWith ( 400 ) ;
358+ expect ( mockRes . end ) . toHaveBeenCalledWith ( expect . stringContaining ( error ) ) ;
359+ expect ( transport . onerror ) . toHaveBeenCalledWith ( new Error ( error ) ) ;
360+ } ) ;
361+
362+ it ( 'should return 400 if message has not a valid schema' , async ( ) => {
363+ const invalidMessage = JSON . stringify ( {
364+ // missing jsonrpc field
365+ method : 'call' ,
366+ params : [ 1 , 2 , 3 ] ,
367+ id : 1 ,
368+ } )
369+ const mockReq = createMockRequest ( {
370+ headers : { 'content-type' : 'application/json' } ,
371+ body : invalidMessage ,
372+ } ) ;
373+ const mockRes = createMockResponse ( ) ;
374+ const endpoint = '/messages' ;
375+ const transport = new SSEServerTransport ( endpoint , mockRes ) ;
376+ await transport . start ( ) ;
377+
378+ transport . onmessage = jest . fn ( ) ;
379+ await transport . handlePostMessage ( mockReq , mockRes ) ;
380+ expect ( mockRes . writeHead ) . toHaveBeenCalledWith ( 400 ) ;
381+ expect ( transport . onmessage ) . not . toHaveBeenCalled ( ) ;
382+ expect ( mockRes . end ) . toHaveBeenCalledWith ( `Invalid message: ${ invalidMessage } ` ) ;
383+ } ) ;
384+
385+ it ( 'should return 202 if message has a valid schema' , async ( ) => {
386+ const validMessage = JSON . stringify ( {
387+ jsonrpc : "2.0" ,
388+ method : 'call' ,
389+ params : {
390+ a : 1 ,
391+ b : 2 ,
392+ c : 3 ,
393+ } ,
394+ id : 1
395+ } )
396+ const mockReq = createMockRequest ( {
397+ headers : { 'content-type' : 'application/json' } ,
398+ body : validMessage ,
399+ } ) ;
400+ const mockRes = createMockResponse ( ) ;
401+ const endpoint = '/messages' ;
402+ const transport = new SSEServerTransport ( endpoint , mockRes ) ;
403+ await transport . start ( ) ;
404+
405+ transport . onmessage = jest . fn ( ) ;
406+ await transport . handlePostMessage ( mockReq , mockRes ) ;
407+ expect ( mockRes . writeHead ) . toHaveBeenCalledWith ( 202 ) ;
408+ expect ( mockRes . end ) . toHaveBeenCalledWith ( 'Accepted' ) ;
409+ expect ( transport . onmessage ) . toHaveBeenCalledWith ( {
410+ jsonrpc : "2.0" ,
411+ method : 'call' ,
412+ params : {
413+ a : 1 ,
414+ b : 2 ,
415+ c : 3 ,
416+ } ,
417+ id : 1
418+ } , {
419+ authInfo : {
420+ token : 'test-token' ,
421+ } ,
422+ requestInfo : {
423+ headers : {
424+ 'content-type' : 'application/json' ,
425+ } ,
426+ } ,
427+ } ) ;
428+ } ) ;
429+ } ) ;
430+
431+ describe ( 'close method' , ( ) => {
432+ it ( 'should call onclose' , async ( ) => {
433+ const mockRes = createMockResponse ( ) ;
434+ const endpoint = '/messages' ;
435+ const transport = new SSEServerTransport ( endpoint , mockRes ) ;
436+ await transport . start ( ) ;
437+ transport . onclose = jest . fn ( ) ;
438+ await transport . close ( ) ;
439+ expect ( transport . onclose ) . toHaveBeenCalled ( ) ;
440+ } ) ;
441+ } ) ;
442+
443+ describe ( 'send method' , ( ) => {
444+ it ( 'should call onsend' , async ( ) => {
445+ const mockRes = createMockResponse ( ) ;
446+ const endpoint = '/messages' ;
447+ const transport = new SSEServerTransport ( endpoint , mockRes ) ;
448+ await transport . start ( ) ;
449+ expect ( mockRes . write ) . toHaveBeenCalledTimes ( 1 ) ;
450+ expect ( mockRes . write ) . toHaveBeenCalledWith (
451+ expect . stringContaining ( 'event: endpoint' ) ) ;
452+ expect ( mockRes . write ) . toHaveBeenCalledWith (
453+ expect . stringContaining ( `data: /messages?sessionId=${ transport . sessionId } ` ) ) ;
454+ } ) ;
455+ } ) ;
456+ } ) ;
0 commit comments