diff options
Diffstat (limited to 'test/bun.js')
-rw-r--r-- | test/bun.js/node-http.test.ts | 377 | ||||
-rw-r--r-- | test/bun.js/node-test-helpers.ts | 21 |
2 files changed, 390 insertions, 8 deletions
diff --git a/test/bun.js/node-http.test.ts b/test/bun.js/node-http.test.ts index cdb0d4758..e3d20f1c0 100644 --- a/test/bun.js/node-http.test.ts +++ b/test/bun.js/node-http.test.ts @@ -1,5 +1,6 @@ -import { describe, expect, it } from "bun:test"; -import { createServer } from "node:http"; +import { describe, expect, it, beforeAll, afterAll } from "bun:test"; +import { createServer, request, get, Agent, globalAgent } from "node:http"; +import { createDoneDotAll } from "node-test-helpers"; describe("node:http", () => { describe("createServer", async () => { @@ -69,4 +70,376 @@ describe("node:http", () => { server.close(); }); }); + + describe("request", () => { + let server; + let timer = null; + beforeAll(() => { + server = createServer((req, res) => { + const reqUrl = new URL(req.url, `http://${req.headers.host}`); + if (reqUrl.pathname) { + if (reqUrl.pathname === "/redirect") { + // Temporary redirect + res.writeHead(301, { + Location: "http://localhost:8126/redirected", + }); + res.end("Got redirect!\n"); + return; + } + if (reqUrl.pathname === "/redirected") { + res.writeHead(404, { "Content-Type": "text/plain" }); + res.end("Not Found"); + return; + } + if (reqUrl.pathname.includes("timeout")) { + if (timer) clearTimeout(timer); + timer = setTimeout(() => { + res.end("Hello World"); + timer = null; + }, 3000); + return; + } + if (reqUrl.pathname === "/pathTest") { + res.end("Path correct!\n"); + return; + } + } + + res.writeHead(200, { "Content-Type": "text/plain" }); + + if (req.headers["x-test"]) { + res.write(`x-test: ${req.headers["x-test"]}\n`); + } + + // Check for body + if (req.method === "POST") { + req.on("data", (chunk) => { + res.write(chunk); + }); + + req.on("end", () => { + res.write("POST\n"); + res.end("Hello World"); + }); + } else { + if (req.headers["X-Test"] !== undefined) { + res.write(`X-Test: test\n`); + } + res.write("Maybe GET maybe not\n"); + res.end("Hello World"); + } + }); + server.listen(8126); + }); + afterAll(() => { + server.close(); + if (timer) clearTimeout(timer); + }); + + it("should make a standard GET request when passed string as first arg", (done) => { + const req = request("http://localhost:8126", (res) => { + let data = ""; + res.setEncoding("utf8"); + res.on("data", (chunk) => { + data += chunk; + }); + res.on("end", () => { + expect(data).toBe("Maybe GET maybe not\nHello World"); + done(); + }); + res.on("error", (err) => done(err)); + }); + req.end(); + }); + + it("should make a POST request when provided POST method, even without a body", (done) => { + const req = request( + { host: "localhost", port: 8126, method: "POST" }, + (res) => { + let data = ""; + res.setEncoding("utf8"); + res.on("data", (chunk) => { + data += chunk; + }); + res.on("end", () => { + expect(data).toBe("POST\nHello World"); + done(); + }); + res.on("error", (err) => done(err)); + }, + ); + req.end(); + }); + + it("should correctly handle a POST request with a body", (done) => { + const req = request( + { host: "localhost", port: 8126, method: "POST" }, + (res) => { + let data = ""; + res.setEncoding("utf8"); + res.on("data", (chunk) => { + data += chunk; + }); + res.on("end", () => { + expect(data).toBe("Posting\nPOST\nHello World"); + done(); + }); + res.on("error", (err) => done(err)); + }, + ); + req.write("Posting\n"); + req.end(); + }); + + it("should noop request.setSocketKeepAlive without error", () => { + const req = request("http://localhost:8126"); + req.setSocketKeepAlive(true, 1000); + req.end(); + expect(true).toBe(true); + }); + + it("should allow us to set timeout with request.setTimeout or `timeout` in options", (done) => { + const createDone = createDoneDotAll(done); + const req1Done = createDone(); + const req2Done = createDone(); + + // const start = Date.now(); + const req1 = request( + { + host: "localhost", + port: 8126, + path: "/timeout", + timeout: 500, + }, + (res) => { + req1Done(new Error("Should not have received response")); + }, + ); + req1.on("timeout", () => req1Done()); + + const req2 = request( + { + host: "localhost", + port: 8126, + path: "/timeout", + }, + (res) => { + req2Done(new Error("Should not have received response")); + }, + ); + + req2.setTimeout(500, () => { + req2Done(); + }); + req1.end(); + req2.end(); + }); + + it("should correctly set path when path provided", (done) => { + const createDone = createDoneDotAll(done); + const req1Done = createDone(); + const req2Done = createDone(); + + const req1 = request("http://localhost:8126/pathTest", (res) => { + let data = ""; + res.setEncoding("utf8"); + res.on("data", (chunk) => { + data += chunk; + }); + res.on("end", () => { + expect(data).toBe("Path correct!\n"); + req1Done(); + }); + res.on("error", (err) => req1Done(err)); + }); + + const req2 = request( + "http://localhost:8126", + { path: "/pathTest" }, + (res) => { + let data = ""; + res.setEncoding("utf8"); + res.on("data", (chunk) => { + data += chunk; + }); + res.on("end", () => { + expect(data).toBe("Path correct!\n"); + req2Done(); + }); + res.on("error", (err) => req2Done(err)); + }, + ); + + req1.end(); + req2.end(); + + expect(req1.path).toBe("/pathTest"); + expect(req2.path).toBe("/pathTest"); + }); + + it("should emit response when response received", (done) => { + const req = request("http://localhost:8126"); + + req.on("response", (res) => { + expect(res.statusCode).toBe(200); + done(); + }); + req.end(); + }); + + // NOTE: Node http.request doesn't follow redirects by default + it("should handle redirects properly", (done) => { + const req = request("http://localhost:8126/redirect", (res) => { + let data = ""; + res.setEncoding("utf8"); + res.on("data", (chunk) => { + data += chunk; + }); + res.on("end", () => { + expect(data).toBe("Got redirect!\n"); + done(); + }); + res.on("error", (err) => done(err)); + }); + req.end(); + }); + + it("should correctly attach headers to request", (done) => { + const req = request( + { host: "localhost", port: 8126, headers: { "X-Test": "test" } }, + (res) => { + let data = ""; + res.setEncoding("utf8"); + res.on("data", (chunk) => { + data += chunk; + }); + res.on("end", () => { + expect(data).toBe("x-test: test\nMaybe GET maybe not\nHello World"); + done(); + }); + res.on("error", (err) => done(err)); + }, + ); + req.end(); + expect(req.getHeader("X-Test")).toBe("test"); + }); + + it("should correct casing of method param", (done) => { + const req = request( + { host: "localhost", port: 8126, method: "get" }, + (res) => { + let data = ""; + res.setEncoding("utf8"); + res.on("data", (chunk) => { + data += chunk; + }); + res.on("end", () => { + expect(data).toBe("Maybe GET maybe not\nHello World"); + done(); + }); + res.on("error", (err) => done(err)); + }, + ); + req.end(); + }); + + it("should allow for port as a string", (done) => { + const req = request( + { host: "localhost", port: "8126", method: "GET" }, + (res) => { + let data = ""; + res.setEncoding("utf8"); + res.on("data", (chunk) => { + data += chunk; + }); + res.on("end", () => { + expect(data).toBe("Maybe GET maybe not\nHello World"); + done(); + }); + res.on("error", (err) => done(err)); + }, + ); + req.end(); + }); + }); + + describe("get", () => { + let server; + beforeAll(() => { + server = createServer((req, res) => { + res.writeHead(200, { "Content-Type": "text/plain" }); + res.end("Hello World"); + }); + server.listen(8127); + }); + afterAll(() => { + server.close(); + }); + it("should make a standard GET request, like request", (done) => { + get("http://127.0.0.1:8127", (res) => { + let data = ""; + res.setEncoding("utf8"); + res.on("data", (chunk) => { + data += chunk; + }); + res.on("end", () => { + expect(data).toBe("Hello World"); + done(); + }); + res.on("error", (err) => done(err)); + }); + }); + }); + + describe("Agent", () => { + let server; + let dummyReq; + let dummyAgent; + beforeAll(() => { + dummyAgent = new Agent(); + server = createServer((req, res) => { + res.writeHead(200, { "Content-Type": "text/plain" }); + res.end("Hello World"); + }); + server.listen(8128, () => { + // Setup request after server is listening + dummyReq = request( + { + host: "localhost", + port: 8128, + agent: dummyAgent, + }, + (res) => {}, + ); + dummyReq.on("error", () => {}); + }); + }); + + afterAll(() => { + dummyReq.end(); + server.close(); + }); + + it("should be a class", () => { + expect(Agent instanceof Function).toBe(true); + }); + + it("should have a default maxSockets of Infinity", () => { + expect(dummyAgent.maxSockets).toBe(Infinity); + }); + + it("should have a keepAlive value", () => { + expect(dummyAgent.keepAlive).toBe(false); + }); + + it("should noop keepSocketAlive", () => { + const agent = new Agent({ keepAlive: true }); + expect(agent.keepAlive).toBe(true); + agent.keepSocketAlive(dummyReq.socket); + }); + + it("should provide globalAgent", () => { + expect(globalAgent instanceof Agent).toBe(true); + }); + }); }); diff --git a/test/bun.js/node-test-helpers.ts b/test/bun.js/node-test-helpers.ts index 34147c194..0eaa7d07b 100644 --- a/test/bun.js/node-test-helpers.ts +++ b/test/bun.js/node-test-helpers.ts @@ -123,13 +123,22 @@ export const createCallCheckCtx = (done: DoneCb) => { // mustCallChecks.push(context); const done = createDone(); const _return = (...args) => { - // @ts-ignore - const result = fn.apply(this, args); - actual++; - if (actual >= expected) { - done(); + try { + // @ts-ignore + const result = fn.apply(this, args); + actual++; + if (actual >= expected) { + done(); + } + return result; + } catch (err) { + if (err instanceof Error) done(err); + else if (err?.toString) done(new Error(err?.toString())); + else { + console.error("Unknown error", err); + done(new Error("Unknown error")); + } } - return result; }; // Function instances have own properties that may be relevant. // Let's replicate those properties to the returned function. |