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