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"); }); });