Add Redis configuration, integrate Celery, and sort fields in models
All checks were successful
Deploy to Server / deploy (push) Successful in 49s

This commit is contained in:
Joakim Hellsén 2026-03-21 19:12:47 +01:00
commit d99579ed2b
Signed by: Joakim Hellsén
SSH key fingerprint: SHA256:/9h/CsExpFp+PRhsfA0xznFx2CGfTT5R/kpuFfUgEQk
15 changed files with 451 additions and 253 deletions

View file

@ -0,0 +1,5 @@
# This will make sure the app is always imported when
# Django starts so that shared_task will use this app.
from .celery import app as celery_app
__all__ = ("celery_app",)

15
config/celery.py Normal file
View file

@ -0,0 +1,15 @@
import os
from celery import Celery
# Set the default Django settings module for the 'celery' program.
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings")
app = Celery("config")
# Using a string here means the worker doesn't have to serialize
# the configuration object to child processes.
app.config_from_object("django.conf:settings", namespace="CELERY")
# Load task modules from all registered Django apps.
app.autodiscover_tasks()

View file

@ -135,14 +135,19 @@ LOGGING: dict[str, Any] = {
}
INSTALLED_APPS: list[str] = [
# Django built-in apps
"django.contrib.contenttypes",
"django.contrib.sessions",
"django.contrib.staticfiles",
"django.contrib.postgres",
# Internal apps
"twitch.apps.TwitchConfig",
"kick.apps.KickConfig",
"youtube.apps.YoutubeConfig",
"core.apps.CoreConfig",
# Third-party apps
"django_celery_results",
"django_celery_beat",
]
MIDDLEWARE: list[str] = [
@ -202,3 +207,24 @@ if not TESTING:
profile_session_sample_rate=1.0,
profile_lifecycle="trace",
)
REDIS_URL_CACHE: str = os.getenv(
key="REDIS_URL_CACHE",
default="redis://localhost:6379/0",
)
REDIS_URL_CELERY: str = os.getenv(
key="REDIS_URL_CELERY",
default="redis://localhost:6379/1",
)
CACHES: dict[str, dict[str, str]] = {
"default": {
"BACKEND": "django.core.cache.backends.redis.RedisCache",
"LOCATION": REDIS_URL_CACHE,
},
}
CELERY_BROKER_URL: str = REDIS_URL_CELERY
CELERY_RESULT_BACKEND = "django-db"
CELERY_RESULT_EXTENDED = True
CELERY_BEAT_SCHEDULER = "django_celery_beat.schedulers:DatabaseScheduler"

View file

@ -0,0 +1,41 @@
from typing import TYPE_CHECKING
from typing import Any
from unittest.mock import patch
import pytest
from celery import Celery
if TYPE_CHECKING:
from collections.abc import Generator
@pytest.fixture
def celery_app() -> Generator[Celery, Any]:
"""Fixture to create a Celery app instance for testing.
Yields:
Celery: A Celery app instance configured for testing.
"""
with patch("os.environ.setdefault") as mock_setenv: # noqa: F841
app = Celery("config")
app.config_from_object("django.conf:settings", namespace="CELERY")
yield app
def test_celery_app_initialization(celery_app: Celery) -> None:
"""Test that the Celery app is initialized with the correct main module."""
assert celery_app.main == "config"
def test_celery_app_config(celery_app: Celery) -> None:
"""Test that the Celery app is configured with the correct settings."""
with patch("celery.Celery.config_from_object") as mock_config:
celery_app.config_from_object("django.conf:settings", namespace="CELERY")
mock_config.assert_called_once_with("django.conf:settings", namespace="CELERY")
def test_celery_task_discovery(celery_app: Celery) -> None:
"""Test that the Celery app discovers tasks correctly."""
with patch("celery.Celery.autodiscover_tasks") as mock_discover:
celery_app.autodiscover_tasks()
mock_discover.assert_called_once()

View file

@ -1,9 +1,14 @@
import importlib
from typing import TYPE_CHECKING
import pytest
from django.conf import settings
from django.test.utils import override_settings
from django.urls import resolve
from django.urls import reverse
from config.urls import urlpatterns
if TYPE_CHECKING:
from collections.abc import Iterable
from types import ModuleType
@ -50,3 +55,43 @@ def test_debug_tools_not_present_while_testing() -> None:
patterns = list(_pattern_strings(mod))
assert not any("silk" in p for p in patterns)
assert not any("__debug__" in p or "debug" in p for p in patterns)
@pytest.mark.parametrize(
"url_name",
[
"sitemap",
"sitemap-static",
"sitemap-twitch-channels",
"sitemap-twitch-drops",
"sitemap-twitch-others",
"sitemap-kick",
"sitemap-youtube",
],
)
def test_static_sitemap_urls(url_name: str) -> None:
"""Test that static sitemap URLs resolve correctly."""
url: str = reverse(url_name)
resolved_view: str = resolve(url).view_name
assert resolved_view == url_name
def test_app_url_inclusion() -> None:
"""Test that app-specific URLs are included in urlpatterns."""
assert any("core.urls" in str(pattern) for pattern in urlpatterns)
assert any("twitch.urls" in str(pattern) for pattern in urlpatterns)
assert any("kick.urls" in str(pattern) for pattern in urlpatterns)
assert any("youtube.urls" in str(pattern) for pattern in urlpatterns)
def test_media_serving_in_development() -> None:
"""Test that media URLs are served in development mode."""
if settings.DEBUG:
assert any("MEDIA_URL" in str(pattern) for pattern in urlpatterns)
def test_debug_toolbar_and_silk_urls() -> None:
"""Test that debug toolbar and Silk URLs are included when appropriate."""
if not settings.TESTING:
assert any("silk.urls" in str(pattern) for pattern in urlpatterns)
assert any("debug_toolbar" in str(pattern) for pattern in urlpatterns)