import { describe, expect, it } from "@effect/vitest"; import { makeAssetsShellHtmlLoader, type ShellAssetsBinding } from "./worker"; import { MCP_APPS_SHELL_DOCUMENT_MARKER, MCP_APPS_SHELL_STABLE_ASSET_PATH, } from "./shell-asset-path"; const SHELL_HTML = `
`; const bindingReturning = ( responses: readonly Response[], ): { readonly binding: ShellAssetsBinding; readonly requests: Request[] } => { const requests: Request[] = []; let call = 0; return { requests, binding: { fetch: (request) => { requests.push(request); const response = responses[Math.min(call, responses.length - 1)]; call += 1; // Running out of scripted responses means the test was wrong about // how many fetches the loader makes; a distinctive 599 makes the // resulting assertion failure say so. return Promise.resolve( response ?? new Response("stub exhausted: unexpected fetch", { status: 599 }), ); }, }, }; }; describe("makeAssetsShellHtmlLoader", () => { it("serves the shell from the stable asset path and caches it", async () => { const { binding, requests } = bindingReturning([new Response(SHELL_HTML)]); const load = makeAssetsShellHtmlLoader({ assets: binding }); expect(await load()).toBe(SHELL_HTML); expect(await load()).toBe(SHELL_HTML); expect(requests).toHaveLength(1); expect(new URL(requests[0]?.url ?? "").pathname).toBe(MCP_APPS_SHELL_STABLE_ASSET_PATH); }); it("rejects on a non-OK response instead of serving a substitute document", async () => { const { binding } = bindingReturning([new Response("not found", { status: 404 })]); const load = makeAssetsShellHtmlLoader({ assets: binding }); await expect(load()).rejects.toThrow(/404/); await expect(load()).rejects.toThrow(MCP_APPS_SHELL_STABLE_ASSET_PATH); }); it("rejects a 200 that is not the shell (SPA not-found fallback)", async () => { // Under `not_found_handling: "single-page-application"` a miss answers // with the console's index.html and a 200 — the exact document that, if // served as the ui:// resource, hangs every client with no error. const { binding } = bindingReturning([ new Response("
console
"), ]); const load = makeAssetsShellHtmlLoader({ assets: binding }); await expect(load()).rejects.toThrow(/different document/); }); it("prefers the dev document and never touches the binding when it is present", async () => { const { binding, requests } = bindingReturning([]); const load = makeAssetsShellHtmlLoader({ assets: binding, devShellHtml: () => Promise.resolve(SHELL_HTML), }); expect(await load()).toBe(SHELL_HTML); expect(requests).toHaveLength(0); }); it("falls through to the binding when the dev document is undefined (a build)", async () => { const { binding } = bindingReturning([new Response(SHELL_HTML)]); const load = makeAssetsShellHtmlLoader({ assets: binding, devShellHtml: () => Promise.resolve(undefined), }); expect(await load()).toBe(SHELL_HTML); }); });