import { createServer } from "node:http"; import { readFile } from "node:fs/promises"; import { createHash } from "node:crypto"; import { commandsFor, renderPage } from "../cf-worker/src/view.js"; /** Real page rendering and real local assets, with no live Worker or upstream calls. */ export async function startBrowserFixture() { const [template, style, script, fixtureKey] = await Promise.all([ readFile(new URL("../site/index.html", import.meta.url), "utf8"), readFile(new URL("../site/assets/style.css", import.meta.url)), readFile(new URL("../site/assets/app.js", import.meta.url)), readFile(new URL("./fixtures/keys.pub", import.meta.url), "utf8"), ]); const [type, encoded] = fixtureKey.trim().split(/\s+/); const comment = 'CI test & "quoted" 中文 ' + "long-comment-".repeat(90); const text = `${type} ${encoded} ${comment}`; const key = { type, text, label: comment, fingerprint: "SHA256:" + createHash("sha256").update(Buffer.from(encoded, "base64")).digest("base64").replace(/=+$/, ""), }; const config = { baseUrl: "https://keys.example.test", repositoryUrl: "https://github.com/example/quickly-conf-sshd" }; const base = { schema: 1, revision: "0123456789abcdef0123456789abcdef01234567", publishedAt: "2026-10-12T12:34:56.000Z", keyCount: 1, allowEmpty: false, sha256: createHash("sha256").update(text + "\n").digest("hex"), testFixture: true, ...config, commands: commandsFor(config), keys: [key], }; const snapshots = { repository: { ...base, source: { type: "repository" } }, fallback: { ...base, source: { type: "fallback", url: "https://keys.example.test/team.keys?comment=" } }, unavailable: { ...base, keys: [], keyCount: 0, sha256: null, source: { type: "unavailable" } }, pending: { ...base, keys: [], keyCount: 0, sha256: null, source: { type: "pending" } }, "explicit-empty": { ...base, allowEmpty: true, keys: [], keyCount: 0, sha256: createHash("sha256").update("").digest("hex"), source: { type: "explicit-empty" } }, }; const pages = new Map(Object.entries(snapshots).map(([name, snapshot]) => ["/" + name, renderPage(template, snapshot)])); const requests = []; const server = createServer((request, response) => { const pathname = new URL(request.url, "http://127.0.0.1").pathname; requests.push(pathname); response.setHeader("Cache-Control", "no-store"); response.setHeader("X-Content-Type-Options", "nosniff"); response.setHeader("Content-Security-Policy", "default-src 'self'; script-src 'self'; style-src 'self'; img-src 'self' data:; connect-src 'self'; object-src 'none'; base-uri 'none'; frame-ancestors 'none'"); if (pathname === "/assets/style.css" || pathname === "/assets/app.js") { const isStyle = pathname.endsWith(".css"); response.setHeader("Content-Type", isStyle ? "text/css; charset=utf-8" : "text/javascript; charset=utf-8"); response.end(isStyle ? style : script); return; } if (pathname === "/favicon.ico") { response.statusCode = 204; response.end(); return; } const page = pages.get(pathname === "/" ? "/repository" : pathname); if (page) { response.statusCode = pathname === "/unavailable" ? 503 : 200; response.setHeader("Content-Type", "text/html; charset=utf-8"); response.end(page); return; } response.statusCode = 404; response.end("Fixture route not found\n"); }); await new Promise((resolve, reject) => { server.once("error", reject); server.listen(0, "127.0.0.1", () => { server.off("error", reject); resolve(); }); }); return { origin: `http://127.0.0.1:${server.address().port}`, snapshots, requests, close: () => new Promise((resolve, reject) => { server.close((error) => error ? reject(error) : resolve()); server.closeAllConnections(); }), }; }