88 lines
3.6 KiB
JavaScript
88 lines
3.6 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-revision-unit-"));
|
|
const sourcePath = path.join(repoRoot, "web/protocol/sequencer-media-revision.ts");
|
|
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, "sequencer-media-revision.mjs"), result.outputText);
|
|
const revision = await import(pathToFileURL(path.join(temporary, "sequencer-media-revision.mjs")));
|
|
|
|
const request = (operation, requestRevision, frame = 24) => ({
|
|
schemaVersion: 1,
|
|
requestId: `media:${requestRevision}`,
|
|
timelineId: "sequencer:main",
|
|
timelineRevision: 7,
|
|
requestRevision,
|
|
operation,
|
|
frame,
|
|
});
|
|
const completed = (value) => ({
|
|
...value,
|
|
status: "COMPLETED",
|
|
sourceFrame: value.frame,
|
|
payloadSha256: "a".repeat(64),
|
|
});
|
|
const state = (latestRequestRevision, timelineRevision = 7) => ({
|
|
schemaVersion: 1,
|
|
timelineId: "sequencer:main",
|
|
timelineRevision,
|
|
latestRequestRevision,
|
|
});
|
|
|
|
test("M11-11 publishes matching latest SEEK, SCRUB and DECODE results", () => {
|
|
for (const [index, operation] of ["SEEK", "SCRUB", "DECODE"].entries()) {
|
|
const active = request(operation, index + 1);
|
|
assert.deepEqual(revision.gateSequencerMediaRevision(active, state(index + 1), completed(active)), {
|
|
status: "PUBLISH",
|
|
code: null,
|
|
operation,
|
|
requestRevision: index + 1,
|
|
});
|
|
}
|
|
});
|
|
|
|
test("M11-11 marks superseded and timeline-replaced results stale", () => {
|
|
const oldSeek = request("SEEK", 3);
|
|
assert.deepEqual(revision.gateSequencerMediaRevision(oldSeek, state(4), completed(oldSeek)), {
|
|
status: "STALE", code: "REVISION_CONFLICT", operation: "SEEK", requestRevision: 3,
|
|
});
|
|
const oldDecode = request("DECODE", 5);
|
|
assert.deepEqual(revision.gateSequencerMediaRevision(oldDecode, state(6, 8), completed(oldDecode)), {
|
|
status: "STALE", code: "REVISION_CONFLICT", operation: "DECODE", requestRevision: 5,
|
|
});
|
|
});
|
|
|
|
test("M11-11 rejects result identity forgery without publishing it", () => {
|
|
const active = request("SCRUB", 9, 48);
|
|
for (const forged of [
|
|
{ ...completed(active), requestId: "media:forged" },
|
|
{ ...completed(active), timelineRevision: 8 },
|
|
{ ...completed(active), requestRevision: 10 },
|
|
{ ...completed(active), operation: "DECODE" },
|
|
{ ...completed(active), frame: 49 },
|
|
]) {
|
|
assert.equal(revision.gateSequencerMediaRevision(active, state(9), forged).status, "STALE");
|
|
}
|
|
});
|
|
|
|
test("M11-11 rejects undeclared fields, invalid operations and fractional revisions", () => {
|
|
const active = request("SEEK", 1);
|
|
assert.throws(() => revision.parseSequencerMediaRevisionRequest({ ...active, signal: "late" }), { code: "SEQUENCER_SCHEMA_INVALID" });
|
|
assert.throws(() => revision.parseSequencerMediaRevisionRequest({ ...active, operation: "PLAY" }), { code: "SEQUENCER_SCHEMA_INVALID" });
|
|
assert.throws(() => revision.parseSequencerMediaRevisionState({ ...state(1), timelineRevision: 7.5 }), { code: "SEQUENCER_SCHEMA_INVALID" });
|
|
assert.throws(() => revision.parseSequencerMediaRevisionResult({ ...completed(active), payloadSha256: "bad" }), { code: "SEQUENCER_SCHEMA_INVALID" });
|
|
});
|
|
|
|
test.after(() => fs.rmSync(temporary, { recursive: true, force: true }));
|