msse-ai-engineering / tests /test_chat_endpoint.py
Tobias Pasquale
fix: correct test import paths and LLM service test expectations
f35ca9e
raw
history blame
14.2 kB
import json
import os
from unittest.mock import MagicMock, patch
import pytest
from app import app as flask_app
@pytest.fixture
def app():
yield flask_app
@pytest.fixture
def client(app):
return app.test_client()
class TestChatEndpoint:
"""Test cases for the /chat endpoint"""
@patch.dict(os.environ, {"OPENROUTER_API_KEY": "test_key"})
@patch("src.rag.rag_pipeline.RAGPipeline")
@patch("src.rag.response_formatter.ResponseFormatter")
@patch("src.llm.llm_service.LLMService")
@patch("src.search.search_service.SearchService")
@patch("src.vector_store.vector_db.VectorDatabase")
@patch("src.embedding.embedding_service.EmbeddingService")
def test_chat_endpoint_valid_request(
self,
mock_embedding,
mock_vector,
mock_search,
mock_llm,
mock_formatter,
mock_rag,
client,
):
"""Test chat endpoint with valid request"""
# Mock the RAG pipeline response
mock_response = {
"answer": (
"Based on the remote work policy, employees can work "
"remotely up to 3 days per week."
),
"confidence": 0.85,
"sources": [
{"chunk_id": "123", "content": "Remote work policy content..."}
],
"citations": ["remote_work_policy.md"],
"processing_time_ms": 1500,
}
# Setup mock instances
mock_rag_instance = MagicMock()
mock_rag_instance.generate_answer.return_value = mock_response
mock_rag.return_value = mock_rag_instance
mock_formatter_instance = MagicMock()
mock_formatter_instance.format_api_response.return_value = {
"status": "success",
"answer": mock_response["answer"],
"confidence": mock_response["confidence"],
"sources": mock_response["sources"],
"citations": mock_response["citations"],
}
mock_formatter.return_value = mock_formatter_instance
# Mock LLMService.from_environment to return a mock instance
mock_llm_instance = MagicMock()
mock_llm.from_environment.return_value = mock_llm_instance
request_data = {
"message": "What is the remote work policy?",
"include_sources": True,
}
response = client.post(
"/chat", data=json.dumps(request_data), content_type="application/json"
)
assert response.status_code == 200
data = response.get_json()
assert data["status"] == "success"
assert "answer" in data
assert "confidence" in data
assert "sources" in data
assert "citations" in data
@patch.dict(os.environ, {"OPENROUTER_API_KEY": "test_key"})
@patch("src.rag.rag_pipeline.RAGPipeline")
@patch("src.rag.response_formatter.ResponseFormatter")
@patch("src.llm.llm_service.LLMService")
@patch("src.search.search_service.SearchService")
@patch("src.vector_store.vector_db.VectorDatabase")
@patch("src.embedding.embedding_service.EmbeddingService")
def test_chat_endpoint_minimal_request(
self,
mock_embedding,
mock_vector,
mock_search,
mock_llm,
mock_formatter,
mock_rag,
client,
):
"""Test chat endpoint with minimal request (only message)"""
mock_response = {
"answer": (
"Employee benefits include health insurance, "
"retirement plans, and PTO."
),
"confidence": 0.78,
"sources": [],
"citations": ["employee_benefits_guide.md"],
"processing_time_ms": 1200,
}
# Setup mock instances
mock_rag_instance = MagicMock()
mock_rag_instance.generate_answer.return_value = mock_response
mock_rag.return_value = mock_rag_instance
mock_formatter_instance = MagicMock()
mock_formatter_instance.format_api_response.return_value = {
"status": "success",
"answer": mock_response["answer"],
}
mock_formatter.return_value = mock_formatter_instance
mock_llm.from_environment.return_value = MagicMock()
request_data = {"message": "What are the employee benefits?"}
response = client.post(
"/chat", data=json.dumps(request_data), content_type="application/json"
)
assert response.status_code == 200
data = response.get_json()
assert data["status"] == "success"
def test_chat_endpoint_missing_message(self, client):
"""Test chat endpoint with missing message parameter"""
request_data = {"include_sources": True}
response = client.post(
"/chat", data=json.dumps(request_data), content_type="application/json"
)
assert response.status_code == 400
data = response.get_json()
assert data["status"] == "error"
assert "message parameter is required" in data["message"]
def test_chat_endpoint_empty_message(self, client):
"""Test chat endpoint with empty message"""
request_data = {"message": ""}
response = client.post(
"/chat", data=json.dumps(request_data), content_type="application/json"
)
assert response.status_code == 400
data = response.get_json()
assert data["status"] == "error"
assert "non-empty string" in data["message"]
def test_chat_endpoint_non_string_message(self, client):
"""Test chat endpoint with non-string message"""
request_data = {"message": 123}
response = client.post(
"/chat", data=json.dumps(request_data), content_type="application/json"
)
assert response.status_code == 400
data = response.get_json()
assert data["status"] == "error"
assert "non-empty string" in data["message"]
def test_chat_endpoint_non_json_request(self, client):
"""Test chat endpoint with non-JSON request"""
response = client.post("/chat", data="not json", content_type="text/plain")
assert response.status_code == 400
data = response.get_json()
assert data["status"] == "error"
assert "application/json" in data["message"]
def test_chat_endpoint_no_llm_config(self, client):
"""Test chat endpoint with no LLM configuration"""
with patch.dict(os.environ, {}, clear=True):
request_data = {"message": "What is the policy?"}
response = client.post(
"/chat", data=json.dumps(request_data), content_type="application/json"
)
assert response.status_code == 503
data = response.get_json()
assert data["status"] == "error"
assert "LLM service configuration error" in data["message"]
@patch.dict(os.environ, {"OPENROUTER_API_KEY": "test_key"})
@patch("src.rag.rag_pipeline.RAGPipeline")
@patch("src.rag.response_formatter.ResponseFormatter")
@patch("src.llm.llm_service.LLMService")
@patch("src.search.search_service.SearchService")
@patch("src.vector_store.vector_db.VectorDatabase")
@patch("src.embedding.embedding_service.EmbeddingService")
def test_chat_endpoint_with_conversation_id(
self,
mock_embedding,
mock_vector,
mock_search,
mock_llm,
mock_formatter,
mock_rag,
client,
):
"""Test chat endpoint with conversation_id parameter"""
mock_response = {
"answer": "The PTO policy allows 15 days of vacation annually.",
"confidence": 0.9,
"sources": [],
"citations": ["pto_policy.md"],
"processing_time_ms": 1100,
}
# Setup mock instances
mock_rag_instance = MagicMock()
mock_rag_instance.generate_answer.return_value = mock_response
mock_rag.return_value = mock_rag_instance
mock_formatter_instance = MagicMock()
mock_formatter_instance.format_chat_response.return_value = {
"status": "success",
"answer": mock_response["answer"],
}
mock_formatter.return_value = mock_formatter_instance
mock_llm.from_environment.return_value = MagicMock()
request_data = {
"message": "What is the PTO policy?",
"conversation_id": "conv_123",
"include_sources": False,
}
response = client.post(
"/chat", data=json.dumps(request_data), content_type="application/json"
)
assert response.status_code == 200
data = response.get_json()
assert data["status"] == "success"
@patch.dict(os.environ, {"OPENROUTER_API_KEY": "test_key"})
@patch("src.rag.rag_pipeline.RAGPipeline")
@patch("src.rag.response_formatter.ResponseFormatter")
@patch("src.llm.llm_service.LLMService")
@patch("src.search.search_service.SearchService")
@patch("src.vector_store.vector_db.VectorDatabase")
@patch("src.embedding.embedding_service.EmbeddingService")
def test_chat_endpoint_with_debug(
self,
mock_embedding,
mock_vector,
mock_search,
mock_llm,
mock_formatter,
mock_rag,
client,
):
"""Test chat endpoint with debug information"""
mock_response = {
"answer": "The security policy requires 2FA authentication.",
"confidence": 0.95,
"sources": [{"chunk_id": "456", "content": "Security requirements..."}],
"citations": ["information_security_policy.md"],
"processing_time_ms": 1800,
"search_results_count": 5,
"context_length": 2048,
}
# Setup mock instances
mock_rag_instance = MagicMock()
mock_rag_instance.generate_answer.return_value = mock_response
mock_rag.return_value = mock_rag_instance
mock_formatter_instance = MagicMock()
mock_formatter_instance.format_api_response.return_value = {
"status": "success",
"answer": mock_response["answer"],
"debug": {"processing_time": 1800},
}
mock_formatter.return_value = mock_formatter_instance
mock_llm.from_environment.return_value = MagicMock()
request_data = {
"message": "What are the security requirements?",
"include_debug": True,
}
response = client.post(
"/chat", data=json.dumps(request_data), content_type="application/json"
)
assert response.status_code == 200
data = response.get_json()
assert data["status"] == "success"
class TestChatHealthEndpoint:
"""Test cases for the /chat/health endpoint"""
@patch.dict(os.environ, {"OPENROUTER_API_KEY": "test_key"})
@patch("src.llm.llm_service.LLMService.from_environment")
@patch("src.rag.rag_pipeline.RAGPipeline.health_check")
def test_chat_health_healthy(self, mock_health_check, mock_llm_service, client):
"""Test chat health endpoint when all services are healthy"""
mock_health_data = {
"pipeline": "healthy",
"components": {
"search_service": {"status": "healthy"},
"llm_service": {"status": "healthy"},
"vector_db": {"status": "healthy"},
},
}
mock_health_check.return_value = mock_health_data
mock_llm_service.return_value = MagicMock()
response = client.get("/chat/health")
assert response.status_code == 200
data = response.get_json()
assert data["status"] == "success"
@patch.dict(os.environ, {"OPENROUTER_API_KEY": "test_key"})
@patch("src.llm.llm_service.LLMService.from_environment")
@patch("src.rag.rag_pipeline.RAGPipeline.health_check")
def test_chat_health_degraded(self, mock_health_check, mock_llm_service, client):
"""Test chat health endpoint when services are degraded"""
mock_health_data = {
"pipeline": "degraded",
"components": {
"search_service": {"status": "healthy"},
"llm_service": {"status": "degraded", "warning": "High latency"},
"vector_db": {"status": "healthy"},
},
}
mock_health_check.return_value = mock_health_data
mock_llm_service.return_value = MagicMock()
response = client.get("/chat/health")
assert response.status_code == 200
data = response.get_json()
assert data["status"] == "success"
def test_chat_health_no_llm_config(self, client):
"""Test chat health endpoint with no LLM configuration"""
with patch.dict(os.environ, {}, clear=True):
response = client.get("/chat/health")
assert response.status_code == 503
data = response.get_json()
assert data["status"] == "error"
assert "LLM configuration error" in data["message"]
@patch.dict(os.environ, {"OPENROUTER_API_KEY": "test_key"})
@patch("src.llm.llm_service.LLMService.from_environment")
@patch("src.rag.rag_pipeline.RAGPipeline.health_check")
def test_chat_health_unhealthy(self, mock_health_check, mock_llm_service, client):
"""Test chat health endpoint when services are unhealthy"""
mock_health_data = {
"pipeline": "unhealthy",
"components": {
"search_service": {
"status": "unhealthy",
"error": "Database connection failed",
},
"llm_service": {"status": "unhealthy", "error": "API unreachable"},
"vector_db": {"status": "unhealthy"},
},
}
mock_health_check.return_value = mock_health_data
mock_llm_service.return_value = MagicMock()
response = client.get("/chat/health")
assert response.status_code == 503
data = response.get_json()
assert data["status"] == "success" # Still returns success, but 503 status code