from pathlib import Path from hockey_data.service import HockeyDataService ROOT = Path(__file__).resolve().parents[1] APP_JS = (ROOT / "ui_builder/static/app.js").read_text(encoding="utf-8") def _penalty(side: str, remaining: int) -> dict: return { "side": side, "infraction": {"code": "TEST"}, "preset": "2", "durationMs": 120000, "remainingMs": remaining, "finished": False, } def test_equal_coincidental_penalties_have_no_side_caption(): settings = { "strength_regulation_skaters": 5, "strength_min_skaters": 3, "strength_state_labels": {"regulation": {"4x4": {"ru": "4 на 4"}}}, } payload = HockeyDataService._strength_payload( settings, stage="regular", current_period="2", timer_state={"penalty_board": {"penalties": [_penalty("home", 100000), _penalty("away", 100000)]}}, language="ru", ) assert payload["strength_label"] == "4×4" assert payload["advantage_side"] == "" assert payload["home_label"] == "" assert payload["away_label"] == "" def test_two_vs_one_penalties_caption_only_advantage_team(): settings = { "strength_regulation_skaters": 5, "strength_min_skaters": 3, "strength_state_labels": {"regulation": {"4x3": {"ru": "Играют в большинстве"}}}, } payload = HockeyDataService._strength_payload( settings, stage="regular", current_period="2", timer_state={"penalty_board": {"penalties": [ _penalty("home", 103000), _penalty("home", 120000), _penalty("away", 103000), ]}}, language="ru", ) assert payload["strength_label"] == "3×4" assert payload["advantage_side"] == "away" assert payload["home_label"] == "" assert payload["away_label"] == "Играют в большинстве" def test_runtime_plan_uses_one_plate_and_global_shortest_transition_timer(): start = APP_JS.index("function penaltyDisplayEntriesByTargetSide") end = APP_JS.index("function hockeyPenaltySideMappingDetail", start) block = APP_JS[start:end] assert 'const all = [...home, ...away].sort' in block assert 'const transitionEntry = all[0] || null;' in block assert 'home: advantageSide === "home" && transitionEntry ? [transitionEntry] : []' in block assert 'away: advantageSide === "away" && transitionEntry ? [transitionEntry] : []' in block assert 'if (!advantageSide)' in block assert 'return { home: [], away: [], routedToAdvantage: false' in block def test_final_full_strength_action_only_runs_after_last_penalty_and_real_advantage(): start = APP_JS.index("function fireConfiguredTimerFinishActions") end = APP_JS.index("async function runShortcutSequenceStep", start) block = APP_JS[start:end] assert 'Number(meta.remaining_total || 0) > 0' in block assert '!Boolean(meta.had_advantage)' in block assert 'full_strength_side' in block assert 'side !== fullStrengthSide' in block def test_rebalance_sends_old_plate_out_before_new_plate_in(): start = APP_JS.index("async function rebalanceVmixPenaltyTargets") end = APP_JS.index("function finishActionMatchesSource", start) block = APP_JS[start:end] assert 'const outCommands = [];' in block assert 'const setCommands = [];' in block assert 'const inCommands = [];' in block assert 'const stopCommands = [];' in block assert 'const runCommands = [];' in block assert 'const commands = [...outCommands, ...stopCommands, ...setCommands, ...runCommands, ...inCommands];' in block