hornwitser.no/utils/http-server.test.ts
Hornwitser 82323c9158 Add development HTTP server
Add an HTTP server for development purposes that replies to requests
with both the static resources and on the fly generated pages.  This
serves the following needs.

- Modern module scripts requires an origin supporting CORS policies,
  which is not supported when pages are read from disk by the browser.

- Provide a way for implemting automatic reloading of pages when changes
  are made to the source files.

- Act as the starting point for making interactive features such as
  comments and analytics.
2025-02-02 10:03:55 +01:00

97 lines
3.4 KiB
TypeScript

import * as assert from "node:assert/strict";
import { after, before, suite, test } from "node:test";
import * as http from "node:http";
import { createServer } from "./http-server.js";
import { once } from "node:events";
import type { AddressInfo } from "node:net";
suite("function createServer", () => {
let server: ReturnType<typeof createServer>;
let baseUrl: URL;
before(async () => {
server = createServer(
ref => {
if (ref === "/test.html") return Buffer.from("<p>Test!</p>");
if (ref === "/style.css") return Buffer.from("p { font-weight: bold; }");
if (ref === "/script.js") return Buffer.from("alert('Hello world!');");
return undefined;
}
);
server.listen(0, "localhost");
await once(server, "listening");
baseUrl = new URL(`http://localhost:${(server.address() as AddressInfo).port}`)
});
after(() => {
server.close();
})
async function makeRequest(method: string, ref: string) {
const url = new URL(ref, baseUrl);
const request = http.request(url, {
method,
timeout: 1000,
});
request.end();
const response: http.IncomingMessage & { body: Buffer } = (await once(request, "response"))[0];
response.body = Buffer.concat(await response.toArray());
return { request, response };
}
async function getTest(ref: string, statusCode: number, content: Buffer, mediaType: string) {
const { response } = await makeRequest("GET", ref);
assert.equal(response.statusCode, statusCode);
assert.equal(response.headers["content-type"], mediaType);
assert.equal(Number.parseInt(response.headers["content-length"]!), content.length);
assert.deepEqual(response.body, content);
}
async function headTest(ref: string, statusCode: number, content: Buffer, mediaType: string) {
const { response } = await makeRequest("HEAD", ref);
assert.equal(response.statusCode, statusCode);
assert.equal(response.headers["content-type"], mediaType);
assert.equal(Number.parseInt(response.headers["content-length"]!), content.length);
assert.deepEqual(response.body, Buffer.alloc(0));
}
test("GET /test.html", async () => {
await getTest("/test.html", 200, Buffer.from("<p>Test!</p>"), "text/html")
});
test("GET /style.css", async () => {
await getTest("/style.css", 200, Buffer.from("p { font-weight: bold; }"), "text/css")
});
test("GET /script.js", async () => {
await getTest("/script.js", 200, Buffer.from("alert('Hello world!');"), "text/javascript")
});
test("GET /does-not-exist", async () => {
await getTest("/does-not-exist", 404, Buffer.from("404 Not Found"), "text/plain")
});
test("HEAD /test.html", async () => {
await headTest("/test.html", 200, Buffer.from("<p>Test!</p>"), "text/html")
});
test("HEAD /style.css", async () => {
await headTest("/style.css", 200, Buffer.from("p { font-weight: bold; }"), "text/css")
});
test("HEAD /script.js", async () => {
await headTest("/script.js", 200, Buffer.from("alert('Hello world!');"), "text/javascript")
});
test("HEAD /does-not-exist", async () => {
await headTest("/does-not-exist", 404, Buffer.from("404 Not Found"), "text/plain")
});
test("POST /test.html", async () => {
const { response } = await makeRequest("POST", "/test.html");
const content = Buffer.from("400 Bad Request");
assert.equal(response.statusCode, 400);
assert.equal(response.headers["content-type"], "text/plain");
assert.equal(Number.parseInt(response.headers["content-length"]!), content.length);
assert.deepEqual(response.body, content);
});
});