@@ -9,7 +9,7 @@ type MockChild = EventEmitter & {
99 kill : ReturnType < typeof vi . fn > ;
1010 stdout ?: EventEmitter ;
1111 stderr ?: EventEmitter ;
12- stdin ?: { write : ReturnType < typeof vi . fn > } ;
12+ stdin ?: EventEmitter & { write : ReturnType < typeof vi . fn > } ;
1313} ;
1414
1515const children : MockChild [ ] = [ ] ;
@@ -34,7 +34,7 @@ vi.mock("node:child_process", async (importOriginal) => {
3434 } ) ,
3535 stdout : new EventEmitter ( ) ,
3636 stderr : new EventEmitter ( ) ,
37- stdin : { write : vi . fn ( ) } ,
37+ stdin : Object . assign ( new EventEmitter ( ) , { write : vi . fn ( ) } ) ,
3838 } ) as MockChild ;
3939 children . push ( child ) ;
4040 return child ;
@@ -201,6 +201,48 @@ describe("google-meet node host bridge sessions", () => {
201201 }
202202 } ) ;
203203
204+ it ( "closes once when command-pair streams fail together" , async ( ) => {
205+ const originalPlatform = process . platform ;
206+ children . length = 0 ;
207+
208+ Object . defineProperty ( process , "platform" , { configurable : true , value : "darwin" } ) ;
209+ try {
210+ const start = JSON . parse (
211+ await handleGoogleMeetNodeHostCommand (
212+ JSON . stringify ( {
213+ action : "start" ,
214+ url : "https://meet.google.com/xyz-abcd-uvw" ,
215+ mode : "realtime" ,
216+ launch : false ,
217+ audioInputCommand : [ "mock-rec" ] ,
218+ audioOutputCommand : [ "mock-play" ] ,
219+ } ) ,
220+ ) ,
221+ ) ;
222+ const [ outputProcess , inputProcess ] = children ;
223+ if ( ! outputProcess || ! inputProcess ) {
224+ throw new Error ( "expected Google Meet node host command-pair processes" ) ;
225+ }
226+
227+ outputProcess . stderr ?. emit ( "error" , new Error ( "output stderr failed" ) ) ;
228+ inputProcess . stdout ?. emit ( "error" , new Error ( "input stdout failed" ) ) ;
229+ inputProcess . stderr ?. emit ( "error" , new Error ( "input stderr failed" ) ) ;
230+
231+ const status = JSON . parse (
232+ await handleGoogleMeetNodeHostCommand (
233+ JSON . stringify ( { action : "status" , bridgeId : start . bridgeId } ) ,
234+ ) ,
235+ ) ;
236+ expect ( status . bridge . closed ) . toBe ( true ) ;
237+ expect ( outputProcess . kill ) . toHaveBeenCalledTimes ( 1 ) ;
238+ expect ( inputProcess . kill ) . toHaveBeenCalledTimes ( 1 ) ;
239+ expect ( outputProcess . kill ) . toHaveBeenCalledWith ( "SIGTERM" ) ;
240+ expect ( inputProcess . kill ) . toHaveBeenCalledWith ( "SIGTERM" ) ;
241+ } finally {
242+ Object . defineProperty ( process , "platform" , { configurable : true , value : originalPlatform } ) ;
243+ }
244+ } ) ;
245+
204246 it ( "lists active bridge sessions and hides closed sessions" , async ( ) => {
205247 const originalPlatform = process . platform ;
206248 children . length = 0 ;
0 commit comments