70 lines
2.7 KiB
Python
70 lines
2.7 KiB
Python
"""Execute the production save/tick/native-Countdown closure with a fake clock.
|
|
|
|
No application startup, external database, browser, network or real vMix is used.
|
|
The backend strength fixtures use its real pure calculator, rather than a second
|
|
implementation of the penalty arithmetic in JavaScript.
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
import os
|
|
from pathlib import Path
|
|
import shutil
|
|
import subprocess
|
|
|
|
import pytest
|
|
|
|
from hockey_data.service import HockeyDataService
|
|
|
|
|
|
ROOT = Path(__file__).resolve().parents[1]
|
|
|
|
|
|
def _strengths() -> dict:
|
|
settings = {
|
|
"strength_regulation_skaters": 5,
|
|
"strength_min_skaters": 3,
|
|
"strength_state_labels": {
|
|
"regulation": {"5x4": {"ru": "PP"}, "4x4": {"ru": "4 на 4"}},
|
|
},
|
|
}
|
|
results = {}
|
|
for home, away in ((0, 0), (1, 0), (0, 1), (1, 1)):
|
|
penalties = [
|
|
{"side": side, "teamPenalty": True, "infraction": {"id": "minor"},
|
|
"preset": "2", "durationMs": 120000, "remainingMs": 25000,
|
|
"running": True, "startedOnce": True, "finished": False}
|
|
for side, count in (("home", home), ("away", away)) for _ in range(count)
|
|
]
|
|
results[f"{home}:{away}"] = HockeyDataService._strength_payload(
|
|
settings, stage="regular", current_period="2",
|
|
timer_state={"penalty_board": {"penalties": penalties}}, language="ru",
|
|
)
|
|
assert results["1:1"]["state_label"] == "4 на 4"
|
|
assert results["0:1"]["state_label"] == results["1:0"]["state_label"] == "PP"
|
|
return results
|
|
|
|
|
|
SCENARIOS = (
|
|
"staggered_save_cadence", "force_save_keeps_earliest_deadline",
|
|
"inflight_save_preserves_latest_snapshot", "viewer_and_hydration_do_not_write",
|
|
"natural_expiry_refreshes_strength_home", "natural_expiry_refreshes_strength_away",
|
|
"native_opposite_stagger_home", "native_opposite_stagger_away",
|
|
"native_last_single_home", "native_last_single_away",
|
|
"native_simultaneous_home", "native_simultaneous_away",
|
|
)
|
|
|
|
|
|
@pytest.mark.parametrize("scenario", SCENARIOS)
|
|
def test_penalty_lifecycle(scenario: str) -> None:
|
|
node = shutil.which("node")
|
|
assert node, "Node is required for the executable penalty lifecycle regressions"
|
|
source = Path(os.environ.get("BUILD196_RUNTIME_SOURCE", ROOT / "ui_builder/static/app.js"))
|
|
result = subprocess.run(
|
|
[node, str(ROOT / "tests/build196_penalty_lifecycle.cjs"), scenario, str(source)],
|
|
input=json.dumps({"strengths": _strengths()}, ensure_ascii=False),
|
|
text=True, capture_output=True, timeout=20,
|
|
)
|
|
assert result.returncode == 0, result.stdout + result.stderr
|
|
assert f"PASS: {scenario}" in result.stdout, "The scenario must finish its assertions"
|