🧪(backend) tests
I add tests to test the app.
This commit is contained in:
@@ -56,14 +56,14 @@ class FindRagBackend(BaseRagBackend): # pylint: disable=too-many-instance-attri
|
||||
self.collection_id = self.collection_id or 1
|
||||
return self.collection_id
|
||||
|
||||
#TODO
|
||||
# TODO
|
||||
def delete_collection(self) -> None:
|
||||
"""
|
||||
Deletion not available
|
||||
"""
|
||||
logger.warning(f"deletion of collections is not yet supported in FindRagBackend")
|
||||
logger.warning("deletion of collections is not yet supported in FindRagBackend")
|
||||
|
||||
#TODO: factor with albert api
|
||||
# TODO: factor with albert api
|
||||
def parse_pdf_document(self, name: str, content_type: str, content: BytesIO) -> str:
|
||||
"""
|
||||
Parse the PDF document content and return the text content.
|
||||
@@ -91,7 +91,7 @@ class FindRagBackend(BaseRagBackend): # pylint: disable=too-many-instance-attri
|
||||
document_page["content"] for document_page in response.json().get("data", [])
|
||||
)
|
||||
|
||||
#TODO: factor with albert api
|
||||
# TODO: factor with albert api
|
||||
def parse_document(self, name: str, content_type: str, content: BytesIO):
|
||||
"""
|
||||
Parse the document and prepare it for the search operation.
|
||||
@@ -142,7 +142,7 @@ class FindRagBackend(BaseRagBackend): # pylint: disable=too-many-instance-attri
|
||||
"updated_at": timezone.now().isoformat(),
|
||||
"tags": [f"collection-{self.collection_id}"],
|
||||
"size": len(content.encode("utf-8")),
|
||||
"users": [], #TODO
|
||||
"users": [], # TODO
|
||||
"groups": [],
|
||||
"reach": "public",
|
||||
"is_active": True,
|
||||
@@ -165,12 +165,11 @@ class FindRagBackend(BaseRagBackend): # pylint: disable=too-many-instance-attri
|
||||
RAGWebResults: The search results.
|
||||
"""
|
||||
logger.debug("search documents in Find with query '%s'", query)
|
||||
#TODO: factor session auth in a decorator
|
||||
session = kwargs.get("session")
|
||||
refresh_access_token(session)
|
||||
oidc_access_token = session.get('oidc_access_token')
|
||||
# TODO: factor session auth in a decorator
|
||||
session = refresh_access_token(kwargs.get("session"))
|
||||
oidc_access_token = session.get("oidc_access_token")
|
||||
if not oidc_access_token:
|
||||
raise AuthenticationError({'error': 'Not authenticated'})
|
||||
raise AuthenticationError({"error": "Not authenticated"})
|
||||
collection_ids = self.get_all_collection_ids()
|
||||
|
||||
response = requests.post(
|
||||
|
||||
@@ -106,7 +106,14 @@ def get_model_configuration(model_hrid: str):
|
||||
class AIAgentService: # pylint: disable=too-many-instance-attributes
|
||||
"""Service class for AI-related operations (Pydantic-AI edition)."""
|
||||
|
||||
def __init__(self, conversation: models.ChatConversation, user, session=None, model_hrid=None, language=None):
|
||||
def __init__( # noqa: PLR0913 # pylint: disable=too-many-arguments,too-many-positional-arguments
|
||||
self,
|
||||
conversation: models.ChatConversation,
|
||||
user,
|
||||
session=None,
|
||||
model_hrid=None,
|
||||
language=None,
|
||||
):
|
||||
"""
|
||||
Initialize the AI agent service.
|
||||
|
||||
|
||||
@@ -38,9 +38,6 @@ def brave_settings(settings):
|
||||
settings.BRAVE_SEARCH_EXTRA_SNIPPETS = True
|
||||
settings.BRAVE_SUMMARIZATION_ENABLED = False
|
||||
settings.BRAVE_CACHE_TTL = 3600
|
||||
settings.RAG_DOCUMENT_SEARCH_BACKEND = (
|
||||
"chat.agent_rag.document_rag_backends.albert_rag_backend.AlbertRagBackend"
|
||||
)
|
||||
settings.BRAVE_RAG_WEB_SEARCH_CHUNK_NUMBER = 5
|
||||
|
||||
|
||||
|
||||
+72
-12
@@ -7,6 +7,7 @@ import json
|
||||
import logging
|
||||
from io import BytesIO
|
||||
from unittest import mock
|
||||
from unittest.mock import Mock
|
||||
|
||||
from django.utils import formats, timezone
|
||||
|
||||
@@ -41,28 +42,59 @@ from chat.tests.utils import replace_uuids_with_placeholder
|
||||
pytestmark = pytest.mark.django_db(transaction=True)
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def ai_settings(settings):
|
||||
@pytest.fixture(
|
||||
autouse=True,
|
||||
params=[
|
||||
"chat.agent_rag.document_rag_backends.find_rag_backend.FindRagBackend",
|
||||
"chat.agent_rag.document_rag_backends.albert_rag_backend.AlbertRagBackend",
|
||||
],
|
||||
)
|
||||
def ai_settings(request, settings):
|
||||
"""Fixture to set AI service URLs for testing."""
|
||||
settings.AI_BASE_URL = "https://www.external-ai-service.com/"
|
||||
settings.AI_API_KEY = "test-api-key"
|
||||
settings.AI_MODEL = "test-model"
|
||||
settings.AI_AGENT_INSTRUCTIONS = "You are a helpful test assistant :)"
|
||||
|
||||
# Enable Albert API for document search
|
||||
settings.RAG_DOCUMENT_SEARCH_BACKEND = (
|
||||
"chat.agent_rag.document_rag_backends.albert_rag_backend.AlbertRagBackend"
|
||||
)
|
||||
settings.ALBERT_API_URL = "https://albert.api.etalab.gouv.fr"
|
||||
settings.ALBERT_API_KEY = "albert-api-key"
|
||||
# enable on rag document search tool
|
||||
settings.RAG_DOCUMENT_SEARCH_BACKEND = request.param
|
||||
settings.RAG_WEB_SEARCH_PROMPT_UPDATE = (
|
||||
"Based on the following document contents:\n\n{search_results}\n\n"
|
||||
"Please answer the user's question: {user_prompt}"
|
||||
)
|
||||
|
||||
settings.AI_BASE_URL = "https://www.external-ai-service.com/"
|
||||
settings.AI_API_KEY = "test-api-key"
|
||||
settings.AI_MODEL = "test-model"
|
||||
settings.AI_AGENT_INSTRUCTIONS = "You are a helpful test assistant :)"
|
||||
|
||||
# Albert API settings
|
||||
settings.ALBERT_API_URL = "https://albert.api.etalab.gouv.fr"
|
||||
settings.ALBERT_API_KEY = "albert-api-key"
|
||||
|
||||
# Find API settings
|
||||
settings.FIND_API_URL = "https://find.api.example.com"
|
||||
settings.FIND_API_KEY = "find-api-key"
|
||||
|
||||
return settings
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def mock_process_request():
|
||||
"""Mock process_request to bypass authentication in tests."""
|
||||
with mock.patch(
|
||||
"lasuite.oidc_login.decorators.RefreshOIDCAccessToken.process_request"
|
||||
) as mocked_process_request:
|
||||
mocked_process_request.return_value = None
|
||||
yield mocked_process_request
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def mock_refresh_access_token():
|
||||
"""Mock refresh_access_token to bypass token refresh in tests."""
|
||||
with mock.patch("utils.oicd.refresh_access_token") as mocked_refresh_access_token:
|
||||
mock_session = Mock(spec=httpx.Client)
|
||||
mock_session.access_token = "mocked-access-token"
|
||||
mocked_refresh_access_token.return_value = mock_session
|
||||
yield mocked_refresh_access_token
|
||||
|
||||
|
||||
@pytest.fixture(name="sample_pdf_content")
|
||||
def fixture_sample_pdf_content():
|
||||
"""Create a dummy PDF content as BytesIO."""
|
||||
@@ -134,6 +166,32 @@ def fixture_mock_albert_api():
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture(name="mock_find_api")
|
||||
def fixture_mock_find_api():
|
||||
"""Fixture to mock the Find API endpoints."""
|
||||
# Mock document indexing (Find API)
|
||||
responses.post(
|
||||
"https://find.api.example.com/api/v1.0/documents/index/",
|
||||
json={"id": "456", "status": "indexed"},
|
||||
status=status.HTTP_200_OK,
|
||||
)
|
||||
|
||||
# Mock document search (Find API)
|
||||
responses.post(
|
||||
"https://find.api.example.com/api/v1.0/documents/search/",
|
||||
json=[
|
||||
{
|
||||
"_source": {
|
||||
"title.fr": "sample.pdf",
|
||||
"content.fr": "This is the content of the PDF.",
|
||||
},
|
||||
"_score": 0.9,
|
||||
}
|
||||
],
|
||||
status=status.HTTP_200_OK,
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture(name="mock_summarization_agent")
|
||||
def fixture_mock_summarization_agent():
|
||||
"""Mock the SummarizationAgent to return a fixed summary."""
|
||||
@@ -220,6 +278,7 @@ def test_post_conversation_with_document_upload(
|
||||
# pylint: disable=too-many-arguments,too-many-positional-arguments
|
||||
api_client,
|
||||
mock_albert_api, # pylint: disable=unused-argument
|
||||
mock_find_api, # pylint: disable=unused-argument
|
||||
sample_pdf_content,
|
||||
today_promt_date,
|
||||
mock_ai_agent_service,
|
||||
@@ -549,6 +608,7 @@ def test_post_conversation_with_document_upload_feature_disabled(
|
||||
def test_post_conversation_with_document_upload_summarize( # pylint: disable=too-many-arguments,too-many-positional-arguments # noqa: PLR0913
|
||||
api_client,
|
||||
mock_albert_api, # pylint: disable=unused-argument
|
||||
mock_find_api, # pylint: disable=unused-argument
|
||||
sample_pdf_content,
|
||||
today_promt_date,
|
||||
mock_ai_agent_service,
|
||||
|
||||
@@ -7,13 +7,12 @@ from uuid import uuid4
|
||||
from django.conf import settings
|
||||
from django.core.files.storage import default_storage
|
||||
from django.http import Http404, StreamingHttpResponse
|
||||
from django.utils.decorators import method_decorator
|
||||
|
||||
import langfuse
|
||||
import magic
|
||||
import posthog
|
||||
from django.utils.decorators import method_decorator
|
||||
from lasuite.malware_detection import malware_detection
|
||||
from lasuite.oidc_login.backends import get_oidc_refresh_token
|
||||
from lasuite.oidc_login.decorators import refresh_oidc_access_token
|
||||
from rest_framework import decorators, filters, mixins, permissions, status, viewsets
|
||||
from rest_framework.exceptions import MethodNotAllowed, PermissionDenied, ValidationError
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
import requests
|
||||
"""Utility functions for OIDC token management."""
|
||||
|
||||
from django.conf import settings
|
||||
|
||||
import requests
|
||||
from lasuite.oidc_login.backends import get_oidc_refresh_token, store_tokens
|
||||
|
||||
|
||||
@@ -7,13 +10,13 @@ def refresh_access_token(session):
|
||||
"""Refresh the OIDC access token using the refresh token."""
|
||||
response = requests.post(
|
||||
settings.OIDC_OP_TOKEN_ENDPOINT,
|
||||
data= {
|
||||
data={
|
||||
"grant_type": "refresh_token",
|
||||
"client_id": settings.OIDC_RP_CLIENT_ID,
|
||||
"client_secret": settings.OIDC_RP_CLIENT_SECRET,
|
||||
"refresh_token": get_oidc_refresh_token(session),
|
||||
},
|
||||
timeout=5
|
||||
timeout=5,
|
||||
)
|
||||
response.raise_for_status()
|
||||
token_info = response.json()
|
||||
@@ -22,5 +25,6 @@ def refresh_access_token(session):
|
||||
session,
|
||||
access_token=token_info.get("access_token"),
|
||||
id_token=None,
|
||||
refresh_token=token_info.get("refresh_token")
|
||||
refresh_token=token_info.get("refresh_token"),
|
||||
)
|
||||
return session
|
||||
|
||||
Reference in New Issue
Block a user