@@ -332,12 +332,160 @@ describe('commands/finalize', () => {
332332
333333 assert . strictEqual ( result . success , true ) ;
334334 assert . strictEqual ( result . result . skipped , true ) ;
335+ assert . strictEqual ( result . result . reason , 'api-unavailable' ) ;
335336 assert . strictEqual ( exitCode , null ) ;
336337 assert . ok (
337338 output . calls . some (
338339 c => c . method === 'warn' && c . args [ 0 ] . includes ( 'API unavailable' )
339340 )
340341 ) ;
341342 } ) ;
343+
344+ it ( 'does not fail CI on 5xx even with --strict flag' , async ( ) => {
345+ let output = createMockOutput ( ) ;
346+ let exitCode = null ;
347+
348+ let apiError = new Error ( 'API request failed: 503 - Service Unavailable' ) ;
349+ apiError . context = { status : 503 } ;
350+
351+ let result = await finalizeCommand (
352+ 'parallel-123' ,
353+ { } ,
354+ { strict : true } ,
355+ {
356+ loadConfig : async ( ) => ( {
357+ apiKey : 'test-token' ,
358+ apiUrl : 'https://api.test' ,
359+ } ) ,
360+ createApiClient : ( ) => ( { request : async ( ) => ( { } ) } ) ,
361+ finalizeParallelBuild : async ( ) => {
362+ throw apiError ;
363+ } ,
364+ output,
365+ exit : code => {
366+ exitCode = code ;
367+ } ,
368+ }
369+ ) ;
370+
371+ // 5xx errors should ALWAYS be resilient, even with --strict
372+ // Infrastructure issues are out of user's control
373+ assert . strictEqual ( result . success , true ) ;
374+ assert . strictEqual ( result . result . skipped , true ) ;
375+ assert . strictEqual ( result . result . reason , 'api-unavailable' ) ;
376+ assert . strictEqual ( exitCode , null ) ;
377+ } ) ;
378+
379+ it ( 'does not fail CI when no build found (404) in non-strict mode' , async ( ) => {
380+ let output = createMockOutput ( ) ;
381+ let exitCode = null ;
382+
383+ let apiError = new Error ( 'API request failed: 404 - Not Found' ) ;
384+ apiError . context = { status : 404 } ;
385+
386+ let result = await finalizeCommand (
387+ 'parallel-123' ,
388+ { } ,
389+ { } ,
390+ {
391+ loadConfig : async ( ) => ( {
392+ apiKey : 'test-token' ,
393+ apiUrl : 'https://api.test' ,
394+ } ) ,
395+ createApiClient : ( ) => ( { request : async ( ) => ( { } ) } ) ,
396+ finalizeParallelBuild : async ( ) => {
397+ throw apiError ;
398+ } ,
399+ output,
400+ exit : code => {
401+ exitCode = code ;
402+ } ,
403+ }
404+ ) ;
405+
406+ assert . strictEqual ( result . success , true ) ;
407+ assert . strictEqual ( result . result . skipped , true ) ;
408+ assert . strictEqual ( result . result . reason , 'no-build-found' ) ;
409+ assert . strictEqual ( exitCode , null ) ;
410+ assert . ok (
411+ output . calls . some (
412+ c => c . method === 'warn' && c . args [ 0 ] . includes ( 'No build found' )
413+ )
414+ ) ;
415+ } ) ;
416+
417+ it ( 'fails CI when no build found (404) in strict mode' , async ( ) => {
418+ let output = createMockOutput ( ) ;
419+ let exitCode = null ;
420+
421+ let apiError = new Error ( 'API request failed: 404 - Not Found' ) ;
422+ apiError . context = { status : 404 } ;
423+
424+ let result = await finalizeCommand (
425+ 'parallel-123' ,
426+ { } ,
427+ { strict : true } ,
428+ {
429+ loadConfig : async ( ) => ( {
430+ apiKey : 'test-token' ,
431+ apiUrl : 'https://api.test' ,
432+ } ) ,
433+ createApiClient : ( ) => ( { request : async ( ) => ( { } ) } ) ,
434+ finalizeParallelBuild : async ( ) => {
435+ throw apiError ;
436+ } ,
437+ output,
438+ exit : code => {
439+ exitCode = code ;
440+ } ,
441+ }
442+ ) ;
443+
444+ assert . strictEqual ( result . success , false ) ;
445+ assert . strictEqual ( result . reason , 'no-build-found' ) ;
446+ assert . strictEqual ( exitCode , 1 ) ;
447+ assert . ok (
448+ output . calls . some (
449+ c => c . method === 'error' && c . args [ 0 ] . includes ( 'No build found' )
450+ )
451+ ) ;
452+ } ) ;
453+
454+ it ( 'shows verbose hints when no build found in non-strict mode' , async ( ) => {
455+ let output = createMockOutput ( ) ;
456+
457+ let apiError = new Error ( 'API request failed: 404 - Not Found' ) ;
458+ apiError . context = { status : 404 } ;
459+
460+ await finalizeCommand (
461+ 'parallel-123' ,
462+ { } ,
463+ { verbose : true } ,
464+ {
465+ loadConfig : async ( ) => ( {
466+ apiKey : 'test-token' ,
467+ apiUrl : 'https://api.test' ,
468+ } ) ,
469+ createApiClient : ( ) => ( { request : async ( ) => ( { } ) } ) ,
470+ finalizeParallelBuild : async ( ) => {
471+ throw apiError ;
472+ } ,
473+ output,
474+ exit : ( ) => { } ,
475+ }
476+ ) ;
477+
478+ // Should show helpful hints in verbose mode
479+ assert . ok (
480+ output . calls . some (
481+ c => c . method === 'info' && c . args [ 0 ] . includes ( 'Possible reasons' )
482+ )
483+ ) ;
484+ assert . ok (
485+ output . calls . some (
486+ c => c . method === 'info' && c . args [ 0 ] . includes ( '--strict' )
487+ )
488+ ) ;
489+ } ) ;
342490 } ) ;
343491} ) ;
0 commit comments