File tree Expand file tree Collapse file tree 3 files changed +74
-0
lines changed Expand file tree Collapse file tree 3 files changed +74
-0
lines changed Original file line number Diff line number Diff line change
1
+ import { Server } from "../server/index.js" ;
2
+ import { StdioServerTransport } from "../server/stdio.js" ;
3
+
4
+ describe ( "Process cleanup" , ( ) => {
5
+ jest . setTimeout ( 5000 ) ; // 5 second timeout
6
+
7
+ it ( "should exit cleanly after closing transport" , async ( ) => {
8
+ const server = new Server (
9
+ {
10
+ name : "test-server" ,
11
+ version : "1.0.0" ,
12
+ } ,
13
+ {
14
+ capabilities : { } ,
15
+ }
16
+ ) ;
17
+
18
+ const transport = new StdioServerTransport ( ) ;
19
+ await server . connect ( transport ) ;
20
+
21
+ // Close the transport
22
+ await transport . close ( ) ;
23
+
24
+ // If we reach here without hanging, the test passes
25
+ // The test runner will fail if the process hangs
26
+ expect ( true ) . toBe ( true ) ;
27
+ } ) ;
28
+ } ) ;
Original file line number Diff line number Diff line change @@ -100,3 +100,41 @@ test("should read multiple messages", async () => {
100
100
await finished ;
101
101
expect ( readMessages ) . toEqual ( messages ) ;
102
102
} ) ;
103
+
104
+ test ( "should properly clean up resources when closed" , async ( ) => {
105
+ // Create mock streams that track their destroyed state
106
+ const mockStdin = new Readable ( {
107
+ read ( ) { } , // No-op implementation
108
+ destroy ( ) {
109
+ this . destroyed = true ;
110
+ return this ;
111
+ }
112
+ } ) ;
113
+ const mockStdout = new Writable ( {
114
+ write ( chunk , encoding , callback ) {
115
+ callback ( ) ;
116
+ } ,
117
+ destroy ( ) {
118
+ this . destroyed = true ;
119
+ return this ;
120
+ }
121
+ } ) ;
122
+
123
+ const transport = new StdioServerTransport ( mockStdin , mockStdout ) ;
124
+ await transport . start ( ) ;
125
+
126
+ // Send a message to potentially create 'drain' listeners
127
+ await transport . send ( { jsonrpc : "2.0" , method : "test" , id : 1 } ) ;
128
+
129
+ // Close the transport
130
+ await transport . close ( ) ;
131
+
132
+ // Check that all listeners were removed
133
+ expect ( mockStdin . listenerCount ( 'data' ) ) . toBe ( 0 ) ;
134
+ expect ( mockStdin . listenerCount ( 'error' ) ) . toBe ( 0 ) ;
135
+ expect ( mockStdout . listenerCount ( 'drain' ) ) . toBe ( 0 ) ;
136
+
137
+ // Check that streams were properly ended
138
+ expect ( mockStdin . destroyed ) . toBe ( true ) ;
139
+ expect ( mockStdout . destroyed ) . toBe ( true ) ;
140
+ } ) ;
Original file line number Diff line number Diff line change @@ -62,8 +62,16 @@ export class StdioServerTransport implements Transport {
62
62
}
63
63
64
64
async close ( ) : Promise < void > {
65
+ // Remove all event listeners
65
66
this . _stdin . off ( "data" , this . _ondata ) ;
66
67
this . _stdin . off ( "error" , this . _onerror ) ;
68
+ this . _stdout . removeAllListeners ( 'drain' ) ;
69
+
70
+ // Destroy both streams
71
+ this . _stdin . destroy ( ) ;
72
+ this . _stdout . destroy ( ) ;
73
+
74
+ // Clear the buffer and notify closure
67
75
this . _readBuffer . clear ( ) ;
68
76
this . onclose ?.( ) ;
69
77
}
You can’t perform that action at this time.
0 commit comments