import { mkdirSync, writeFileSync } from "node:fs"; import { resolve } from "node:path"; import { expect, test } from "@playwright/test"; import { createServer, type ViteDevServer } from "vite"; const releaseVersion = "asset-20260803.1"; const publicId = "7f0c9530-a7d9-4bf1-8c65-0e9298dd04ac"; const previewId = "ab18fd72-60e1-44e3-a9a0-3dfccb12e17c"; const privateId = "e3792605-5252-4d3b-a101-827408ab3515"; let vite: ViteDevServer; let webUrl: string; const requestCounts = { preview: 0, private: 0, public: 0 }; function writeEvidence(directory: string | undefined, name: string, value: unknown) { if (!directory) return; mkdirSync(directory, { recursive: true }); writeFileSync(resolve(directory, name), `${JSON.stringify(value, null, 2)}\n`); } test.beforeAll(async () => { vite = await createServer({ configFile: false, plugins: [{ name: "wp5-04-three-resource-classes", configureServer(server) { server.middlewares.use((request, response, next) => { const routes = [ { access: "public", body: "public-content", id: publicId, prefix: "/api/v1/assets/public/" }, { access: "preview", body: "preview-content", id: previewId, prefix: "/api/v1/assets/preview/" }, { access: "private", body: "private-content", id: privateId, prefix: "/api/v1/private-assets/" }, ] as const; const route = routes.find((item) => request.url === `${item.prefix}${releaseVersion}/${item.id}`); if (!route) return next(); requestCounts[route.access] += 1; response.statusCode = 200; response.setHeader("Cache-Control", route.access === "public" ? "public, max-age=31536000, immutable" : "private, no-store"); response.setHeader("Content-Type", "application/octet-stream"); response.end(route.body); }); }, }], publicDir: resolve("apps/web/public"), root: process.cwd(), server: { host: "127.0.0.1", port: 0 }, }); await vite.listen(); const address = vite.httpServer?.address(); if (!address || typeof address === "string") throw new Error("Vite did not expose a test port."); webUrl = `http://127.0.0.1:${address.port}`; }); test.afterAll(async () => vite.close()); test("TDD-WP5-04 enumerates no preview or private client state", async ({ context, page }) => { requestCounts.preview = 0; requestCounts.private = 0; requestCounts.public = 0; await page.goto(`${webUrl}/tests/e2e/fixtures/public-asset-cache.html`); await expect(page.locator("#status")).toHaveText("ready"); await page.reload(); await expect(page.locator("#status")).toHaveText("ready"); const online = await page.evaluate(async ({ privateId, previewId, publicId, releaseVersion }) => { const cache = window.dadaCacheProbe.cache; await cache.clear(); const cached = await cache.cache({ access_class: "public_release_asset", cache_kind: "thumbnail", release_version: releaseVersion, resource_id: publicId, }); const rejected = await Promise.all([ cache.cache({ access_class: "internal_preview_asset", cache_kind: "thumbnail", release_version: releaseVersion, resource_id: previewId }), cache.cache({ access_class: "private_user_asset", cache_kind: "thumbnail", release_version: releaseVersion, resource_id: privateId }), ]); const preview = await fetch(`/api/v1/assets/preview/${releaseVersion}/${previewId}`); const privateAsset = await fetch(`/api/v1/private-assets/${releaseVersion}/${privateId}`); const inspection = await cache.inspect(); const registrations = await navigator.serviceWorker.getRegistrations(); const databases = await indexedDB.databases(); return { cached, inspection, private_bytes: (await privateAsset.arrayBuffer()).byteLength, private_cache_control: privateAsset.headers.get("cache-control"), preview_bytes: (await preview.arrayBuffer()).byteLength, preview_cache_control: preview.headers.get("cache-control"), rejected, service_workers: registrations.map((registration) => ({ active: registration.active?.state, scope: registration.scope, script_url: registration.active?.scriptURL, })), indexed_db_names: databases.map((database) => database.name).filter(Boolean).sort(), local_storage_keys: Object.keys(localStorage), session_storage_keys: Object.keys(sessionStorage), }; }, { privateId, previewId, publicId, releaseVersion }); await context.setOffline(true); const offline = await page.evaluate(async ({ privateId, previewId, publicId, releaseVersion }) => { const read = async (url: string) => { try { const response = await fetch(url); return { body: await response.text(), status: response.status }; } catch { return { body: null, status: "network_error" }; } }; return { preview: await read(`/api/v1/assets/preview/${releaseVersion}/${previewId}`), private: await read(`/api/v1/private-assets/${releaseVersion}/${privateId}`), public: await read(`/api/v1/assets/public/${releaseVersion}/${publicId}`), }; }, { privateId, previewId, publicId, releaseVersion }); await context.setOffline(false); expect(online.cached.status).toBe("cached"); expect(online.rejected).toEqual([ { status: "rejected_not_allowlisted" }, { status: "rejected_not_allowlisted" }, ]); expect(online.preview_cache_control).toBe("private, no-store"); expect(online.private_cache_control).toBe("private, no-store"); expect(online.inspection.cache_keys).toHaveLength(1); expect(online.inspection.cache_names).toEqual(["dada-public-assets-v1"]); expect(online.inspection.entries).toEqual([expect.objectContaining({ resource_id: publicId })]); expect(JSON.stringify(online.inspection)).not.toContain(previewId); expect(JSON.stringify(online.inspection)).not.toContain(privateId); expect(online.indexed_db_names).toEqual(["dada-public-asset-cache-v1"]); expect(online.local_storage_keys).toEqual([]); expect(online.session_storage_keys).toEqual([]); expect(online.service_workers).toHaveLength(1); expect(offline.public).toEqual({ body: "public-content", status: 200 }); expect(offline.preview.status).toBe("network_error"); expect(offline.private.status).toBe("network_error"); expect(requestCounts).toEqual({ preview: 1, private: 1, public: 1 }); const cacheEnumeration = { business_database_calls: 0, offline, online, origin_request_counts: { ...requestCounts }, }; writeEvidence(process.env.DADA_EVIDENCE_DIR_WP5_CACHE, "cache-enumeration.json", cacheEnumeration); writeEvidence(process.env.DADA_EVIDENCE_DIR_WP5_CACHE, "service-worker.json", { registrations: online.service_workers }); writeEvidence(process.env.DADA_EVIDENCE_DIR_WP5_RES, "cache-enumeration.json", cacheEnumeration); });