[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 72bc5997ff - Show all commits

View File

@@ -3,7 +3,6 @@ from unittest.mock import patch
from django.contrib.auth import get_user_model from django.contrib.auth import get_user_model
from django.test import TestCase from django.test import TestCase
from django.urls import reverse
from django.utils import timezone from django.utils import timezone
from fupogfakta.models import Category, GameSession, Guess, LieAnswer, Player, Question, RoundConfig, RoundQuestion, ScoreEvent from fupogfakta.models import Category, GameSession, Guess, LieAnswer, Player, Question, RoundConfig, RoundQuestion, ScoreEvent
@@ -194,74 +193,6 @@ class FupOgFaktaExtractionSliceTests(TestCase):
self.assertEqual(result.session.status, GameSession.Status.FINISHED) self.assertEqual(result.session.status, GameSession.Status.FINISHED)
self.assertEqual(self.session.status, GameSession.Status.FINISHED) self.assertEqual(self.session.status, GameSession.Status.FINISHED)
@patch("lobby.views.sync_broadcast_phase_event")
@patch("lobby.views._start_next_round")
def test_start_next_round_view_delegates_to_fupogfakta_service(self, mock_start_next_round, mock_broadcast):
self.client.login(username="host", password="secret123")
self.session.status = GameSession.Status.SCOREBOARD
self.session.save(update_fields=["status"])
response_payload = {
"session": {
"code": self.session.code,
"status": GameSession.Status.LIE,
"current_round": 2,
}
}
mock_start_next_round.return_value = type(
"Transition",
(),
{
"session": self.session,
"should_broadcast": True,
"response_payload": response_payload,
"phase_event_name": "phase.lie_started",
"phase_event_payload": {"round_number": 2},
},
)()
response = self.client.post(reverse("lobby:start_next_round", kwargs={"code": self.session.code}))
self.assertEqual(response.status_code, 200)
self.assertEqual(response.json(), response_payload)
mock_start_next_round.assert_called_once_with(self.session)
mock_broadcast.assert_called_once_with(self.session.code, "phase.lie_started", {"round_number": 2})
@patch("lobby.views.sync_broadcast_phase_event")
@patch("lobby.views._finish_game")
def test_finish_game_view_delegates_to_fupogfakta_service(self, mock_finish_game, mock_broadcast):
self.client.login(username="host", password="secret123")
self.session.status = GameSession.Status.SCOREBOARD
self.session.save(update_fields=["status"])
response_payload = {
"session": {
"code": self.session.code,
"status": GameSession.Status.FINISHED,
"current_round": 1,
},
"winner": None,
"leaderboard": [],
}
finished_session = GameSession.objects.get(pk=self.session.pk)
finished_session.status = GameSession.Status.FINISHED
mock_finish_game.return_value = type(
"Transition",
(),
{
"session": finished_session,
"should_broadcast": True,
"response_payload": response_payload,
"phase_event_name": "phase.game_over",
"phase_event_payload": {"leaderboard": []},
},
)()
response = self.client.post(reverse("lobby:finish_game", kwargs={"code": self.session.code}))
self.assertEqual(response.status_code, 200)
self.assertEqual(response.json(), response_payload)
mock_finish_game.assert_called_once_with(self.session)
mock_broadcast.assert_called_once_with(self.session.code, "phase.game_over", {"leaderboard": []})
def test_promote_reveal_to_scoreboard_moves_transition_into_service(self): def test_promote_reveal_to_scoreboard_moves_transition_into_service(self):
round_question = RoundQuestion.objects.create( round_question = RoundQuestion.objects.create(
session=self.session, session=self.session,