generated from coulomb/repo-seed
FR-2 RoutingPolicy: - RoutingPolicy + RoutingRule dataclasses in llm_connect/routing.py - resolve(task_type, estimated_cost_per_1k=None) with cost-cap fallback - Exported from llm_connect.__init__; contract doc at contracts/functional/routing-policy.md - 11 tests covering rule match, cost-cap, fallback, unknown type, no-match FR-1 HTTP serve mode: - LLMServer in llm_connect/server.py (stdlib http.server, zero extra deps) - POST /execute + GET /health; CLI via python -m llm_connect.server - [server] optional-dep group added to pyproject.toml - Contract doc at contracts/functional/server.md - 9 tests: health, round-trip, 400/404/500 errors, config forwarding - Added "mock" provider to factory for CLI default All 101 tests pass. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
98 lines
4.1 KiB
Python
98 lines
4.1 KiB
Python
"""
|
|
Tests for create_adapter() and create_embedding_adapter() factories.
|
|
"""
|
|
|
|
import pytest
|
|
from llm_connect.factory import create_adapter
|
|
from llm_connect.embedding_factory import create_embedding_adapter
|
|
from llm_connect.exceptions import LLMConfigurationError
|
|
from llm_connect.adapter import LLMAdapter
|
|
from llm_connect.embedding_adapter import EmbeddingAdapter
|
|
from llm_connect.openrouter import OpenRouterAdapter
|
|
from llm_connect.claude_code import ClaudeCodeAdapter
|
|
from llm_connect.openai import OpenAIAdapter
|
|
from llm_connect.gemini import GeminiAdapter
|
|
from llm_connect.embedding_openai import OpenAICompatibleEmbeddingAdapter
|
|
|
|
|
|
class TestCreateAdapter:
|
|
def test_unknown_provider_raises(self):
|
|
with pytest.raises(LLMConfigurationError, match="Unknown LLM provider"):
|
|
create_adapter("nonexistent-provider")
|
|
|
|
def test_unknown_provider_error_lists_known(self):
|
|
with pytest.raises(LLMConfigurationError) as exc_info:
|
|
create_adapter("bad")
|
|
assert "openai" in str(exc_info.value)
|
|
assert "gemini" in str(exc_info.value)
|
|
|
|
def test_openrouter_returns_adapter(self):
|
|
adapter = create_adapter("openrouter", api_key="test-key")
|
|
assert isinstance(adapter, OpenRouterAdapter)
|
|
assert isinstance(adapter, LLMAdapter)
|
|
|
|
def test_openrouter_no_key_still_constructs(self):
|
|
# OpenRouterAdapter defers key validation to execute_prompt
|
|
adapter = create_adapter("openrouter")
|
|
assert isinstance(adapter, OpenRouterAdapter)
|
|
|
|
def test_openai_with_key_returns_adapter(self):
|
|
adapter = create_adapter("openai", api_key="sk-test-key")
|
|
assert isinstance(adapter, OpenAIAdapter)
|
|
assert isinstance(adapter, LLMAdapter)
|
|
|
|
def test_openai_without_key_raises(self, monkeypatch):
|
|
monkeypatch.delenv("OPENAI_API_KEY", raising=False)
|
|
with pytest.raises(LLMConfigurationError):
|
|
create_adapter("openai")
|
|
|
|
def test_gemini_with_key_returns_adapter(self):
|
|
adapter = create_adapter("gemini", api_key="aistudio-test-key")
|
|
assert isinstance(adapter, GeminiAdapter)
|
|
assert isinstance(adapter, LLMAdapter)
|
|
|
|
def test_gemini_without_key_raises(self, monkeypatch):
|
|
monkeypatch.delenv("GEMINI_API_KEY", raising=False)
|
|
with pytest.raises(LLMConfigurationError):
|
|
create_adapter("gemini")
|
|
|
|
def test_claude_code_returns_adapter(self):
|
|
adapter = create_adapter("claude-code")
|
|
assert isinstance(adapter, ClaudeCodeAdapter)
|
|
assert isinstance(adapter, LLMAdapter)
|
|
|
|
def test_claude_code_with_model(self):
|
|
adapter = create_adapter("claude-code", model="claude-opus-4-6")
|
|
assert isinstance(adapter, ClaudeCodeAdapter)
|
|
|
|
def test_all_known_providers_are_reachable(self):
|
|
known = {"openrouter", "openai", "gemini", "claude-code", "mock"}
|
|
# Just verify each key is in the factory registry (no construction needed)
|
|
from llm_connect.factory import _PROVIDERS
|
|
assert known == set(_PROVIDERS.keys())
|
|
|
|
|
|
class TestCreateEmbeddingAdapter:
|
|
def test_unknown_provider_raises(self):
|
|
with pytest.raises(LLMConfigurationError, match="Unknown embedding provider"):
|
|
create_embedding_adapter("nonexistent")
|
|
|
|
def test_openai_returns_adapter(self):
|
|
adapter = create_embedding_adapter("openai", api_key="sk-test")
|
|
assert isinstance(adapter, OpenAICompatibleEmbeddingAdapter)
|
|
assert isinstance(adapter, EmbeddingAdapter)
|
|
|
|
def test_openrouter_returns_adapter(self):
|
|
adapter = create_embedding_adapter("openrouter", api_key="or-test")
|
|
assert isinstance(adapter, OpenAICompatibleEmbeddingAdapter)
|
|
assert isinstance(adapter, EmbeddingAdapter)
|
|
|
|
def test_validate_returns_true_when_key_set(self):
|
|
adapter = create_embedding_adapter("openai", api_key="sk-test")
|
|
assert adapter.validate() is True
|
|
|
|
def test_validate_returns_false_when_no_key(self, monkeypatch):
|
|
monkeypatch.delenv("OPENAI_API_KEY", raising=False)
|
|
adapter = create_embedding_adapter("openai")
|
|
assert adapter.validate() is False
|