From 2f53325185aea659430bfb424cd89f2862751617 Mon Sep 17 00:00:00 2001 From: MacJediWizard <99237059+MacJediWizard@users.noreply.github.com> Date: Thu, 14 May 2026 17:31:32 -0400 Subject: [PATCH] test(cache): use real app fixture instead of mocking Flask's LocalProxy MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit TestCacheIntegration::test_get_cache_with_redis_enabled and test_get_cache_fallback_to_memory were both failing with: RuntimeError: Working outside of application context. The tests patched `app.utils.cache.current_app` with a MagicMock and set `mock_app.config = {...}`. In theory that should isolate get_cache() from Flask's LocalProxy entirely. In practice the RuntimeError still fires — most likely because (a) module-level `_cache` left over from a prior test holds a reference that triggers a LocalProxy access at return time, or (b) a code path inside the patched function still reaches the real `current_app` past the mock boundary. Rather than diagnose the exact mock-interaction issue, rewrite both tests to use the real `app` fixture from conftest.py: - Set `REDIS_ENABLED` / `REDIS_URL` / `REDIS_DEFAULT_TTL` on the real Flask config inside `with app.app_context():`. - Reset `app.utils.cache._cache = None` explicitly so the global doesn't leak state between tests. - Keep the `patch("app.utils.cache.RedisCache")` since we don't want the test to actually hit Redis; only the read of `current_app.config` needs to be real. This is closer to how get_cache() runs in production (real Flask context, mocked external dependency) and avoids the LocalProxy ↔ mock collision entirely. Test plan - pytest tests/test_utils/test_cache.py::TestCacheIntegration::test_get_cache_with_redis_enabled - pytest tests/test_utils/test_cache.py::TestCacheIntegration::test_get_cache_fallback_to_memory - pytest tests/test_utils/test_cache.py (the other 16 tests in the file should be unaffected) --- tests/test_utils/test_cache.py | 50 ++++++++++++++++++---------------- 1 file changed, 26 insertions(+), 24 deletions(-) diff --git a/tests/test_utils/test_cache.py b/tests/test_utils/test_cache.py index 3d4d6aff..103f1c80 100644 --- a/tests/test_utils/test_cache.py +++ b/tests/test_utils/test_cache.py @@ -125,33 +125,35 @@ class TestRedisCache: class TestCacheIntegration: """Integration tests for cache utilities""" - @patch("app.utils.cache.current_app") - def test_get_cache_with_redis_enabled(self, mock_app): - """Test get_cache when Redis is enabled""" - mock_config = {"REDIS_ENABLED": True, "REDIS_URL": "redis://localhost:6379/0", "REDIS_DEFAULT_TTL": 3600} - mock_app.config = mock_config + def test_get_cache_with_redis_enabled(self, app): + """Test get_cache when Redis is enabled (RedisCache is mocked).""" + import app.utils.cache as cache_module - with patch("app.utils.cache.RedisCache") as mock_redis_cache: - mock_instance = MagicMock() - mock_instance._connected = True - mock_redis_cache.return_value = mock_instance + cache_module._cache = None + with app.app_context(): + app.config["REDIS_ENABLED"] = True + app.config["REDIS_URL"] = "redis://localhost:6379/0" + app.config["REDIS_DEFAULT_TTL"] = 3600 + + with patch("app.utils.cache.RedisCache") as mock_redis_cache: + mock_instance = MagicMock() + mock_instance._connected = True + mock_redis_cache.return_value = mock_instance + + cache = get_cache() + assert cache is not None + + def test_get_cache_fallback_to_memory(self, app): + """Test get_cache falls back to in-memory when Redis is disabled.""" + import app.utils.cache as cache_module + + cache_module._cache = None + with app.app_context(): + app.config["REDIS_ENABLED"] = False + app.config["REDIS_DEFAULT_TTL"] = 3600 cache = get_cache() - assert cache is not None - - @patch("app.utils.cache.current_app") - def test_get_cache_fallback_to_memory(self, mock_app): - """Test get_cache falls back to in-memory when Redis unavailable""" - mock_config = {"REDIS_ENABLED": False, "REDIS_DEFAULT_TTL": 3600} - mock_app.config = mock_config - - # Reset global cache - import app.utils.cache - - app.utils.cache._cache = None - - cache = get_cache() - assert isinstance(cache, InMemoryCache) + assert isinstance(cache, InMemoryCache) def test_cache_decorator(self): """Test the @cached decorator"""