Spaces:
Running
Running
| """Coverage tests for animetix.api.games.blindtest. | |
| Exercises the Blindtest endpoints (state / start / guess) against the real | |
| Django session. The DI ``catalog_service`` / ``blind_test_service`` / | |
| ``game_service`` providers are mocked via instance-level container overrides. | |
| ``blind_test_service`` is a mock; ``get_state`` returns a controllable | |
| ``SimpleNamespace`` standing in for the Blindtest state object. | |
| """ | |
| from types import SimpleNamespace | |
| from unittest.mock import MagicMock | |
| import pytest | |
| from animetix.containers import container | |
| from dependency_injector import providers | |
| from django.contrib.auth.models import User | |
| from django.urls import reverse | |
| from rest_framework.test import APIClient | |
| def api_client(): | |
| return APIClient() | |
| def mock_catalog(): | |
| return { | |
| "title_to_full_data": { | |
| "Naruto": {"image": "http://naruto.jpg"}, | |
| "Bleach": {"image": "http://bleach.jpg"}, | |
| } | |
| } | |
| def _state(**kwargs): | |
| defaults = dict( | |
| secret=None, | |
| video=None, | |
| type=None, | |
| sequence=None, | |
| song=None, | |
| artists=None, | |
| guesses=[], | |
| game_over=False, | |
| is_daily=False, | |
| difficulty="Normal", | |
| ) | |
| defaults.update(kwargs) | |
| return SimpleNamespace(**defaults) | |
| _THEME = { | |
| "anime_title": "Naruto", | |
| "video_url": "http://video.mp4", | |
| "type": "opening", | |
| "song_title": "Blue Bird", | |
| "artists": ["Ikimono"], | |
| } | |
| # --------------------------------------------------------------------------- # | |
| # BlindtestGameStateView | |
| # --------------------------------------------------------------------------- # | |
| def test_state_no_game(api_client): | |
| bt = MagicMock() | |
| bt.get_state.return_value = _state(secret=None) | |
| with container.core.blind_test_service.override(providers.Object(bt)): | |
| resp = api_client.get(reverse("api_blindtest_state")) | |
| assert resp.status_code == 400 | |
| assert resp.json()["error"] == "No game in progress" | |
| def test_state_in_progress_hides_secret(api_client): | |
| bt = MagicMock() | |
| bt.get_state.return_value = _state( | |
| secret="Naruto", | |
| video="http://video.mp4", | |
| type="opening", | |
| song="Blue Bird", | |
| artists=["Ikimono"], | |
| game_over=False, | |
| ) | |
| with container.core.blind_test_service.override(providers.Object(bt)): | |
| resp = api_client.get(reverse("api_blindtest_state")) | |
| assert resp.status_code == 200 | |
| data = resp.json() | |
| assert data["video_url"] == "http://video.mp4" | |
| assert data["blindtest_song"] == "Blue Bird" | |
| assert data["secret_title"] is None # hidden while running | |
| def test_state_game_over_reveals_secret(api_client, mock_catalog): | |
| cat = MagicMock() | |
| cat.load_data.return_value = mock_catalog | |
| bt = MagicMock() | |
| bt.get_state.return_value = _state(secret="Naruto", game_over=True) | |
| with ( | |
| container.core.catalog_service.override(providers.Object(cat)), | |
| container.core.blind_test_service.override(providers.Object(bt)), | |
| ): | |
| resp = api_client.get(reverse("api_blindtest_state")) | |
| assert resp.status_code == 200 | |
| data = resp.json() | |
| assert data["secret_title"] == "Naruto" | |
| assert data["secret_image"] == "http://naruto.jpg" | |
| # --------------------------------------------------------------------------- # | |
| # BlindtestGameStartView | |
| # --------------------------------------------------------------------------- # | |
| def test_start_catalog_not_found(api_client): | |
| cat = MagicMock() | |
| cat.load_data.return_value = None | |
| bt = MagicMock() | |
| with ( | |
| container.core.catalog_service.override(providers.Object(cat)), | |
| container.core.blind_test_service.override(providers.Object(bt)), | |
| ): | |
| resp = api_client.post(reverse("api_blindtest_start"), {}, format="json") | |
| assert resp.status_code == 404 | |
| assert resp.json()["error"] == "Catalog not found" | |
| def test_start_no_theme(api_client, mock_catalog): | |
| cat = MagicMock() | |
| cat.load_data.return_value = mock_catalog | |
| bt = MagicMock() | |
| bt.get_random_theme.return_value = None | |
| with ( | |
| container.core.catalog_service.override(providers.Object(cat)), | |
| container.core.blind_test_service.override(providers.Object(bt)), | |
| ): | |
| resp = api_client.post(reverse("api_blindtest_start"), {}, format="json") | |
| assert resp.status_code == 500 | |
| assert resp.json()["error"] == "Failed to select theme" | |
| def test_start_random_success(api_client, mock_catalog): | |
| cat = MagicMock() | |
| cat.load_data.return_value = mock_catalog | |
| bt = MagicMock() | |
| bt.get_random_theme.return_value = _THEME | |
| bt.get_state.return_value = _state() | |
| with ( | |
| container.core.catalog_service.override(providers.Object(cat)), | |
| container.core.blind_test_service.override(providers.Object(bt)), | |
| ): | |
| resp = api_client.post( | |
| reverse("api_blindtest_start"), {"type": "opening"}, format="json" | |
| ) | |
| assert resp.status_code == 200 | |
| data = resp.json() | |
| assert data["video_url"] == "http://video.mp4" | |
| assert data["theme_type"] == "opening" | |
| assert data["game_over"] is False | |
| bt.get_random_theme.assert_called_once_with(theme_type="opening") | |
| bt.save_state.assert_called_once() | |
| def test_start_daily_success(api_client, mock_catalog): | |
| cat = MagicMock() | |
| cat.load_data.return_value = mock_catalog | |
| bt = MagicMock() | |
| bt.get_daily_theme.return_value = _THEME | |
| bt.get_state.return_value = _state() | |
| with ( | |
| container.core.catalog_service.override(providers.Object(cat)), | |
| container.core.blind_test_service.override(providers.Object(bt)), | |
| ): | |
| resp = api_client.post( | |
| reverse("api_blindtest_start"), {"is_daily": True}, format="json" | |
| ) | |
| assert resp.status_code == 200 | |
| assert resp.json()["is_daily"] is True | |
| bt.get_daily_theme.assert_called_once() | |
| bt.get_random_theme.assert_not_called() | |
| # --------------------------------------------------------------------------- # | |
| # BlindtestGameGuessView | |
| # --------------------------------------------------------------------------- # | |
| def test_guess_no_game(api_client, mock_catalog): | |
| cat = MagicMock() | |
| cat.load_data.return_value = mock_catalog | |
| bt = MagicMock() | |
| bt.get_state.return_value = _state(secret=None) | |
| game = MagicMock() | |
| with ( | |
| container.core.catalog_service.override(providers.Object(cat)), | |
| container.core.blind_test_service.override(providers.Object(bt)), | |
| container.core.game_service.override(providers.Object(game)), | |
| ): | |
| resp = api_client.post( | |
| reverse("api_blindtest_guess"), {"guess": "Naruto"}, format="json" | |
| ) | |
| assert resp.status_code == 400 | |
| assert resp.json()["error"] == "No game in progress" | |
| def test_guess_already_over(api_client, mock_catalog): | |
| cat = MagicMock() | |
| cat.load_data.return_value = mock_catalog | |
| bt = MagicMock() | |
| bt.get_state.return_value = _state(secret="Naruto", game_over=True) | |
| game = MagicMock() | |
| with ( | |
| container.core.catalog_service.override(providers.Object(cat)), | |
| container.core.blind_test_service.override(providers.Object(bt)), | |
| container.core.game_service.override(providers.Object(game)), | |
| ): | |
| resp = api_client.post( | |
| reverse("api_blindtest_guess"), {"guess": "Naruto"}, format="json" | |
| ) | |
| assert resp.status_code == 400 | |
| assert resp.json()["error"] == "Game already over" | |
| def test_guess_missing_guess(api_client, mock_catalog): | |
| cat = MagicMock() | |
| cat.load_data.return_value = mock_catalog | |
| bt = MagicMock() | |
| bt.get_state.return_value = _state(secret="Naruto") | |
| game = MagicMock() | |
| with ( | |
| container.core.catalog_service.override(providers.Object(cat)), | |
| container.core.blind_test_service.override(providers.Object(bt)), | |
| container.core.game_service.override(providers.Object(game)), | |
| ): | |
| resp = api_client.post(reverse("api_blindtest_guess"), {}, format="json") | |
| assert resp.status_code == 400 | |
| assert resp.json()["error"] == "Guess is required" | |
| def test_guess_incorrect(api_client, mock_catalog): | |
| cat = MagicMock() | |
| cat.load_data.return_value = mock_catalog | |
| bt = MagicMock() | |
| bt.get_state.return_value = _state(secret="Naruto", guesses=[]) | |
| game = MagicMock() | |
| game.check_title_match.return_value = False | |
| with ( | |
| container.core.catalog_service.override(providers.Object(cat)), | |
| container.core.blind_test_service.override(providers.Object(bt)), | |
| container.core.game_service.override(providers.Object(game)), | |
| ): | |
| resp = api_client.post( | |
| reverse("api_blindtest_guess"), {"guess": "Bleach"}, format="json" | |
| ) | |
| assert resp.status_code == 200 | |
| data = resp.json() | |
| assert data["is_correct"] is False | |
| assert data["game_over"] is False | |
| assert data["guesses"][0]["title"] == "Bleach" | |
| assert data["guesses"][0]["image"] == "http://bleach.jpg" | |
| def test_guess_unknown_title_image_none(api_client, mock_catalog): | |
| """A guess not present in the catalog -> image None, still scored.""" | |
| cat = MagicMock() | |
| cat.load_data.return_value = mock_catalog | |
| bt = MagicMock() | |
| bt.get_state.return_value = _state(secret="Naruto", guesses=[]) | |
| game = MagicMock() | |
| game.check_title_match.return_value = False | |
| with ( | |
| container.core.catalog_service.override(providers.Object(cat)), | |
| container.core.blind_test_service.override(providers.Object(bt)), | |
| container.core.game_service.override(providers.Object(game)), | |
| ): | |
| resp = api_client.post( | |
| reverse("api_blindtest_guess"), {"guess": "Unknown"}, format="json" | |
| ) | |
| assert resp.status_code == 200 | |
| data = resp.json() | |
| assert data["guesses"][0]["title"] == "Unknown" | |
| assert data["guesses"][0]["image"] is None | |
| def test_guess_correct_authenticated_creates_session(api_client, mock_catalog): | |
| from animetix.models import GameplaySession | |
| user = User.objects.create_user("btwin", password="x") | |
| api_client.force_authenticate(user=user) | |
| ach = MagicMock() | |
| ach.name = "BT Win" | |
| ach.description = "d" | |
| ach.xp_reward = 5 | |
| ach.badge_url = "http://b.png" | |
| user.profile.add_win = MagicMock(return_value=[ach]) | |
| cat = MagicMock() | |
| cat.load_data.return_value = mock_catalog | |
| bt = MagicMock() | |
| bt.get_state.return_value = _state(secret="Naruto", guesses=[]) | |
| game = MagicMock() | |
| game.check_title_match.return_value = True | |
| with ( | |
| container.core.catalog_service.override(providers.Object(cat)), | |
| container.core.blind_test_service.override(providers.Object(bt)), | |
| container.core.game_service.override(providers.Object(game)), | |
| ): | |
| resp = api_client.post( | |
| reverse("api_blindtest_guess"), {"guess": "Naruto"}, format="json" | |
| ) | |
| assert resp.status_code == 200 | |
| data = resp.json() | |
| assert data["is_correct"] is True | |
| assert data["game_over"] is True | |
| assert data["secret_title"] == "Naruto" | |
| assert data["secret_image"] == "http://naruto.jpg" | |
| assert data["newly_unlocked_achievements"][0]["name"] == "BT Win" | |
| assert GameplaySession.objects.filter(target_item="Naruto", was_won=True).exists() | |
| def test_guess_correct_add_win_raises_is_handled(api_client, mock_catalog): | |
| from animetix.models import GameplaySession | |
| user = User.objects.create_user("btwinerr", password="x") | |
| api_client.force_authenticate(user=user) | |
| user.profile.add_win = MagicMock(side_effect=RuntimeError("boom")) | |
| cat = MagicMock() | |
| cat.load_data.return_value = mock_catalog | |
| bt = MagicMock() | |
| bt.get_state.return_value = _state(secret="Naruto", guesses=[]) | |
| game = MagicMock() | |
| game.check_title_match.return_value = True | |
| with ( | |
| container.core.catalog_service.override(providers.Object(cat)), | |
| container.core.blind_test_service.override(providers.Object(bt)), | |
| container.core.game_service.override(providers.Object(game)), | |
| ): | |
| resp = api_client.post( | |
| reverse("api_blindtest_guess"), {"guess": "Naruto"}, format="json" | |
| ) | |
| assert resp.status_code == 200 | |
| data = resp.json() | |
| assert data["is_correct"] is True | |
| assert data["newly_unlocked_achievements"] == [] | |
| assert GameplaySession.objects.filter(target_item="Naruto", was_won=True).exists() | |