Skip to content

Commit

Permalink
add retry tests (#5)
Browse files Browse the repository at this point in the history
* Also retry POST requests

* Add tests for retry logic
  • Loading branch information
KeKs0r authored Oct 31, 2024
1 parent 31b8b3e commit 629335b
Show file tree
Hide file tree
Showing 2 changed files with 120 additions and 1 deletion.
111 changes: 111 additions & 0 deletions src/util/retry-fetch.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
import { describe, expect, it, beforeEach, afterEach, mock } from "bun:test";
import { retryFetch } from "./retry-fetch";
import { AbortError } from "p-retry";

// Mock the global fetch
const originalFetch = global.fetch;
let mockFetch: ReturnType<typeof mock>;

describe("retryFetch", () => {
beforeEach(() => {
// Reset mock before each test
mockFetch = mock(() => {});
global.fetch = mockFetch;
});

afterEach(() => {
mockFetch.mockReset();
});

it("should not retry on 401 unauthorized", async () => {
// Mock fetch to return 401
mockFetch.mockImplementation(() =>
Promise.resolve({
status: 401,
json: () => Promise.resolve({ message: "Unauthorized" }),
})
);

// Attempt the fetch
await expect(retryFetch("/test", {})).rejects.toThrow();

// Verify fetch was only called once
expect(mockFetch).toHaveBeenCalledTimes(1);
});

it("should retry on 500 GET server error", async () => {
// Mock fetch to fail twice with 500, then succeed
mockFetch
.mockImplementationOnce(() =>
Promise.resolve({
status: 500,
json: () => Promise.resolve({ message: "Server Error" }),
})
)
.mockImplementationOnce(() =>
Promise.resolve({
status: 500,
json: () => Promise.resolve({ message: "Server Error" }),
})
)
.mockImplementationOnce(() =>
Promise.resolve({
status: 200,
json: () => Promise.resolve({ message: "Success" }),
})
);

// Attempt the fetch
await retryFetch("/test", {});

// Verify fetch was called multiple times
expect(mockFetch).toHaveBeenCalledTimes(3);
});

it("should retry on 500 POST server error", async () => {
// Mock fetch to fail twice with 500, then succeed
mockFetch
.mockImplementationOnce(() =>
Promise.resolve({
status: 500,
json: () => Promise.resolve({ message: "Server Error" }),
})
)
.mockImplementationOnce(() =>
Promise.resolve({
status: 500,
json: () => Promise.resolve({ message: "Server Error" }),
})
)
.mockImplementationOnce(() =>
Promise.resolve({
status: 200,
json: () => Promise.resolve({ message: "Success" }),
})
);

// Attempt the fetch
await retryFetch("/test", {
method: "POST",
});

// Verify fetch was called multiple times
expect(mockFetch).toHaveBeenCalledTimes(3);
});

it("should succeed immediately on 200", async () => {
// Mock fetch to return 200
mockFetch.mockImplementation(() =>
Promise.resolve({
status: 200,
json: () => Promise.resolve({ message: "Success" }),
})
);

// Attempt the fetch
await retryFetch("/test", {});

// Verify fetch was only called once
expect(mockFetch.mock.calls.length).toBe(1);
});
});
10 changes: 9 additions & 1 deletion src/util/retry-fetch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,15 @@ async function wrappedFetch(...args: Parameters<typeof fetch>) {
}

// Source: https://github.com/sindresorhus/got/blob/main/documentation/7-retry.md
const retryMethods = ["GET", "PUT", "HEAD", "DELETE", "OPTIONS", "TRACE"];
const retryMethods = [
"GET",
"PUT",
"HEAD",
"DELETE",
"OPTIONS",
"TRACE",
"POST", // This is not in the default, but I think it makes sense for us
];
const retryStatusCodes = [408, 413, 429, 500, 502, 503, 504, 521, 522, 524];
const retryErrorCodes = [
"ETIMEDOUT",
Expand Down

0 comments on commit 629335b

Please sign in to comment.