Scaffold Docker-only Node 24 + TypeScript EJS web backend

This commit is contained in:
2026-06-14 11:45:30 +02:00
commit 4eed701419
25 changed files with 2443 additions and 0 deletions

43
src/app.test.ts Normal file
View File

@@ -0,0 +1,43 @@
import assert from "node:assert/strict";
import type { AddressInfo } from "node:net";
import { after, before, test } from "node:test";
import { createApp } from "./app.ts";
import { contentTypeFor, resolveStaticPath } from "./static.ts";
const server = createApp();
let base = "";
before(async () => {
await new Promise<void>((resolve) => server.listen(0, resolve));
base = `http://localhost:${(server.address() as AddressInfo).port}`;
});
after(() => server.close());
test("serves the home page as HTML", async () => {
const res = await fetch(base + "/");
assert.equal(res.status, 200);
assert.match(res.headers.get("content-type") ?? "", /text\/html/);
assert.match(await res.text(), /Plainpages/);
});
test("serves static CSS", async () => {
const res = await fetch(base + "/public/css/style.css");
assert.equal(res.status, 200);
assert.match(res.headers.get("content-type") ?? "", /text\/css/);
});
test("returns 404 for unknown routes", async () => {
const res = await fetch(base + "/missing");
assert.equal(res.status, 404);
});
test("resolveStaticPath blocks traversal, allows nested files", () => {
assert.equal(resolveStaticPath("/srv/public", "../app.ts"), null);
assert.equal(resolveStaticPath("/srv/public", "css/style.css"), "/srv/public/css/style.css");
});
test("contentTypeFor maps known and unknown extensions", () => {
assert.match(contentTypeFor("a.css"), /text\/css/);
assert.equal(contentTypeFor("a.bin"), "application/octet-stream");
});