feat(docker): build and serve with Node and npm instead of Nginx
This commit is contained in:
@@ -0,0 +1,87 @@
|
||||
import test from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
import fs from "node:fs";
|
||||
import http from "node:http";
|
||||
import path from "node:path";
|
||||
import { execSync } from "node:child_process";
|
||||
import { createServer } from "../server.js";
|
||||
|
||||
const TEST_PORT = 8999;
|
||||
const BASE_URL = `http://127.0.0.1:${TEST_PORT}`;
|
||||
|
||||
// Helper to perform HTTP GET requests
|
||||
function get(urlPath) {
|
||||
return new Promise((resolve, reject) => {
|
||||
http.get(`${BASE_URL}${urlPath}`, (res) => {
|
||||
let data = "";
|
||||
res.on("data", (chunk) => (data += chunk));
|
||||
res.on("end", () => {
|
||||
resolve({
|
||||
statusCode: res.statusCode,
|
||||
headers: res.headers,
|
||||
body: data
|
||||
});
|
||||
});
|
||||
}).on("error", reject);
|
||||
});
|
||||
}
|
||||
|
||||
test("Node Static Server: lifecycle, routing, and adversarial security tests", async (t) => {
|
||||
// Ensure _site exists before running server tests
|
||||
if (!fs.existsSync(path.resolve(process.cwd(), "_site/index.html"))) {
|
||||
execSync("npm run build", {
|
||||
stdio: "pipe",
|
||||
env: { ...process.env, SITE_DOMAIN: "riverrooks.dev" }
|
||||
});
|
||||
}
|
||||
|
||||
const server = createServer();
|
||||
await new Promise((resolve) => server.listen(TEST_PORT, "127.0.0.1", resolve));
|
||||
|
||||
t.after(() => {
|
||||
return new Promise((resolve) => server.close(resolve));
|
||||
});
|
||||
|
||||
await t.test("serves root index.html with 200 OK and text/html", async () => {
|
||||
const res = await get("/");
|
||||
assert.equal(res.statusCode, 200);
|
||||
assert.match(res.headers["content-type"], /text\/html/);
|
||||
assert.match(res.body, /<!DOCTYPE html>/i);
|
||||
// Verify security headers
|
||||
assert.equal(res.headers["x-content-type-options"], "nosniff");
|
||||
assert.equal(res.headers["x-frame-options"], "DENY");
|
||||
});
|
||||
|
||||
await t.test("serves clean directory URL (/dispatches/)", async () => {
|
||||
const res = await get("/dispatches/");
|
||||
assert.equal(res.statusCode, 200);
|
||||
assert.match(res.headers["content-type"], /text\/html/);
|
||||
});
|
||||
|
||||
await t.test("serves static assets with appropriate Content-Type", async () => {
|
||||
const res = await get("/assets/css/main.css");
|
||||
assert.equal(res.statusCode, 200);
|
||||
assert.match(res.headers["content-type"], /text\/css/);
|
||||
});
|
||||
|
||||
await t.test("responds with 200 OK JSON on /healthz", async () => {
|
||||
const res = await get("/healthz");
|
||||
assert.equal(res.statusCode, 200);
|
||||
assert.match(res.headers["content-type"], /application\/json/);
|
||||
const body = JSON.parse(res.body);
|
||||
assert.equal(body.status, "ok");
|
||||
});
|
||||
|
||||
await t.test("returns 404 Not Found for non-existent routes", async () => {
|
||||
const res = await get("/non-existent-file-xyz.html");
|
||||
assert.equal(res.statusCode, 404);
|
||||
});
|
||||
|
||||
await t.test("adversarial: blocks path traversal attempts", async () => {
|
||||
const res = await get("/../../../../etc/passwd");
|
||||
// Must either return 403 Forbidden or 404 Not Found, never 200
|
||||
assert.notEqual(res.statusCode, 200);
|
||||
assert.ok(res.statusCode === 403 || res.statusCode === 404);
|
||||
assert.doesNotMatch(res.body, /root:x:/);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user