import assert from "node:assert/strict"; import fs from "node:fs"; import os from "node:os"; import path from "node:path"; import { pathToFileURL } from "node:url"; import test from "node:test"; import ts from "typescript"; const repoRoot = path.resolve(import.meta.dirname, "../../.."); const temporary = fs.mkdtempSync(path.join(os.tmpdir(), "sequencer-media-cache-unit-")); function transpile(sourceName, outputName, replacements = []) { const sourcePath = path.join(repoRoot, "web/protocol", sourceName); const result = ts.transpileModule(fs.readFileSync(sourcePath, "utf8"), { compilerOptions: { module: ts.ModuleKind.ES2022, target: ts.ScriptTarget.ES2022 }, fileName: sourcePath, reportDiagnostics: true, }); assert.deepEqual(result.diagnostics, []); fs.writeFileSync(path.join(temporary, outputName), replacements.reduce((value, [from, to]) => value.replaceAll(from, to), result.outputText)); } transpile("capability-gates.ts", "capability-gates.mjs"); transpile("asset-path.ts", "asset-path.mjs"); transpile("sequencer.ts", "sequencer.mjs", [ ['from "./capability-gates"', 'from "./capability-gates.mjs"'], ['from "./asset-path"', 'from "./asset-path.mjs"'], ]); transpile("sequencer-media-cache.ts", "sequencer-media-cache.mjs", [ ['from "./sequencer"', 'from "./sequencer.mjs"'], ]); const mediaCache = await import(pathToFileURL(path.join(temporary, "sequencer-media-cache.mjs"))); const source = { schemaVersion: 1, stripType: "MOVIE", mimeType: "video/mp4", byteLength: 1_484, sourceSha256: "a".repeat(64), }; const capability = { ...source, status: "READY", backend: "HTML_MEDIA", reason: null, decoded: { width: 16, height: 16, durationMicros: 1_000_000 }, }; const profile = { kind: "MOVIE_RGBA8_FRAME", width: 2, height: 2, colorSpace: "SRGB8", alphaMode: "STRAIGHT", }; const payload = new Uint8Array([ 1, 2, 3, 255, 4, 5, 6, 255, 7, 8, 9, 255, 10, 11, 12, 255, ]).buffer; test("M11-10 binds proxy identity to source, decode receipt, profile and source frame", async () => { const manifest = await mediaCache.createSequencerMediaCacheManifest(source, capability, profile, 12, payload); assert.equal(manifest.payloadByteLength, 16); assert.match(manifest.identitySha256, /^[a-f0-9]{64}$/); assert.match(manifest.payloadSha256, /^[a-f0-9]{64}$/); assert.equal(mediaCache.sequencerMediaCacheKey(manifest), `sequencer-media-cache:v1:${manifest.identitySha256}`); assert.deepEqual(await mediaCache.verifySequencerMediaCacheEntry(manifest, payload, source, capability), manifest); const reordered = { ...capability, decoded: { durationMicros: 1_000_000, height: 16, width: 16 } }; assert.equal( await mediaCache.computeSequencerMediaCacheIdentity(source, capability, profile, 12), await mediaCache.computeSequencerMediaCacheIdentity(source, reordered, profile, 12), ); assert.notEqual( await mediaCache.computeSequencerMediaCacheIdentity(source, capability, profile, 12), await mediaCache.computeSequencerMediaCacheIdentity(source, capability, profile, 13), ); }); test("M11-10 rejects source, decode capability, identity and payload drift independently", async () => { const manifest = await mediaCache.createSequencerMediaCacheManifest(source, capability, profile, 0, payload); const changedSource = { ...source, sourceSha256: "b".repeat(64) }; const changedSourceCapability = { ...capability, sourceSha256: changedSource.sourceSha256 }; await assert.rejects( mediaCache.verifySequencerMediaCacheEntry(manifest, payload, changedSource, changedSourceCapability), { code: "SEQUENCER_CACHE_SOURCE_MISMATCH" }, ); const changedCapability = { ...capability, decoded: { ...capability.decoded, durationMicros: 999_999 } }; await assert.rejects( mediaCache.verifySequencerMediaCacheEntry(manifest, payload, source, changedCapability), { code: "SEQUENCER_CACHE_CAPABILITY_MISMATCH" }, ); await assert.rejects( mediaCache.verifySequencerMediaCacheEntry({ ...manifest, identitySha256: "c".repeat(64) }, payload, source, capability), { code: "SEQUENCER_CACHE_IDENTITY_MISMATCH" }, ); const corrupted = payload.slice(0); new Uint8Array(corrupted)[0] ^= 0xff; await assert.rejects( mediaCache.verifySequencerMediaCacheEntry(manifest, corrupted, source, capability), { code: "SEQUENCER_CACHE_HASH_MISMATCH" }, ); }); test("M11-10 keeps blocked receipts, extension fields and oversized profiles out of cache", async () => { await assert.rejects( mediaCache.createSequencerMediaCacheManifest(source, { ...capability, status: "BLOCKED", backend: null, reason: "RUNTIME_UNAVAILABLE", decoded: null }, profile, 0, payload), { code: "SEQUENCER_CODEC_UNSUPPORTED" }, ); assert.throws(() => mediaCache.parseSequencerMediaProxyProfile({ ...profile, codec: "h264" }), { code: "SEQUENCER_SCHEMA_INVALID" }); assert.throws( () => mediaCache.parseSequencerMediaProxyProfile({ ...profile, width: 4096, height: 4097 }), { code: "SEQUENCER_BUDGET_EXCEEDED" }, ); await assert.rejects( mediaCache.createSequencerMediaCacheManifest(source, capability, { ...profile, width: 17 }, 0, new ArrayBuffer(17 * 2 * 4)), { code: "SEQUENCER_SCHEMA_INVALID" }, ); }); test.after(() => fs.rmSync(temporary, { recursive: true, force: true }));