@@ -4823,77 +4823,142 @@ describe('rerun --wait — dashboardUrl on terminal output', () => {
48234823// ---------------------------------------------------------------------------
48244824// Batch --all --wait fan-out: RequestTimeoutError must not leave stdout empty
48254825// ---------------------------------------------------------------------------
4826- describe ( '[finding-5] batch rerun --wait: RequestTimeoutError during fan-out poll writes JSON stdout + exit 7' , ( ) => {
4827- it ( 'stdout contains accepted[] with runIds when member polls throw RequestTimeoutError' , async ( ) => {
4828- const creds = makeCreds ( ) ;
4829- const batchResp : BatchRerunResponse = {
4830- accepted : [
4831- { testId : 'test_1' , runId : 'run_b1' , enqueuedAt : '2026-06-03T10:00:00.000Z' } ,
4832- { testId : 'test_2' , runId : 'run_b2' , enqueuedAt : '2026-06-03T10:00:00.000Z' }
4833- ] ,
4834- deferred : [ ] ,
4835- conflicts : [ ] ,
4836- closure : { byProject : [ ] }
4837- } ;
4838- const fetchImpl = makeFetch ( ( url ) => {
4839- if ( url . includes ( '/tests/batch/rerun' ) ) return { status : 202 , body : batchResp } ;
4840- if ( url . includes ( '/runs/' ) ) throw new RequestTimeoutError ( 120000 , 'req_timeout_batch' ) ;
4841- return errorBody ( 'NOT_FOUND' ) ;
4842- } ) ;
4843- const stdoutLines : string [ ] = [ ] ;
4844- const err = await runTestRerun (
4845- {
4846- testIds : [ 'test_1' , 'test_2' ] ,
4847- all : false ,
4848- wait : true ,
4849- timeoutSeconds : 60 ,
4850- autoHeal : false ,
4851- autoHealExplicit : false ,
4852- skipDependencies : false ,
4853- maxConcurrency : 1 ,
4854- profile : 'default' ,
4855- output : 'json' ,
4856- debug : false
4826+ describe (
4827+ '[finding-5] batch rerun --wait: RequestTimeoutError during fan-out poll writes JSON stdout + exit 7' ,
4828+ ( ) => {
4829+ it (
4830+ 'stdout contains accepted[] with runIds when member polls throw RequestTimeoutError' ,
4831+ async ( ) => {
4832+ const creds = makeCreds ( ) ;
4833+ const batchResp : BatchRerunResponse = {
4834+ accepted : [
4835+ { testId : 'test_1' , runId : 'run_b1' , enqueuedAt : '2026-06-03T10:00:00.000Z' } ,
4836+ { testId : 'test_2' , runId : 'run_b2' , enqueuedAt : '2026-06-03T10:00:00.000Z' } ,
4837+ ] ,
4838+ deferred : [ ] ,
4839+ conflicts : [ ] ,
4840+ closure : { byProject : [ ] } ,
4841+ } ;
4842+
4843+ const fetchImpl = makeFetch ( ( url ) => {
4844+ if ( url . includes ( '/tests/batch/rerun' ) ) {
4845+ return { status : 202 , body : batchResp } ;
4846+ }
4847+ if ( url . includes ( '/runs/' ) ) {
4848+ throw new RequestTimeoutError ( 120000 , 'req_timeout_batch_rerun' ) ;
4849+ }
4850+ return errorBody ( 'NOT_FOUND' ) ;
4851+ } ) ;
4852+
4853+ const stdoutLines : string [ ] = [ ] ;
4854+ const err = await runTestRerun (
4855+ {
4856+ testIds : [ 'test_1' , 'test_2' ] ,
4857+ all : false ,
4858+ wait : true ,
4859+ timeoutSeconds : 60 ,
4860+ autoHeal : false ,
4861+ autoHealExplicit : false ,
4862+ skipDependencies : false ,
4863+ maxConcurrency : 1 ,
4864+ profile : 'default' ,
4865+ output : 'json' ,
4866+ debug : false ,
4867+ } ,
4868+ {
4869+ ...creds ,
4870+ sleep : instantSleep ,
4871+ fetchImpl : fetchImpl as unknown as FetchImpl ,
4872+ stdout : ( line ) => stdoutLines . push ( line ) ,
4873+ stderr : ( ) => undefined ,
4874+ } ,
4875+ ) . catch ( ( e ) => e ) ;
4876+
4877+ expect ( err ) . toMatchObject ( { exitCode : 7 } ) ;
4878+ const parsed = JSON . parse ( stdoutLines . join ( '\n' ) ) as {
4879+ accepted : Array < { testId : string ; runId : string ; status : string } > ;
4880+ } ;
4881+ expect ( parsed . accepted ) . toHaveLength ( 2 ) ;
4882+ expect ( parsed . accepted . map ( ( r ) => r . runId ) . sort ( ) ) . toEqual ( [ 'run_b1' , 'run_b2' ] ) ;
4883+ expect ( parsed . accepted . every ( ( r ) => r . status === 'timeout' ) ) . toBe ( true ) ;
48574884 } ,
4858- { ...creds , sleep : instantSleep , fetchImpl : fetchImpl as unknown as FetchImpl , stdout : ( l ) => stdoutLines . push ( l ) , stderr : ( ) => undefined }
4859- ) . catch ( ( e ) => e ) ;
4860- expect ( err ) . toMatchObject ( { exitCode : 7 } ) ;
4861- } ) ;
4862- } ) ;
4885+ ) ;
4886+ } ,
4887+ ) ;
48634888
48644889// ---------------------------------------------------------------------------
48654890// TimeoutError on single FE rerun --wait: partial stdout + exit 7
48664891// ---------------------------------------------------------------------------
48674892describe ( '[finding-4] single FE rerun --wait: TimeoutError writes partial JSON to stdout' , ( ) => {
4868- it ( 'exit 7 AND stdout contains {runId, status:"running"} when --timeout polling deadline is exceeded' , async ( ) => {
4869- const creds = makeCreds ( ) ;
4870- const rerunResp = { runId : 'run_fe_01' , status : 'accepted' } ;
4871- const fetchImpl : FetchImpl = async ( input ) => {
4872- const url = typeof input === 'string' ? input : ( input as Request ) . url ;
4873- if ( url . includes ( '/runs/rerun' ) ) return new Response ( JSON . stringify ( rerunResp ) , { status : 202 } ) ;
4874- if ( url . includes ( '/runs/' ) ) return new Response ( JSON . stringify ( { runId : rerunResp . runId , status : 'running' , finishedAt : null } ) , { status : 200 } ) ;
4875- return new Response ( JSON . stringify ( { error : { code : 'NOT_FOUND' } } ) , { status : 404 } ) ;
4876- } ;
4877- const stdoutLines : string [ ] = [ ] ;
4878- const err = await runTestRerun (
4879- {
4880- testIds : [ 'test_fe_01' ] ,
4881- all : false ,
4882- wait : true ,
4883- timeoutSeconds : 0 ,
4884- autoHeal : false ,
4885- autoHealExplicit : false ,
4886- skipDependencies : false ,
4887- maxConcurrency : 10 ,
4888- output : 'json' ,
4889- profile : 'default' ,
4890- debug : false
4891- } ,
4892- { ...creds , sleep : instantSleep , fetchImpl : fetchImpl as unknown as FetchImpl , stdout : ( l ) => stdoutLines . push ( l ) , stderr : ( ) => undefined }
4893- ) . catch ( ( e ) => e ) ;
4894- expect ( err ) . toMatchObject ( { exitCode : 7 } ) ;
4895- const parsed = JSON . parse ( stdoutLines . join ( '\n' ) ) as { runId : string ; status : string } ;
4896- expect ( parsed . runId ) . toBe ( rerunResp . runId ) ;
4897- expect ( parsed . status ) . toBe ( 'running' ) ;
4898- } ) ;
4893+ it (
4894+ 'exit 7 AND stdout contains {runId, status:"running"} when --timeout polling deadline is exceeded' ,
4895+ async ( ) => {
4896+ const creds = makeCreds ( ) ;
4897+ const rerunResp = makeFeRerunResp ( ) ;
4898+
4899+ let fetchCallCount = 0 ;
4900+ const fetchImpl : typeof globalThis . fetch = async ( input , _init ) => {
4901+ const url =
4902+ typeof input === 'string'
4903+ ? input
4904+ : input instanceof URL
4905+ ? input . toString ( )
4906+ : ( input as { url : string } ) . url ;
4907+ fetchCallCount ++ ;
4908+ if ( url . includes ( '/tests/test_fe_01/runs/rerun' ) ) {
4909+ return new Response ( JSON . stringify ( rerunResp ) , {
4910+ status : 202 ,
4911+ headers : { 'content-type' : 'application/json' } ,
4912+ } ) ;
4913+ }
4914+ if ( url . includes ( '/runs/' ) ) {
4915+ const runningRun : RunResponse = {
4916+ ...makeTerminalRun ( rerunResp . runId , 'passed' ) ,
4917+ status : 'running' ,
4918+ finishedAt : null ,
4919+ } ;
4920+ return new Response ( JSON . stringify ( runningRun ) , {
4921+ status : 200 ,
4922+ headers : { 'content-type' : 'application/json' } ,
4923+ } ) ;
4924+ }
4925+ return new Response ( JSON . stringify ( { error : { code : 'NOT_FOUND' } } ) , { status : 404 } ) ;
4926+ } ;
4927+
4928+ const stdoutLines : string [ ] = [ ] ;
4929+
4930+ const err = await runTestRerun (
4931+ {
4932+ testIds : [ 'test_fe_01' ] ,
4933+ all : false ,
4934+ wait : true ,
4935+ timeoutSeconds : 0 ,
4936+ autoHeal : false ,
4937+ autoHealExplicit : false ,
4938+ skipDependencies : false ,
4939+ maxConcurrency : 10 ,
4940+ output : 'json' ,
4941+ profile : 'default' ,
4942+ dryRun : false ,
4943+ debug : false ,
4944+ verbose : false ,
4945+ } ,
4946+ {
4947+ ...creds ,
4948+ sleep : instantSleep ,
4949+ fetchImpl : fetchImpl as unknown as FetchImpl ,
4950+ stdout : ( line ) => stdoutLines . push ( line ) ,
4951+ stderr : ( ) => undefined ,
4952+ } ,
4953+ ) . catch ( ( e ) => e ) ;
4954+
4955+ expect ( err ) . toMatchObject ( { exitCode : 7 } ) ;
4956+ expect ( stdoutLines . length ) . toBeGreaterThan ( 0 ) ;
4957+ const parsed = JSON . parse ( stdoutLines . join ( '\n' ) ) as { runId : string ; status : string } ;
4958+ expect ( parsed . runId ) . toBe ( rerunResp . runId ) ;
4959+ expect ( parsed . status ) . toBe ( 'running' ) ;
4960+
4961+ void fetchCallCount ;
4962+ } ,
4963+ ) ;
48994964} ) ;
0 commit comments