import { expect, test } from "@playwright/test"; import crypto from "node:crypto"; import fs from "node:fs"; import path from "node:path"; const root = path.resolve(import.meta.dirname, "../../.."); const manifest = JSON.parse(fs.readFileSync(path.join(root, "tests/golden/M11-14/render-compositor-media-recovery.json"), "utf8")) as { domains: Array<{ domain: "RENDER" | "COMPOSITOR" | "MEDIA"; fixture: string; sha256: string; cancellationCode: string; budgetCode: string; }>; textureFixture: { path: string; sha256: string }; }; const fixtures = Object.fromEntries(manifest.domains.map((item) => [item.domain, fs.readFileSync(path.join(root, item.fixture))])); const texture = fs.readFileSync(path.join(root, manifest.textureFixture.path)); const sha256 = (value: Buffer) => crypto.createHash("sha256").update(value).digest("hex"); test("M11-14 recovers real render, compositor and media production paths after lifecycle faults", async ({ page }) => { test.setTimeout(120_000); for (const item of manifest.domains) expect(sha256(fixtures[item.domain])).toBe(item.sha256); expect(sha256(texture)).toBe(manifest.textureFixture.sha256); await page.goto("/"); const reports = await page.evaluate(async ({ renderBlend, compositorBlend, movie, texturePng }) => { const { runRenderCompositorMediaRecoverySuite } = await import("/src/testing/render-compositor-media-recovery.ts"); return runRenderCompositorMediaRecoverySuite({ renderBlend: Uint8Array.from(renderBlend).buffer, compositorBlend: Uint8Array.from(compositorBlend).buffer, movie: { mimeType: "video/mp4", data: Uint8Array.from(movie).buffer }, texturePng: Uint8Array.from(texturePng).buffer, }); }, { renderBlend: Array.from(fixtures.RENDER), compositorBlend: Array.from(fixtures.COMPOSITOR), movie: Array.from(fixtures.MEDIA), texturePng: Array.from(texture), }); expect(reports.map((report) => report.domain)).toEqual(["RENDER", "COMPOSITOR", "MEDIA"]); for (const [index, report] of reports.entries()) { const expected = manifest.domains[index]; expect(report.source.sha256).toBe(expected.sha256); expect(report.cancellation).toEqual({ status: "CANCELLED", code: expected.cancellationCode, publishedResults: 0, temporaryResourcesAfter: 0, }); expect(report.restart).toMatchObject({ status: "RECOVERED", generationBefore: 1, generationAfter: 2 }); expect(report.restart.identityAfter).toBe(report.restart.identityBefore); expect(report.restart.outputSha256After).toBe(report.restart.outputSha256Before); expect(report.budget).toEqual({ status: "BLOCKED", code: expected.budgetCode, retainedIdentityHash: report.restart.identityAfter, temporaryResourcesAfter: 0, }); expect(report.release.status).toBe("RELEASED"); expect(report.release.releasedBytes).toBeGreaterThan(0); expect(report.release.releasedResources).toBeGreaterThan(0); expect(report.release.resourcesAfter).toBe(0); expect(report.recovery).toMatchObject({ status: "RECOVERED", identityHash: report.restart.identityAfter, outputSha256: report.restart.outputSha256After, }); expect(report.recovery.outputBytes).toBeGreaterThan(0); expect(report.recovery.visibleUnits).toBeGreaterThan(0); } });