Files
hockey_new/tests/build201_single_goal.cjs

98 lines
7.4 KiB
JavaScript

'use strict';
// Keep the established I/O doubles; the planner, score observer, board mutations
// and vMix transport are real definitions from the selected production app.js.
const fs = require('node:fs'), path = require('node:path'), assert = require('node:assert/strict');
const previous = fs.readFileSync(path.join(__dirname, 'build200_goal_ack.cjs'), 'utf8');
const boundary = previous.indexOf('\nconst cases = {};');
const helpersBegin = previous.indexOf('\nfunction nativeModel(');
const helpersEnd = previous.indexOf('\ncases.normal_goal_ack_before_row_delete');
assert.ok(boundary > 0 && helpersBegin > boundary && helpersEnd > helpersBegin);
const fixture = previous.slice(0, boundary) + previous.slice(helpersBegin, helpersEnd);
const { harness, penalty, flush, clone, assertInfrastructure, nativeModel, prepareMatch, settlePhysical } =
new Function('require', 'process', '__dirname', fixture + '\nreturn { harness, penalty, flush, clone, assertInfrastructure, nativeModel, prepareMatch, settlePhysical };')
(require, process, __dirname);
const cases = {};
const readyPenalty = (side, preset = '2') => penalty('ready-' + side, side, preset, preset === '2' ? 120000 : preset === '5' ? 300000 : 240000,
{ startedOnce: false, running: false });
const other = side => side === 'home' ? 'away' : 'home';
async function prime(h) {
prepareMatch(h); await h.prime(); await settlePhysical(h); await h.score(0, 0); await settlePhysical(h);
h.frames.length = 0; h.persisted.length = 0;
}
function scoreGoal(h, side, manual = false) {
if (manual) return (async () => { await h.c.hockeyScoreCommand(side + '_plus', { refreshMapping: false, announce: false }); await h.settle(); })();
return h.score(side === 'home' ? 1 : 0, side === 'away' ? 1 : 0);
}
async function readyGoal(side, manual) {
const h = harness([readyPenalty(side)]), native = nativeModel(h), input = other(side) === 'home' ? 'HOME-plate' : 'AWAY-plate';
await prime(h); assert.equal(h.c.currentHockeyPenaltyEntries().length, 1);
assert.equal(h.board().penalties[0].startedOnce, false, 'The test must reach the visible but never-started state');
assert.equal(native.clocks.get(input).visible, true); assert.equal(native.clocks.get(input).positionMs, 120000);
const held = h.hold(), goal = scoreGoal(h, other(side), manual); await flush(); assertInfrastructure(h);
assert.equal(held.started, true, 'A ready minor already changes strength and must reach physical removal after the opponent scores');
assert.equal(h.board().penalties.length, 1, 'Keep the card until physical ACK');
assert.equal(h.persisted.some(row => row.penalty_board.penalties.length === 0), false);
assert.ok(held.row.body.commands.some(command => /^OverlayInput[1-8]Out$/.test(command.Function) && command.Input === input));
h.release(held); await goal; assertInfrastructure(h);
assert.equal(h.board().penalties.length, 0); assert.equal(native.clocks.get(input).visible, false);
assert.equal(native.clocks.get(input).positionMs, 0); assert.ok(h.persisted.some(row => row.penalty_board.penalties.length === 0));
}
for (const side of ['home', 'away']) for (const source of ['external', 'manual']) {
cases[`ready_${side}_${source}_goal`] = () => readyGoal(side, source === 'manual');
}
cases.running_minor_still_releases = async () => {
const h = harness([penalty('running', 'away', '2', 90000, { running: true })]); await prime(h);
await scoreGoal(h, 'home'); assertInfrastructure(h); assert.equal(h.board().penalties.length, 0);
};
cases.started_paused_minor_still_releases = async () => {
const h = harness([penalty('paused', 'home', '2', 90000, { startedOnce: true, running: false })]); await prime(h);
await scoreGoal(h, 'away'); assertInfrastructure(h); assert.equal(h.board().penalties.length, 0);
};
cases.f2_off_ready_minor_stays = async () => {
const h = harness([readyPenalty('away')]); h.server.flags.scoreboard_auxiliaries_enabled = false;
h.state.data.hockey.game_control.flags.scoreboard_auxiliaries_enabled = false; await prime(h);
await scoreGoal(h, 'home'); assertInfrastructure(h); assert.equal(h.board().penalties.length, 1); assert.equal(h.frames.length, 0);
};
cases.ready_opponent_prevents_false_powerplay = async () => {
const h = harness([readyPenalty('home'), penalty('started-away', 'away', '4', 180000)]); await prime(h);
await scoreGoal(h, 'home'); assertInfrastructure(h);
assert.equal(h.board().penalties.length, 2, 'Ready opposing penalties already count in the same numerical strength used by the scoreboard');
assert.equal(h.board().penalties.find(row => row.id === 'started-away').remainingMs, 180000);
assert.equal(h.frames.length, 0, 'An equal-strength goal must not issue removal or shortening');
};
cases.ready_major_stays = async () => {
const h = harness([readyPenalty('away', '5')]); await prime(h); await scoreGoal(h, 'home');
assertInfrastructure(h); assert.equal(h.board().penalties.length, 1); assert.equal(h.board().penalties[0].remainingMs, 300000);
assert.equal(h.frames.length, 0);
};
for (const outcome of ['nack', 'unknown']) cases[`ready_${outcome}_keeps_card`] = async () => {
const h = harness([readyPenalty('away')]); await prime(h); const held = h.hold(), goal = scoreGoal(h, 'home'); await flush();
assert.equal(held.started, true, 'Reach the actual delivery boundary before testing failure');
if (outcome === 'nack') h.release(held, { ok: false, confirmation: 'ack', applied: 0,
results: held.row.body.commands.map(command => ({ ok: false, function: command.Function, ack_waited: true, reason: 'failed' })) });
else held.wait.reject(Object.assign(new Error('timeout'), { name: 'AbortError' }));
await goal; assertInfrastructure(h); assert.equal(h.board().penalties.length, 1);
assert.ok(h.board().penalties[0].goalResolution); const sent = h.frames.length;
await scoreGoal(h, 'home'); assert.equal(h.frames.length, sent, 'Do not replay an unconfirmed removal on a duplicate snapshot');
};
cases.ready_success_duplicate_is_not_replayed = async () => {
const h = harness([readyPenalty('away')]); await prime(h); await scoreGoal(h, 'home');
assertInfrastructure(h); assert.equal(h.board().penalties.length, 0); const sent = h.frames.length;
await scoreGoal(h, 'home'); assert.equal(h.frames.length, sent);
};
cases.ready_double_minor_keeps_second_segment = async () => {
const h = harness([readyPenalty('away', '4')]); await prime(h); const held = h.hold(), goal = scoreGoal(h, 'home'); await flush();
assert.equal(held.started, true); assert.equal(h.board().penalties[0].remainingMs, 240000);
h.release(held); await goal; assertInfrastructure(h); assert.equal(h.board().penalties.length, 1);
assert.equal(h.board().penalties[0].remainingMs, 120000); assert.equal(h.board().penalties[0].running, false);
assert.ok(held.row.body.commands.every(command => /^OverlayInput[1-8]Out$/.test(command.Function)));
const wire = h.commands(), hidden = wire.findIndex(x => x.Function === 'OverlayInput2Out');
const seeded = wire.findIndex(x => x.Function === 'SetCountdown' && x.Value === '00:02:00');
const shown = wire.findLastIndex(x => x.Function === 'OverlayInput2In');
assert.ok(hidden >= 0 && seeded > hidden && shown > seeded, 'Hide, prepare the remaining two minutes, then show again');
};
(async () => {
const name = process.argv[2]; assert.equal(typeof cases[name], 'function', 'Unknown single-goal case ' + name);
await cases[name](); console.log('PASS BUILD201 single goal: ' + name);
})().catch(error => { console.error(error); process.exitCode = 1; });