143 lines
3.6 KiB
TypeScript
143 lines
3.6 KiB
TypeScript
// Generated by continue
|
|
|
|
import {
|
|
withExponentialBackoff,
|
|
APIError,
|
|
RETRY_AFTER_HEADER,
|
|
} from "./withExponentialBackoff";
|
|
|
|
describe.skip("withExponentialBackoff", () => {
|
|
it("should return result when apiCall succeeds on first attempt", async () => {
|
|
// Arrange
|
|
const apiCall = jest.fn().mockResolvedValue("Success");
|
|
|
|
// Act
|
|
const result = await withExponentialBackoff(apiCall);
|
|
|
|
// Assert
|
|
expect(result).toBe("Success");
|
|
expect(apiCall).toHaveBeenCalledTimes(1);
|
|
});
|
|
|
|
it("should throw error when apiCall fails with non-429 error", async () => {
|
|
// Arrange
|
|
const error = new Error("Some error");
|
|
const apiCall = jest.fn().mockRejectedValue(error);
|
|
|
|
// Act & Assert
|
|
await expect(withExponentialBackoff(apiCall)).rejects.toThrow("Some error");
|
|
expect(apiCall).toHaveBeenCalledTimes(1);
|
|
});
|
|
|
|
it("should retry when apiCall fails with 429 and no Retry-After header", async () => {
|
|
// Arrange
|
|
const apiCall = jest.fn();
|
|
|
|
const firstError: APIError = new Error("Rate limit");
|
|
firstError.response = {
|
|
status: 429,
|
|
headers: {
|
|
get: () => null, // No Retry-After header
|
|
},
|
|
} as unknown as Response;
|
|
|
|
// First call fails with 429, second call succeeds
|
|
apiCall.mockRejectedValueOnce(firstError).mockResolvedValueOnce("Success");
|
|
|
|
// Act
|
|
const result = await withExponentialBackoff(
|
|
apiCall,
|
|
5,
|
|
0.01, // initialDelaySeconds
|
|
);
|
|
|
|
// Assert
|
|
expect(result).toBe("Success");
|
|
expect(apiCall).toHaveBeenCalledTimes(2);
|
|
});
|
|
|
|
it("should retry when apiCall fails with 429 and Retry-After header", async () => {
|
|
// Arrange
|
|
const apiCall = jest.fn();
|
|
|
|
const firstError: APIError = new Error("Rate limit");
|
|
firstError.response = {
|
|
status: 429,
|
|
headers: {
|
|
get: (headerName: string) => {
|
|
if (headerName === RETRY_AFTER_HEADER) {
|
|
return "0.02";
|
|
}
|
|
return null;
|
|
},
|
|
},
|
|
} as unknown as Response;
|
|
|
|
// First call fails with 429, second call succeeds
|
|
apiCall.mockRejectedValueOnce(firstError).mockResolvedValueOnce("Success");
|
|
|
|
jest.useFakeTimers();
|
|
|
|
const promise = withExponentialBackoff(apiCall, 5, 0.01);
|
|
|
|
// Advance timers to cover the delay
|
|
await jest.advanceTimersByTimeAsync(20);
|
|
|
|
const result = await promise;
|
|
|
|
// Assert
|
|
expect(result).toBe("Success");
|
|
expect(apiCall).toHaveBeenCalledTimes(2);
|
|
|
|
jest.useRealTimers();
|
|
});
|
|
|
|
it("should throw error after maxTries reached", async () => {
|
|
// Arrange
|
|
const apiCall = jest.fn();
|
|
|
|
const error: APIError = new Error("Rate limit");
|
|
error.response = {
|
|
status: 429,
|
|
headers: {
|
|
get: () => null,
|
|
},
|
|
} as unknown as Response;
|
|
|
|
apiCall.mockRejectedValue(error);
|
|
|
|
const maxTries = 3;
|
|
const initialDelaySeconds = 0.01;
|
|
|
|
// Act & Assert
|
|
await expect(
|
|
withExponentialBackoff(apiCall, maxTries, initialDelaySeconds),
|
|
).rejects.toThrow("Failed to make API call after max tries");
|
|
|
|
expect(apiCall).toHaveBeenCalledTimes(maxTries);
|
|
});
|
|
|
|
it("should not call if maxTries is 0", async () => {
|
|
const apiCall = jest.fn();
|
|
|
|
const error: APIError = new Error("Rate limit");
|
|
error.response = {
|
|
status: 429,
|
|
headers: {
|
|
get: () => null,
|
|
},
|
|
} as unknown as Response;
|
|
|
|
apiCall.mockRejectedValue(error);
|
|
|
|
const maxTries = 0;
|
|
const initialDelaySeconds = 0.01;
|
|
|
|
await expect(
|
|
withExponentialBackoff(apiCall, maxTries, initialDelaySeconds),
|
|
).rejects.toThrow("Failed to make API call after max tries");
|
|
|
|
expect(apiCall).toHaveBeenCalledTimes(0);
|
|
});
|
|
});
|