import { describe, expect, it, vi } from "vitest"; import { bootstrapDisplayHost, loadModuleDisplay, moduleDisplayDescriptorPath, parseDisplayRoute, parseHostModuleDisplay, } from "../src/host-protocol.js"; const apiBaseUrl = "https://ekklesia.example.test"; function displayPayload(overrides: Record = {}): { readonly module: Record; } { return { module: { api: { baseUrl: "/api/v1/modules/announcements" }, display: { url: "/api/v1/module-ui/announcements/1.2.0/display" }, id: "announcements", permissions: ["announcements.slideshow.view"], realtime: { baseUrl: "/api/v1/module-realtime/announcements" }, version: "1.2.0", ...overrides, }, }; } function json(body: unknown, status = 200): Response { return new Response(JSON.stringify(body), { headers: { "content-type": "application/json" }, status, }); } describe("display host protocol — strict descriptor", () => { it("parses the exact display role into the generic mount descriptor", () => { const parsed = parseHostModuleDisplay(displayPayload()); expect(parsed).toEqual({ module: { apiBaseUrl: "/api/v1/modules/announcements", id: "announcements", permissions: ["announcements.slideshow.view"], realtimeBaseUrl: "/api/v1/module-realtime/announcements", uiUrl: "/api/v1/module-ui/announcements/1.2.0/display", version: "1.2.0", }, }); expect(Object.isFrozen(parsed)).toBe(true); expect(Object.isFrozen(parsed.module)).toBe(true); expect(Object.isFrozen(parsed.module.permissions)).toBe(true); }); it("rejects malformed identities, shapes, permissions, and any non-root-relative URL", () => { const invalid: unknown[] = [ {}, { ...displayPayload(), extra: true }, displayPayload({ id: "Announcements" }), displayPayload({ id: "announcements/other" }), displayPayload({ id: "a".repeat(65) }), displayPayload({ version: "v1.2.0" }), displayPayload({ version: "01.2.0" }), displayPayload({ permissions: "announcements.slideshow.view" }), displayPayload({ permissions: [""] }), displayPayload({ permissions: ["announcements.slideshow.view", "announcements.slideshow.view"], }), displayPayload({ api: { baseUrl: "https://evil.example.test/api" } }), displayPayload({ display: { url: "//evil.example.test/display.js" } }), displayPayload({ display: { url: "/display.js?token=secret" } }), displayPayload({ display: { url: "/api/%2e%2e/grants" } }), displayPayload({ display: { url: "/api/%2Fgrants" } }), displayPayload({ display: { url: "/api//display.js" } }), displayPayload({ realtime: { baseUrl: "/\\evil.example.test/live" } }), displayPayload({ display: { url: "/display.js", extra: true } }), ]; for (const payload of invalid) { expect(() => parseHostModuleDisplay(payload), JSON.stringify(payload)).toThrowError( expect.objectContaining({ code: "HOST_RESPONSE_INVALID" }), ); } }); it("does not accept a Dashboard artifact in place of a display artifact", () => { const module = (displayPayload() as { module: Record }).module; const { display: _display, ...withoutDisplay } = module; expect(() => parseHostModuleDisplay({ module: { ...withoutDisplay, ui: { url: "/api/v1/module-ui/announcements/1.2.0" }, }, }), ).toThrowError(expect.objectContaining({ code: "HOST_RESPONSE_INVALID" })); }); }); describe("display host protocol — outer route", () => { it("accepts one exact stable version-independent module route", () => { expect(parseDisplayRoute("/display/announcements")).toBe("announcements"); expect(moduleDisplayDescriptorPath("announcements")).toBe( "/api/v1/module-displays/announcements", ); }); it("rejects extra segments, trailing slashes, malformed escapes, and path escapes", () => { for (const pathname of [ "/display", "/display/", "/display/announcements/", "/display/announcements/settings", "/display/%2fapi", "/display/..", "/display/%", "/modules/announcements/display", ]) { expect(() => parseDisplayRoute(pathname), pathname).toThrowError( expect.objectContaining({ code: "DISPLAY_ROUTE_INVALID" }), ); } }); }); describe("display host protocol — authenticated bootstrap", () => { it("loads principal first, then the exact descriptor, with host-owned credentials", async () => { const calls: Array<{ init: RequestInit | undefined; url: string }> = []; const fetch = vi.fn(async (input: RequestInfo | URL, init?: RequestInit) => { const url = String(input); calls.push({ init, url }); return url.endsWith("/api/v1/principal") ? json({ kind: "authenticated", userId: "user-1" }) : json(displayPayload()); }) as unknown as typeof globalThis.fetch; const bootstrap = await bootstrapDisplayHost({ apiBaseUrl, fetch }, "announcements"); expect(bootstrap.status).toBe("ready"); expect(calls.map((call) => call.url)).toEqual([ `${apiBaseUrl}/api/v1/principal`, `${apiBaseUrl}/api/v1/module-displays/announcements`, ]); expect(calls.every((call) => call.init?.credentials === "include")).toBe(true); }); it("does not probe a display descriptor for an anonymous principal", async () => { const fetch = vi.fn(async () => json({ kind: "anonymous" }), ) as unknown as typeof globalThis.fetch; const bootstrap = await bootstrapDisplayHost({ apiBaseUrl, fetch }, "announcements"); expect(bootstrap).toEqual({ principal: { kind: "anonymous" }, status: "signed-out" }); expect(fetch).toHaveBeenCalledTimes(1); }); it("distinguishes denied, unavailable, unauthenticated, and infrastructure failures safely", async () => { for (const [status, code] of [ [401, "HOST_UNAUTHENTICATED"], [403, "DISPLAY_ACCESS_DENIED"], [404, "DISPLAY_NOT_AVAILABLE"], [503, "HOST_REQUEST_FAILED"], ] as const) { const fetch = vi.fn(async () => json({ detail: "/srv/modules/announcements secret-token" }, status), ) as unknown as typeof globalThis.fetch; const error = await loadModuleDisplay({ apiBaseUrl, fetch }, "announcements").catch( (caught: unknown) => caught, ); expect(error).toMatchObject({ code }); expect((error as Error).message).not.toContain("/srv/"); expect((error as Error).message).not.toContain("secret-token"); } }); it("rejects a stale or misrouted descriptor whose identity differs from the requested display", async () => { const fetch = vi.fn(async () => json(displayPayload({ id: "calendar" })), ) as unknown as typeof globalThis.fetch; await expect(loadModuleDisplay({ apiBaseUrl, fetch }, "announcements")).rejects.toMatchObject({ code: "HOST_RESPONSE_INVALID", }); }); });