""" Tests for Memory System Configuration. """ import pytest from pydantic import ValidationError from app.services.memory.config import ( MemorySettings, get_default_settings, get_memory_settings, reset_memory_settings, ) class TestMemorySettings: """Tests for MemorySettings class.""" def test_default_settings(self) -> None: """Test that default settings are valid.""" settings = MemorySettings() # Working memory defaults assert settings.working_memory_backend == "redis" assert settings.working_memory_default_ttl_seconds == 3600 assert settings.working_memory_max_items_per_session == 1000 # Redis defaults assert settings.redis_url == "redis://localhost:6379/0" assert settings.redis_prefix == "mem" # Episodic defaults assert settings.episodic_max_episodes_per_project == 10000 assert settings.episodic_default_importance == 0.5 # Semantic defaults assert settings.semantic_max_facts_per_project == 50000 assert settings.semantic_min_confidence == 0.1 # Procedural defaults assert settings.procedural_max_procedures_per_project == 1000 assert settings.procedural_min_success_rate == 0.3 # Embedding defaults assert settings.embedding_model == "text-embedding-3-small" assert settings.embedding_dimensions == 1536 # Retrieval defaults assert settings.retrieval_default_limit == 10 assert settings.retrieval_max_limit == 100 def test_invalid_backend(self) -> None: """Test that invalid backend raises error.""" with pytest.raises(ValidationError) as exc_info: MemorySettings(working_memory_backend="invalid") assert "backend must be one of" in str(exc_info.value) def test_valid_backends(self) -> None: """Test valid backend values.""" redis_settings = MemorySettings(working_memory_backend="redis") assert redis_settings.working_memory_backend == "redis" memory_settings = MemorySettings(working_memory_backend="memory") assert memory_settings.working_memory_backend == "memory" def test_invalid_embedding_model(self) -> None: """Test that invalid embedding model raises error.""" with pytest.raises(ValidationError) as exc_info: MemorySettings(embedding_model="invalid-model") assert "embedding_model must be one of" in str(exc_info.value) def test_valid_embedding_models(self) -> None: """Test valid embedding model values.""" for model in [ "text-embedding-3-small", "text-embedding-3-large", "text-embedding-ada-002", ]: settings = MemorySettings(embedding_model=model) assert settings.embedding_model == model def test_retrieval_limit_validation(self) -> None: """Test that default limit cannot exceed max limit.""" with pytest.raises(ValidationError) as exc_info: MemorySettings( retrieval_default_limit=50, retrieval_max_limit=25, ) assert "cannot exceed retrieval_max_limit" in str(exc_info.value) def test_valid_retrieval_limits(self) -> None: """Test valid retrieval limit combinations.""" settings = MemorySettings( retrieval_default_limit=10, retrieval_max_limit=50, ) assert settings.retrieval_default_limit == 10 assert settings.retrieval_max_limit == 50 # Equal limits should be valid settings = MemorySettings( retrieval_default_limit=25, retrieval_max_limit=25, ) assert settings.retrieval_default_limit == 25 assert settings.retrieval_max_limit == 25 def test_ttl_bounds(self) -> None: """Test TTL setting bounds.""" # Valid TTL settings = MemorySettings(working_memory_default_ttl_seconds=1800) assert settings.working_memory_default_ttl_seconds == 1800 # Too low with pytest.raises(ValidationError): MemorySettings(working_memory_default_ttl_seconds=30) # Too high with pytest.raises(ValidationError): MemorySettings(working_memory_default_ttl_seconds=100000) def test_confidence_bounds(self) -> None: """Test confidence score bounds.""" # Valid confidence settings = MemorySettings(semantic_min_confidence=0.5) assert settings.semantic_min_confidence == 0.5 # Bounds settings = MemorySettings(semantic_min_confidence=0.0) assert settings.semantic_min_confidence == 0.0 settings = MemorySettings(semantic_min_confidence=1.0) assert settings.semantic_min_confidence == 1.0 # Out of bounds with pytest.raises(ValidationError): MemorySettings(semantic_min_confidence=-0.1) with pytest.raises(ValidationError): MemorySettings(semantic_min_confidence=1.1) def test_get_working_memory_config(self) -> None: """Test working memory config dictionary.""" settings = MemorySettings() config = settings.get_working_memory_config() assert config["backend"] == "redis" assert config["default_ttl_seconds"] == 3600 assert config["max_items_per_session"] == 1000 assert config["max_value_size_bytes"] == 1048576 assert config["checkpoint_enabled"] is True def test_get_redis_config(self) -> None: """Test Redis config dictionary.""" settings = MemorySettings() config = settings.get_redis_config() assert config["url"] == "redis://localhost:6379/0" assert config["prefix"] == "mem" assert config["connection_timeout_seconds"] == 5 def test_get_embedding_config(self) -> None: """Test embedding config dictionary.""" settings = MemorySettings() config = settings.get_embedding_config() assert config["model"] == "text-embedding-3-small" assert config["dimensions"] == 1536 assert config["batch_size"] == 100 assert config["cache_enabled"] is True def test_get_consolidation_config(self) -> None: """Test consolidation config dictionary.""" settings = MemorySettings() config = settings.get_consolidation_config() assert config["enabled"] is True assert config["batch_size"] == 100 assert config["schedule_cron"] == "0 3 * * *" assert config["working_to_episodic_delay_minutes"] == 30 def test_to_dict(self) -> None: """Test full settings to dictionary.""" settings = MemorySettings() config = settings.to_dict() assert "working_memory" in config assert "redis" in config assert "episodic" in config assert "semantic" in config assert "procedural" in config assert "embedding" in config assert "retrieval" in config assert "consolidation" in config assert "pruning" in config assert "cache" in config assert "performance" in config class TestMemorySettingsSingleton: """Tests for MemorySettings singleton functions.""" def setup_method(self) -> None: """Reset singleton before each test.""" reset_memory_settings() def teardown_method(self) -> None: """Reset singleton after each test.""" reset_memory_settings() def test_get_memory_settings_singleton(self) -> None: """Test that get_memory_settings returns same instance.""" settings1 = get_memory_settings() settings2 = get_memory_settings() assert settings1 is settings2 def test_reset_memory_settings(self) -> None: """Test that reset creates new instance.""" settings1 = get_memory_settings() reset_memory_settings() settings2 = get_memory_settings() assert settings1 is not settings2 def test_get_default_settings_cached(self) -> None: """Test that get_default_settings is cached.""" # Clear the lru_cache first get_default_settings.cache_clear() settings1 = get_default_settings() settings2 = get_default_settings() assert settings1 is settings2 def test_default_settings_immutable_pattern(self) -> None: """Test that default settings provide consistent values.""" defaults = get_default_settings() assert defaults.working_memory_backend == "redis" assert defaults.embedding_model == "text-embedding-3-small"