From 706d372dc71328ab20e715446dbbeb8f80038b7e Mon Sep 17 00:00:00 2001 From: ChuckBuilds Date: Sun, 2 Aug 2026 12:26:27 -0400 Subject: [PATCH] test(celebrations): make the expiry tests actually test expiry CodeRabbit (Major) on the merge re-review: celebration_duration is clamped to a 1.0s floor, so the two expiry tests that configured 0 and expected instant expiration never actually hit the expiry branch. They passed only because _draw_celebration_layout raises in the harness (no real fonts) and its exception branch clears the celebration the same way -- so they were really re-testing the render-failure path, not expiry. Now use a valid 1s duration, backdate started_at past the window, and mock _draw_celebration_layout with assert_not_called() so an expired celebration provably does NOT render. Verified discriminating: both fail if has_active_celebration is forced to never expire. Production code unchanged -- the expiry logic was already correct; only the tests were mismodelling it. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01KEZK1P1Q1fu5pcuVrkrCFZ --- test/test_sports_capabilities.py | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/test/test_sports_capabilities.py b/test/test_sports_capabilities.py index cee06ed5..f43a2b61 100644 --- a/test/test_sports_capabilities.py +++ b/test/test_sports_capabilities.py @@ -16,6 +16,7 @@ See docs/SPORTS_UNIFICATION.md. """ import sys +import time from unittest.mock import MagicMock import pytest @@ -754,20 +755,35 @@ class TestDisplayTakeover: manager._draw_celebration_layout.assert_called_once() def test_expired_celebration_clears_and_defers(self, celebrating): - manager = celebrating(mode_config={"celebration_duration": 0}) + # A short-but-valid duration: celebration_duration is clamped to a 1.0s + # floor, so a config of 0 does NOT expire on the next frame. Backdate + # started_at past the window to exercise the real expiry branch — + # otherwise the celebration is still active and this only passes + # because _draw_celebration_layout happens to raise in the harness + # (that path is covered by test_a_render_failure_falls_through). + manager = celebrating(mode_config={"celebration_duration": 1}) manager._check_for_score(game("g1", home_score=0)) manager._check_for_score(game("g1", home_score=1)) + manager.active_celebration["started_at"] = time.time() - 2 # past the 1s window + # Mock the layout so a render can't raise: otherwise the exception + # branch clears the celebration too, and this test would pass whether + # or not expiry actually fired. An expired celebration must NOT render. + manager._draw_celebration_layout = MagicMock() assert manager.display() is True + manager._draw_celebration_layout.assert_not_called() assert manager.active_celebration is None assert manager.display_calls == [False] def test_expiry_resets_the_dwell_clock(self, celebrating): """So the scorebug resumes on the scoring game for a full duration before rotation can move on.""" - manager = celebrating(mode_config={"celebration_duration": 0}) + manager = celebrating(mode_config={"celebration_duration": 1}) manager._check_for_score(game("g1", home_score=0)) manager._check_for_score(game("g1", home_score=1)) + manager.active_celebration["started_at"] = time.time() - 2 # past the 1s window + manager._draw_celebration_layout = MagicMock() # expiry, not a render failure manager.display() + manager._draw_celebration_layout.assert_not_called() assert manager.last_game_switch > 0 def test_a_render_failure_falls_through_to_the_scorebug(self, celebrating):