import { createHash } from "node:crypto"; import { beforeEach, describe, expect, it, vi } from "vitest"; const readFileMock = vi.hoisted(() => vi.fn()); vi.mock("node:fs/promises", () => ({ readFile: readFileMock })); vi.mock("@ekklesia/shell/display-assets", () => ({ displayHostAssetUrls: Object.freeze({ html: "file:///surface/browser/display/index.html", script: "file:///surface/browser/display/app.js", serviceWorker: "file:///surface/browser/display/sw.js", }), })); import { createDisplayHostAssets, DisplayHostAssetError, loadDisplayHostAssets, } from "../src/display-host-assets.js"; describe("display host asset loading", () => { beforeEach(() => readFileMock.mockReset()); it("reads only the three declared file URLs in order and freezes one consistent snapshot", async () => { const html = '
'; const script = "export const displayHost = true;\n"; const serviceWorker = "export const displayWorker = true;\n"; readFileMock .mockResolvedValueOnce(html) .mockResolvedValueOnce(script) .mockResolvedValueOnce(serviceWorker); const assets = await loadDisplayHostAssets(); expect(readFileMock.mock.calls).toEqual([ [new URL("file:///surface/browser/display/index.html"), "utf8"], [new URL("file:///surface/browser/display/app.js"), "utf8"], [new URL("file:///surface/browser/display/sw.js"), "utf8"], ]); expect(assets).toEqual({ html, script, scriptEtag: `"${createHash("sha256").update(script).digest("base64url")}"`, serviceWorker, serviceWorkerEtag: `"${createHash("sha256").update(serviceWorker).digest("base64url")}"`, }); expect(Object.isFrozen(assets)).toBe(true); }); it("fails deterministically without attempting a neighboring file or exposing the read error", async () => { readFileMock.mockRejectedValueOnce( new Error("ENOENT /home/operator/private/surface-build secret-token"), ); const failure = await loadDisplayHostAssets().catch((error: unknown) => error); expect(readFileMock).toHaveBeenCalledTimes(1); expect(failure).toBeInstanceOf(DisplayHostAssetError); expect(failure).toMatchObject({ code: "DISPLAY_HOST_ASSET_UNAVAILABLE", message: "The display host browser assets are unavailable.", name: "DisplayHostAssetError", }); expect((failure as Error).stack).toBe( "DisplayHostAssetError: The display host browser assets are unavailable.", ); const publicText = `${String(failure)}\n${JSON.stringify(failure)}\n${(failure as Error).stack}`; for (const secret of ["/home/", "operator", "ENOENT", "secret-token"]) { expect(publicText).not.toContain(secret); } }); it("rejects an empty page, script, or service worker as an unavailable host build", () => { for (const [html, script, serviceWorker] of [ ["", "export {};", "export {};"], ["", " \n", "export {};"], ["", "export {};", " \n"], ] as const) { expect(() => createDisplayHostAssets(html, script, serviceWorker)).toThrow( DisplayHostAssetError, ); } }); });