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();
}),
};
}