🧪(backend) tests

I add tests to test the app.
This commit is contained in:
charles
2025-12-17 17:01:13 +01:00
parent b62fffc69d
commit 713b34fdcd
6 changed files with 98 additions and 32 deletions
@@ -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(
+8 -1
View File
@@ -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
@@ -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,
+1 -2
View File
@@ -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
+8 -4
View File
@@ -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