Files
llm-connect/tests/test_adapter.py
Bernd Worsch d71f4114d1 feat: WP-0001 foundation + WP-0002 core extensions
WP-0001 — Foundation & GAAF Baseline
- SCOPE.md, ARCHITECTURE-LAYERS.md, contracts/ tree
- .claude/rules/ stubs filled (architecture, stack, boundary)
- 57 tests (pytest), pyproject.toml with ruff+mypy, CI workflow

WP-0002 — Core Extensions (FR-4 + FR-3)
- FR-4: BudgetTracker (thread-safe) + LLMBudgetExceededError +
  optional RunConfig.budget_tracker + enforcement in all adapters
- FR-3: async_execute_prompt on LLMAdapter ABC (asyncio.to_thread
  fallback) + native asyncio.create_subprocess_exec in ClaudeCodeAdapter

81 tests passing.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-01 22:24:14 +00:00

78 lines
3.1 KiB
Python

"""
Tests for MockLLMAdapter and ErrorLLMAdapter (Core adapter utilities).
"""
import pytest
from llm_connect.adapter import MockLLMAdapter, ErrorLLMAdapter
from llm_connect.models import RunConfig, LLMResponse
class TestMockLLMAdapter:
def test_returns_mock_response(self, mock_adapter, run_config):
response = mock_adapter.execute_prompt("hello", run_config)
assert response.content == "test response"
def test_returns_llm_response(self, mock_adapter, run_config):
response = mock_adapter.execute_prompt("hello", run_config)
assert isinstance(response, LLMResponse)
def test_call_count_increments(self, mock_adapter, run_config):
assert mock_adapter.call_count == 0
mock_adapter.execute_prompt("a", run_config)
mock_adapter.execute_prompt("b", run_config)
assert mock_adapter.call_count == 2
def test_records_last_prompt(self, mock_adapter, run_config):
mock_adapter.execute_prompt("my prompt", run_config)
assert mock_adapter.last_prompt == "my prompt"
def test_records_last_config(self, mock_adapter, run_config):
mock_adapter.execute_prompt("x", run_config)
assert mock_adapter.last_config is run_config
def test_reset_clears_state(self, mock_adapter, run_config):
mock_adapter.execute_prompt("x", run_config)
mock_adapter.reset()
assert mock_adapter.call_count == 0
assert mock_adapter.last_prompt is None
assert mock_adapter.last_config is None
def test_validate_config_always_true(self, mock_adapter, run_config):
assert mock_adapter.validate_config(run_config) is True
def test_usage_contains_expected_keys(self, mock_adapter, run_config):
response = mock_adapter.execute_prompt("prompt text", run_config)
assert "prompt_tokens" in response.usage
assert "completion_tokens" in response.usage
assert "total_tokens" in response.usage
def test_custom_response_text(self, run_config):
adapter = MockLLMAdapter(mock_response="custom answer")
response = adapter.execute_prompt("q", run_config)
assert response.content == "custom answer"
def test_default_response_text(self, run_config):
adapter = MockLLMAdapter()
response = adapter.execute_prompt("q", run_config)
assert response.content == "Mock LLM response"
def test_metadata_marks_as_mock(self, mock_adapter, run_config):
response = mock_adapter.execute_prompt("q", run_config)
assert response.metadata.get("mock") is True
class TestErrorLLMAdapter:
def test_raises_on_execute(self, run_config):
adapter = ErrorLLMAdapter()
with pytest.raises(RuntimeError):
adapter.execute_prompt("q", run_config)
def test_raises_with_custom_message(self, run_config):
adapter = ErrorLLMAdapter(error_message="boom")
with pytest.raises(RuntimeError, match="boom"):
adapter.execute_prompt("q", run_config)
def test_validate_config_returns_true(self, run_config):
adapter = ErrorLLMAdapter()
assert adapter.validate_config(run_config) is True