Add RequestContext primitive (todo §0); harden static serving (HEAD, control-char, stream-error logging)

This commit is contained in:
2026-06-14 19:33:17 +02:00
parent b4c149db27
commit c544387d3a
12 changed files with 158 additions and 34 deletions
+51
View File
@@ -0,0 +1,51 @@
import assert from "node:assert/strict";
import { IncomingMessage, ServerResponse } from "node:http";
import { Socket } from "node:net";
import { test } from "node:test";
import { buildContext, type User } from "./context.ts";
// A req/res pair without a live server — enough to build and inspect a context.
function reqRes(url?: string): { req: IncomingMessage; res: ServerResponse } {
const req = new IncomingMessage(new Socket());
if (url !== undefined) req.url = url;
req.method = "GET";
return { req, res: new ServerResponse(req) };
}
test("buildContext parses the URL and defaults to an anonymous user", () => {
const { req, res } = reqRes("/users?q=ann");
const ctx = buildContext(req, res);
assert.equal(ctx.req, req);
assert.equal(ctx.res, res);
assert.equal(ctx.url.pathname, "/users");
assert.equal(ctx.user, null);
assert.deepEqual(ctx.roles, []);
assert.deepEqual(ctx.params, {});
});
test("buildContext exposes query as the URL's search params", () => {
const { req, res } = reqRes("/users?q=ann&page=2");
const ctx = buildContext(req, res);
assert.equal(ctx.query, ctx.url.searchParams); // same instance, not a copy
assert.equal(ctx.query.get("q"), "ann");
assert.equal(ctx.query.get("page"), "2");
});
test("buildContext threads path params supplied by the router", () => {
const { req, res } = reqRes("/users/42");
const ctx = buildContext(req, res, { params: { id: "42" } });
assert.equal(ctx.params.id, "42");
});
test("buildContext threads the user and derives roles from it", () => {
const { req, res } = reqRes("/");
const user: User = { email: "a@b.c", id: "u1", roles: ["admin", "editor"] };
const ctx = buildContext(req, res, { user });
assert.equal(ctx.user, user);
assert.equal(ctx.roles, user.roles); // same reference, never a divergent copy — buildContext is the only writer
});
test("buildContext defaults a missing request URL to /", () => {
const { req, res } = reqRes();
assert.equal(buildContext(req, res).url.pathname, "/");
});