1
+ import { StdioClientTransport } from "./stdio.js" ;
2
+ import spawn from "cross-spawn" ;
3
+ import { JSONRPCMessage } from "../types.js" ;
4
+ import { ChildProcess } from "node:child_process" ;
5
+
6
+ // mock cross-spawn
7
+ jest . mock ( "cross-spawn" ) ;
8
+ const mockSpawn = spawn as jest . MockedFunction < typeof spawn > ;
9
+
10
+ describe ( "StdioClientTransport using cross-spawn" , ( ) => {
11
+ beforeEach ( ( ) => {
12
+ // mock cross-spawn's return value
13
+ mockSpawn . mockImplementation ( ( ) => {
14
+ const mockProcess : {
15
+ on : jest . Mock ;
16
+ stdin ?: { on : jest . Mock ; write : jest . Mock } ;
17
+ stdout ?: { on : jest . Mock } ;
18
+ stderr ?: null ;
19
+ } = {
20
+ on : jest . fn ( ( event : string , callback : ( ) => void ) => {
21
+ if ( event === "spawn" ) {
22
+ callback ( ) ;
23
+ }
24
+ return mockProcess ;
25
+ } ) ,
26
+ stdin : {
27
+ on : jest . fn ( ) ,
28
+ write : jest . fn ( ) . mockReturnValue ( true )
29
+ } ,
30
+ stdout : {
31
+ on : jest . fn ( )
32
+ } ,
33
+ stderr : null
34
+ } ;
35
+ return mockProcess as unknown as ChildProcess ;
36
+ } ) ;
37
+ } ) ;
38
+
39
+ afterEach ( ( ) => {
40
+ jest . clearAllMocks ( ) ;
41
+ } ) ;
42
+
43
+ test ( "should call cross-spawn correctly" , async ( ) => {
44
+ const transport = new StdioClientTransport ( {
45
+ command : "test-command" ,
46
+ args : [ "arg1" , "arg2" ]
47
+ } ) ;
48
+
49
+ await transport . start ( ) ;
50
+
51
+ // verify spawn is called correctly
52
+ expect ( mockSpawn ) . toHaveBeenCalledWith (
53
+ "test-command" ,
54
+ [ "arg1" , "arg2" ] ,
55
+ expect . objectContaining ( {
56
+ shell : false
57
+ } )
58
+ ) ;
59
+ } ) ;
60
+
61
+ test ( "should pass environment variables correctly" , async ( ) => {
62
+ const customEnv = { TEST_VAR : "test-value" } ;
63
+ const transport = new StdioClientTransport ( {
64
+ command : "test-command" ,
65
+ env : customEnv
66
+ } ) ;
67
+
68
+ await transport . start ( ) ;
69
+
70
+ // verify environment variables are passed correctly
71
+ expect ( mockSpawn ) . toHaveBeenCalledWith (
72
+ "test-command" ,
73
+ [ ] ,
74
+ expect . objectContaining ( {
75
+ env : customEnv
76
+ } )
77
+ ) ;
78
+ } ) ;
79
+
80
+ test ( "should send messages correctly" , async ( ) => {
81
+ const transport = new StdioClientTransport ( {
82
+ command : "test-command"
83
+ } ) ;
84
+
85
+ // get the mock process object
86
+ const mockProcess : {
87
+ on : jest . Mock ;
88
+ stdin : {
89
+ on : jest . Mock ;
90
+ write : jest . Mock ;
91
+ once : jest . Mock ;
92
+ } ;
93
+ stdout : {
94
+ on : jest . Mock ;
95
+ } ;
96
+ stderr : null ;
97
+ } = {
98
+ on : jest . fn ( ( event : string , callback : ( ) => void ) => {
99
+ if ( event === "spawn" ) {
100
+ callback ( ) ;
101
+ }
102
+ return mockProcess ;
103
+ } ) ,
104
+ stdin : {
105
+ on : jest . fn ( ) ,
106
+ write : jest . fn ( ) . mockReturnValue ( true ) ,
107
+ once : jest . fn ( )
108
+ } ,
109
+ stdout : {
110
+ on : jest . fn ( )
111
+ } ,
112
+ stderr : null
113
+ } ;
114
+
115
+ mockSpawn . mockReturnValue ( mockProcess as unknown as ChildProcess ) ;
116
+
117
+ await transport . start ( ) ;
118
+
119
+ // 关键修复:确保 jsonrpc 是字面量 "2.0"
120
+ const message : JSONRPCMessage = {
121
+ jsonrpc : "2.0" ,
122
+ id : "test-id" ,
123
+ method : "test-method"
124
+ } ;
125
+
126
+ await transport . send ( message ) ;
127
+
128
+ // verify message is sent correctly
129
+ expect ( mockProcess . stdin . write ) . toHaveBeenCalled ( ) ;
130
+ } ) ;
131
+ } ) ;
0 commit comments