158 lines
5.8 KiB
Python
158 lines
5.8 KiB
Python
from unittest import mock
|
|
|
|
import httpx
|
|
from django.core.cache import cache
|
|
from django.test import TestCase, override_settings
|
|
|
|
from accounts.models import CustomUser
|
|
from events.models import Event
|
|
|
|
from .models import EventGame, Game
|
|
from .services.steam_store import refresh_steam_games
|
|
from .tasks import refresh_steam_games_task, schedule_steam_refresh
|
|
|
|
LOCMEM_CACHE = {"default": {"BACKEND": "django.core.cache.backends.locmem.LocMemCache"}}
|
|
|
|
|
|
def steam_response(app_id, data, success=True):
|
|
return httpx.Response(
|
|
200,
|
|
json={str(app_id): {"success": success, "data": data}},
|
|
request=httpx.Request("GET", "https://store.steampowered.com"),
|
|
)
|
|
|
|
|
|
class RefreshSteamGamesTests(TestCase):
|
|
def setUp(self):
|
|
self.game = Game.objects.create(name="Noita", path="noita", steam_app_id=881100)
|
|
|
|
@mock.patch("games.services.steam_store.httpx.get")
|
|
def test_stores_price_and_discount(self, get):
|
|
get.return_value = steam_response(
|
|
881100,
|
|
{
|
|
"is_free": False,
|
|
"short_description": "Every pixel is simulated.",
|
|
"header_image": "https://example.com/header.jpg",
|
|
"price_overview": {
|
|
"currency": "CHF",
|
|
"initial": 1950,
|
|
"final": 780,
|
|
"discount_percent": 60,
|
|
},
|
|
},
|
|
)
|
|
|
|
refresh_steam_games()
|
|
|
|
self.game.refresh_from_db()
|
|
self.assertEqual(self.game.steam_price_initial, 1950)
|
|
self.assertEqual(self.game.steam_price_final, 780)
|
|
self.assertEqual(self.game.steam_discount_percent, 60)
|
|
self.assertEqual(self.game.steam_currency, "CHF")
|
|
self.assertEqual(self.game.steam_short_description, "Every pixel is simulated.")
|
|
self.assertIsNotNone(self.game.steam_info_updated_at)
|
|
|
|
@mock.patch("games.services.steam_store.httpx.get")
|
|
def test_free_game_has_no_price(self, get):
|
|
get.return_value = steam_response(881100, {"is_free": True})
|
|
|
|
refresh_steam_games()
|
|
|
|
self.game.refresh_from_db()
|
|
self.assertTrue(self.game.steam_is_free)
|
|
self.assertIsNone(self.game.steam_price_final)
|
|
|
|
@mock.patch("games.services.steam_store.httpx.get")
|
|
def test_failure_keeps_previous_info(self, get):
|
|
Game.objects.filter(pk=self.game.pk).update(steam_price_final=999)
|
|
get.side_effect = httpx.ConnectError("down")
|
|
|
|
refresh_steam_games()
|
|
|
|
self.game.refresh_from_db()
|
|
self.assertEqual(self.game.steam_price_final, 999)
|
|
|
|
|
|
@override_settings(CACHES=LOCMEM_CACHE)
|
|
class ScheduleSteamRefreshTests(TestCase):
|
|
def setUp(self):
|
|
cache.clear()
|
|
|
|
@mock.patch("games.tasks.refresh_steam_games_task")
|
|
def test_only_one_loop_is_scheduled(self, task):
|
|
self.assertTrue(schedule_steam_refresh())
|
|
self.assertFalse(schedule_steam_refresh())
|
|
task.enqueue.assert_called_once_with()
|
|
|
|
@mock.patch("games.tasks.refresh_steam_games")
|
|
def test_task_reschedules_itself_even_on_failure(self, refresh):
|
|
refresh.side_effect = RuntimeError("boom")
|
|
with mock.patch("games.tasks.refresh_steam_games_task") as patched:
|
|
with self.assertRaises(RuntimeError):
|
|
refresh_steam_games_task.call()
|
|
|
|
patched.using.return_value.enqueue.assert_called_once_with()
|
|
|
|
@mock.patch("games.tasks.refresh_steam_games")
|
|
def test_one_off_run_does_not_reschedule(self, refresh):
|
|
with mock.patch("games.tasks.refresh_steam_games_task") as patched:
|
|
refresh_steam_games_task.call(game_ids=[1], reschedule=False)
|
|
|
|
refresh.assert_called_once_with([1])
|
|
patched.using.assert_not_called()
|
|
|
|
|
|
@override_settings(CACHES=LOCMEM_CACHE)
|
|
class EventSteamGamesApiTests(TestCase):
|
|
def setUp(self):
|
|
self.event = Event.objects.create(name="Event", path="event")
|
|
noita = Game.objects.create(
|
|
name="Noita", path="noita", steam_app_id=881100, steam_price_final=780
|
|
)
|
|
clock = Game.objects.create(name="Clock", path="clock", custom_path=True)
|
|
EventGame.objects.create(event=self.event, game=noita)
|
|
EventGame.objects.create(event=self.event, game=clock)
|
|
user = CustomUser.objects.create_user(username="user", password="x")
|
|
self.client.force_login(user)
|
|
|
|
@mock.patch("games.api.schedule_steam_refresh")
|
|
def test_lists_only_steam_games_with_store_info(self, schedule):
|
|
response = self.client.get("/api/events/event/games/steam")
|
|
|
|
self.assertEqual(response.status_code, 200)
|
|
[game] = response.json()
|
|
self.assertEqual(game["name"], "Noita")
|
|
self.assertEqual(game["price_final"], 780)
|
|
self.assertEqual(game["store_url"], "https://store.steampowered.com/app/881100")
|
|
schedule.assert_called_once()
|
|
|
|
|
|
class EventGamesDescriptionTests(TestCase):
|
|
def setUp(self):
|
|
self.event = Event.objects.create(name="Event", path="event")
|
|
self.user = CustomUser.objects.create_user(username="user", password="x")
|
|
self.client.force_login(self.user)
|
|
|
|
def test_steam_description_is_fallback_for_admin_one(self):
|
|
steam = Game.objects.create(
|
|
name="Noita",
|
|
path="noita",
|
|
steam_app_id=881100,
|
|
steam_short_description="From Steam",
|
|
)
|
|
custom = Game.objects.create(
|
|
name="Opus",
|
|
path="opus",
|
|
steam_app_id=558990,
|
|
description="From admin",
|
|
steam_short_description="From Steam",
|
|
)
|
|
EventGame.objects.create(event=self.event, game=steam)
|
|
EventGame.objects.create(event=self.event, game=custom)
|
|
|
|
response = self.client.get("/api/events/event/games")
|
|
|
|
descriptions = {g["name"]: g["description"] for g in response.json()}
|
|
self.assertEqual(descriptions, {"Noita": "From Steam", "Opus": "From admin"})
|