"""Tests for ArchitectureMemory Redis fallback behavior.""" import json from pathlib import Path from unittest.mock import MagicMock, patch import pytest def _count_default_patterns_in_json() -> int: """Return the number of patterns in the fallback JSON file.""" from app.core.architecture_memory import _FALLBACK_PATTERNS_PATH with open(_FALLBACK_PATTERNS_PATH) as f: return len(json.load(f)) class TestArchitectureMemoryRedisFallback: """DS-07: ArchitectureMemory should fall back to local file when Redis is unavailable.""" @patch("app.core.architecture_memory.settings") async def test_seed_falls_back_to_local_file( self, mock_settings ) -> None: """When Redis is not configured, seed_default_patterns loads from local file.""" mock_settings.upstash_redis_rest_url = "" mock_settings.upstash_redis_rest_token = "" from app.core.architecture_memory import ArchitectureMemory mem = ArchitectureMemory() assert not mem.is_configured seeded = await mem.seed_default_patterns() expected = _count_default_patterns_in_json() assert seeded == expected @patch("app.core.architecture_memory.settings") async def test_cache_session_returns_false_no_crash( self, mock_settings ) -> None: """When Redis is unavailable, cache_session returns False without crashing.""" mock_settings.upstash_redis_rest_url = "" mock_settings.upstash_redis_rest_token = "" from app.core.architecture_memory import ArchitectureMemory mem = ArchitectureMemory() assert not mem.is_configured result = await mem.cache_session("test-session", {"key": "value"}) assert result is False @patch("app.core.architecture_memory.settings") async def test_get_pattern_returns_none_when_redis_unavailable( self, mock_settings ) -> None: """When Redis is unavailable, get_pattern returns None.""" mock_settings.upstash_redis_rest_url = "" mock_settings.upstash_redis_rest_token = "" from app.core.architecture_memory import ArchitectureMemory mem = ArchitectureMemory() assert not mem.is_configured pattern = await mem.get_pattern("pattern_monolithic") assert pattern is None @patch("app.core.architecture_memory.settings") async def test_redis_unavailable_then_recovers( self, mock_settings ) -> None: """When Redis returns to health, subsequent calls use Redis again.""" mock_settings.upstash_redis_rest_url = "" mock_settings.upstash_redis_rest_token = "" from app.core.architecture_memory import ArchitectureMemory from app.core.architecture_memory import _FALLBACK_PATTERNS_PATH mem = ArchitectureMemory() assert not mem.is_configured # Seed from fallback - should work without Redis seeded = await mem.seed_default_patterns() assert seeded > 0 # Now simulate Redis being configured by patching URL and token mock_settings.upstash_redis_rest_url = "https://test.upstash.com" mock_settings.upstash_redis_rest_token = "test-token" # Re-create memory so it connects mem2 = ArchitectureMemory() # It won't actually connect (mock redis), but is_configured should # be True because settings now have values assert mem2.url == "https://test.upstash.com" # The initialization will attempt to import redis - let's mock it # For this test, just verify the settings were picked up @patch("app.core.architecture_memory.settings") async def test_seed_returns_zero_without_any_fallback( self, mock_settings ) -> None: """When fallback file is missing, seed_default_patterns returns 0.""" mock_settings.upstash_redis_rest_url = "" mock_settings.upstash_redis_rest_token = "" from app.core.architecture_memory import ( ArchitectureMemory, _FALLBACK_PATTERNS_PATH, ) mem = ArchitectureMemory() assert not mem.is_configured original_path = _FALLBACK_PATTERNS_PATH # Temporarily point to a non-existent file with patch( "app.core.architecture_memory._FALLBACK_PATTERNS_PATH", Path("/nonexistent/patterns.json"), ): seeded = await mem.seed_default_patterns() assert seeded == 0