import {retry, retryTypedResponse} from '../src/internal/requestUtils' import {HttpClientError} from '@actions/http-client' import * as requestUtils from '../src/internal/requestUtils' interface ITestResponse { statusCode: number result: string | null error: Error | null } function TestResponse( action: number | Error, result: string | null = null ): ITestResponse { if (action instanceof Error) { return { statusCode: -1, result, error: action } } else { return { statusCode: action, result, error: null } } } async function handleResponse( response: ITestResponse | undefined ): Promise { if (!response) { fail('Retry method called too many times') } if (response.error) { throw response.error } else { return Promise.resolve(response) } } async function testRetryExpectingResult( responses: ITestResponse[], expectedResult: string | null ): Promise { responses = responses.reverse() // Reverse responses since we pop from end const actualResult = await retry( 'test', async () => handleResponse(responses.pop()), (response: ITestResponse) => response.statusCode, 2, // maxAttempts 0 // delay ) expect(actualResult.result).toEqual(expectedResult) } async function testRetryConvertingErrorToResult( responses: ITestResponse[], expectedStatus: number, expectedResult: string | null ): Promise { responses = responses.reverse() // Reverse responses since we pop from end const actualResult = await retry( 'test', async () => handleResponse(responses.pop()), (response: ITestResponse) => response.statusCode, 2, // maxAttempts 0, // delay (e: Error) => { if (e instanceof HttpClientError) { return { statusCode: e.statusCode, result: null, error: null } } } ) expect(actualResult.statusCode).toEqual(expectedStatus) expect(actualResult.result).toEqual(expectedResult) } async function testRetryExpectingError( responses: ITestResponse[] ): Promise { responses = responses.reverse() // Reverse responses since we pop from end expect( retry( 'test', async () => handleResponse(responses.pop()), (response: ITestResponse) => response.statusCode, 2, // maxAttempts, 0 // delay ) ).rejects.toBeInstanceOf(Error) } test('retry works on successful response', async () => { await testRetryExpectingResult([TestResponse(200, 'Ok')], 'Ok') }) test('retry works after retryable status code', async () => { await testRetryExpectingResult( [TestResponse(503), TestResponse(200, 'Ok')], 'Ok' ) }) test('retry fails after exhausting retries', async () => { await testRetryExpectingError([ TestResponse(503), TestResponse(503), TestResponse(200, 'Ok') ]) }) test('retry fails after non-retryable status code', async () => { await testRetryExpectingError([TestResponse(501), TestResponse(200, 'Ok')]) }) test('retry works after error', async () => { await testRetryExpectingResult( [TestResponse(new Error('Test error')), TestResponse(200, 'Ok')], 'Ok' ) }) test('retry returns after client error', async () => { await testRetryExpectingResult( [TestResponse(400), TestResponse(200, 'Ok')], null ) }) test('retry converts errors to response object', async () => { await testRetryConvertingErrorToResult( [TestResponse(new HttpClientError('Test error', 409))], 409, null ) }) test('retryTypedResponse gives an error with error message', async () => { const httpClientError = new HttpClientError( 'The cache filesize must be between 0 and 10 * 1024 * 1024 bytes', 400 ) jest.spyOn(requestUtils, 'retry').mockReturnValue( new Promise(resolve => { resolve(httpClientError) }) ) try { await retryTypedResponse( 'reserveCache', async () => new Promise(resolve => { resolve({ statusCode: 400, result: '', headers: {}, error: httpClientError }) }) ) } catch (error) { expect(error).toHaveProperty( 'message', 'The cache filesize must be between 0 and 10 * 1024 * 1024 bytes' ) } })