98 lines
3.0 KiB
TypeScript
98 lines
3.0 KiB
TypeScript
import { test, expect } from "bun:test";
|
|
import { EventEmitter } from "node:events";
|
|
import { toRequest, writeResponse, nodeListener } from "../src/adapters/node.ts";
|
|
|
|
function mockReq(opts: {
|
|
method?: string;
|
|
url?: string;
|
|
headers?: Record<string, string>;
|
|
body?: string;
|
|
}): any {
|
|
const em = new EventEmitter() as any;
|
|
em.method = opts.method ?? "GET";
|
|
em.url = opts.url ?? "/";
|
|
em.headers = opts.headers ?? { host: "localhost" };
|
|
setTimeout(() => {
|
|
if (opts.body != null) em.emit("data", Buffer.from(opts.body));
|
|
em.emit("end");
|
|
}, 0);
|
|
return em;
|
|
}
|
|
|
|
function mockRes(): any {
|
|
const chunks: Buffer[] = [];
|
|
return {
|
|
statusCode: 0,
|
|
headers: null as any,
|
|
headersSent: false,
|
|
ended: false,
|
|
writeHead(status: number, headers: any) {
|
|
this.statusCode = status;
|
|
this.headers = headers;
|
|
this.headersSent = true;
|
|
},
|
|
write(chunk: any) {
|
|
chunks.push(Buffer.from(chunk));
|
|
},
|
|
end(chunk?: any) {
|
|
if (chunk) chunks.push(Buffer.from(chunk));
|
|
this.ended = true;
|
|
},
|
|
body() {
|
|
return Buffer.concat(chunks).toString();
|
|
},
|
|
};
|
|
}
|
|
|
|
test("toRequest converts method, URL, headers and body", async () => {
|
|
const req = mockReq({
|
|
method: "POST",
|
|
url: "/api/x?a=1",
|
|
headers: { host: "ex.test", "content-type": "application/json" },
|
|
body: '{"k":1}',
|
|
});
|
|
const r = await toRequest(req);
|
|
expect(r.method).toBe("POST");
|
|
expect(r.url).toBe("http://ex.test/api/x?a=1");
|
|
expect(r.headers.get("content-type")).toBe("application/json");
|
|
expect(await r.json()).toEqual({ k: 1 });
|
|
});
|
|
|
|
test("toRequest honours x-forwarded-proto for the origin", async () => {
|
|
const req = mockReq({ url: "/p", headers: { host: "ex.test", "x-forwarded-proto": "https" } });
|
|
const r = await toRequest(req);
|
|
expect(r.url).toBe("https://ex.test/p");
|
|
});
|
|
|
|
test("writeResponse writes status, body and keeps multiple Set-Cookie separate", async () => {
|
|
const res = mockRes();
|
|
const response = new Response("hello", {
|
|
status: 201,
|
|
headers: { "content-type": "text/plain" },
|
|
});
|
|
response.headers.append("set-cookie", "a=1");
|
|
response.headers.append("set-cookie", "b=2");
|
|
await writeResponse(res, response);
|
|
expect(res.statusCode).toBe(201);
|
|
expect(res.headers["content-type"]).toBe("text/plain");
|
|
expect(res.headers["set-cookie"]).toEqual(["a=1", "b=2"]);
|
|
expect(res.body()).toBe("hello");
|
|
});
|
|
|
|
test("nodeListener dispatches a request through a fetch handler", async () => {
|
|
const handler = (req: Request) => Response.json({ path: new URL(req.url).pathname });
|
|
const res = mockRes();
|
|
await nodeListener(handler)(mockReq({ url: "/hi", headers: { host: "x" } }), res);
|
|
expect(res.statusCode).toBe(200);
|
|
expect(JSON.parse(res.body())).toEqual({ path: "/hi" });
|
|
});
|
|
|
|
test("nodeListener returns 500 when the handler throws", async () => {
|
|
const handler = () => {
|
|
throw new Error("boom");
|
|
};
|
|
const res = mockRes();
|
|
await nodeListener(handler)(mockReq({ url: "/x", headers: { host: "x" } }), res);
|
|
expect(res.statusCode).toBe(500);
|
|
});
|