mirror of
https://github.com/aljazceru/enclava.git
synced 2025-12-17 15:34:36 +01:00
296 lines
10 KiB
Python
296 lines
10 KiB
Python
"""
|
|
Simple validation tests for the new LLM service integration.
|
|
Tests basic functionality without complex mocking.
|
|
"""
|
|
import pytest
|
|
from httpx import AsyncClient
|
|
from unittest.mock import patch, MagicMock
|
|
|
|
|
|
class TestLLMServiceValidation:
|
|
"""Basic validation tests for LLM service integration."""
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_llm_models_endpoint_exists(self, client: AsyncClient):
|
|
"""Test that the LLM models endpoint exists and is accessible."""
|
|
response = await client.get(
|
|
"/api/v1/llm/models",
|
|
headers={"Authorization": "Bearer test-api-key"}
|
|
)
|
|
|
|
# Should not return 404 (endpoint exists)
|
|
assert response.status_code != 404
|
|
# May return 500 or other error due to missing LLM service, but endpoint exists
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_llm_chat_endpoint_exists(self, client: AsyncClient):
|
|
"""Test that the LLM chat endpoint exists and is accessible."""
|
|
response = await client.post(
|
|
"/api/v1/llm/chat/completions",
|
|
json={
|
|
"model": "test-model",
|
|
"messages": [
|
|
{"role": "user", "content": "Hello"}
|
|
]
|
|
},
|
|
headers={"Authorization": "Bearer test-api-key"}
|
|
)
|
|
|
|
# Should not return 404 (endpoint exists)
|
|
assert response.status_code != 404
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_llm_embeddings_endpoint_exists(self, client: AsyncClient):
|
|
"""Test that the LLM embeddings endpoint exists and is accessible."""
|
|
response = await client.post(
|
|
"/api/v1/llm/embeddings",
|
|
json={
|
|
"model": "test-embedding-model",
|
|
"input": "Test text"
|
|
},
|
|
headers={"Authorization": "Bearer test-api-key"}
|
|
)
|
|
|
|
# Should not return 404 (endpoint exists)
|
|
assert response.status_code != 404
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_llm_provider_status_endpoint_exists(self, client: AsyncClient):
|
|
"""Test that the provider status endpoint exists and is accessible."""
|
|
response = await client.get(
|
|
"/api/v1/llm/providers/status",
|
|
headers={"Authorization": "Bearer test-api-key"}
|
|
)
|
|
|
|
# Should not return 404 (endpoint exists)
|
|
assert response.status_code != 404
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_chat_with_mocked_service(self, client: AsyncClient):
|
|
"""Test chat completion with mocked LLM service."""
|
|
from app.services.llm.models import ChatResponse, ChatChoice, ChatMessage, TokenUsage
|
|
|
|
# Mock successful response
|
|
mock_response = ChatResponse(
|
|
id="test-123",
|
|
object="chat.completion",
|
|
created=1234567890,
|
|
model="privatemode-llama-3-70b",
|
|
provider="PrivateMode.ai",
|
|
choices=[
|
|
ChatChoice(
|
|
index=0,
|
|
message=ChatMessage(
|
|
role="assistant",
|
|
content="Hello! How can I help you?"
|
|
),
|
|
finish_reason="stop"
|
|
)
|
|
],
|
|
usage=TokenUsage(
|
|
prompt_tokens=10,
|
|
completion_tokens=8,
|
|
total_tokens=18
|
|
),
|
|
security_check=True,
|
|
risk_score=0.1,
|
|
detected_patterns=[],
|
|
latency_ms=250.5
|
|
)
|
|
|
|
with patch("app.services.llm.service.llm_service.create_chat_completion") as mock_chat:
|
|
mock_chat.return_value = mock_response
|
|
|
|
response = await client.post(
|
|
"/api/v1/llm/chat/completions",
|
|
json={
|
|
"model": "privatemode-llama-3-70b",
|
|
"messages": [
|
|
{"role": "user", "content": "Hello"}
|
|
]
|
|
},
|
|
headers={"Authorization": "Bearer test-api-key"}
|
|
)
|
|
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
|
|
# Verify basic response structure
|
|
assert "id" in data
|
|
assert "choices" in data
|
|
assert len(data["choices"]) == 1
|
|
assert data["choices"][0]["message"]["content"] == "Hello! How can I help you?"
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_embedding_with_mocked_service(self, client: AsyncClient):
|
|
"""Test embedding generation with mocked LLM service."""
|
|
from app.services.llm.models import EmbeddingResponse, EmbeddingData, TokenUsage
|
|
|
|
# Create a simple embedding vector
|
|
embedding_vector = [0.1, 0.2, 0.3] * 341 + [0.1, 0.2, 0.3] # 1024 dimensions
|
|
|
|
mock_response = EmbeddingResponse(
|
|
object="list",
|
|
data=[
|
|
EmbeddingData(
|
|
object="embedding",
|
|
index=0,
|
|
embedding=embedding_vector
|
|
)
|
|
],
|
|
model="privatemode-embeddings",
|
|
provider="PrivateMode.ai",
|
|
usage=TokenUsage(
|
|
prompt_tokens=5,
|
|
completion_tokens=0,
|
|
total_tokens=5
|
|
),
|
|
security_check=True,
|
|
risk_score=0.0,
|
|
latency_ms=150.0
|
|
)
|
|
|
|
with patch("app.services.llm.service.llm_service.create_embedding") as mock_embedding:
|
|
mock_embedding.return_value = mock_response
|
|
|
|
response = await client.post(
|
|
"/api/v1/llm/embeddings",
|
|
json={
|
|
"model": "privatemode-embeddings",
|
|
"input": "Test text for embedding"
|
|
},
|
|
headers={"Authorization": "Bearer test-api-key"}
|
|
)
|
|
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
|
|
# Verify basic response structure
|
|
assert "data" in data
|
|
assert len(data["data"]) == 1
|
|
assert len(data["data"][0]["embedding"]) == 1024
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_models_with_mocked_service(self, client: AsyncClient):
|
|
"""Test models listing with mocked LLM service."""
|
|
from app.services.llm.models import ModelInfo
|
|
|
|
mock_models = [
|
|
ModelInfo(
|
|
id="privatemode-llama-3-70b",
|
|
object="model",
|
|
created=1234567890,
|
|
owned_by="PrivateMode.ai",
|
|
provider="PrivateMode.ai",
|
|
capabilities=["tee", "chat"],
|
|
context_window=32768,
|
|
supports_streaming=True
|
|
),
|
|
ModelInfo(
|
|
id="privatemode-embeddings",
|
|
object="model",
|
|
created=1234567890,
|
|
owned_by="PrivateMode.ai",
|
|
provider="PrivateMode.ai",
|
|
capabilities=["tee", "embeddings"],
|
|
context_window=512,
|
|
supports_streaming=False
|
|
)
|
|
]
|
|
|
|
with patch("app.services.llm.service.llm_service.get_models") as mock_models_call:
|
|
mock_models_call.return_value = mock_models
|
|
|
|
response = await client.get(
|
|
"/api/v1/llm/models",
|
|
headers={"Authorization": "Bearer test-api-key"}
|
|
)
|
|
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
|
|
# Verify basic response structure
|
|
assert "data" in data
|
|
assert len(data["data"]) == 2
|
|
assert data["data"][0]["id"] == "privatemode-llama-3-70b"
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_provider_status_with_mocked_service(self, client: AsyncClient):
|
|
"""Test provider status with mocked LLM service."""
|
|
mock_status = {
|
|
"privatemode": {
|
|
"provider": "PrivateMode.ai",
|
|
"status": "healthy",
|
|
"latency_ms": 250.5,
|
|
"success_rate": 0.98,
|
|
"last_check": "2025-01-01T12:00:00Z",
|
|
"models_available": ["privatemode-llama-3-70b", "privatemode-embeddings"]
|
|
}
|
|
}
|
|
|
|
with patch("app.services.llm.service.llm_service.get_provider_status") as mock_provider:
|
|
mock_provider.return_value = mock_status
|
|
|
|
response = await client.get(
|
|
"/api/v1/llm/providers/status",
|
|
headers={"Authorization": "Bearer test-api-key"}
|
|
)
|
|
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
|
|
# Verify basic response structure
|
|
assert "data" in data
|
|
assert "privatemode" in data["data"]
|
|
assert data["data"]["privatemode"]["status"] == "healthy"
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_unauthorized_access(self, client: AsyncClient):
|
|
"""Test that unauthorized requests are properly rejected."""
|
|
# Test without authorization header
|
|
response = await client.get("/api/v1/llm/models")
|
|
assert response.status_code == 401
|
|
|
|
response = await client.post(
|
|
"/api/v1/llm/chat/completions",
|
|
json={
|
|
"model": "test-model",
|
|
"messages": [{"role": "user", "content": "Hello"}]
|
|
}
|
|
)
|
|
assert response.status_code == 401
|
|
|
|
response = await client.post(
|
|
"/api/v1/llm/embeddings",
|
|
json={
|
|
"model": "test-model",
|
|
"input": "Hello"
|
|
}
|
|
)
|
|
assert response.status_code == 401
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_invalid_request_data(self, client: AsyncClient):
|
|
"""Test that invalid request data is properly handled."""
|
|
# Test invalid JSON structure
|
|
response = await client.post(
|
|
"/api/v1/llm/chat/completions",
|
|
json={
|
|
# Missing required fields
|
|
"model": "test-model"
|
|
# messages field is missing
|
|
},
|
|
headers={"Authorization": "Bearer test-api-key"}
|
|
)
|
|
assert response.status_code == 422 # Unprocessable Entity
|
|
|
|
# Test empty messages
|
|
response = await client.post(
|
|
"/api/v1/llm/chat/completions",
|
|
json={
|
|
"model": "test-model",
|
|
"messages": [] # Empty messages
|
|
},
|
|
headers={"Authorization": "Bearer test-api-key"}
|
|
)
|
|
assert response.status_code == 422 # Unprocessable Entity |