|
1 | 1 | import { createTwoFilesPatch } from "diff"; |
| 2 | +import { BackoffOptions, backOff } from "exponential-backoff"; |
2 | 3 | import { Mocked, mocked } from "jest-mock"; |
3 | 4 | import { randomUUID } from "node:crypto"; |
4 | 5 | import fs, { PathLike } from "node:fs"; |
@@ -37,6 +38,9 @@ jest.mock("../infrastructure/github"); |
37 | 38 | jest.mock("./integrity-hash"); |
38 | 39 | jest.mock("./release-archive"); |
39 | 40 | jest.mock("node:fs"); |
| 41 | +jest.mock("exponential-backoff"); |
| 42 | + |
| 43 | +const realExponentialBackoff = jest.requireActual("exponential-backoff"); |
40 | 44 |
|
41 | 45 | const mockedFileReads: { [path: string]: string } = {}; |
42 | 46 | const EXTRACTED_MODULE_PATH = "/fake/path/to/MODULE.bazel"; |
@@ -1061,6 +1065,17 @@ describe("commitEntryToNewBranch", () => { |
1061 | 1065 | }); |
1062 | 1066 |
|
1063 | 1067 | describe("pushEntryToFork", () => { |
| 1068 | + beforeEach(() => { |
| 1069 | + // Reduce the exponential-backoff delay to 0 for tests |
| 1070 | + (backOff as unknown as jest.SpyInstance).mockImplementation( |
| 1071 | + (request: () => Promise<void>, options?: BackoffOptions) => |
| 1072 | + realExponentialBackoff.backOff(request, { |
| 1073 | + ...options, |
| 1074 | + startingDelay: 0, |
| 1075 | + }) |
| 1076 | + ); |
| 1077 | + }); |
| 1078 | + |
1064 | 1079 | test("acquires an authenticated remote url for the bcr fork", async () => { |
1065 | 1080 | const bcrRepo = CANONICAL_BCR; |
1066 | 1081 | const bcrForkRepo = new Repository("bazel-central-registry", "aspect"); |
@@ -1136,6 +1151,45 @@ describe("pushEntryToFork", () => { |
1136 | 1151 | branchName |
1137 | 1152 | ); |
1138 | 1153 | }); |
| 1154 | + |
| 1155 | + test("retries 5 times if it fails", async () => { |
| 1156 | + const bcrRepo = CANONICAL_BCR; |
| 1157 | + const bcrForkRepo = new Repository("bazel-central-registry", "aspect"); |
| 1158 | + const branchName = `repo/[email protected]`; |
| 1159 | + |
| 1160 | + (backOff as unknown as jest.SpyInstance).mockImplementation( |
| 1161 | + (request: () => Promise<any>, options?: BackoffOptions) => { |
| 1162 | + return realExponentialBackoff.backOff(request, { |
| 1163 | + ...options, |
| 1164 | + startingDelay: 0, |
| 1165 | + }); |
| 1166 | + } |
| 1167 | + ); |
| 1168 | + |
| 1169 | + mockGitClient.push |
| 1170 | + .mockRejectedValueOnce(new Error("failed push")) |
| 1171 | + .mockRejectedValueOnce(new Error("failed push")) |
| 1172 | + .mockRejectedValueOnce(new Error("failed push")) |
| 1173 | + .mockRejectedValueOnce(new Error("failed push")) |
| 1174 | + .mockResolvedValueOnce(undefined); |
| 1175 | + |
| 1176 | + await createEntryService.pushEntryToFork(bcrForkRepo, bcrRepo, branchName); |
| 1177 | + |
| 1178 | + expect(mockGitClient.push).toHaveBeenCalledTimes(5); |
| 1179 | + }); |
| 1180 | + |
| 1181 | + test("fails after the 5th retry", async () => { |
| 1182 | + const bcrRepo = CANONICAL_BCR; |
| 1183 | + const bcrForkRepo = new Repository("bazel-central-registry", "aspect"); |
| 1184 | + const branchName = `repo/[email protected]`; |
| 1185 | + |
| 1186 | + mockGitClient.push.mockRejectedValue(new Error("failed push")); |
| 1187 | + |
| 1188 | + await expect( |
| 1189 | + createEntryService.pushEntryToFork(bcrForkRepo, bcrRepo, branchName) |
| 1190 | + ).rejects.toThrow(); |
| 1191 | + expect(mockGitClient.push).toHaveBeenCalledTimes(5); |
| 1192 | + }); |
1139 | 1193 | }); |
1140 | 1194 |
|
1141 | 1195 | function mockRulesetFiles( |
|
0 commit comments