From 7eb3507934ca5842dc3628225f0e5951c9738cff Mon Sep 17 00:00:00 2001 From: dev-bot Date: Tue, 17 Mar 2026 17:06:59 +0000 Subject: [PATCH] fix(gameplay): refresh stale next-round bootstrap config --- fupogfakta/services.py | 39 ++++++++++++++++++++++++++++-------- fupogfakta/tests.py | 38 +++++++++++++++++++++++++++++++++++ lobby/tests.py | 45 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 114 insertions(+), 8 deletions(-) diff --git a/fupogfakta/services.py b/fupogfakta/services.py index ba37a18..467e261 100644 --- a/fupogfakta/services.py +++ b/fupogfakta/services.py @@ -140,21 +140,44 @@ def start_next_round(session: GameSession) -> RoundTransitionResult: raise ValueError("round_config_missing") next_round_number = locked_session.current_round + 1 - next_round_config = RoundConfig( + next_round_config, _created = RoundConfig.objects.get_or_create( session=locked_session, number=next_round_number, - category=previous_round_config.category, - lie_seconds=previous_round_config.lie_seconds, - 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, + defaults={ + "category": previous_round_config.category, + "lie_seconds": previous_round_config.lie_seconds, + "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, + }, ) + round_config_update_fields: list[str] = [] + if next_round_config.category_id != previous_round_config.category_id: + next_round_config.category = previous_round_config.category + round_config_update_fields.append("category") + if next_round_config.lie_seconds != previous_round_config.lie_seconds: + next_round_config.lie_seconds = previous_round_config.lie_seconds + round_config_update_fields.append("lie_seconds") + if next_round_config.guess_seconds != previous_round_config.guess_seconds: + next_round_config.guess_seconds = previous_round_config.guess_seconds + round_config_update_fields.append("guess_seconds") + if next_round_config.points_correct != previous_round_config.points_correct: + next_round_config.points_correct = previous_round_config.points_correct + round_config_update_fields.append("points_correct") + if next_round_config.points_bluff != previous_round_config.points_bluff: + next_round_config.points_bluff = previous_round_config.points_bluff + round_config_update_fields.append("points_bluff") + if not next_round_config.started_from_scoreboard: + next_round_config.started_from_scoreboard = True + round_config_update_fields.append("started_from_scoreboard") + if round_config_update_fields: + next_round_config.save(update_fields=round_config_update_fields) + locked_session.current_round = next_round_number round_question = reset_round_question_bootstrap_state(select_round_question(locked_session, next_round_config)) - next_round_config.save() locked_session.status = GameSession.Status.LIE locked_session.save(update_fields=["current_round", "status"]) should_broadcast = True diff --git a/fupogfakta/tests.py b/fupogfakta/tests.py index 85bd65d..0e7811b 100644 --- a/fupogfakta/tests.py +++ b/fupogfakta/tests.py @@ -135,6 +135,44 @@ class FupOgFaktaExtractionSliceTests(TestCase): self.assertEqual(stale_round_question.lies.count(), 0) self.assertEqual(stale_round_question.guesses.count(), 0) + def test_start_next_round_reuses_existing_bootstrap_round_config_with_fresh_canonical_values(self): + self.session.status = GameSession.Status.SCOREBOARD + self.session.save(update_fields=["status"]) + stale_category = Category.objects.create(name="Sport", slug="sport", is_active=True) + stale_round_config = RoundConfig.objects.create( + session=self.session, + number=2, + category=stale_category, + lie_seconds=12, + guess_seconds=18, + points_correct=9, + points_bluff=7, + started_from_scoreboard=False, + ) + stale_round_question = RoundQuestion.objects.create( + session=self.session, + round_number=2, + question=self.question_two, + correct_answer=self.question_two.correct_answer, + shown_at=timezone.now() - timedelta(minutes=10), + mixed_answers=["Stale truth"], + ) + + result = start_next_round(self.session) + + stale_round_config.refresh_from_db() + stale_round_question.refresh_from_db() + self.assertEqual(result.round_config.id, stale_round_config.id) + self.assertEqual(RoundConfig.objects.filter(session=self.session, number=2).count(), 1) + self.assertEqual(stale_round_config.category_id, self.round_config.category_id) + self.assertEqual(stale_round_config.lie_seconds, self.round_config.lie_seconds) + self.assertEqual(stale_round_config.guess_seconds, self.round_config.guess_seconds) + self.assertEqual(stale_round_config.points_correct, self.round_config.points_correct) + self.assertEqual(stale_round_config.points_bluff, self.round_config.points_bluff) + self.assertTrue(stale_round_config.started_from_scoreboard) + self.assertEqual(result.round_question.id, stale_round_question.id) + self.assertEqual(stale_round_question.mixed_answers, []) + def test_finish_game_moves_scoreboard_transition_into_service(self): self.session.status = GameSession.Status.SCOREBOARD self.session.save(update_fields=["status"]) diff --git a/lobby/tests.py b/lobby/tests.py index b4f4143..8846217 100644 --- a/lobby/tests.py +++ b/lobby/tests.py @@ -1643,6 +1643,51 @@ class RevealRoundFlowTests(TestCase): detail_payload = self.client.get(reverse("lobby:session_detail", kwargs={"code": self.session.code})).json() self.assertEqual(detail_payload["session"]["status"], GameSession.Status.LIE) + + def test_start_next_round_reuses_existing_next_round_config_with_refreshed_canonical_values(self): + self.client.login(username="host_reveal", password="secret123") + self.client.get(reverse("lobby:reveal_scoreboard", kwargs={"code": self.session.code})) + + stale_category = Category.objects.create(name="Sport reveal", slug="sport-reveal", is_active=True) + stale_round_config = RoundConfig.objects.create( + session=self.session, + number=2, + category=stale_category, + lie_seconds=12, + guess_seconds=18, + points_correct=9, + points_bluff=7, + started_from_scoreboard=False, + ) + stale_round_question = RoundQuestion.objects.create( + session=self.session, + round_number=2, + question=self.next_question, + correct_answer=self.next_question.correct_answer, + shown_at=timezone.now() - timedelta(minutes=10), + mixed_answers=["Stale truth"], + ) + + response = self.client.post(reverse("lobby:start_next_round", kwargs={"code": self.session.code})) + + self.assertEqual(response.status_code, 200) + self.session.refresh_from_db() + stale_round_config.refresh_from_db() + stale_round_question.refresh_from_db() + self.assertEqual(self.session.status, GameSession.Status.LIE) + self.assertEqual(self.session.current_round, 2) + self.assertEqual(RoundConfig.objects.filter(session=self.session, number=2).count(), 1) + self.assertEqual(stale_round_config.category_id, self.round_config.category_id) + self.assertEqual(stale_round_config.lie_seconds, self.round_config.lie_seconds) + self.assertEqual(stale_round_config.guess_seconds, self.round_config.guess_seconds) + self.assertEqual(stale_round_config.points_correct, self.round_config.points_correct) + self.assertEqual(stale_round_config.points_bluff, self.round_config.points_bluff) + self.assertTrue(stale_round_config.started_from_scoreboard) + self.assertEqual(response.json()["round_question"]["id"], stale_round_question.id) + self.assertEqual(response.json()["config"]["lie_seconds"], self.round_config.lie_seconds) + expected_deadline = stale_round_question.shown_at + timedelta(seconds=self.round_config.lie_seconds) + self.assertEqual(response.json()["round_question"]["lie_deadline_at"], expected_deadline.isoformat()) + detail_payload = self.client.get(reverse("lobby:session_detail", kwargs={"code": self.session.code})).json() self.assertEqual(detail_payload["session"]["current_round"], 2) self.assertEqual(detail_payload["round_question"]["id"], stale_round_question.id) self.assertEqual(detail_payload["round_question"]["answers"], [])