122 lines
5.2 KiB
TypeScript
122 lines
5.2 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 resources = new Map([
|
|
["123e4567-e89b-42d3-a456-426614174001", "aaaaaaaa"],
|
|
["123e4567-e89b-42d3-a456-426614174002", "bbbbbbbb"],
|
|
["123e4567-e89b-42d3-a456-426614174003", "cccccccc"],
|
|
]);
|
|
let vite: ViteDevServer;
|
|
let webUrl: string;
|
|
let publicFetchCount = 0;
|
|
|
|
test.beforeAll(async () => {
|
|
vite = await createServer({
|
|
configFile: false,
|
|
plugins: [{
|
|
name: "wp0-06-public-assets",
|
|
configureServer(server) {
|
|
server.middlewares.use((request, response, next) => {
|
|
const match = request.url?.match(/^\/api\/v1\/assets\/public\/[^/]+\/([0-9a-f-]+)$/i);
|
|
if (!match) return next();
|
|
const body = resources.get(match[1] ?? "");
|
|
if (!body) { response.statusCode = 404; response.end(); return; }
|
|
publicFetchCount += 1;
|
|
response.setHeader("Cache-Control", "public, max-age=31536000, immutable");
|
|
response.setHeader("Content-Type", "application/octet-stream");
|
|
response.end(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-WP0-CACHE-001-public-lru enumerates isolated browser persistence", async ({ page }) => {
|
|
publicFetchCount = 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 result = await page.evaluate(async () => {
|
|
const ids = [
|
|
"123e4567-e89b-42d3-a456-426614174001",
|
|
"123e4567-e89b-42d3-a456-426614174002",
|
|
"123e4567-e89b-42d3-a456-426614174003",
|
|
];
|
|
const descriptor = (resource_id: string, release_version: string) => ({
|
|
access_class: "public_release_asset" as const,
|
|
cache_kind: "thumbnail" as const,
|
|
release_version,
|
|
resource_id,
|
|
});
|
|
const cache = window.dadaCacheProbe.cache;
|
|
await cache.clear();
|
|
const first = await cache.cache(descriptor(ids[0]!, "asset-20260727.1"));
|
|
const second = await cache.cache(descriptor(ids[1]!, "asset-20260727.1"));
|
|
await fetch(`/api/v1/assets/public/asset-20260727.1/${ids[0]}`);
|
|
const third = await cache.cache(descriptor(ids[2]!, "asset-20260727.2"));
|
|
const rejected = [];
|
|
for (const [access_class, cache_kind] of [
|
|
["internal_preview_asset", "thumbnail"], ["private_user_asset", "thumbnail"],
|
|
["public_release_asset", "project"], ["public_release_asset", "prompt"],
|
|
["public_release_asset", "verification_code"], ["public_release_asset", "session"],
|
|
["public_release_asset", "unsaved_edit"], ["public_release_asset", "user_download"],
|
|
]) {
|
|
rejected.push(await cache.cache({ access_class, cache_kind, release_version: "asset-20260727.2", resource_id: ids[0] }));
|
|
}
|
|
const enumeration = await cache.inspect();
|
|
const registrations = await navigator.serviceWorker.getRegistrations();
|
|
return {
|
|
enumeration,
|
|
local_storage_keys: Object.keys(localStorage),
|
|
rejected,
|
|
results: [first, second, third],
|
|
service_workers: registrations.map((item) => ({ active: item.active?.state, scope: item.scope, script_url: item.active?.scriptURL })),
|
|
session_storage_keys: Object.keys(sessionStorage),
|
|
};
|
|
});
|
|
|
|
expect(result.results[2]).toMatchObject({ evicted_resource_ids: ["123e4567-e89b-42d3-a456-426614174002"], status: "cached" });
|
|
expect(result.enumeration.policy.current_public_cache_bytes).toBe(16);
|
|
expect(result.enumeration.policy.max_public_cache_bytes).toBe(20);
|
|
expect(result.enumeration.entries.map((item: { resource_id: string }) => item.resource_id).sort()).toEqual([
|
|
"123e4567-e89b-42d3-a456-426614174001",
|
|
"123e4567-e89b-42d3-a456-426614174003",
|
|
]);
|
|
expect(result.enumeration.cache_keys).toHaveLength(2);
|
|
expect(result.enumeration.indexed_db_names).toEqual(["dada-public-asset-cache-v1"]);
|
|
expect(result.rejected).toEqual(Array.from({ length: 8 }, () => ({ status: "rejected_not_allowlisted" })));
|
|
expect(result.local_storage_keys).toEqual([]);
|
|
expect(result.session_storage_keys).toEqual([]);
|
|
expect(result.service_workers).toHaveLength(1);
|
|
expect(publicFetchCount).toBe(3);
|
|
|
|
const evidenceDirectory = process.env.DADA_EVIDENCE_DIR_CACHE;
|
|
if (evidenceDirectory) {
|
|
mkdirSync(evidenceDirectory, { recursive: true });
|
|
writeFileSync(resolve(evidenceDirectory, "cache-enumeration.json"), `${JSON.stringify({
|
|
business_database_calls: 0,
|
|
public_network_fetches: publicFetchCount,
|
|
...result,
|
|
}, null, 2)}\n`);
|
|
writeFileSync(resolve(evidenceDirectory, "lru-trace.json"), `${JSON.stringify({
|
|
capacity_driver: { production_max_bytes: 157_286_400, test_max_bytes: 20 },
|
|
prewrite_eviction: true,
|
|
results: result.results,
|
|
}, null, 2)}\n`);
|
|
}
|
|
});
|