@@ -378,9 +378,8 @@ describe('downloadGitHubReleaseBinary', () => {
378378 it ( 'recovers from a stale lock (dead PID) and re-runs download' , async ( ) => {
379379 const eexistErr = Object . assign ( new Error ( 'EEXIST' ) , { code : 'EEXIST' } )
380380
381- // First call: EEXIST. After stale cleanup, the recursive call's
382- // writeFile succeeds. To exit the recursive call, make the binary
383- // appear right after download/extract.
381+ // First writeFile: EEXIST (lock held). After the stale cleanup recurses,
382+ // the recursive call short-circuits on the cache check (existsSync below).
384383 let writeCount = 0
385384 mockFsWriteFile . mockImplementation ( async ( ) => {
386385 writeCount += 1
@@ -390,40 +389,31 @@ describe('downloadGitHubReleaseBinary', () => {
390389 return undefined
391390 } )
392391
393- // Lock file polling reads the PID; we'll make it return so that the
394- // first poll iteration goes to lock-aliveness check (i % 5 === 4 only on i=4).
395- // Simpler path: make the binary appear before the loop even fires.
396- let existsCount = 0
397- mockExistsSync . mockImplementation ( ( ) => {
398- existsCount += 1
399- // call 1: initial cache check (false)
400- // call 2+ depends on flow. After we send recursion, we want the
401- // recursive call to short-circuit on cache-check.
402- return existsCount >= 3
403- } )
404-
392+ // Dead lock holder: the liveness probe (process.kill(pid, 0)) throws.
405393 const realKill = process . kill
406- // First kill(0) call (in stale check) throws -> stale.
407394 let killCount = 0
408395 ; ( process as { kill : unknown } ) . kill = vi . fn ( ( ) => {
409396 killCount += 1
410- if ( killCount === 1 ) {
411- // We're inside the stale-check path (kill(pid, 0)) — wait no,
412- // the lock-busy branch is only hit on EEXIST. Stale check happens
413- // inside the inner `for` poll's `i % 5 === 4` branch.
414- // Actually the FIRST EEXIST goes into the wait-loop, not the stale
415- // check directly. So skip — just throw consistently to mark stale.
416- throw new Error ( 'ESRCH' )
417- }
418- return true
397+ throw new Error ( 'ESRCH' )
419398 } )
420399
400+ // Keep the binary absent until the liveness probe has run, so the wait
401+ // loop reaches the i % 5 === 4 stale check instead of returning early;
402+ // after the stale cleanup, the recursive call's cache check succeeds.
403+ mockExistsSync . mockImplementation ( ( ) => killCount >= 1 )
404+
405+ // setTimeout: bypass the actual 1s polls so reaching i = 4 is instant.
406+ const realSetTimeout = globalThis . setTimeout
407+ ; ( globalThis as { setTimeout : unknown } ) . setTimeout = ( ( cb : ( ) => void ) => {
408+ cb ( )
409+ return 0 as never
410+ } ) as never
411+
421412 try {
422- // Either branch is fine — we just want coverage.
423- await downloadGitHubReleaseBinary ( baseSpec ) . catch ( ( ) => {
424- // Some branches may throw timeout — accept it.
425- } )
413+ const result = await downloadGitHubReleaseBinary ( baseSpec )
414+ expect ( result ) . toContain ( 'tool' )
426415 } finally {
416+ ; ( globalThis as { setTimeout : unknown } ) . setTimeout = realSetTimeout
427417 ; ( process as { kill : unknown } ) . kill = realKill
428418 }
429419
0 commit comments