@@ -328,4 +328,267 @@ describe("await-remote-run", () => {
328328 expect ( coreErrorLogMock . mock . calls [ 1 ] ?. [ 0 ] ) . toMatchSnapshot ( ) ;
329329 } ) ;
330330 } ) ;
331+
332+ describe ( "getWorkflowRunResult" , ( ) => {
333+ let apiFetchWorkflowRunStateMock : MockInstance <
334+ typeof api . fetchWorkflowRunState
335+ > ;
336+ let apiRetryOnErrorMock : MockInstance < typeof api . retryOnError > ;
337+
338+ beforeEach ( ( ) => {
339+ vi . useFakeTimers ( ) ;
340+
341+ apiFetchWorkflowRunStateMock = vi . spyOn ( api , "fetchWorkflowRunState" ) ;
342+ apiRetryOnErrorMock = vi . spyOn ( api , "retryOnError" ) ;
343+ } ) ;
344+
345+ afterEach ( ( ) => {
346+ vi . useRealTimers ( ) ;
347+ } ) ;
348+
349+ it ( "succeeds on the completion of a run" , async ( ) => {
350+ apiFetchWorkflowRunStateMock . mockResolvedValue ( {
351+ status : WorkflowRunStatus . Completed ,
352+ conclusion : WorkflowRunConclusion . Success ,
353+ } ) ;
354+ apiRetryOnErrorMock . mockImplementation ( async ( toTry ) => ( {
355+ success : true ,
356+ value : await toTry ( ) ,
357+ } ) ) ;
358+
359+ // Behaviour
360+ const getWorkflowRunResultPromise = getWorkflowRunResult ( {
361+ startTime : Date . now ( ) ,
362+ pollIntervalMs : 100 ,
363+ runId : 0 ,
364+ runTimeoutMs : 10_000 ,
365+ } ) ;
366+ await expect ( getWorkflowRunResultPromise ) . resolves . not . toThrow ( ) ;
367+ const result = await getWorkflowRunResultPromise ;
368+ expect ( result ) . toStrictEqual ( {
369+ success : true ,
370+ value : {
371+ conclusion : WorkflowRunConclusion . Success ,
372+ status : WorkflowRunStatus . Completed ,
373+ } ,
374+ } ) ;
375+
376+ // Logging
377+ assertNoneCalled ( ) ;
378+ } ) ;
379+
380+ it ( "retries on request failures" , async ( ) => {
381+ const pollIntervalMs = 100 ;
382+ apiFetchWorkflowRunStateMock . mockResolvedValue ( {
383+ status : WorkflowRunStatus . Completed ,
384+ conclusion : WorkflowRunConclusion . Success ,
385+ } ) ;
386+ apiRetryOnErrorMock
387+ . mockImplementation ( async ( toTry ) => ( {
388+ success : true ,
389+ value : await toTry ( ) ,
390+ } ) )
391+ . mockResolvedValueOnce ( { success : false , reason : "timeout" } )
392+ . mockResolvedValueOnce ( { success : false , reason : "timeout" } ) ;
393+
394+ // Behaviour
395+ const getWorkflowRunResultPromise = getWorkflowRunResult ( {
396+ startTime : Date . now ( ) ,
397+ pollIntervalMs : pollIntervalMs ,
398+ runId : 0 ,
399+ runTimeoutMs : 10_000 ,
400+ } ) ;
401+
402+ // First iteration
403+ await vi . advanceTimersByTimeAsync ( 1 ) ;
404+ expect ( coreDebugLogMock ) . toHaveBeenCalledOnce ( ) ;
405+
406+ // Second iteration
407+ await vi . advanceTimersByTimeAsync ( 100 ) ;
408+ expect ( coreDebugLogMock ) . toHaveBeenCalledTimes ( 2 ) ;
409+
410+ // Final iteration
411+ await vi . advanceTimersByTimeAsync ( 100 ) ;
412+ await expect ( getWorkflowRunResultPromise ) . resolves . not . toThrow ( ) ;
413+ const result = await getWorkflowRunResultPromise ;
414+ expect ( result ) . toStrictEqual ( {
415+ success : true ,
416+ value : {
417+ conclusion : WorkflowRunConclusion . Success ,
418+ status : WorkflowRunStatus . Completed ,
419+ } ,
420+ } ) ;
421+
422+ assertOnlyCalled ( coreDebugLogMock ) ;
423+ expect ( coreDebugLogMock ) . toBeCalledTimes ( 2 ) ;
424+ expect ( coreDebugLogMock . mock . calls ) . toMatchSnapshot ( ) ;
425+ } ) ;
426+
427+ it ( "returns the conclusion if available" , async ( ) => {
428+ const expectedConclusion = WorkflowRunConclusion . Skipped ;
429+ apiFetchWorkflowRunStateMock . mockResolvedValue ( {
430+ status : WorkflowRunStatus . Completed ,
431+ conclusion : expectedConclusion ,
432+ } ) ;
433+ apiRetryOnErrorMock . mockImplementation ( async ( toTry ) => ( {
434+ success : true ,
435+ value : await toTry ( ) ,
436+ } ) ) ;
437+
438+ // Behaviour
439+ const getWorkflowRunResultPromise = getWorkflowRunResult ( {
440+ startTime : Date . now ( ) ,
441+ pollIntervalMs : 100 ,
442+ runId : 0 ,
443+ runTimeoutMs : 10_000 ,
444+ } ) ;
445+ await expect ( getWorkflowRunResultPromise ) . resolves . not . toThrow ( ) ;
446+ const result = await getWorkflowRunResultPromise ;
447+ expect ( result ) . toStrictEqual ( {
448+ success : true ,
449+ value : {
450+ conclusion : expectedConclusion ,
451+ status : WorkflowRunStatus . Completed ,
452+ } ,
453+ } ) ;
454+
455+ // Logging
456+ assertOnlyCalled ( coreErrorLogMock ) ;
457+ expect ( coreErrorLogMock ) . toHaveBeenCalledOnce ( ) ;
458+ expect ( coreErrorLogMock . mock . calls ) . toMatchSnapshot ( ) ;
459+ } ) ;
460+
461+ it ( "returns a failure on timeout conclusion" , async ( ) => {
462+ const expectedConclusion = WorkflowRunConclusion . TimedOut ;
463+ apiFetchWorkflowRunStateMock . mockResolvedValue ( {
464+ status : WorkflowRunStatus . Completed ,
465+ conclusion : expectedConclusion ,
466+ } ) ;
467+ apiRetryOnErrorMock . mockImplementation ( async ( toTry ) => ( {
468+ success : true ,
469+ value : await toTry ( ) ,
470+ } ) ) ;
471+
472+ // Behaviour
473+ const getWorkflowRunResultPromise = getWorkflowRunResult ( {
474+ startTime : Date . now ( ) ,
475+ pollIntervalMs : 100 ,
476+ runId : 0 ,
477+ runTimeoutMs : 10_000 ,
478+ } ) ;
479+ await expect ( getWorkflowRunResultPromise ) . resolves . not . toThrow ( ) ;
480+ const result = await getWorkflowRunResultPromise ;
481+ expect ( result ) . toStrictEqual ( {
482+ success : false ,
483+ reason : "timeout" ,
484+ } ) ;
485+
486+ // Logging
487+ assertOnlyCalled ( coreErrorLogMock ) ;
488+ expect ( coreErrorLogMock ) . toHaveBeenCalledOnce ( ) ;
489+ expect ( coreErrorLogMock . mock . calls ) . toMatchSnapshot ( ) ;
490+ } ) ;
491+
492+ it ( "returns a failure on an unsupported conclusion" , async ( ) => {
493+ const expectedConclusion = "weird" ;
494+ apiFetchWorkflowRunStateMock . mockResolvedValue ( {
495+ status : WorkflowRunStatus . Completed ,
496+ conclusion : expectedConclusion as any ,
497+ } ) ;
498+ apiRetryOnErrorMock . mockImplementation ( async ( toTry ) => ( {
499+ success : true ,
500+ value : await toTry ( ) ,
501+ } ) ) ;
502+
503+ // Behaviour
504+ const getWorkflowRunResultPromise = getWorkflowRunResult ( {
505+ startTime : Date . now ( ) ,
506+ pollIntervalMs : 100 ,
507+ runId : 0 ,
508+ runTimeoutMs : 10_000 ,
509+ } ) ;
510+ await expect ( getWorkflowRunResultPromise ) . resolves . not . toThrow ( ) ;
511+ const result = await getWorkflowRunResultPromise ;
512+ expect ( result ) . toStrictEqual ( {
513+ success : false ,
514+ reason : "unsupported" ,
515+ value : expectedConclusion ,
516+ } ) ;
517+
518+ // Logging
519+ assertOnlyCalled ( coreErrorLogMock , coreInfoLogMock ) ;
520+ expect ( coreErrorLogMock ) . toHaveBeenCalledOnce ( ) ;
521+ expect ( coreErrorLogMock . mock . calls ) . toMatchSnapshot ( ) ;
522+ expect ( coreInfoLogMock ) . toHaveBeenCalledOnce ( ) ;
523+ expect ( coreInfoLogMock . mock . calls ) . toMatchSnapshot ( ) ;
524+ } ) ;
525+
526+ it ( "returns a failure if the status is unsupported" , async ( ) => {
527+ const expectedStatus = "weird" ;
528+ apiFetchWorkflowRunStateMock . mockResolvedValue ( {
529+ status : expectedStatus as any ,
530+ conclusion : WorkflowRunConclusion . Failure ,
531+ } ) ;
532+ apiRetryOnErrorMock . mockImplementation ( async ( toTry ) => ( {
533+ success : true ,
534+ value : await toTry ( ) ,
535+ } ) ) ;
536+
537+ // Behaviour
538+ const getWorkflowRunResultPromise = getWorkflowRunResult ( {
539+ startTime : Date . now ( ) ,
540+ pollIntervalMs : 100 ,
541+ runId : 0 ,
542+ runTimeoutMs : 10_000 ,
543+ } ) ;
544+ await expect ( getWorkflowRunResultPromise ) . resolves . not . toThrow ( ) ;
545+ const result = await getWorkflowRunResultPromise ;
546+ expect ( result ) . toStrictEqual ( {
547+ success : false ,
548+ reason : "unsupported" ,
549+ value : "weird" ,
550+ } ) ;
551+
552+ // Logging
553+ assertOnlyCalled ( coreErrorLogMock , coreInfoLogMock ) ;
554+ expect ( coreErrorLogMock ) . toHaveBeenCalledOnce ( ) ;
555+ expect ( coreErrorLogMock . mock . calls ) . toMatchSnapshot ( ) ;
556+ expect ( coreInfoLogMock ) . toHaveBeenCalledOnce ( ) ;
557+ expect ( coreInfoLogMock . mock . calls ) . toMatchSnapshot ( ) ;
558+ } ) ;
559+
560+ it ( "returns a timeout" , async ( ) => {
561+ const pollIntervalMs = 100 ;
562+ const runTimeoutMs = 1000 ;
563+ const expectedIterations = runTimeoutMs / pollIntervalMs ;
564+ apiFetchWorkflowRunStateMock . mockResolvedValue ( {
565+ status : WorkflowRunStatus . InProgress ,
566+ conclusion : null ,
567+ } ) ;
568+ apiRetryOnErrorMock . mockImplementation ( async ( toTry ) => ( {
569+ success : true ,
570+ value : await toTry ( ) ,
571+ } ) ) ;
572+
573+ // Behaviour
574+ const getWorkflowRunResultPromise = getWorkflowRunResult ( {
575+ startTime : Date . now ( ) ,
576+ pollIntervalMs : pollIntervalMs ,
577+ runId : 0 ,
578+ runTimeoutMs : runTimeoutMs ,
579+ } ) ;
580+ await vi . advanceTimersByTimeAsync ( 1000 ) ;
581+ await expect ( getWorkflowRunResultPromise ) . resolves . not . toThrow ( ) ;
582+ const result = await getWorkflowRunResultPromise ;
583+ expect ( result ) . toStrictEqual ( {
584+ success : false ,
585+ reason : "timeout" ,
586+ } ) ;
587+
588+ // Logging
589+ assertOnlyCalled ( coreDebugLogMock ) ;
590+ expect ( coreDebugLogMock ) . toHaveBeenCalledTimes ( expectedIterations ) ;
591+ expect ( coreDebugLogMock . mock . calls ) . toMatchSnapshot ( ) ;
592+ } ) ;
593+ } ) ;
331594} ) ;
0 commit comments