""" Tests for config/settings.py — model client routing logic. """ import pytest from unittest.mock import patch, MagicMock def _get_routing_fn(llm_mode: str): import importlib import sys with patch.dict("os.environ", {"LLM_MODE": llm_mode}): for key in list(sys.modules.keys()): if "config.settings" in key or key == "config.settings": del sys.modules[key] import config.settings as s importlib.reload(s) return s.get_model_client, s class TestModelClientRouting: def _mock_clients(self, settings_mod): cheap = MagicMock(name="cheap") reasoning = MagicMock(name="reasoning") analysis = MagicMock(name="analysis") local = MagicMock(name="local") settings_mod._cheap_client = lambda: cheap settings_mod._reasoning_client = lambda: reasoning settings_mod._analysis_client = lambda: analysis settings_mod._local_client = lambda: local return cheap, reasoning, analysis, local def test_cloud_data_uses_cheap(self): fn, mod = _get_routing_fn("cloud") cheap, reasoning, analysis, local = self._mock_clients(mod) result = fn("data") assert result is cheap def test_cloud_reasoning_uses_reasoning(self): fn, mod = _get_routing_fn("cloud") cheap, reasoning, analysis, local = self._mock_clients(mod) result = fn("reasoning") assert result is reasoning def test_cloud_analysis_uses_analysis(self): fn, mod = _get_routing_fn("cloud") cheap, reasoning, analysis, local = self._mock_clients(mod) result = fn("analysis") assert result is analysis def test_local_mode_all_tasks_use_local(self): fn, mod = _get_routing_fn("local") cheap, reasoning, analysis, local = self._mock_clients(mod) for task in ["data", "reasoning", "analysis"]: assert fn(task) is local, f"task={task} should use local in local mode" def test_hybrid_data_uses_local(self): fn, mod = _get_routing_fn("hybrid") cheap, reasoning, analysis, local = self._mock_clients(mod) assert fn("data") is local def test_hybrid_reasoning_uses_reasoning(self): fn, mod = _get_routing_fn("hybrid") cheap, reasoning, analysis, local = self._mock_clients(mod) assert fn("reasoning") is reasoning def test_hybrid_analysis_uses_analysis(self): fn, mod = _get_routing_fn("hybrid") cheap, reasoning, analysis, local = self._mock_clients(mod) assert fn("analysis") is analysis def test_unknown_task_defaults_to_analysis(self): fn, mod = _get_routing_fn("cloud") cheap, reasoning, analysis, local = self._mock_clients(mod) assert fn("unknown_task") is analysis class TestGroqClientFallback: """Ensure cheap/reasoning clients gracefully fall back when GROQ key absent.""" def test_cheap_client_falls_back_without_groq_key(self): with patch.dict("os.environ", {"GROQ_API_KEY": "", "LLM_MODE": "cloud"}): import importlib, sys for key in list(sys.modules.keys()): if "config.settings" in key: del sys.modules[key] import config.settings as s importlib.reload(s) # Should not raise — falls back to next provider # We just assert the function exists and returns something try: client = s._cheap_client() assert client is not None except Exception as e: # If no fallback keys set either, it's expected to fail downstream # but should not be a silent swallowed error assert "api_key" in str(e).lower() or "key" in str(e).lower()