import { describe, expect, it, vi } from "vitest"; import { CACHEABLE_ASSET_CONTENT_TYPES, decodeStaticAssetManifest, } from "../../src/contracts/service-worker-static-manifest.ts"; /** * SW-RR-03. The build generator and the shared runtime decoder must agree on * exactly which asset kinds exist. A generator that emits `.mjs` or `.png` while * the decoder refuses them turns a correct build into a runtime contract * failure, and the reverse admits a kind no build produces. */ describe("SW-RR-03 one authoritative cacheable asset table", () => { it("covers every extension the generator emits", async () => { const generator = await import( "../../scripts/generate-service-worker-assets.ts" ); expect(generator).toBeDefined(); for (const extension of [ ".js", ".mjs", ".css", ".woff2", ".svg", ".png", ".webp", ]) { expect(CACHEABLE_ASSET_CONTENT_TYPES[extension]).toBeDefined(); } }); it("decodes a manifest row for every table entry", () => { const assets = Object.entries(CACHEABLE_ASSET_CONTENT_TYPES).map( ([extension, contentType], index) => ({ url: `/assets/name-abcdefgh${index}${extension}`, sha256: `sha256:${"a".repeat(64)}`, bytes: 16, contentType, }), ); const decoded = decodeStaticAssetManifest({ schemaVersion: 1, buildId: "build-1", releaseId: "release-1", assets, setDigest: `sha256:${"b".repeat(64)}`, }); expect(decoded).toMatchObject({ ok: true }); }); it("refuses a row whose extension is not in the table", () => { const decoded = decodeStaticAssetManifest({ schemaVersion: 1, buildId: "build-1", releaseId: "release-1", setDigest: `sha256:${"b".repeat(64)}`, assets: [ { url: "/assets/control-abcdefgh.json", sha256: `sha256:${"a".repeat(64)}`, bytes: 16, contentType: "application/json", }, ], }); expect(decoded.ok).toBe(false); }); });