stock-bot/libs/http-client/src/BunHttpClient.test.ts

182 lines
4.9 KiB
TypeScript

import { describe, test, expect, mock, beforeEach, afterEach } from "bun:test";
import { BunHttpClient, HttpClientError, TimeoutError } from "../src";
// Mock GlobalFetch to avoid making real network requests
const mockFetchSuccess = mock(() =>
Promise.resolve(new Response(
JSON.stringify({ result: "success" }),
{ status: 200, headers: { "content-type": "application/json" } }
))
);
const mockFetchFailure = mock(() =>
Promise.resolve(new Response(
JSON.stringify({ error: "Not found" }),
{ status: 404, headers: { "content-type": "application/json" } }
))
);
const mockFetchTimeout = mock(() => {
return new Promise((_, reject) => {
setTimeout(() => {
const error = new Error("Timeout");
error.name = "AbortError";
reject(error);
}, 10);
});
});
describe("BunHttpClient", () => {
let client: BunHttpClient;
const originalFetch = global.fetch;
beforeEach(() => {
// Create a fresh client for each test
client = new BunHttpClient({
baseURL: "https://api.example.com",
timeout: 1000,
retries: 1
});
});
afterEach(async () => {
// Cleanup after each test
await client.close();
global.fetch = originalFetch;
});
test("should make successful GET requests", async () => {
global.fetch = mockFetchSuccess;
const response = await client.get("/users");
expect(response.status).toBe(200);
expect(response.data).toEqual({ result: "success" });
expect(mockFetchSuccess).toHaveBeenCalledTimes(1);
});
test("should handle failed requests", async () => {
global.fetch = mockFetchFailure;
try {
await client.get("/missing");
expect("Should have thrown").toBe("But didn't");
} catch (error) {
expect(error).toBeInstanceOf(HttpClientError);
expect(error.status).toBe(404);
}
expect(mockFetchFailure).toHaveBeenCalledTimes(1);
});
test("should handle request timeouts", async () => {
global.fetch = mockFetchTimeout;
try {
await client.get("/slow");
expect("Should have thrown").toBe("But didn't");
} catch (error) {
expect(error).toBeInstanceOf(TimeoutError);
}
});
test("should build full URLs properly", async () => {
global.fetch = mockFetchSuccess;
await client.get("/users/123");
expect(mockFetchSuccess).toHaveBeenCalledWith(
"https://api.example.com/users/123",
expect.objectContaining({
method: "GET"
})
);
});
test("should make POST requests with body", async () => {
global.fetch = mockFetchSuccess;
const data = { name: "John", email: "john@example.com" };
await client.post("/users", data);
expect(mockFetchSuccess).toHaveBeenCalledWith(
"https://api.example.com/users",
expect.objectContaining({
method: "POST",
body: JSON.stringify(data)
})
);
});
test("should provide convenience methods for all HTTP verbs", async () => {
global.fetch = mockFetchSuccess;
await client.get("/users");
await client.post("/users", { name: "Test" });
await client.put("/users/1", { name: "Updated" });
await client.patch("/users/1", { status: "active" });
await client.delete("/users/1");
await client.head("/users");
await client.options("/users");
expect(mockFetchSuccess).toHaveBeenCalledTimes(7);
});
test("should merge config options correctly", async () => {
global.fetch = mockFetchSuccess;
await client.get("/users", {
headers: { "X-Custom": "Value" },
timeout: 5000
});
expect(mockFetchSuccess).toHaveBeenCalledWith(
"https://api.example.com/users",
expect.objectContaining({
headers: expect.objectContaining({
"X-Custom": "Value"
})
})
);
});
test("should handle absolute URLs", async () => {
global.fetch = mockFetchSuccess;
await client.get("https://other-api.com/endpoint");
expect(mockFetchSuccess).toHaveBeenCalledWith(
"https://other-api.com/endpoint",
expect.anything()
);
});
test("should update configuration", async () => {
global.fetch = mockFetchSuccess;
client.setBaseURL("https://new-api.com");
client.setDefaultHeaders({ "Authorization": "Bearer token" });
client.setTimeout(2000);
await client.get("/resource");
expect(mockFetchSuccess).toHaveBeenCalledWith(
"https://new-api.com/resource",
expect.objectContaining({
headers: expect.objectContaining({
"Authorization": "Bearer token"
})
})
);
});
test("should get connection stats", async () => {
global.fetch = mockFetchSuccess;
await client.get("/users");
const stats = client.getStats();
expect(stats).toHaveProperty("successfulRequests", 1);
expect(stats).toHaveProperty("activeConnections");
expect(stats).toHaveProperty("averageResponseTime");
});
});