Advance M8-M11 parity workflows
This commit is contained in:
132
web/tests/unit/sequencer-final-export.test.mjs
Normal file
132
web/tests/unit/sequencer-final-export.test.mjs
Normal file
@@ -0,0 +1,132 @@
|
||||
import assert from "node:assert/strict";
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
import test from "node:test";
|
||||
import ts from "typescript";
|
||||
|
||||
const root = path.resolve(import.meta.dirname, "../../..");
|
||||
const sourcePath = path.join(root, "web/protocol/sequencer-export.ts");
|
||||
const source = fs.readFileSync(sourcePath, "utf8").replace('import type { ErrorCode } from "./error";\n', "");
|
||||
const transpiled = ts.transpileModule(source, {
|
||||
compilerOptions: { module: ts.ModuleKind.ES2022, target: ts.ScriptTarget.ES2022 },
|
||||
fileName: sourcePath,
|
||||
reportDiagnostics: true,
|
||||
});
|
||||
assert.deepEqual(transpiled.diagnostics, []);
|
||||
const finalExport = await import("data:text/javascript;base64," + Buffer.from(transpiled.outputText).toString("base64"));
|
||||
const golden = JSON.parse(fs.readFileSync(path.join(root, "tests/golden/M11-12/sequencer-final-export.json"), "utf8"));
|
||||
|
||||
const request = (overrides = {}) => ({
|
||||
schemaVersion: 1,
|
||||
timelineId: "sequencer:scene:SequencerScene",
|
||||
timelineRevision: 1,
|
||||
sourceBlendSha256: golden.sourceBlendSha256,
|
||||
frameStart: 1,
|
||||
frameEnd: 250,
|
||||
fpsNumerator: 24000,
|
||||
fpsDenominator: 1001,
|
||||
width: 1920,
|
||||
height: 1080,
|
||||
container: "MPEG4",
|
||||
videoCodec: "H264",
|
||||
audioCodec: "AAC",
|
||||
...overrides,
|
||||
});
|
||||
|
||||
test("M11-12 requires the declared server export capability", async () => {
|
||||
const blocked = await finalExport.routeSequencerFinalExport(request(), {
|
||||
serverExportAvailable: false,
|
||||
browserVideoEncoderAvailable: false,
|
||||
});
|
||||
assert.deepEqual(blocked, golden.withoutServer);
|
||||
|
||||
const routed = await finalExport.routeSequencerFinalExport(request(), {
|
||||
serverExportAvailable: true,
|
||||
browserVideoEncoderAvailable: false,
|
||||
});
|
||||
assert.deepEqual(routed, golden.withServer);
|
||||
});
|
||||
|
||||
test("M11-12 never treats browser VideoEncoder detection as local final-export support", async () => {
|
||||
for (const serverExportAvailable of [false, true]) {
|
||||
const withoutEncoder = await finalExport.routeSequencerFinalExport(request(), {
|
||||
serverExportAvailable,
|
||||
browserVideoEncoderAvailable: false,
|
||||
});
|
||||
const withEncoder = await finalExport.routeSequencerFinalExport(request(), {
|
||||
serverExportAvailable,
|
||||
browserVideoEncoderAvailable: true,
|
||||
});
|
||||
assert.equal(withoutEncoder.route, "SERVER_EXPORT");
|
||||
assert.equal(withEncoder.route, "SERVER_EXPORT");
|
||||
assert.equal(withoutEncoder.localEncoding, "BLOCKED");
|
||||
assert.equal(withEncoder.localEncoding, "BLOCKED");
|
||||
assert.equal(withEncoder.browserVideoEncoderDetected, true);
|
||||
assert.deepEqual(
|
||||
{ ...withEncoder, browserVideoEncoderDetected: false },
|
||||
withoutEncoder,
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
test("M11-12 hashes settings and source revision into deterministic request identities", async () => {
|
||||
const baseline = await finalExport.routeSequencerFinalExport(request(), {
|
||||
serverExportAvailable: true,
|
||||
browserVideoEncoderAvailable: false,
|
||||
});
|
||||
assert.equal(baseline.settingsSha256, golden.settingsSha256);
|
||||
assert.equal(baseline.requestSha256, golden.requestSha256);
|
||||
assert.deepEqual(
|
||||
await finalExport.routeSequencerFinalExport({ ...request(), container: "MPEG4" }, {
|
||||
serverExportAvailable: true,
|
||||
browserVideoEncoderAvailable: false,
|
||||
}),
|
||||
baseline,
|
||||
);
|
||||
|
||||
const revisionDrift = await finalExport.routeSequencerFinalExport(request({ timelineRevision: 2 }), {
|
||||
serverExportAvailable: true,
|
||||
browserVideoEncoderAvailable: false,
|
||||
});
|
||||
assert.equal(revisionDrift.settingsSha256, baseline.settingsSha256);
|
||||
assert.notEqual(revisionDrift.requestSha256, baseline.requestSha256);
|
||||
|
||||
const settingsDrift = await finalExport.routeSequencerFinalExport(request({ width: 1280 }), {
|
||||
serverExportAvailable: true,
|
||||
browserVideoEncoderAvailable: false,
|
||||
});
|
||||
assert.notEqual(settingsDrift.settingsSha256, baseline.settingsSha256);
|
||||
assert.notEqual(settingsDrift.requestSha256, baseline.requestSha256);
|
||||
});
|
||||
|
||||
test("M11-12 rejects undeclared, malformed, and over-budget export requests", async () => {
|
||||
await assert.rejects(
|
||||
finalExport.routeSequencerFinalExport({ ...request(), localEncoding: "READY" }, {
|
||||
serverExportAvailable: true,
|
||||
browserVideoEncoderAvailable: true,
|
||||
}),
|
||||
{ code: "SEQUENCER_EXPORT_REQUEST_INVALID" },
|
||||
);
|
||||
await assert.rejects(
|
||||
finalExport.routeSequencerFinalExport(request({ container: "WEBM", videoCodec: "H264" }), {
|
||||
serverExportAvailable: true,
|
||||
browserVideoEncoderAvailable: true,
|
||||
}),
|
||||
{ code: "SEQUENCER_EXPORT_REQUEST_INVALID" },
|
||||
);
|
||||
await assert.rejects(
|
||||
finalExport.routeSequencerFinalExport(request({ frameStart: -1_000_000, frameEnd: 1_000_000 }), {
|
||||
serverExportAvailable: true,
|
||||
browserVideoEncoderAvailable: true,
|
||||
}),
|
||||
{ code: "SEQUENCER_EXPORT_REQUEST_INVALID" },
|
||||
);
|
||||
await assert.rejects(
|
||||
finalExport.routeSequencerFinalExport(request(), {
|
||||
serverExportAvailable: true,
|
||||
browserVideoEncoderAvailable: true,
|
||||
codecName: "h264",
|
||||
}),
|
||||
{ code: "SEQUENCER_EXPORT_REQUEST_INVALID" },
|
||||
);
|
||||
});
|
||||
Reference in New Issue
Block a user