From 4a34f0f98c9283c1078bda44869333b013ebfd66 Mon Sep 17 00:00:00 2001 From: Will Date: Tue, 8 Oct 2024 14:30:16 -0700 Subject: [PATCH] Fix the test --- jest.setup.ts | 5 +++-- src/domain/release-archive.spec.ts | 13 ++++++++++--- src/domain/release-archive.ts | 17 ++++++++++------- 3 files changed, 23 insertions(+), 12 deletions(-) diff --git a/jest.setup.ts b/jest.setup.ts index e8bdc5f..502a8e4 100644 --- a/jest.setup.ts +++ b/jest.setup.ts @@ -9,10 +9,10 @@ declare global { ): R; } interface Expect { - matchesPredicate(predicate: (actual: any) => boolean): any + matchesPredicate(predicate: (actual: any) => boolean): any; } interface ExpectExtendMap { - matchesPredicate: MatcherFunction<[predicate: (actual: any) => boolean]> + matchesPredicate: MatcherFunction<[predicate: (actual: any) => boolean]>; } } } @@ -67,6 +67,7 @@ But instead it was: message: () => "Expected function to throw but it did not", }; }, + matchesPredicate(actual: any, predicate: (actual: any) => boolean) { return { pass: predicate(actual), diff --git a/src/domain/release-archive.spec.ts b/src/domain/release-archive.spec.ts index d44744b..8608ae6 100644 --- a/src/domain/release-archive.spec.ts +++ b/src/domain/release-archive.spec.ts @@ -5,6 +5,7 @@ import fs, { WriteStream } from "node:fs"; import os from "node:os"; import path from "node:path"; import tar from "tar"; +import "../../jest.setup"; import { fakeModuleFile } from "../test/mock-template-files"; import { expectThrownError } from "../test/util"; import { @@ -66,6 +67,11 @@ describe("fetch", () => { }); test("retries the request if it fails", async () => { + // Restore the original behavior of exponentialDelay. + mocked(axiosRetry.exponentialDelay).mockImplementation( + jest.requireActual("axios-retry").exponentialDelay + ); + await ReleaseArchive.fetch(RELEASE_ARCHIVE_URL, STRIP_PREFIX); expect(axiosRetry).toHaveBeenCalledWith(axios, { @@ -73,9 +79,10 @@ describe("fetch", () => { retryDelay: expect.matchesPredicate((retryDelayFn: Function) => { // Make sure the retry delays follow exponential backoff // and the final retry happens after at least 1 minute total. - let firstRetryDelay = retryDelayFn(0); - let secondRetryDelay = retryDelayFn(1); - let thirdRetryDelay = retryDelayFn(2); + // Axios also randomly adds up to 20% to each delay, so test the upper bounds as well. + let firstRetryDelay = retryDelayFn.call(this, 0); + let secondRetryDelay = retryDelayFn.call(this, 1); + let thirdRetryDelay = retryDelayFn.call(this, 2); return 10000 <= firstRetryDelay && firstRetryDelay <= 12000 && 20000 <= secondRetryDelay && secondRetryDelay <= 24000 && 40000 <= thirdRetryDelay && thirdRetryDelay <= 48000; diff --git a/src/domain/release-archive.ts b/src/domain/release-archive.ts index d104189..30cde86 100644 --- a/src/domain/release-archive.ts +++ b/src/domain/release-archive.ts @@ -108,11 +108,12 @@ export class ReleaseArchive { } } -function exponentialDelay(retryCount: number, error: AxiosError = undefined): number { - // Using exponential backoff with 3 retries and a delay factor of 10 seconds - // gives you at least 70 seconds to publish a release archive. - const tenSeconds = 10000; - return axiosRetry.exponentialDelay(retryCount, error, tenSeconds); +function tenSecondExponentialDelay( + retryCount: number, + error: AxiosError | undefined +): number { + const tenSeconds = 10000; + return axiosRetry.exponentialDelay(retryCount, error, tenSeconds); } async function download(url: string, dest: string): Promise { @@ -131,10 +132,12 @@ async function download(url: string, dest: string): Promise { const writer = fs.createWriteStream(dest, { flags: "w" }); - // Retry the request in case the artifact is still being uploaded + // Retry the request in case the artifact is still being uploaded. + // Exponential backoff with 3 retries and a delay factor of 10 seconds + // gives you at least 70 seconds to upload a release archive. axiosRetry(axios, { retries: 3, - retryDelay: exponentialDelay, + retryDelay: tenSecondExponentialDelay, shouldResetTimeout: true, });