feat: harden isolated Tornado test workflow
This commit is contained in:
77
tests/Web/playout-safety.test.cjs
Normal file
77
tests/Web/playout-safety.test.cjs
Normal file
@@ -0,0 +1,77 @@
|
||||
"use strict";
|
||||
|
||||
const test = require("node:test");
|
||||
const assert = require("node:assert/strict");
|
||||
const safety = require("../../Web/playout-safety.js");
|
||||
|
||||
function ready(overrides = {}) {
|
||||
return {
|
||||
pending: false,
|
||||
outcomeUnknown: false,
|
||||
commandAvailable: true,
|
||||
engineState: "IDLE",
|
||||
preparedCode: null,
|
||||
onAirCode: null,
|
||||
...overrides
|
||||
};
|
||||
}
|
||||
|
||||
test("NEXT is blocked until a scene is on air", () => {
|
||||
assert.equal(safety.commandBlockReason(ready(), "next"), "next-requires-on-air");
|
||||
assert.equal(
|
||||
safety.commandBlockReason(ready({ engineState: "PREPARED", preparedCode: "5001" }), "next"),
|
||||
"next-requires-on-air");
|
||||
assert.equal(
|
||||
safety.commandBlockReason(ready({ engineState: "PROGRAM", onAirCode: "5001" }), "next"),
|
||||
null);
|
||||
});
|
||||
|
||||
test("OutcomeUnknown and unavailable states block every command", () => {
|
||||
for (const command of ["prepare", "take-in", "next", "take-out"]) {
|
||||
assert.equal(
|
||||
safety.commandBlockReason(ready({ outcomeUnknown: true }), command),
|
||||
"outcome-unknown");
|
||||
assert.equal(
|
||||
safety.commandBlockReason(ready({ commandAvailable: false }), command),
|
||||
"unavailable");
|
||||
}
|
||||
});
|
||||
|
||||
test("TAKE IN and TAKE OUT preserve the native state prerequisites", () => {
|
||||
assert.equal(safety.commandBlockReason(ready(), "take-in"), "take-in-requires-prepare");
|
||||
assert.equal(safety.commandBlockReason(ready(), "take-out"), "nothing-to-take-out");
|
||||
assert.equal(
|
||||
safety.commandBlockReason(ready({ preparedCode: "5001" }), "take-in"),
|
||||
null);
|
||||
assert.equal(
|
||||
safety.commandBlockReason(ready({ onAirCode: "5001" }), "take-out"),
|
||||
null);
|
||||
});
|
||||
|
||||
test("Browser response timeout follows the bounded native operation timeout", () => {
|
||||
assert.equal(safety.responseTimeoutFromNative(5000), 10000);
|
||||
assert.equal(safety.responseTimeoutFromNative(300000), 305000);
|
||||
assert.equal(safety.responseTimeoutFromNative(100), 5100);
|
||||
assert.equal(safety.responseTimeoutFromNative(99), 15000);
|
||||
assert.equal(safety.responseTimeoutFromNative("not-a-number"), 15000);
|
||||
});
|
||||
|
||||
test("Connection state normalization is allowlisted", () => {
|
||||
assert.equal(safety.normalizeConnectionState("Outcome_Unknown"), "outcome-unknown");
|
||||
assert.equal(safety.normalizeConnectionState("RECONNECTING"), "reconnecting");
|
||||
assert.equal(safety.normalizeConnectionState("attacker-defined"), "unavailable");
|
||||
});
|
||||
|
||||
test("Late or mismatched native responses cannot complete a pending command", () => {
|
||||
const pending = { requestId: "REQ-2", command: "next" };
|
||||
assert.equal(
|
||||
safety.matchesPendingCommand(pending, { requestId: "REQ-2", command: "next" }),
|
||||
true);
|
||||
assert.equal(
|
||||
safety.matchesPendingCommand(pending, { requestId: "REQ-1", command: "next" }),
|
||||
false);
|
||||
assert.equal(
|
||||
safety.matchesPendingCommand(pending, { requestId: "REQ-2", command: "take-in" }),
|
||||
false);
|
||||
assert.equal(safety.matchesPendingCommand(null, { requestId: "REQ-2", command: "next" }), false);
|
||||
});
|
||||
Reference in New Issue
Block a user