@@ -18,6 +18,36 @@ const createMockResponse = () => {
18
18
return res as unknown as jest . Mocked < http . ServerResponse > ;
19
19
} ;
20
20
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
+
21
51
/**
22
52
* Helper to create and start test HTTP server with MCP setup
23
53
*/
@@ -298,4 +328,129 @@ describe('SSEServerTransport', () => {
298
328
expect ( mockRes . write ) . toHaveBeenCalledWith ( `event: message\ndata: ${ JSON . stringify ( expectedMessage ) } \n\n` ) ;
299
329
} ) ;
300
330
} ) ;
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