69 lines
3.8 KiB
JavaScript
69 lines
3.8 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-codec-probe-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"'],
|
|
]);
|
|
const sequencer = await import(pathToFileURL(path.join(temporary, "sequencer.mjs")));
|
|
|
|
const requests = [
|
|
{ schemaVersion: 1, stripType: "IMAGE", mimeType: "image/png", byteLength: 261, sourceSha256: "a".repeat(64) },
|
|
{ schemaVersion: 1, stripType: "SOUND", mimeType: "audio/wav", byteLength: 16_044, sourceSha256: "b".repeat(64) },
|
|
{ schemaVersion: 1, stripType: "MOVIE", mimeType: "video/mp4", byteLength: 1_484, sourceSha256: "c".repeat(64) },
|
|
];
|
|
const ready = [
|
|
{ backend: "IMAGE_BITMAP", decoded: { width: 8, height: 8 } },
|
|
{ backend: "WEB_AUDIO", decoded: { sampleRate: 8_000, channels: 1, durationFrames: 8_000 } },
|
|
{ backend: "HTML_MEDIA", decoded: { width: 16, height: 16, durationMicros: 1_000_000 } },
|
|
];
|
|
|
|
test("M11-09 gates IMAGE, SOUND and MOVIE only with identity-bound ready probe receipts", () => {
|
|
for (const [index, request] of requests.entries()) {
|
|
assert.deepEqual(sequencer.parseSequencerCodecProbeRequest(request), request);
|
|
const result = { ...request, status: "READY", backend: ready[index].backend, reason: null, decoded: ready[index].decoded };
|
|
assert.equal(sequencer.gateSequencerCodec(request, result).status, "READY");
|
|
}
|
|
});
|
|
|
|
test("M11-09 rejects extension fields, family mismatch, forged backend and source drift", () => {
|
|
assert.throws(() => sequencer.parseSequencerCodecProbeRequest({ ...requests[0], sourcePath: "image.mp4" }), { code: "SEQUENCER_SCHEMA_INVALID" });
|
|
assert.throws(() => sequencer.parseSequencerCodecProbeRequest({ ...requests[0], mimeType: "video/mp4" }), { code: "SEQUENCER_SCHEMA_INVALID" });
|
|
const forged = { ...requests[0], status: "READY", backend: "HTML_MEDIA", reason: null, decoded: { width: 8, height: 8 } };
|
|
assert.deepEqual(sequencer.gateSequencerCodec(requests[0], forged).issues.map((issue) => issue.code), ["SEQUENCER_CODEC_UNSUPPORTED"]);
|
|
const drift = { ...requests[0], sourceSha256: "d".repeat(64), status: "READY", backend: "IMAGE_BITMAP", reason: null, decoded: { width: 8, height: 8 } };
|
|
assert.deepEqual(sequencer.gateSequencerCodec(requests[0], drift).issues.map((issue) => issue.code), ["SEQUENCER_CODEC_UNSUPPORTED"]);
|
|
});
|
|
|
|
test("M11-09 keeps runtime unavailability and decode failure blocked", () => {
|
|
for (const [index, request] of requests.entries()) {
|
|
const result = { ...request, status: "BLOCKED", backend: ready[index].backend, reason: "DECODE_FAILED", decoded: null };
|
|
const gate = sequencer.gateSequencerCodec(request, result);
|
|
assert.equal(gate.status, "BLOCKED");
|
|
assert.deepEqual(gate.issues.map((issue) => issue.code), ["SEQUENCER_CODEC_UNSUPPORTED"]);
|
|
}
|
|
});
|
|
|
|
test.after(() => fs.rmSync(temporary, { recursive: true, force: true }));
|