72 lines
3.6 KiB
Python
72 lines
3.6 KiB
Python
from datetime import datetime
|
|
from pathlib import Path
|
|
from zoneinfo import ZoneInfo
|
|
|
|
from ui_builder.auth import EditorAuthManager
|
|
|
|
ROOT = Path(__file__).resolve().parents[1]
|
|
APP_JS = (ROOT / "ui_builder/static/app.js").read_text(encoding="utf-8")
|
|
APP = (ROOT / "app.py").read_text(encoding="utf-8")
|
|
AUTH = (ROOT / "ui_builder/auth.py").read_text(encoding="utf-8")
|
|
|
|
|
|
def _block(start: str, end: str) -> str:
|
|
return APP_JS.split(start, 1)[1].split(end, 1)[0]
|
|
|
|
|
|
def test_vmix_queue_resolves_countdown_values_at_actual_send_time():
|
|
compact = _block("function compactVmixCommand", "function currentRuntimeVmixDeviceId")
|
|
assert 'typeof rawValue === "function" ? rawValue() : rawValue' in compact
|
|
sender = _block("async function sendRuntimeVmixSequence", "function splitVmixInputs")
|
|
assert 'const rawCommands = typeof commands === "function" ? commands() : commands;' in sender
|
|
assert 'const clean = (rawCommands || []).map(compactVmixCommand)' in sender
|
|
|
|
|
|
def test_countdown_start_is_simple_set_start():
|
|
helper = _block("function vmixCountdownSyncCommands", "function buildShortcutRuntimeContext")
|
|
start_branch = helper.split('return [', 3)[-1]
|
|
assert 'Function: "SuspendCountdown"' not in start_branch
|
|
assert start_branch.index('Function: "SetCountdown"') < start_branch.index('Function: "StartCountdown"')
|
|
assert 'Value: currentValue' in helper
|
|
|
|
|
|
def test_pause_and_stop_use_stop_then_set():
|
|
helper = _block("function vmixCountdownSyncCommands", "function buildShortcutRuntimeContext")
|
|
branch = helper.split('if (action === "stop" || action === "pause")', 1)[1].split('return [', 1)[1].split('];', 1)[0]
|
|
assert 'Function: "StopCountdown"' in branch
|
|
assert 'Function: "SetCountdown"' in branch
|
|
assert branch.index('Function: "StopCountdown"') < branch.index('Function: "SetCountdown"')
|
|
assert 'Function: "SuspendCountdown"' not in branch
|
|
assert 'Function: "PauseCountdown"' not in branch
|
|
|
|
|
|
def test_penalty_rebalance_hard_syncs_running_countdown_and_pauses_stably():
|
|
block = _block("async function rebalanceVmixPenaltyTargets", "function finishActionMatchesSource")
|
|
assert 'stopCommands.push({ Function: "StopCountdown"' in block
|
|
assert 'Value: () => vmixCountdownValue(entry.event.remainingMs)' in block
|
|
assert 'runCommands.push({ Function: "StartCountdown"' in block
|
|
assert 'sendRuntimeVmixTimerSequence(commands)' in block
|
|
assert 'Function: "SuspendCountdown"' not in block
|
|
assert 'Function: "PauseCountdown"' not in block
|
|
|
|
|
|
def test_running_penalty_is_preferred_over_paused_shorter_penalty():
|
|
sorted_block = _block("function sortedPenaltyEntries", "function penaltyLocalAdvantageSide")
|
|
assert 'const runningOrder = Number(Boolean(b.event?.running)) - Number(Boolean(a.event?.running));' in sorted_block
|
|
display = _block("function penaltyDisplayEntriesByTargetSide", "function rememberPenaltyAdvantagePlan")
|
|
assert 'const runningOrder = Number(Boolean(b.event?.running)) - Number(Boolean(a.event?.running));' in display
|
|
|
|
|
|
def test_fixed_pin_1993_replaces_daily_login(tmp_path, monkeypatch):
|
|
monkeypatch.delenv("EDITOR_FIXED_PIN", raising=False)
|
|
auth = EditorAuthManager(tmp_path)
|
|
assert auth.settings["fixed_pin"] == "1993"
|
|
# The old daily algorithm remains diagnostic-only; for 24.08.2026 its
|
|
# default result is 6225, but login validation no longer uses it.
|
|
assert auth.daily_pin(datetime(2026, 8, 24, 12, 0, tzinfo=ZoneInfo("Europe/Moscow"))) == "6225"
|
|
assert 'valid = hmac.compare_digest(supplied, str(self.settings["fixed_pin"]))' in AUTH
|
|
|
|
|
|
def test_build98_runtime_version():
|
|
assert 'BUILD_VERSION = "2026.08.24.4"' in APP
|