import importlib import sys from typing import Any import pytest from django.utils import autoreload def load_settings_for_runtime( monkeypatch: pytest.MonkeyPatch, *, debug: bool, testing: bool, ) -> dict[str, Any]: """Load the settings module with environment variables set for the given runtime. Returns: The settings as a dictionary. Note that this is not a full Django settings object, but just the module's global variables. """ # DJANGO_SECRET_KEY must be set to a non-empty value to not exit early with an error about missing secret key. # If DJANGO_DEBUG is set to True or PYTEST_VERSION is set, the settings module will consider it a non-production runtime and load debug-only components. monkeypatch.setenv(name="DJANGO_SECRET_KEY", value="test-secret-key") monkeypatch.setenv(name="DJANGO_DEBUG", value="true" if debug else "false") if testing: monkeypatch.setenv(name="PYTEST_VERSION", value="9.0.0") monkeypatch.setattr(target="sys.argv", name=["manage.py", "test"]) else: monkeypatch.delenv(name="PYTEST_VERSION", raising=False) monkeypatch.setattr(target="sys.argv", name=["manage.py", "runserver"]) # Remove the settings module from sys.modules to force it to be reloaded with the new environment variables. original_get_reloader = autoreload.get_reloader sys.modules.pop("config.settings", None) try: settings_module = importlib.import_module("config.settings") return vars(settings_module).copy() finally: autoreload.get_reloader = original_get_reloader def test_production_runtime_disables_debug_stuff( monkeypatch: pytest.MonkeyPatch, ) -> None: """Production mode must not load any debug-only components.""" settings: dict[str, Any] = load_settings_for_runtime( monkeypatch=monkeypatch, debug=False, testing=False, ) msg: str = f"Expected production runtime to have DEBUG=False and TESTING=False, but got DEBUG={settings['DEBUG']} and TESTING={settings['TESTING']}" assert settings["DEBUG"] is False, msg assert settings["TESTING"] is False, msg middleware: list[str] = settings["MIDDLEWARE"] msg = f"Expected MIDDLEWARE to be a list, but got {type(middleware)}" assert isinstance(middleware, list), msg msg = f"Expected production runtime to not have debug-only middleware, but got {middleware}" assert "debug_toolbar.middleware.DebugToolbarMiddleware" not in middleware, msg msg = f"Expected production runtime to not have django_browser_reload middleware, but got {middleware}" assert "django_browser_reload.middleware.BrowserReloadMiddleware" not in middleware, msg # fmt: skip msg = f"Expected production runtime to not have zeal middleware, but got {middleware}" # fmt: skip assert "zeal.middleware.zeal_middleware" not in middleware, msg installed_apps: list[str] = settings["INSTALLED_APPS"] msg = f"Expected INSTALLED_APPS to be a list, but got {type(installed_apps)}" assert isinstance(installed_apps, list), msg msg = f"debug_toolbar should not be in INSTALLED_APPS in production runtime, but got {installed_apps}" assert "debug_toolbar" not in installed_apps, msg msg = f"django_browser_reload should not be in INSTALLED_APPS in production runtime, but got {installed_apps}" assert "django_browser_reload" not in installed_apps, msg msg = f"django_watchfiles should not be in INSTALLED_APPS in production runtime, but got {installed_apps}" assert "django_watchfiles" not in installed_apps, msg msg = f"zeal should not be in INSTALLED_APPS in production runtime, but got {installed_apps}" assert "zeal" not in installed_apps, msg msg = f"DEBUG_TOOLBAR_CONFIG should not be in settings in production runtime, but got {settings}" assert "DEBUG_TOOLBAR_CONFIG" not in settings, msg @pytest.mark.parametrize( ("debug", "testing"), [ (True, False), (False, True), ], ) def test_dev_or_testing_runtime_enables_debug_stuff( monkeypatch: pytest.MonkeyPatch, debug: bool, testing: bool, ) -> None: """Dev or test mode must load all debug-only components.""" settings: dict[str, Any] = load_settings_for_runtime( monkeypatch=monkeypatch, debug=debug, testing=testing, ) msg: str = f"Expected runtime to have DEBUG={debug} and TESTING={testing}, but got DEBUG={settings['DEBUG']} and TESTING={settings['TESTING']}" assert settings["DEBUG"] is debug, msg assert settings["TESTING"] is testing, msg middleware: list[str] = settings["MIDDLEWARE"] msg = f"Expected MIDDLEWARE to be a list, but got {type(middleware)}" assert isinstance(middleware, list), msg msg = f"Expected runtime to have debug-only middleware, but got {middleware}" assert "debug_toolbar.middleware.DebugToolbarMiddleware" in middleware, msg assert "django_browser_reload.middleware.BrowserReloadMiddleware" in middleware, msg assert "zeal.middleware.zeal_middleware" in middleware, msg installed_apps: list[str] = settings["INSTALLED_APPS"] msg = f"Expected INSTALLED_APPS to be a list, but got {type(installed_apps)}" assert isinstance(installed_apps, list), msg msg = f"debug_toolbar should be in INSTALLED_APPS in dev/test runtime, but got {installed_apps}" assert "debug_toolbar" in installed_apps, msg assert "django_browser_reload" in installed_apps, msg assert "django_watchfiles" in installed_apps, msg assert "zeal" in installed_apps, msg msg = f"DEBUG_TOOLBAR_CONFIG should be in settings in dev/test runtime, but got {settings}" assert "DEBUG_TOOLBAR_CONFIG" in settings, msg msg = f"Expected DEBUG_TOOLBAR_CONFIG to be a dict, but got {type(settings['DEBUG_TOOLBAR_CONFIG'])}" assert isinstance(settings["DEBUG_TOOLBAR_CONFIG"], dict), msg msg = f"Expected DEBUG_TOOLBAR_CONFIG to have ROOT_TAG_EXTRA_ATTRS='hx-preserve', but got {settings['DEBUG_TOOLBAR_CONFIG']}" assert settings["DEBUG_TOOLBAR_CONFIG"] == {"ROOT_TAG_EXTRA_ATTRS": "hx-preserve"}