Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions src/helpers/web.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,10 +63,12 @@ async function requestWithRetry(
return response
} catch (error: unknown) {
if (
((error instanceof errors.UndiciError && error.code == 'ECONNRESET') ||
(
(error instanceof errors.UndiciError && error.code == 'ECONNRESET') ||
(error instanceof errors.UndiciError && error.code == 'ETIMEDOUT') ||
error instanceof errors.ConnectTimeoutError ||
error instanceof errors.SocketError) &&
retryCount < maxRetries
error instanceof errors.SocketError
) && retryCount < maxRetries
) {
const backoffDelay = baseBackoffDelayMs * 2 ** retryCount
await sleep(backoffDelay)
Expand Down
111 changes: 111 additions & 0 deletions test/helpers/web.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -403,6 +403,117 @@ describe('Web Helpers', () => {
expect(response.resultURL.href).toStrictEqual('https://results.codecov.io/')
});
});

describe('Uploader should retry POST on ETIMEDOUT', () => {
it('should retry and return response data when ETIMEDOUT occurs once', async () => {
const envs: UploaderEnvs = {}
const args: UploaderArgs = {
flags: '',
slug: '',
upstream: '',
}
const error = new errors.UndiciError("conn reset error")
error.code = 'ETIMEDOUT'
uploadURL = 'http://codecov.io'
mockAgent.disableNetConnect()
mockClient = mockAgent.get(uploadURL)

mockClient.intercept({
method: 'POST',
path: `/upload/v4?package=uploader-${version}&token=${token}&hello`,
}).replyWithError(new errors.ConnectTimeoutError('timeout error')).times(1)

mockClient.intercept({
method: 'POST',
path: `/upload/v4?package=uploader-${version}&token=${token}&hello`,
}).replyWithError(error).times(1)

mockClient.intercept({
method: 'POST',
path: `/upload/v4?package=uploader-${version}&token=${token}&hello`,
}).reply(200, 'testPOSTHTTP')

const responseData = await uploadToCodecovPOST(new URL(uploadURL), token, query, source, envs, args);
try {
expect(responseData).toBe('testPOSTHTTP')
} catch (error) {
throw new Error(`${responseData} - ${error}`)
}
});

it('should fail with error if ETIMEDOUT happens 5 times', async () => {
const mockSleep = jest.spyOn(utilModule, 'sleep').mockResolvedValue(42)
const envs: UploaderEnvs = {}
const args: UploaderArgs = {
flags: '',
slug: '',
upstream: '',
}
const error = new errors.UndiciError("conn reset error")
error.code = 'ETIMEDOUT'
uploadURL = 'http://codecov.io'
mockAgent.disableNetConnect()
mockClient = mockAgent.get(uploadURL)

mockClient.intercept({
method: 'POST',
path: `/upload/v4?package=uploader-${version}&token=${token}&hello`,
}).replyWithError(error).times(5)

mockClient.intercept({
method: 'POST',
path: `/upload/v4?package=uploader-${version}&token=${token}&hello`,
}).reply(200, 'testPOSTHTTP')

try {
await uploadToCodecovPOST(new URL(uploadURL), token, query, source, envs, args);
expect(true).toBe(false)
} catch (error) {
expect(error).toBeInstanceOf(errors.UndiciError)
expect(mockSleep).toBeCalledTimes(4)
}
})
});

describe('Uploader should retry PUT on ETIMEDOUT', () => {
it('should retry and return response data when ETIMEDOUT occurs once', async () => {
// Replace the original setTimeout with fakeSetTimeout
const envs: UploaderEnvs = {}
const args: UploaderArgs = {
flags: '',
slug: '',
upstream: '',
}
jest.spyOn(console, 'log').mockImplementation(() => void {})
const postResults: PostResults = {
putURL: new URL('https://codecov.io'),
resultURL: new URL('https://results.codecov.io'),
}

const error = new errors.UndiciError("conn reset error")
error.code = 'ETIMEDOUT'
mockAgent.disableNetConnect()
mockClient = mockAgent.get(postResults.putURL.origin)

mockClient.intercept({
method: 'PUT',
path: `/`,
}).replyWithError(new errors.ConnectTimeoutError('timeout error')).times(1)

mockClient.intercept({
method: 'PUT',
path: `/`,
}).replyWithError(error).times(1)

mockClient.intercept({
method: 'PUT',
path: `/`,
}).reply(200, postResults.resultURL.href)

const response = await uploadToCodecovPUT(postResults, uploadFile, envs, args)
expect(response.resultURL.href).toStrictEqual('https://results.codecov.io/')
});
});
})

describe('displayChangelog()', () => {
Expand Down