Advance M8-M11 parity workflows
This commit is contained in:
120
web/tests/unit/sequencer-media-cache.test.mjs
Normal file
120
web/tests/unit/sequencer-media-cache.test.mjs
Normal file
@@ -0,0 +1,120 @@
|
||||
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 }));
|
||||
Reference in New Issue
Block a user