150 lines
6.7 KiB
TypeScript
150 lines
6.7 KiB
TypeScript
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);
|
|
});
|