This repository has been archived on 2026-02-15. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
breakpilot-pwa/backend/tests/test_llm_gateway/test_inference_service.py
BreakPilot Dev 19855efacc
Some checks failed
Tests / Go Tests (push) Has been cancelled
Tests / Python Tests (push) Has been cancelled
Tests / Integration Tests (push) Has been cancelled
Tests / Go Lint (push) Has been cancelled
Tests / Python Lint (push) Has been cancelled
Tests / Security Scan (push) Has been cancelled
Tests / All Checks Passed (push) Has been cancelled
Security Scanning / Secret Scanning (push) Has been cancelled
Security Scanning / Dependency Vulnerability Scan (push) Has been cancelled
Security Scanning / Go Security Scan (push) Has been cancelled
Security Scanning / Python Security Scan (push) Has been cancelled
Security Scanning / Node.js Security Scan (push) Has been cancelled
Security Scanning / Docker Image Security (push) Has been cancelled
Security Scanning / Security Summary (push) Has been cancelled
CI/CD Pipeline / Go Tests (push) Has been cancelled
CI/CD Pipeline / Python Tests (push) Has been cancelled
CI/CD Pipeline / Website Tests (push) Has been cancelled
CI/CD Pipeline / Linting (push) Has been cancelled
CI/CD Pipeline / Security Scan (push) Has been cancelled
CI/CD Pipeline / Docker Build & Push (push) Has been cancelled
CI/CD Pipeline / Integration Tests (push) Has been cancelled
CI/CD Pipeline / Deploy to Staging (push) Has been cancelled
CI/CD Pipeline / Deploy to Production (push) Has been cancelled
CI/CD Pipeline / CI Summary (push) Has been cancelled
ci/woodpecker/manual/build-ci-image Pipeline was successful
ci/woodpecker/manual/main Pipeline failed
feat: BreakPilot PWA - Full codebase (clean push without large binaries)
All services: admin-v2, studio-v2, website, ai-compliance-sdk,
consent-service, klausur-service, voice-service, and infrastructure.
Large PDFs and compiled binaries excluded via .gitignore.
2026-02-11 13:25:58 +01:00

196 lines
7.1 KiB
Python

