import { TCPSocketListener } from "bun"; import { afterAll, beforeAll, describe, expect, mock, spyOn, test } from "bun:test"; let server; let requestCount = 0; beforeAll(async () => { server = Bun.serve({ port: 0, fetch(request) { requestCount++; return new Response(undefined, { headers: request.headers }); }, }); }); afterAll(() => { server!.stop(true); }); test("fetch(request subclass with headers)", async () => { class MyRequest extends Request { constructor(input: RequestInfo, init?: RequestInit) { super(input, init); this.headers.set("hello", "world"); } } const myRequest = new MyRequest(server!.url + "/"); const { headers } = await fetch(myRequest); expect(headers.get("hello")).toBe("world"); }); test("fetch(RequestInit, headers)", async () => { const myRequest = { headers: { "hello": "world", }, url: server!.url, }; const { headers } = await fetch(myRequest, { headers: { "hello": "world2", }, }); expect(headers.get("hello")).toBe("world2"); }); test("fetch(url, RequestSubclass)", async () => { class MyRequest extends Request { constructor(input: RequestInfo, init?: RequestInit) { super(input, init); this.headers.set("hello", "world"); } } const myRequest = new MyRequest(server!.url); const { headers } = await fetch(server.url, myRequest); expect(headers.get("hello")).toBe("world"); }); test("fetch({toString throwing}, {headers} isn't accessed)", async () => { const obj = { headers: null, }; const mocked = spyOn(obj, "headers"); const str = { toString: mock(() => { throw new Error("bad2"); }), }; expect(async () => await fetch(str, obj)).toThrow("bad2"); expect(mocked).not.toHaveBeenCalled(); expect(str.toString).toHaveBeenCalledTimes(1); }); test("fetch(RequestSubclass, undefined)", async () => { class MyRequest extends Request { constructor(input: RequestInfo, init?: RequestInit) { super(input, init); this.headers.set("hello", "world"); } } const myRequest = new MyRequest(server!.url); const { headers } = await fetch(myRequest, undefined); expect(headers.get("hello")).toBe("world"); }); describe("does not send a request when", () => { let requestCount = 0; let server: TCPSocketListener | undefined; let url: string; beforeAll(async () => { server = Bun.listen({ port: 0, hostname: "127.0.0.1", socket: { open(socket) { requestCount++; socket.terminate(); }, data(socket, data) { socket.terminate(); }, }, }); }); afterAll(() => { server!.stop(true); url = "http://" + server!.hostname + ":" + server!.port; }); test("Invalid headers", async () => { const prevCount = requestCount; expect( async () => await fetch(url, { headers: { "πŸ˜€smile ": "πŸ˜€", }, }), ).toThrow(); // Give it a chance to possibly send the request. await Bun.sleep(2); expect(requestCount).toBe(prevCount); }); test("Invalid url", async () => { const prevCount = requestCount; expect(async () => await fetch("πŸ˜€")).toThrow(); // Give it a chance to possibly send the request. await Bun.sleep(2); expect(requestCount).toBe(prevCount); }); test("Invalid redirect", async () => { const prevCount = requestCount; expect(async () => await fetch(url, { redirect: "πŸ˜€" })).toThrow(); // Give it a chance to possibly send the request. await Bun.sleep(2); expect(requestCount).toBe(prevCount); }); test("proxy and unix", async () => { const prevCount = requestCount; expect(async () => await fetch(url, { proxy: url, unix: "/tmp/abc.sock" })).toThrow(); // Give it a chance to possibly send the request. await Bun.sleep(2); expect(requestCount).toBe(prevCount); }); test("Invalid ca in tls", async () => { const prevCount = requestCount; expect(async () => await fetch(url, { tls: { ca: 123 } })).toThrow(); // Give it a chance to possibly send the request. await Bun.sleep(2); expect(requestCount).toBe(prevCount); }); const propertyNamesToThrow = [ "body", "decompression", "headers", "keepalive", "method", "proxy", "redirect", "signal", "timeout", "tls", "unix", "verbose", ]; test(`ReadableStream body throws`, async () => { const prevCount = requestCount; expect( async () => await fetch(url, { body: async function* () { throw new Error("boom"); }, }), ).toThrow(); // Give it a chance to possibly send the request. await Bun.sleep(2); expect(requestCount).toBe(prevCount); }); for (const propertyName of propertyNamesToThrow) { test(`get "${propertyName}" throws (url, 1st arg)`, async () => { const prevCount = requestCount; expect( async () => await fetch(url, { get [propertyName]() { throw new Error("boom"); }, }), ).toThrow(); // Give it a chance to possibly send the request. await Bun.sleep(2); expect(requestCount).toBe(prevCount); }); test(`get "${propertyName}" throws (1st arg)`, async () => { const prevCount = requestCount; expect( async () => await fetch({ url, get [propertyName]() { throw new Error("boom"); }, }), ).toThrow(); // Give it a chance to possibly send the request. await Bun.sleep(2); expect(requestCount).toBe(prevCount); }); test(`get "${propertyName}" throws (Request object, 1st arg)`, async () => { const prevCount = requestCount; expect( async () => await fetch(new Request(url), { get [propertyName]() { throw new Error("boom"); }, }), ).toThrow(); // Give it a chance to possibly send the request. await Bun.sleep(2); expect(requestCount).toBe(prevCount); }); } });