import { randomUUID } from "node:crypto"; import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs"; import { tmpdir } from "node:os"; import { join, resolve } from "node:path"; import { afterEach, describe, expect, it } from "vitest"; import { createApp } from "../../apps/api/src/app.js"; import { RegistrationService } from "../../apps/api/src/registration.js"; import { MockResendAdapter } from "../../apps/api/src/resend-adapter.js"; import { createAssetReleaseManifest } from "../../packages/asset-release-manifest/src/index.js"; const now = Date.parse("2026-08-03T10:00:00.000Z"); const releaseVersion = "asset-20260803.1"; const publicId = "7f0c9530-a7d9-4bf1-8c65-0e9298dd04ac"; const previewId = "ab18fd72-60e1-44e3-a9a0-3dfccb12e17c"; const ungrantedPreviewId = "d8fe890d-6df4-46a9-a578-96c1f8361ac0"; const privateId = "e3792605-5252-4d3b-a101-827408ab3515"; const headers = { host: "127.0.0.1:43121", origin: "http://127.0.0.1:43121" }; const roots: string[] = []; const registrations: RegistrationService[] = []; function addUser(registration: RegistrationService, role: "super_admin" | "user") { const userId = randomUUID(); registration.database.prepare(`INSERT INTO users ( user_id, normalized_email, role, status, counts_toward_stage_limit, registration_id, created_at ) VALUES (?, ?, ?, 'active', ?, ?, ?)`).run( userId, `${role}-${userId}@example.invalid`, role, role === "user" ? 1 : 0, randomUUID(), now, ); if (role === "user") { registration.database.prepare("INSERT INTO user_profiles (user_id, creator_name, social_id) VALUES (?, 'Asset User', '@asset_user')").run(userId); registration.database.prepare("INSERT INTO credit_accounts (user_id, available_balance, reserved_balance, updated_at) VALUES (?, 10, 0, ?)").run(userId, now); } else { registration.database.prepare("INSERT INTO admin_access (user_id, allowed) VALUES (?, 1)").run(userId); } return { session: registration.issueAuthenticatedSession(userId, role === "user" ? "user" : "admin"), userId }; } function evidence(name: string, value: unknown) { const directory = process.env.DADA_EVIDENCE_DIR_WP5_RES; if (!directory) return; mkdirSync(directory, { recursive: true }); writeFileSync(resolve(directory, name), `${JSON.stringify(value, null, 2)}\n`); } function harness() { const root = mkdtempSync(join(tmpdir(), "dada-wp5-04-api-")); roots.push(root); const registration = new RegistrationService({ challengePepper: Buffer.alloc(32, 0x31), clock: () => now, currentPrivacyNoticeVersion: "p0a-registration-notice-v1", databasePath: join(root, "dada.sqlite3"), invitePepper: Buffer.alloc(32, 0x32), resend: new MockResendAdapter(), sessionPepper: Buffer.alloc(32, 0x33), }); registrations.push(registration); const owner = addUser(registration, "user"); const intruder = addUser(registration, "user"); const admin = addUser(registration, "super_admin"); const assetReleases = createAssetReleaseManifest({ items: [ { access_class: "public_release_asset", cache_kind: "thumbnail", content: Buffer.from("public"), mime_type: "image/png", relative_path: "public/FLOWER001.png", resource_id: publicId, root_ref: "canonical-assets" }, { access_class: "internal_preview_asset", content: Buffer.from("preview"), mime_type: "image/webp", relative_path: "preview/FLOWER009.webp", resource_id: previewId, root_ref: "canonical-assets" }, { access_class: "internal_preview_asset", content: Buffer.from("ungranted-preview"), mime_type: "image/webp", relative_path: "preview/FLOWER010.webp", resource_id: ungrantedPreviewId, root_ref: "canonical-assets" }, { access_class: "private_user_asset", content: Buffer.from("private"), mime_type: "image/png", owner_id: owner.userId, relative_path: "private/generated.png", resource_id: privateId, root_ref: "managed-assets" }, ], release_version: releaseVersion, }); let previewGranted = true; let previewChecks = 0; const appPromise = createApp({ assetReleases, browserGate: false, networkBoundary: { allowTestPort: true }, previewAssetAuthorizer: ({ resourceId, userId }) => { previewChecks += 1; return previewGranted && userId === owner.userId && resourceId === previewId; }, privateAssetAdminAuthorizer: ({ adminUserId, ownerId }) => adminUserId === admin.userId && ownerId === owner.userId, registration, }); return { admin, appPromise, intruder, owner, previewChecks: () => previewChecks, revokePreview: () => { previewGranted = false; } }; } afterEach(() => { for (const registration of registrations.splice(0)) registration.close(); for (const root of roots.splice(0)) rmSync(root, { force: true, recursive: true }); }); describe("TDD-WP5-RES-001 three access classes", () => { it("separates route projections, per-request authorization, and cache headers", async () => { const test = harness(); const app = await test.appPromise; const ownerCookie = `dada_session=${test.owner.session.sessionToken}`; const intruderCookie = `dada_session=${test.intruder.session.sessionToken}`; const adminCookie = `dada_admin_session=${test.admin.session.sessionToken}`; const publicManifest = await app.inject({ headers, method: "GET", url: `/api/v1/assets/public/${releaseVersion}/manifest` }); const publicAsset = await app.inject({ headers, method: "GET", url: `/api/v1/assets/public/${releaseVersion}/${publicId}` }); const previewManifest = await app.inject({ headers: { ...headers, cookie: ownerCookie }, method: "GET", url: `/api/v1/assets/preview/${releaseVersion}/manifest` }); const previewAsset = await app.inject({ headers: { ...headers, cookie: ownerCookie }, method: "GET", url: `/api/v1/assets/preview/${releaseVersion}/${previewId}` }); const privateManifest = await app.inject({ headers: { ...headers, cookie: ownerCookie }, method: "GET", url: `/api/v1/private-assets/${releaseVersion}/manifest` }); const privateAsset = await app.inject({ headers: { ...headers, cookie: ownerCookie }, method: "GET", url: `/api/v1/private-assets/${releaseVersion}/${privateId}` }); const deniedPrivate = await app.inject({ headers: { ...headers, cookie: intruderCookie }, method: "GET", url: `/api/v1/private-assets/${releaseVersion}/${privateId}` }); const controlledAdmin = await app.inject({ headers: { ...headers, cookie: adminCookie }, method: "GET", url: `/api/v1/private-assets/${releaseVersion}/${privateId}` }); expect(publicManifest.statusCode).toBe(200); expect(publicManifest.headers["cache-control"]).toBe("public, max-age=31536000, immutable"); expect(publicManifest.json().items).toEqual([expect.objectContaining({ access_class: "public_release_asset", resource_id: publicId })]); expect(JSON.stringify(publicManifest.json())).not.toMatch(/preview|private|relative_path|root_ref|[A-Z]:\\\\/i); expect(publicAsset.statusCode).toBe(200); expect(publicAsset.rawPayload).toEqual(Buffer.from("public")); expect(publicAsset.headers["cache-control"]).toBe("public, max-age=31536000, immutable"); expect(previewManifest.statusCode).toBe(200); expect(previewManifest.headers["cache-control"]).toBe("private, no-store"); expect(previewManifest.json().items).toEqual([expect.objectContaining({ resource_id: previewId })]); expect(JSON.stringify(previewManifest.json())).not.toContain(ungrantedPreviewId); expect(previewAsset.statusCode).toBe(200); expect(previewAsset.headers["cache-control"]).toBe("private, no-store"); expect(privateManifest.json().items).toEqual([expect.objectContaining({ access_class: "private_user_asset", resource_id: privateId })]); expect(privateAsset.rawPayload).toEqual(Buffer.from("private")); expect(privateAsset.headers["cache-control"]).toBe("private, no-store"); expect(deniedPrivate.statusCode).toBe(404); expect(controlledAdmin.statusCode).toBe(200); const publicGuess = await app.inject({ headers, method: "GET", url: `/api/v1/assets/public/${releaseVersion}/${privateId}` }); expect(publicGuess.statusCode).toBe(404); test.revokePreview(); const revoked = await app.inject({ headers: { ...headers, cookie: ownerCookie }, method: "GET", url: `/api/v1/assets/preview/${releaseVersion}/${previewId}` }); expect(revoked.statusCode).toBe(404); expect(test.previewChecks()).toBe(4); evidence("response.json", { controlled_admin_status: controlledAdmin.statusCode, private_intruder_status: deniedPrivate.statusCode, public_guess_status: publicGuess.statusCode, revoked_preview_status: revoked.statusCode, }); evidence("headers.json", { private: privateAsset.headers["cache-control"], preview: previewAsset.headers["cache-control"], public: publicAsset.headers["cache-control"], }); await app.close(); }); });