Update Dockerfile and settings for user permissions, add test URL patterns, and adjust volume paths

This commit is contained in:
Joakim Hellsén 2026-02-15 20:52:08 +01:00
commit b08143980c
Signed by: Joakim Hellsén
SSH key fingerprint: SHA256:/9h/CsExpFp+PRhsfA0xznFx2CGfTT5R/kpuFfUgEQk
5 changed files with 219 additions and 4 deletions

View file

@ -2,6 +2,7 @@ from __future__ import annotations
import importlib
import os
import sys
from contextlib import contextmanager
from typing import TYPE_CHECKING
@ -50,12 +51,21 @@ def reload_settings_module() -> Generator[Callable[..., ModuleType]]:
env[key] = value
with temporary_env(env):
return importlib.reload(settings)
# If another test removed the module from sys.modules (we do that in a
# few tests to simulate startup failure), importlib.reload() will
# raise. Import afresh in that case so the fixture is resilient.
if "config.settings" in sys.modules:
return importlib.reload(sys.modules["config.settings"])
return importlib.import_module("config.settings")
yield _reload
with temporary_env(original_env):
importlib.reload(settings)
# Ensure `config.settings` is restored even if some tests removed the
# module from sys.modules (several tests intentionally pop it). Use
# importlib.import_module to (re)import and then reload to execute
# module-level initialization under the original environment.
importlib.reload(importlib.import_module("config.settings"))
def test_env_bool_truthy_values(monkeypatch: pytest.MonkeyPatch) -> None:
@ -134,3 +144,149 @@ def test_sessions_app_installed() -> None:
def test_session_table_exists() -> None:
"""The sessions table should be available in the database."""
Session.objects.count()
def test_env_bool_falsey_values(monkeypatch: pytest.MonkeyPatch) -> None:
"""env_bool should treat common falsey strings as False."""
falsy_values: list[str] = ["0", "false", "no", "n", "off", "", " "]
for value in falsy_values:
monkeypatch.setenv("FEATURE_FLAG", value)
assert settings.env_bool("FEATURE_FLAG") is False
def test_env_int_invalid_raises_value_error(monkeypatch: pytest.MonkeyPatch) -> None:
"""env_int should raise ValueError for non-integer strings."""
monkeypatch.setenv("MAX_COUNT", "not-an-int")
with pytest.raises(ValueError, match="invalid literal for int"):
settings.env_int("MAX_COUNT", 1)
def test_testing_true_when_sys_argv_contains_test(
monkeypatch: pytest.MonkeyPatch,
reload_settings_module: Callable[..., ModuleType],
) -> None:
"""TESTING should be true when 'test' is present in sys.argv."""
monkeypatch.setattr("sys.argv", ["manage.py", "test"])
reloaded: ModuleType = reload_settings_module()
assert reloaded.TESTING is True
def test_testing_true_when_pytest_version_set(reload_settings_module: Callable[..., ModuleType]) -> None:
"""TESTING should be true when PYTEST_VERSION is set in the env."""
reloaded: ModuleType = reload_settings_module(PYTEST_VERSION="7.0.0")
assert reloaded.TESTING is True
def test_media_and_static_directories_created_on_import(
monkeypatch: pytest.MonkeyPatch,
tmp_path: Path,
reload_settings_module: Callable[..., ModuleType],
) -> None:
"""Importing settings should create MEDIA_ROOT and STATIC_ROOT under DATA_DIR."""
fake_dir: Path = tmp_path / "app_data"
def fake_user_data_dir(**_: str) -> str:
fake_dir.mkdir(parents=True, exist_ok=True)
return str(fake_dir)
monkeypatch.setattr("platformdirs.user_data_dir", fake_user_data_dir)
reloaded: ModuleType = reload_settings_module()
assert fake_dir == reloaded.DATA_DIR
assert reloaded.MEDIA_ROOT.exists() is True
assert reloaded.STATIC_ROOT.exists() is True
def test_missing_secret_key_causes_system_exit(monkeypatch: pytest.MonkeyPatch) -> None:
"""Missing DJANGO_SECRET_KEY should abort startup with SystemExit."""
monkeypatch.delenv("DJANGO_SECRET_KEY", raising=False)
# Prevent load_dotenv from repopulating values from the repository .env file
monkeypatch.setattr("dotenv.load_dotenv", lambda *a, **k: None)
# Remove the cached module so the import executes module-level code freshly
sys.modules.pop("config.settings", None)
with pytest.raises(SystemExit):
__import__("config.settings")
def test_email_settings_from_env(reload_settings_module: Callable[..., ModuleType]) -> None:
"""EMAIL_* values should be read from the environment and cast correctly."""
reloaded: ModuleType = reload_settings_module(
EMAIL_HOST="smtp.example.com",
EMAIL_PORT="1025",
EMAIL_USE_TLS="0",
EMAIL_USE_SSL="1",
EMAIL_HOST_USER="me@example.com",
EMAIL_HOST_PASSWORD="s3cret",
EMAIL_TIMEOUT="3",
)
assert reloaded.EMAIL_HOST == "smtp.example.com"
assert reloaded.EMAIL_PORT == 1025
assert reloaded.EMAIL_USE_TLS is False
assert reloaded.EMAIL_USE_SSL is True
assert reloaded.EMAIL_HOST_USER == "me@example.com"
assert reloaded.EMAIL_HOST_PASSWORD == "s3cret"
assert reloaded.EMAIL_TIMEOUT == 3
# DEFAULT_FROM_EMAIL and SERVER_EMAIL mirror EMAIL_HOST_USER in settings.py
assert reloaded.DEFAULT_FROM_EMAIL == "me@example.com"
assert reloaded.SERVER_EMAIL == "me@example.com"
def test_database_settings_when_not_testing(
monkeypatch: pytest.MonkeyPatch,
reload_settings_module: Callable[..., ModuleType],
) -> None:
"""When not running tests, DATABASES should use the Postgres configuration."""
# Ensure the module believes it's not running tests
monkeypatch.setattr("sys.argv", ["manage.py", "runserver"])
monkeypatch.delenv("PYTEST_VERSION", raising=False)
reloaded: ModuleType = reload_settings_module(
POSTGRES_DB="prod_db",
POSTGRES_USER="prod_user",
POSTGRES_PASSWORD="secret",
POSTGRES_HOST="db.host",
POSTGRES_PORT="5433",
CONN_MAX_AGE="120",
CONN_HEALTH_CHECKS="0",
)
assert reloaded.TESTING is False
db_cfg = reloaded.DATABASES["default"]
assert db_cfg["ENGINE"] == "django.db.backends.postgresql"
assert db_cfg["NAME"] == "prod_db"
assert db_cfg["USER"] == "prod_user"
assert db_cfg["PASSWORD"] == "secret"
assert db_cfg["HOST"] == "db.host"
assert db_cfg["PORT"] == 5433
assert db_cfg["CONN_MAX_AGE"] == 120
assert db_cfg["CONN_HEALTH_CHECKS"] is False
def test_debug_tools_installed_only_when_not_testing(
monkeypatch: pytest.MonkeyPatch,
reload_settings_module: Callable[..., ModuleType],
) -> None:
"""`debug_toolbar` and `silk` are only added when not TESTING."""
# Not testing -> tools should be present
monkeypatch.setattr("sys.argv", ["manage.py", "runserver"])
monkeypatch.delenv("PYTEST_VERSION", raising=False)
not_testing: ModuleType = reload_settings_module()
assert "debug_toolbar" in not_testing.INSTALLED_APPS
assert "silk" in not_testing.INSTALLED_APPS
# Testing -> tools should not be present
testing: ModuleType = reload_settings_module(PYTEST_VERSION="7.0.0")
assert "debug_toolbar" not in testing.INSTALLED_APPS
assert "silk" not in testing.INSTALLED_APPS
def test_logging_configuration_structure() -> None:
"""Basic logging structure and levels should be present in LOGGING."""
assert settings.LOGGING["handlers"]["console"]["class"] == "logging.StreamHandler"
assert settings.LOGGING["loggers"]["ttvdrops"]["level"] == "DEBUG"
assert settings.LOGGING["loggers"][""]["level"] == "INFO"