@@ -2,7 +2,9 @@ import fs from 'node:fs';
22import { afterEach , beforeEach , test , vi } from 'vitest' ;
33import assert from 'node:assert/strict' ;
44import type { DeviceInfo } from '../../../utils/device.ts' ;
5+ import type { ExecBackgroundResult } from '../../../utils/exec.ts' ;
56import { AppError } from '../../../utils/errors.ts' ;
7+ import type { RunnerSession } from '../runner-session-types.ts' ;
68
79const { mockRunCmd } = vi . hoisted ( ( ) => ( {
810 mockRunCmd : vi . fn ( ) ,
@@ -106,6 +108,37 @@ test('waitForRunner keeps tunnel IP lookup request-local when no tunnel IP is av
106108 assert . equal ( vi . mocked ( fetch ) . mock . calls [ 0 ] ?. [ 0 ] , 'http://127.0.0.1:8100/command' ) ;
107109} ) ;
108110
111+ test ( 'waitForRunner uses simulator fallback within the attempt for ready sessions' , async ( ) => {
112+ vi . stubGlobal (
113+ 'fetch' ,
114+ vi . fn ( async ( ) => {
115+ throw new Error ( 'ECONNREFUSED' ) ;
116+ } ) ,
117+ ) ;
118+ mockRunCmd . mockResolvedValue ( { exitCode : 0 , stdout : '{"ok":true}' , stderr : '' } ) ;
119+
120+ const response = await waitForRunner (
121+ iosSimulator ,
122+ 8100 ,
123+ { command : 'uptime' } ,
124+ undefined ,
125+ 5_000 ,
126+ makeReadyRunnerSession ( ) ,
127+ ) ;
128+
129+ assert . equal ( await response . text ( ) , '{"ok":true}' ) ;
130+ assert . equal ( vi . mocked ( fetch ) . mock . calls . length , 1 ) ;
131+ assert . equal ( mockRunCmd . mock . calls . length , 1 ) ;
132+ assert . equal ( mockRunCmd . mock . calls [ 0 ] ?. [ 0 ] , 'xcrun' ) ;
133+ assert . deepEqual ( mockRunCmd . mock . calls [ 0 ] ?. [ 1 ] ?. slice ( 0 , 5 ) , [
134+ 'simctl' ,
135+ 'spawn' ,
136+ 'sim-1' ,
137+ '/usr/bin/curl' ,
138+ '-s' ,
139+ ] ) ;
140+ } ) ;
141+
109142test ( 'waitForRunner invalidates cached tunnel IP when localhost fallback succeeds' , async ( ) => {
110143 const tunnelIps = [ 'fd00::123' , 'fd00::456' ] ;
111144 mockRunCmd . mockImplementation ( async ( _cmd : string , args : string [ ] ) => {
@@ -170,3 +203,17 @@ function stubSuccessfulFetch(): void {
170203 vi . fn ( async ( ) => new Response ( '{}' ) ) ,
171204 ) ;
172205}
206+
207+ function makeReadyRunnerSession ( ) : RunnerSession {
208+ return {
209+ sessionId : 'ready-session' ,
210+ device : iosSimulator ,
211+ deviceId : iosSimulator . id ,
212+ port : 8100 ,
213+ xctestrunPath : '/tmp/runner.xctestrun' ,
214+ jsonPath : '/tmp/runner.json' ,
215+ testPromise : Promise . resolve ( { exitCode : 0 , stdout : '' , stderr : '' } ) ,
216+ child : { pid : 1234 , exitCode : null } as ExecBackgroundResult [ 'child' ] ,
217+ ready : true ,
218+ } ;
219+ }
0 commit comments