import importlib import os from unittest.mock import patch from django.core.exceptions import ImproperlyConfigured from django.test import SimpleTestCase class SecretKeyFailClosedTests(SimpleTestCase): """ تست‌های متمرکز روی این فیکس: SECRET_KEY دیگه در پروداکشن (DEBUG=False) دیفالتِ هاردکد/قابل‌حدس‌زدن نداره. اگه DJANGO_SECRET_KEY ست نشده باشه، settings.py باید fail-closed بشه (خطای صریح موقعِ import)، نه اینکه با یک fallback شناخته‌شده بالا بیاد. برای DEBUG=True، fallbackِ محلیِ قبلی باید دست‌نخورده بمونه. مثل SecretDefaultsFailClosedTests در payment/tests.py، چون SECRET_KEY فقط یک‌بار موقعِ import محاسبه می‌شه (نه چیزی که override_settings بتونه toggle کنه)، ماژولِ config.settings رو با متغیرهای محیطیِ کنترل‌شده reload می‌کنیم. """ allow_database_queries = True def _reload_settings_with_env(self, env_overrides, env_removed=()): from config import settings as settings_module clean_env = {k: v for k, v in os.environ.items() if k not in env_removed} clean_env.update(env_overrides) with patch.dict(os.environ, clean_env, clear=True): importlib.reload(settings_module) return settings_module def tearDown(self): # همیشه ماژول رو به حالتِ واقعیِ محیطِ تست برمی‌گردونیم تا روی # بقیه‌ی تست‌ها اثر نذاره. from config import settings as settings_module importlib.reload(settings_module) def test_production_missing_secret_key_fails_closed(self): with self.assertRaises(ImproperlyConfigured): self._reload_settings_with_env( env_overrides={'DJANGO_DEBUG': ''}, env_removed={'DJANGO_SECRET_KEY'}, ) def test_production_configured_secret_key_used(self): # ⚠️ اصلاح (باگِ خودِ تست، نه اپلیکیشن): این تست فقط رفتارِ # SECRET_KEY رو می‌سنجه، اما چون CACHE_URL رو ست نمی‌کرد، وقتی # فیکسِ جداگانه‌ی CACHE_URL fail-closed (پایین‌ترِ همین فایل، نگاه # کنید به CacheUrlFailClosedTests) اضافه شد، همین import/reload با # DEBUG=False و بدونِ CACHE_URL به‌جای رسیدن به assertion، با # ImproperlyConfigured مربوط به CACHE_URL (نه SECRET_KEY) fail # می‌شد — یک false negative روی چیزی که این تست اصلاً قرار نیست # بسنجه. یک CACHE_URL معتبر هم ست می‌کنیم تا این تست فقط دقیقاً # همون چیزی که اسمش می‌گه (SECRET_KEY) رو بسنجه. settings_module = self._reload_settings_with_env( env_overrides={ 'DJANGO_DEBUG': '', 'DJANGO_SECRET_KEY': 'prod-secret-xyz', 'CACHE_URL': 'redis://localhost:6379/0', }, ) self.assertFalse(settings_module.DEBUG) self.assertEqual(settings_module.SECRET_KEY, 'prod-secret-xyz') def test_debug_mode_missing_secret_key_uses_local_fallback(self): settings_module = self._reload_settings_with_env( env_overrides={'DJANGO_DEBUG': '1'}, env_removed={'DJANGO_SECRET_KEY'}, ) self.assertTrue(settings_module.DEBUG) self.assertEqual( settings_module.SECRET_KEY, 'django-insecure-jn!s*25lz^+bjm101tzmlz8jlj70i0qka++*h-(!0s^^vk-0ss', ) class CacheUrlFailClosedTests(SimpleTestCase): """ 🔒 تستِ متمرکز روی فیکسِ CACHE_URL: در پروداکشن (DEBUG=False)، اگه CACHE_URL ست نشده باشه، settings.py باید fail-closed بشه (خطای صریح موقعِ import)، نه fallback خاموش به LocMemCache (که بینِ worker/instance ها مشترک نیست و throttle/rate-limit ها رو بی‌اثر می‌کنه). """ allow_database_queries = True def _reload_settings_with_env(self, env_overrides, env_removed=()): from config import settings as settings_module clean_env = {k: v for k, v in os.environ.items() if k not in env_removed} clean_env.update(env_overrides) with patch.dict(os.environ, clean_env, clear=True): importlib.reload(settings_module) return settings_module def tearDown(self): from config import settings as settings_module importlib.reload(settings_module) def test_production_missing_cache_url_fails_closed(self): with self.assertRaises(ImproperlyConfigured): self._reload_settings_with_env( env_overrides={'DJANGO_DEBUG': '', 'DJANGO_SECRET_KEY': 'prod-secret-xyz'}, env_removed={'CACHE_URL'}, ) def test_debug_mode_missing_cache_url_uses_locmem_fallback(self): settings_module = self._reload_settings_with_env( env_overrides={'DJANGO_DEBUG': '1'}, env_removed={'CACHE_URL'}, ) self.assertTrue(settings_module.DEBUG) self.assertEqual( settings_module.CACHES['default']['BACKEND'], 'django.core.cache.backends.locmem.LocMemCache', )