From 046212d29af7ef157d45c74b3b312aded3a94f1f Mon Sep 17 00:00:00 2001 From: DEV-bot Date: Sat, 28 Feb 2026 20:22:58 +0000 Subject: [PATCH] Normalize session code input in join and lookup flows --- lobby/tests.py | 37 +++++++++++++++++++++++++++++++++++++ lobby/views.py | 26 +++++++++++++++----------- 2 files changed, 52 insertions(+), 11 deletions(-) diff --git a/lobby/tests.py b/lobby/tests.py index 6516313..6248811 100644 --- a/lobby/tests.py +++ b/lobby/tests.py @@ -60,6 +60,28 @@ class LobbyFlowTests(TestCase): self.assertTrue(body["player"]["session_token"]) self.assertTrue(Player.objects.filter(session=session, nickname="Luna").exists()) + def test_player_can_join_with_trimmed_code(self): + session = GameSession.objects.create(host=self.host, code="ABCD23") + + response = self.client.post( + reverse("lobby:join_session"), + data={"code": " abcd23 ", "nickname": "Luna"}, + content_type="application/json", + ) + + self.assertEqual(response.status_code, 201) + self.assertTrue(Player.objects.filter(session=session, nickname="Luna").exists()) + + def test_join_rejects_code_empty_after_trim(self): + response = self.client.post( + reverse("lobby:join_session"), + data={"code": " ", "nickname": "Luna"}, + content_type="application/json", + ) + + self.assertEqual(response.status_code, 400) + self.assertEqual(response.json()["error"], "Session code is required") + def test_join_rejects_duplicate_nickname_case_insensitive(self): session = GameSession.objects.create(host=self.host, code="QWER12") Player.objects.create(session=session, nickname="Luna") @@ -131,6 +153,21 @@ class StartRoundTests(TestCase): round_config = RoundConfig.objects.get(session=self.session, number=1) self.assertEqual(round_config.category, self.category) + def test_host_start_round_uses_normalized_session_code_from_path(self): + self.client.login(username="host", password="secret123") + + response = self.client.post( + reverse("lobby:start_round", kwargs={"code": " abcd23 "}), + data={"category_slug": self.category.slug}, + content_type="application/json", + ) + + self.assertEqual(response.status_code, 201) + 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) + def test_start_round_requires_host(self): self.client.login(username="other", password="secret123") diff --git a/lobby/views.py b/lobby/views.py index 8247508..30534ec 100644 --- a/lobby/views.py +++ b/lobby/views.py @@ -45,6 +45,10 @@ def _generate_session_code() -> str: return "".join(random.choices(SESSION_CODE_ALPHABET, k=SESSION_CODE_LENGTH)) +def _normalize_session_code(code: str) -> str: + return code.strip().upper() + + def _create_unique_session_code() -> str: for _ in range(MAX_CODE_GENERATION_ATTEMPTS): code = _generate_session_code() @@ -77,7 +81,7 @@ def create_session(request: HttpRequest) -> JsonResponse: def join_session(request: HttpRequest) -> JsonResponse: payload = _json_body(request) - code = str(payload.get("code", "")).strip().upper() + code = _normalize_session_code(str(payload.get("code", ""))) nickname = str(payload.get("nickname", "")).strip() if not code: @@ -118,7 +122,7 @@ def join_session(request: HttpRequest) -> JsonResponse: @require_GET def session_detail(request: HttpRequest, code: str) -> JsonResponse: - session_code = code.strip().upper() + session_code = _normalize_session_code(code) try: session = GameSession.objects.get(code=session_code) @@ -175,7 +179,7 @@ def start_round(request: HttpRequest, code: str) -> JsonResponse: if not category_slug: return JsonResponse({"error": "category_slug is required"}, status=400) - session_code = code.strip().upper() + session_code = _normalize_session_code(code) try: session = GameSession.objects.get(code=session_code) @@ -234,7 +238,7 @@ def start_round(request: HttpRequest, code: str) -> JsonResponse: @require_POST @login_required def show_question(request: HttpRequest, code: str) -> JsonResponse: - session_code = code.strip().upper() + session_code = _normalize_session_code(code) try: session = GameSession.objects.get(code=session_code) @@ -294,7 +298,7 @@ def show_question(request: HttpRequest, code: str) -> JsonResponse: @require_POST def submit_lie(request: HttpRequest, code: str, round_question_id: int) -> JsonResponse: payload = _json_body(request) - session_code = code.strip().upper() + session_code = _normalize_session_code(code) player_id = payload.get("player_id") session_token = str(payload.get("session_token", "")).strip() @@ -367,7 +371,7 @@ def submit_lie(request: HttpRequest, code: str, round_question_id: int) -> JsonR @require_POST @login_required def mix_answers(request: HttpRequest, code: str, round_question_id: int) -> JsonResponse: - session_code = code.strip().upper() + session_code = _normalize_session_code(code) try: session = GameSession.objects.get(code=session_code) @@ -437,7 +441,7 @@ def mix_answers(request: HttpRequest, code: str, round_question_id: int) -> Json @require_POST def submit_guess(request: HttpRequest, code: str, round_question_id: int) -> JsonResponse: payload = _json_body(request) - session_code = code.strip().upper() + session_code = _normalize_session_code(code) player_id = payload.get("player_id") session_token = str(payload.get("session_token", "")).strip() @@ -543,7 +547,7 @@ def submit_guess(request: HttpRequest, code: str, round_question_id: int) -> Jso @require_GET @login_required def reveal_scoreboard(request: HttpRequest, code: str) -> JsonResponse: - session_code = code.strip().upper() + session_code = _normalize_session_code(code) try: session = GameSession.objects.get(code=session_code) @@ -577,7 +581,7 @@ def reveal_scoreboard(request: HttpRequest, code: str) -> JsonResponse: @require_POST @login_required def start_next_round(request: HttpRequest, code: str) -> JsonResponse: - session_code = code.strip().upper() + session_code = _normalize_session_code(code) try: session = GameSession.objects.get(code=session_code) @@ -609,7 +613,7 @@ def start_next_round(request: HttpRequest, code: str) -> JsonResponse: @require_POST @login_required def finish_game(request: HttpRequest, code: str) -> JsonResponse: - session_code = code.strip().upper() + session_code = _normalize_session_code(code) try: session = GameSession.objects.get(code=session_code) @@ -651,7 +655,7 @@ def finish_game(request: HttpRequest, code: str) -> JsonResponse: @require_POST @login_required def calculate_scores(request: HttpRequest, code: str, round_question_id: int) -> JsonResponse: - session_code = code.strip().upper() + session_code = _normalize_session_code(code) try: session = GameSession.objects.get(code=session_code)