1
0
Fork 0

Clean up test code

pull/558/head
Dave Hadka 2020-10-12 13:21:01 -05:00
parent de52c861c1
commit c3c81d44c1
5 changed files with 4265 additions and 110 deletions

View File

@ -1,36 +1,48 @@
import {retry} from '../src/internal/requestUtils' import {retry} from '../src/internal/requestUtils'
import {HttpClientError} from '@actions/http-client'
interface TestResponse { interface ITestResponse {
statusCode: number statusCode: number
result: string | null 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( async function handleResponse(
response: TestResponse | undefined response: ITestResponse | undefined
): Promise<TestResponse> { ): Promise<ITestResponse> {
if (!response) { if (!response) {
// eslint-disable-next-line no-undef // eslint-disable-next-line no-undef
fail('Retry method called too many times') fail('Retry method called too many times')
} }
// Status codes >= 600 will throw an Error instead of returning a response object. This if (response.error) {
// mimics the behavior of the http-client *Json methods, which throw an error on any throw response.error
// non-successful status codes. Values in the 6xx, 7xx, and 8xx range are converted
// to the corresponding 3xx, 4xx, and 5xx status code.
if (response.statusCode >= 900) {
throw Error('Test Error')
} else if (response.statusCode >= 600) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const error = Error('Test Error with Status Code') as any
error['statusCode'] = response.statusCode - 300
throw error
} else { } else {
return Promise.resolve(response) return Promise.resolve(response)
} }
} }
async function testRetryExpectingResult( async function testRetryExpectingResult(
responses: TestResponse[], responses: ITestResponse[],
expectedResult: string | null expectedResult: string | null
): Promise<void> { ): Promise<void> {
responses = responses.reverse() // Reverse responses since we pop from end responses = responses.reverse() // Reverse responses since we pop from end
@ -38,7 +50,7 @@ async function testRetryExpectingResult(
const actualResult = await retry( const actualResult = await retry(
'test', 'test',
async () => handleResponse(responses.pop()), async () => handleResponse(responses.pop()),
(response: TestResponse) => response.statusCode, (response: ITestResponse) => response.statusCode,
2, // maxAttempts 2, // maxAttempts
0 // delay 0 // delay
) )
@ -47,7 +59,7 @@ async function testRetryExpectingResult(
} }
async function testRetryConvertingErrorToResult( async function testRetryConvertingErrorToResult(
responses: TestResponse[], responses: ITestResponse[],
expectedStatus: number, expectedStatus: number,
expectedResult: string | null expectedResult: string | null
): Promise<void> { ): Promise<void> {
@ -56,15 +68,16 @@ async function testRetryConvertingErrorToResult(
const actualResult = await retry( const actualResult = await retry(
'test', 'test',
async () => handleResponse(responses.pop()), async () => handleResponse(responses.pop()),
(response: TestResponse) => response.statusCode, (response: ITestResponse) => response.statusCode,
2, // maxAttempts 2, // maxAttempts
0, // delay 0, // delay
(e: Error) => { (e: Error) => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any if (e instanceof HttpClientError) {
const error = e as any return {
return { statusCode: e.statusCode,
statusCode: error['statusCode'], result: null,
result: error['result'] ?? null error: null
}
} }
} }
) )
@ -74,7 +87,7 @@ async function testRetryConvertingErrorToResult(
} }
async function testRetryExpectingError( async function testRetryExpectingError(
responses: TestResponse[] responses: ITestResponse[]
): Promise<void> { ): Promise<void> {
responses = responses.reverse() // Reverse responses since we pop from end responses = responses.reverse() // Reverse responses since we pop from end
@ -82,7 +95,7 @@ async function testRetryExpectingError(
retry( retry(
'test', 'test',
async () => handleResponse(responses.pop()), async () => handleResponse(responses.pop()),
(response: TestResponse) => response.statusCode, (response: ITestResponse) => response.statusCode,
2, // maxAttempts, 2, // maxAttempts,
0 // delay 0 // delay
) )
@ -90,103 +103,45 @@ async function testRetryExpectingError(
} }
test('retry works on successful response', async () => { test('retry works on successful response', async () => {
await testRetryExpectingResult( await testRetryExpectingResult([TestResponse(200, 'Ok')], 'Ok')
[
{
statusCode: 200,
result: 'Ok'
}
],
'Ok'
)
}) })
test('retry works after retryable status code', async () => { test('retry works after retryable status code', async () => {
await testRetryExpectingResult( await testRetryExpectingResult(
[ [TestResponse(503), TestResponse(200, 'Ok')],
{
statusCode: 503,
result: null
},
{
statusCode: 200,
result: 'Ok'
}
],
'Ok' 'Ok'
) )
}) })
test('retry fails after exhausting retries', async () => { test('retry fails after exhausting retries', async () => {
await testRetryExpectingError([ await testRetryExpectingError([
{ TestResponse(503),
statusCode: 503, TestResponse(503),
result: null TestResponse(200, 'Ok')
},
{
statusCode: 503,
result: null
},
{
statusCode: 200,
result: 'Ok'
}
]) ])
}) })
test('retry fails after non-retryable status code', async () => { test('retry fails after non-retryable status code', async () => {
await testRetryExpectingError([ await testRetryExpectingError([TestResponse(500), TestResponse(200, 'Ok')])
{
statusCode: 500,
result: null
},
{
statusCode: 200,
result: 'Ok'
}
])
}) })
test('retry works after error', async () => { test('retry works after error', async () => {
await testRetryExpectingResult( await testRetryExpectingResult(
[ [TestResponse(new Error('Test error')), TestResponse(200, 'Ok')],
{
statusCode: 999,
result: null
},
{
statusCode: 200,
result: 'Ok'
}
],
'Ok' 'Ok'
) )
}) })
test('retry returns after client error', async () => { test('retry returns after client error', async () => {
await testRetryExpectingResult( await testRetryExpectingResult(
[ [TestResponse(400), TestResponse(200, 'Ok')],
{
statusCode: 400,
result: null
},
{
statusCode: 200,
result: 'Ok'
}
],
null null
) )
}) })
test('retry converts errors to response object', async () => { test('retry converts errors to response object', async () => {
await testRetryConvertingErrorToResult( await testRetryConvertingErrorToResult(
[ [TestResponse(new HttpClientError('Test error', 409))],
{
statusCode: 709, // throw a 409 Conflict error
result: null
}
],
409, 409,
null null
) )

4203
packages/cache/package-lock.json generated vendored

File diff suppressed because it is too large Load Diff

View File

@ -40,7 +40,7 @@
"@actions/core": "^1.2.4", "@actions/core": "^1.2.4",
"@actions/exec": "^1.0.1", "@actions/exec": "^1.0.1",
"@actions/glob": "^0.1.0", "@actions/glob": "^0.1.0",
"@actions/http-client": "^1.0.8", "@actions/http-client": "^1.0.9",
"@actions/io": "^1.0.1", "@actions/io": "^1.0.1",
"@azure/ms-rest-js": "^2.0.7", "@azure/ms-rest-js": "^2.0.7",
"@azure/storage-blob": "^12.1.2", "@azure/storage-blob": "^12.1.2",

View File

@ -11,8 +11,11 @@ export enum CompressionMethod {
Zstd = 'zstd' Zstd = 'zstd'
} }
// The default number of retry attempts.
export const DefaultRetryAttempts = 2
// The default delay in milliseconds between retry attempts. // The default delay in milliseconds between retry attempts.
export const RetryDelay = 5000 export const DefaultRetryDelay = 5000
// Socket timeout in milliseconds during download. If no traffic is received // Socket timeout in milliseconds during download. If no traffic is received
// over the socket during this period, the socket is destroyed and the download // over the socket during this period, the socket is destroyed and the download

View File

@ -1,10 +1,10 @@
import * as core from '@actions/core' import * as core from '@actions/core'
import {HttpCodes} from '@actions/http-client' import {HttpCodes, HttpClientError} from '@actions/http-client'
import { import {
IHttpClientResponse, IHttpClientResponse,
ITypedResponse ITypedResponse
} from '@actions/http-client/interfaces' } from '@actions/http-client/interfaces'
import {RetryDelay} from './constants' import {DefaultRetryDelay, DefaultRetryAttempts} from './constants'
export function isSuccessStatusCode(statusCode?: number): boolean { export function isSuccessStatusCode(statusCode?: number): boolean {
if (!statusCode) { if (!statusCode) {
@ -40,8 +40,8 @@ export async function retry<T>(
name: string, name: string,
method: () => Promise<T>, method: () => Promise<T>,
getStatusCode: (arg0: T) => number | undefined, getStatusCode: (arg0: T) => number | undefined,
maxAttempts = 2, maxAttempts = DefaultRetryAttempts,
delay = RetryDelay, delay = DefaultRetryDelay,
onError: ((arg0: Error) => T | undefined) | undefined = undefined onError: ((arg0: Error) => T | undefined) | undefined = undefined
): Promise<T> { ): Promise<T> {
let errorMessage = '' let errorMessage = ''
@ -95,22 +95,21 @@ export async function retry<T>(
export async function retryTypedResponse<T>( export async function retryTypedResponse<T>(
name: string, name: string,
method: () => Promise<ITypedResponse<T>>, method: () => Promise<ITypedResponse<T>>,
maxAttempts = 2 maxAttempts = DefaultRetryAttempts,
delay = DefaultRetryDelay
): Promise<ITypedResponse<T>> { ): Promise<ITypedResponse<T>> {
return await retry( return await retry(
name, name,
method, method,
(response: ITypedResponse<T>) => response.statusCode, (response: ITypedResponse<T>) => response.statusCode,
maxAttempts, maxAttempts,
RetryDelay, delay,
// If the error object contains the statusCode property, extract it and return // If the error object contains the statusCode property, extract it and return
// an ITypedResponse<T> so it can be processed by the retry logic. // an ITypedResponse<T> so it can be processed by the retry logic.
(e: Error) => { (error: Error) => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any if (error instanceof HttpClientError) {
const error = e as any
if (error['statusCode']) {
return { return {
statusCode: error['statusCode'], statusCode: error.statusCode,
result: null, result: null,
headers: {} headers: {}
} }
@ -124,13 +123,14 @@ export async function retryTypedResponse<T>(
export async function retryHttpClientResponse<T>( export async function retryHttpClientResponse<T>(
name: string, name: string,
method: () => Promise<IHttpClientResponse>, method: () => Promise<IHttpClientResponse>,
maxAttempts = 2 maxAttempts = DefaultRetryAttempts,
delay = DefaultRetryDelay
): Promise<IHttpClientResponse> { ): Promise<IHttpClientResponse> {
return await retry( return await retry(
name, name,
method, method,
(response: IHttpClientResponse) => response.message.statusCode, (response: IHttpClientResponse) => response.message.statusCode,
maxAttempts, maxAttempts,
RetryDelay delay
) )
} }