74 lines
2.6 KiB
TypeScript
74 lines
2.6 KiB
TypeScript
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",
|
|
}),
|
|
}));
|
|
|
|
import {
|
|
createDisplayHostAssets,
|
|
DisplayHostAssetError,
|
|
loadDisplayHostAssets,
|
|
} from "../src/display-host-assets.js";
|
|
|
|
describe("display host asset loading", () => {
|
|
beforeEach(() => readFileMock.mockReset());
|
|
|
|
it("reads only the two declared file URLs in order and freezes one consistent snapshot", async () => {
|
|
const html = '<!doctype html><div id="ekklesia-display"></div>';
|
|
const script = "export const displayHost = true;\n";
|
|
readFileMock.mockResolvedValueOnce(html).mockResolvedValueOnce(script);
|
|
|
|
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"],
|
|
]);
|
|
expect(assets).toEqual({
|
|
html,
|
|
script,
|
|
scriptEtag: `"${createHash("sha256").update(script).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 or script as an unavailable host build", () => {
|
|
for (const [html, script] of [
|
|
["", "export {};"],
|
|
["<html></html>", " \n"],
|
|
] as const) {
|
|
expect(() => createDisplayHostAssets(html, script)).toThrow(DisplayHostAssetError);
|
|
}
|
|
});
|
|
});
|