"""
Tests für Inference Service.
"""
import pytest
from unittest.mock import AsyncMock, patch, MagicMock
from llm_gateway.services.inference import (
InferenceService,
InferenceResult,
get_inference_service,
)
from llm_gateway.models.chat import (
ChatCompletionRequest,
ChatMessage,
Usage,
)
class TestInferenceServiceModelMapping:
"""Tests für Model Mapping."""
def setup_method(self):
"""Setup für jeden Test."""
self.service = InferenceService()
def test_map_breakpilot_model_to_ollama(self):
"""Test Mapping von BreakPilot Modell zu Ollama."""
# Mock Ollama als verfügbares Backend
with patch.object(self.service, 'config') as mock_config:
mock_config.ollama = MagicMock()
mock_config.ollama.name = "ollama"
mock_config.ollama.enabled = True
mock_config.vllm = None
mock_config.anthropic = None
mock_config.backend_priority = ["ollama", "vllm", "anthropic"]
actual_model, backend = self.service._map_model_to_backend("breakpilot-teacher-8b")
assert actual_model == "llama3.1:8b"
assert backend.name == "ollama"
def test_map_breakpilot_70b_model(self):
"""Test Mapping von 70B Modell."""
with patch.object(self.service, 'config') as mock_config:
mock_config.ollama = MagicMock()
mock_config.ollama.name = "ollama"
mock_config.ollama.enabled = True
mock_config.vllm = None
mock_config.anthropic = None
mock_config.backend_priority = ["ollama"]
actual_model, backend = self.service._map_model_to_backend("breakpilot-teacher-70b")
assert "70b" in actual_model.lower()
def test_map_claude_model_to_anthropic(self):
"""Test Mapping von Claude Modell zu Anthropic."""
with patch.object(self.service, 'config') as mock_config:
mock_config.ollama = None
mock_config.vllm = None
mock_config.anthropic = MagicMock()
mock_config.anthropic.name = "anthropic"
mock_config.anthropic.enabled = True
mock_config.anthropic.default_model = "claude-3-5-sonnet-20241022"
mock_config.backend_priority = ["anthropic"]
actual_model, backend = self.service._map_model_to_backend("claude-3-5-sonnet")
assert backend.name == "anthropic"
assert "claude" in actual_model.lower()
def test_map_model_no_backend_available(self):
"""Test Fehler wenn kein Backend verfügbar."""
with patch.object(self.service, 'config') as mock_config:
mock_config.ollama = None
mock_config.vllm = None
mock_config.anthropic = None
mock_config.backend_priority = []
with pytest.raises(ValueError, match="No LLM backend available"):
self.service._map_model_to_backend("breakpilot-teacher-8b")
class TestInferenceServiceBackendSelection:
"""Tests für Backend-Auswahl."""
def setup_method(self):
"""Setup für jeden Test."""
self.service = InferenceService()
def test_get_available_backend_priority(self):
"""Test Backend-Auswahl nach Priorität."""
with patch.object(self.service, 'config') as mock_config:
# Beide Backends verfügbar
mock_config.ollama = MagicMock()
mock_config.ollama.enabled = True
mock_config.vllm = MagicMock()
mock_config.vllm.enabled = True
mock_config.anthropic = None
mock_config.backend_priority = ["vllm", "ollama"]
backend = self.service._get_available_backend()
# vLLM hat höhere Priorität
assert backend == mock_config.vllm
def test_get_available_backend_fallback(self):
"""Test Fallback wenn primäres Backend nicht verfügbar."""
with patch.object(self.service, 'config') as mock_config:
mock_config.ollama = MagicMock()
mock_config.ollama.enabled = True
mock_config.vllm = MagicMock()
mock_config.vllm.enabled = False # Deaktiviert
mock_config.anthropic = None
mock_config.backend_priority = ["vllm", "ollama"]
backend = self.service._get_available_backend()
# Ollama als Fallback
assert backend == mock_config.ollama
def test_get_available_backend_none_available(self):
"""Test wenn kein Backend verfügbar."""
with patch.object(self.service, 'config') as mock_config:
mock_config.ollama = None
mock_config.vllm = None
mock_config.anthropic = None
mock_config.backend_priority = ["ollama", "vllm", "anthropic"]
backend = self.service._get_available_backend()
assert backend is None
class TestInferenceResult:
"""Tests für InferenceResult Dataclass."""
def test_inference_result_creation(self):
"""Test InferenceResult erstellen."""
result = InferenceResult(
content="Hello, world!",
model="llama3.1:8b",
backend="ollama",
usage=Usage(prompt_tokens=10, completion_tokens=5, total_tokens=15),
finish_reason="stop",
)
assert result.content == "Hello, world!"
assert result.model == "llama3.1:8b"
assert result.backend == "ollama"
assert result.usage.total_tokens == 15
def test_inference_result_defaults(self):
"""Test Standardwerte."""
result = InferenceResult(
content="Test",
model="test",
backend="test",
)
assert result.usage is None
assert result.finish_reason == "stop"
class TestInferenceServiceComplete:
"""Tests für complete() Methode."""
@pytest.mark.asyncio
async def test_complete_calls_correct_backend(self):
"""Test dass correct Backend aufgerufen wird."""
service = InferenceService()
request = ChatCompletionRequest(
model="breakpilot-teacher-8b",
messages=[ChatMessage(role="user", content="Hello")],
)
# Mock das Backend
with patch.object(service, '_map_model_to_backend') as mock_map:
with patch.object(service, '_call_ollama') as mock_call:
mock_backend = MagicMock()
mock_backend.name = "ollama"
mock_map.return_value = ("llama3.1:8b", mock_backend)
mock_call.return_value = InferenceResult(
content="Hello!",
model="llama3.1:8b",
backend="ollama",
)
response = await service.complete(request)
mock_call.assert_called_once()
assert response.choices[0].message.content == "Hello!"
class TestGetInferenceServiceSingleton:
"""Tests für Singleton Pattern."""
def test_singleton_returns_same_instance(self):
"""Test dass get_inference_service Singleton zurückgibt."""
service1 = get_inference_service()
service2 = get_inference_service()
assert service1 is service2