feat(lobby): canonicalize round phase ownership
This commit is contained in:
@@ -204,11 +204,14 @@ class StartRoundTests(TestCase):
|
||||
self.assertEqual(body["session"]["status"], GameSession.Status.LIE)
|
||||
self.assertEqual(body["round"]["number"], 1)
|
||||
self.assertEqual(body["round"]["category"]["slug"], self.category.slug)
|
||||
self.assertEqual(body["round_question"]["prompt"], "Hvilket år faldt muren?")
|
||||
self.assertIn("lie_deadline_at", body["round_question"])
|
||||
|
||||
self.session.refresh_from_db()
|
||||
self.assertEqual(self.session.status, GameSession.Status.LIE)
|
||||
round_config = RoundConfig.objects.get(session=self.session, number=1)
|
||||
self.assertEqual(round_config.category, self.category)
|
||||
self.assertTrue(RoundQuestion.objects.filter(session=self.session, round_number=1).exists())
|
||||
|
||||
def test_host_start_round_uses_normalized_session_code_from_path(self):
|
||||
self.client.login(username="host", password="secret123")
|
||||
@@ -715,6 +718,76 @@ class GuessSubmissionTests(TestCase):
|
||||
self.assertEqual(response.json()["error"], "Invalid player session token")
|
||||
|
||||
|
||||
class CanonicalRoundFlowTests(TestCase):
|
||||
def setUp(self):
|
||||
self.host = User.objects.create_user(username="host_canonical", password="secret123")
|
||||
self.session = GameSession.objects.create(host=self.host, code="CN2871")
|
||||
self.category = Category.objects.create(name="Kanon", slug="kanon", is_active=True)
|
||||
self.question = Question.objects.create(
|
||||
category=self.category,
|
||||
prompt="Hvem skrev Hamlet?",
|
||||
correct_answer="Shakespeare",
|
||||
is_active=True,
|
||||
)
|
||||
self.players = [
|
||||
Player.objects.create(session=self.session, nickname="Luna"),
|
||||
Player.objects.create(session=self.session, nickname="Mads"),
|
||||
Player.objects.create(session=self.session, nickname="Nora"),
|
||||
]
|
||||
|
||||
def test_canonical_round_flow_auto_advances_from_start_to_scoreboard(self):
|
||||
self.client.login(username="host_canonical", password="secret123")
|
||||
|
||||
start_response = self.client.post(
|
||||
reverse("lobby:start_round", kwargs={"code": self.session.code}),
|
||||
data={"category_slug": self.category.slug},
|
||||
content_type="application/json",
|
||||
)
|
||||
self.assertEqual(start_response.status_code, 201)
|
||||
round_question_id = start_response.json()["round_question"]["id"]
|
||||
self.assertEqual(start_response.json()["session"]["status"], GameSession.Status.LIE)
|
||||
|
||||
lie_responses = []
|
||||
for index, player in enumerate(self.players, start=1):
|
||||
lie_responses.append(
|
||||
self.client.post(
|
||||
reverse("lobby:submit_lie", kwargs={"code": self.session.code, "round_question_id": round_question_id}),
|
||||
data={"player_id": player.id, "session_token": player.session_token, "text": f"Løgn {index}"},
|
||||
content_type="application/json",
|
||||
)
|
||||
)
|
||||
|
||||
self.assertTrue(all(response.status_code == 201 for response in lie_responses))
|
||||
self.assertEqual(lie_responses[-1].json()["session"]["status"], GameSession.Status.GUESS)
|
||||
self.assertTrue(lie_responses[-1].json()["phase_transition"]["auto_advanced"])
|
||||
self.assertGreaterEqual(len(lie_responses[-1].json()["answers"]), 2)
|
||||
|
||||
guess_targets = ["Shakespeare", "Løgn 1", "Shakespeare"]
|
||||
guess_responses = []
|
||||
for player, selected_text in zip(self.players, guess_targets, strict=True):
|
||||
guess_responses.append(
|
||||
self.client.post(
|
||||
reverse("lobby:submit_guess", kwargs={"code": self.session.code, "round_question_id": round_question_id}),
|
||||
data={"player_id": player.id, "session_token": player.session_token, "selected_text": selected_text},
|
||||
content_type="application/json",
|
||||
)
|
||||
)
|
||||
|
||||
self.assertTrue(all(response.status_code == 201 for response in guess_responses))
|
||||
self.assertEqual(guess_responses[-1].json()["session"]["status"], GameSession.Status.REVEAL)
|
||||
self.assertTrue(guess_responses[-1].json()["phase_transition"]["auto_advanced"])
|
||||
self.assertIsNotNone(guess_responses[-1].json()["reveal"])
|
||||
|
||||
detail_response = self.client.get(reverse("lobby:session_detail", kwargs={"code": self.session.code}))
|
||||
self.assertEqual(detail_response.status_code, 200)
|
||||
payload = detail_response.json()
|
||||
self.assertEqual(payload["session"]["status"], GameSession.Status.SCOREBOARD)
|
||||
self.assertEqual(payload["phase_view_model"]["current_phase"], GameSession.Status.SCOREBOARD)
|
||||
self.assertTrue(payload["phase_view_model"]["readiness"]["scoreboard_ready"])
|
||||
self.assertEqual([entry["nickname"] for entry in payload["scoreboard"]], ["Luna", "Nora", "Mads"])
|
||||
self.assertEqual(payload["reveal"]["correct_answer"], "Shakespeare")
|
||||
|
||||
|
||||
class ScoreCalculationTests(TestCase):
|
||||
def setUp(self):
|
||||
self.host = User.objects.create_user(username="host_score", password="secret123")
|
||||
@@ -1502,8 +1575,8 @@ class SessionDetailRoundQuestionTests(TestCase):
|
||||
scoreboard_payload = self.client.get(reverse("lobby:session_detail", kwargs={"code": self.session.code})).json()
|
||||
|
||||
self.assertEqual(reveal_payload["reveal"], scoreboard_payload["reveal"])
|
||||
self.assertTrue(reveal_payload["phase_view_model"]["host"]["can_reveal_scoreboard"])
|
||||
self.assertFalse(scoreboard_payload["phase_view_model"]["host"]["can_reveal_scoreboard"])
|
||||
self.assertTrue(reveal_payload["phase_view_model"]["readiness"]["scoreboard_ready"])
|
||||
self.assertTrue(scoreboard_payload["phase_view_model"]["readiness"]["scoreboard_ready"])
|
||||
self.assertFalse(reveal_payload["phase_view_model"]["host"]["can_start_next_round"])
|
||||
self.assertTrue(scoreboard_payload["phase_view_model"]["host"]["can_start_next_round"])
|
||||
|
||||
@@ -1530,7 +1603,10 @@ class SessionDetailPhaseViewModelTests(TestCase):
|
||||
self.assertTrue(phase["constraints"]["min_players_reached"])
|
||||
self.assertTrue(phase["constraints"]["max_players_allowed"])
|
||||
self.assertTrue(phase["host"]["can_start_round"])
|
||||
self.assertEqual(phase["current_phase"], GameSession.Status.LOBBY)
|
||||
self.assertFalse(phase["host"]["can_show_question"])
|
||||
self.assertFalse(phase["readiness"]["question_ready"])
|
||||
self.assertFalse(phase["readiness"]["scoreboard_ready"])
|
||||
self.assertTrue(phase["player"]["can_join"])
|
||||
self.assertFalse(phase["player"]["can_submit_lie"])
|
||||
self.assertFalse(phase["player"]["can_submit_guess"])
|
||||
@@ -1555,7 +1631,8 @@ class SessionDetailPhaseViewModelTests(TestCase):
|
||||
lie_payload = self.client.get(reverse("lobby:session_detail", kwargs={"code": self.session.code})).json()
|
||||
lie_phase = lie_payload["phase_view_model"]
|
||||
self.assertFalse(lie_phase["host"]["can_show_question"])
|
||||
self.assertTrue(lie_phase["host"]["can_mix_answers"])
|
||||
self.assertFalse(lie_phase["host"]["can_mix_answers"])
|
||||
self.assertTrue(lie_phase["readiness"]["question_ready"])
|
||||
self.assertTrue(lie_phase["player"]["can_submit_lie"])
|
||||
self.assertFalse(lie_phase["player"]["can_submit_guess"])
|
||||
|
||||
@@ -1563,8 +1640,9 @@ class SessionDetailPhaseViewModelTests(TestCase):
|
||||
self.session.save(update_fields=["status"])
|
||||
guess_payload = self.client.get(reverse("lobby:session_detail", kwargs={"code": self.session.code})).json()
|
||||
guess_phase = guess_payload["phase_view_model"]
|
||||
self.assertTrue(guess_phase["host"]["can_mix_answers"])
|
||||
self.assertTrue(guess_phase["host"]["can_calculate_scores"])
|
||||
self.assertFalse(guess_phase["host"]["can_mix_answers"])
|
||||
self.assertFalse(guess_phase["host"]["can_calculate_scores"])
|
||||
self.assertFalse(guess_phase["readiness"]["scoreboard_ready"])
|
||||
self.assertFalse(guess_phase["player"]["can_submit_lie"])
|
||||
self.assertTrue(guess_phase["player"]["can_submit_guess"])
|
||||
|
||||
@@ -1573,7 +1651,8 @@ class SessionDetailPhaseViewModelTests(TestCase):
|
||||
self.session.save(update_fields=["status"])
|
||||
reveal_payload = self.client.get(reverse("lobby:session_detail", kwargs={"code": self.session.code})).json()
|
||||
reveal_phase = reveal_payload["phase_view_model"]
|
||||
self.assertTrue(reveal_phase["host"]["can_reveal_scoreboard"])
|
||||
self.assertFalse(reveal_phase["host"]["can_reveal_scoreboard"])
|
||||
self.assertTrue(reveal_phase["readiness"]["scoreboard_ready"])
|
||||
self.assertFalse(reveal_phase["host"]["can_start_next_round"])
|
||||
self.assertFalse(reveal_phase["host"]["can_finish_game"])
|
||||
self.assertFalse(reveal_phase["player"]["can_view_final_result"])
|
||||
@@ -1583,6 +1662,7 @@ class SessionDetailPhaseViewModelTests(TestCase):
|
||||
scoreboard_payload = self.client.get(reverse("lobby:session_detail", kwargs={"code": self.session.code})).json()
|
||||
scoreboard_phase = scoreboard_payload["phase_view_model"]
|
||||
self.assertFalse(scoreboard_phase["host"]["can_reveal_scoreboard"])
|
||||
self.assertTrue(scoreboard_phase["readiness"]["scoreboard_ready"])
|
||||
self.assertTrue(scoreboard_phase["host"]["can_start_next_round"])
|
||||
self.assertTrue(scoreboard_phase["host"]["can_finish_game"])
|
||||
self.assertFalse(scoreboard_phase["player"]["can_view_final_result"])
|
||||
@@ -1620,12 +1700,10 @@ class SmokeStagingCommandTests(TestCase):
|
||||
"create_session",
|
||||
"join_players",
|
||||
"start_round",
|
||||
"show_question",
|
||||
"submit_lies",
|
||||
"mix_answers",
|
||||
"auto_guess_transition",
|
||||
"submit_guesses",
|
||||
"calculate_scores",
|
||||
"reveal_scoreboard",
|
||||
"auto_reveal_to_scoreboard",
|
||||
"finish_game",
|
||||
],
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user