[READY][Gameplay] #310 Host transition idempotency and error catalog for scoreboard -> next round / finish #320

Merged
agw merged 45 commits from dev/issue-310-host-transition-idempotency-v2 into main 2026-03-18 06:52:04 +01:00
Showing only changes of commit 9baade0105 - Show all commits

View File

@@ -145,6 +145,74 @@ class LobbyGameplayExtractionTests(TestCase):
{"winner": None, "leaderboard": []},
)
@patch("lobby.views.sync_broadcast_phase_event")
@patch("lobby.views._build_start_next_round_response", return_value={"ok": True})
@patch("lobby.views._build_start_next_round_phase_event")
@patch("lobby.views._start_next_round")
def test_start_next_round_view_skips_broadcast_on_service_replay(
self,
mock_start_next_round,
mock_build_phase_event,
mock_build_response,
mock_sync_broadcast_phase_event,
):
replay_round_config = RoundConfig.objects.create(
session=self.session,
number=2,
category=self.category,
started_from_scoreboard=True,
)
round_question = RoundQuestion.objects.create(
session=self.session,
round_number=2,
question=self.question,
correct_answer=self.question.correct_answer,
)
replay_session = GameSession.objects.get(pk=self.session.pk)
replay_session.status = GameSession.Status.LIE
replay_session.current_round = 2
transition = gameplay_services.RoundTransitionResult(
session=replay_session,
round_config=replay_round_config,
round_question=round_question,
should_broadcast=False,
)
mock_start_next_round.return_value = transition
response = self.client.post(reverse("lobby:start_next_round", kwargs={"code": self.session.code}))
self.assertEqual(response.status_code, 200)
self.assertEqual(response.json(), {"ok": True})
mock_start_next_round.assert_called_once_with(self.session)
mock_build_phase_event.assert_not_called()
mock_build_response.assert_called_once_with(replay_session, replay_round_config, round_question)
mock_sync_broadcast_phase_event.assert_not_called()
@patch("lobby.views.sync_broadcast_phase_event")
@patch("lobby.views._build_finish_game_response", return_value={"ok": True})
@patch("lobby.views._build_finish_game_phase_event")
@patch("lobby.views._finish_game")
def test_finish_game_view_skips_broadcast_on_service_replay(
self,
mock_finish_game,
mock_build_phase_event,
mock_build_response,
mock_sync_broadcast_phase_event,
):
finished_session = GameSession.objects.get(pk=self.session.pk)
finished_session.status = GameSession.Status.FINISHED
transition = gameplay_services.FinishGameResult(session=finished_session, should_broadcast=False)
mock_finish_game.return_value = transition
response = self.client.post(reverse("lobby:finish_game", kwargs={"code": self.session.code}))
self.assertEqual(response.status_code, 200)
self.assertEqual(response.json(), {"ok": True})
mock_finish_game.assert_called_once_with(self.session)
mock_build_phase_event.assert_not_called()
mock_build_response.assert_called_once_with(finished_session)
mock_sync_broadcast_phase_event.assert_not_called()
class LobbyFlowTests(TestCase):
def setUp(self):