animetix-web / tests /api /games /test_blindtest_coverage.py
MissawB's picture
Upload folder using huggingface_hub (part 6)
5294c4d verified
Raw
History Blame Contribute Delete
12.7 kB
"""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
@pytest.fixture
def api_client():
return APIClient()
@pytest.fixture
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
# --------------------------------------------------------------------------- #
@pytest.mark.django_db
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"
@pytest.mark.django_db
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
@pytest.mark.django_db
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
# --------------------------------------------------------------------------- #
@pytest.mark.django_db
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"
@pytest.mark.django_db
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"
@pytest.mark.django_db
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()
@pytest.mark.django_db
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
# --------------------------------------------------------------------------- #
@pytest.mark.django_db
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"
@pytest.mark.django_db
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"
@pytest.mark.django_db
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"
@pytest.mark.django_db
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"
@pytest.mark.django_db
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
@pytest.mark.django_db
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()
@pytest.mark.django_db
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()