Files
workinf_Blender_Wasm/web/tests/unit/sequencer-media-cache.test.mjs
mes123456 0fe8d2bb56
Some checks are pending
M6 deployable RC / quick (push) Waiting to run
M6 deployable RC / chromium (push) Blocked by required conditions
M6 deployable RC / release (push) Blocked by required conditions
Advance M8-M11 parity workflows
2026-08-17 04:37:07 -04:00

121 lines
5.2 KiB
JavaScript

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 }));