fix(gameplay): gate next-round replay on scoreboard exit marker
All checks were successful
CI / test-and-quality (push) Successful in 3m25s
CI / test-and-quality (pull_request) Successful in 3m26s

This commit is contained in:
2026-03-17 08:25:57 +00:00
parent 47659ed673
commit 212549373b
4 changed files with 32 additions and 22 deletions

View File

@@ -1377,7 +1377,12 @@ class RevealRoundFlowTests(TestCase):
self.assertEqual(self.session.status, GameSession.Status.LIE)
self.assertEqual(self.session.current_round, 2)
self.assertTrue(
RoundConfig.objects.filter(session=self.session, number=2, category=self.category).exists()
RoundConfig.objects.filter(
session=self.session,
number=2,
category=self.category,
started_from_scoreboard=True,
).exists()
)
self.assertTrue(
RoundQuestion.objects.filter(session=self.session, round_number=2, question=self.next_question).exists()
@@ -1415,7 +1420,7 @@ class RevealRoundFlowTests(TestCase):
self.session.status = GameSession.Status.LIE
self.session.current_round = 2
self.session.save(update_fields=["status", "current_round"])
RoundConfig.objects.create(session=self.session, number=2, category=self.category)
RoundConfig.objects.create(session=self.session, number=2, category=self.category, started_from_scoreboard=False)
RoundQuestion.objects.create(
session=self.session,
round_number=2,

View File

@@ -1001,6 +1001,7 @@ def start_next_round(request: HttpRequest, code: str) -> JsonResponse:
guess_seconds=previous_round_config.guess_seconds,
points_correct=previous_round_config.points_correct,
points_bluff=previous_round_config.points_bluff,
started_from_scoreboard=True,
)
locked_session.current_round = next_round_number
@@ -1019,31 +1020,16 @@ def start_next_round(request: HttpRequest, code: str) -> JsonResponse:
if locked_session.current_round <= 1:
return api_error(request, code="next_round_invalid_phase", status=400)
previous_round_question = RoundQuestion.objects.filter(
session=locked_session,
round_number=locked_session.current_round - 1,
).first()
if previous_round_question is None:
return api_error(request, code="next_round_invalid_phase", status=400)
previous_round_players_count = Player.objects.filter(session=locked_session).count()
previous_round_guess_count = Guess.objects.filter(round_question=previous_round_question).count()
previous_round_has_score_events = ScoreEvent.objects.filter(
session=locked_session,
meta__round_question_id=previous_round_question.id,
).exists()
previous_round_reveal_resolved = previous_round_has_score_events or (
previous_round_players_count > 0 and previous_round_guess_count >= previous_round_players_count
)
if not previous_round_reveal_resolved:
return api_error(request, code="next_round_invalid_phase", status=400)
next_round_config = RoundConfig.objects.filter(
session=locked_session,
number=locked_session.current_round,
).select_related("category").first()
round_question = _get_current_round_question(locked_session)
if next_round_config is None or round_question is None:
if (
next_round_config is None
or not next_round_config.started_from_scoreboard
or round_question is None
):
return api_error(request, code="next_round_invalid_phase", status=400)
else:
return api_error(request, code="next_round_invalid_phase", status=400)