
- Infrastructure complète de traitement de documents notariaux - API FastAPI d'ingestion et d'orchestration - Pipelines Celery pour le traitement asynchrone - Support des formats PDF, JPEG, PNG, TIFF, HEIC - OCR avec Tesseract et correction lexicale - Classification automatique des documents avec Ollama - Extraction de données structurées - Indexation dans AnythingLLM et OpenSearch - Système de vérifications et contrôles métier - Base de données PostgreSQL pour le métier - Stockage objet avec MinIO - Base de données graphe Neo4j - Recherche plein-texte avec OpenSearch - Supervision avec Prometheus et Grafana - Scripts d'installation pour Debian - Documentation complète - Tests unitaires et de performance - Service systemd pour le déploiement - Scripts de déploiement automatisés
123 lines
5.0 KiB
Python
123 lines
5.0 KiB
Python
"""
|
|
Tests de performance avec Locust
|
|
"""
|
|
from locust import HttpUser, task, between
|
|
import random
|
|
import os
|
|
|
|
class NotariatPipelineUser(HttpUser):
|
|
"""Utilisateur simulé pour les tests de performance"""
|
|
|
|
wait_time = between(1, 3)
|
|
|
|
def on_start(self):
|
|
"""Initialisation de l'utilisateur"""
|
|
self.etude_id = f"E-{random.randint(100, 999)}"
|
|
self.utilisateur_id = f"U-{random.randint(1000, 9999)}"
|
|
self.dossier_id = f"D-2025-{random.randint(100, 999)}"
|
|
|
|
@task(3)
|
|
def import_document(self):
|
|
"""Test d'import de document"""
|
|
# Simulation d'un fichier PDF
|
|
files = {
|
|
"file": ("test_document.pdf", self._generate_fake_pdf(), "application/pdf")
|
|
}
|
|
|
|
data = {
|
|
"id_dossier": self.dossier_id,
|
|
"source": "upload",
|
|
"etude_id": self.etude_id,
|
|
"utilisateur_id": self.utilisateur_id
|
|
}
|
|
|
|
with self.client.post("/api/import", files=files, data=data, catch_response=True) as response:
|
|
if response.status_code == 200:
|
|
result = response.json()
|
|
if result.get("status") == "queued":
|
|
response.success()
|
|
else:
|
|
response.failure(f"Status non attendu: {result.get('status')}")
|
|
else:
|
|
response.failure(f"Code de statut: {response.status_code}")
|
|
|
|
@task(1)
|
|
def health_check(self):
|
|
"""Test de vérification de santé"""
|
|
with self.client.get("/api/health", catch_response=True) as response:
|
|
if response.status_code == 200:
|
|
data = response.json()
|
|
if data.get("status") in ["healthy", "degraded"]:
|
|
response.success()
|
|
else:
|
|
response.failure(f"Status de santé: {data.get('status')}")
|
|
else:
|
|
response.failure(f"Code de statut: {response.status_code}")
|
|
|
|
@task(1)
|
|
def get_documents(self):
|
|
"""Test de récupération des documents"""
|
|
params = {
|
|
"etude_id": self.etude_id,
|
|
"limit": 10
|
|
}
|
|
|
|
with self.client.get("/api/documents", params=params, catch_response=True) as response:
|
|
if response.status_code == 200:
|
|
data = response.json()
|
|
if isinstance(data, list):
|
|
response.success()
|
|
else:
|
|
response.failure("Format de réponse invalide")
|
|
else:
|
|
response.failure(f"Code de statut: {response.status_code}")
|
|
|
|
@task(1)
|
|
def admin_stats(self):
|
|
"""Test des statistiques d'administration"""
|
|
with self.client.get("/api/admin/stats", catch_response=True) as response:
|
|
if response.status_code == 200:
|
|
data = response.json()
|
|
if "documents" in data and "processing" in data:
|
|
response.success()
|
|
else:
|
|
response.failure("Données de statistiques manquantes")
|
|
else:
|
|
response.failure(f"Code de statut: {response.status_code}")
|
|
|
|
def _generate_fake_pdf(self):
|
|
"""Génération d'un faux PDF pour les tests"""
|
|
# En réalité, on utiliserait un vrai fichier PDF de test
|
|
return b"%PDF-1.4\n1 0 obj\n<<\n/Type /Catalog\n/Pages 2 0 R\n>>\nendobj\n2 0 obj\n<<\n/Type /Pages\n/Kids [3 0 R]\n/Count 1\n>>\nendobj\n3 0 obj\n<<\n/Type /Page\n/Parent 2 0 R\n/MediaBox [0 0 612 792]\n/Contents 4 0 R\n>>\nendobj\n4 0 obj\n<<\n/Length 44\n>>\nstream\nBT\n/F1 12 Tf\n72 720 Td\n(Test Document) Tj\nET\nendstream\nendobj\nxref\n0 5\n0000000000 65535 f \n0000000009 00000 n \n0000000058 00000 n \n0000000115 00000 n \n0000000204 00000 n \ntrailer\n<<\n/Size 5\n/Root 1 0 R\n>>\nstartxref\n297\n%%EOF"
|
|
|
|
class HighLoadUser(HttpUser):
|
|
"""Utilisateur pour tests de charge élevée"""
|
|
|
|
wait_time = between(0.1, 0.5)
|
|
|
|
def on_start(self):
|
|
"""Initialisation pour charge élevée"""
|
|
self.etude_id = f"E-{random.randint(100, 999)}"
|
|
self.utilisateur_id = f"U-{random.randint(1000, 9999)}"
|
|
self.dossier_id = f"D-2025-{random.randint(100, 999)}"
|
|
|
|
@task(10)
|
|
def rapid_import(self):
|
|
"""Import rapide de documents"""
|
|
files = {
|
|
"file": ("rapid_test.pdf", self._generate_fake_pdf(), "application/pdf")
|
|
}
|
|
|
|
data = {
|
|
"id_dossier": f"{self.dossier_id}-{random.randint(1, 1000)}",
|
|
"source": "upload",
|
|
"etude_id": self.etude_id,
|
|
"utilisateur_id": self.utilisateur_id
|
|
}
|
|
|
|
self.client.post("/api/import", files=files, data=data)
|
|
|
|
def _generate_fake_pdf(self):
|
|
"""Génération d'un faux PDF léger"""
|
|
return b"%PDF-1.4\n1 0 obj\n<<\n/Type /Catalog\n/Pages 2 0 R\n>>\nendobj\n2 0 obj\n<<\n/Type /Pages\n/Kids [3 0 R]\n/Count 1\n>>\nendobj\n3 0 obj\n<<\n/Type /Page\n/Parent 2 0 R\n/MediaBox [0 0 612 792]\n>>\nendobj\nxref\n0 4\n0000000000 65535 f \n0000000009 00000 n \n0000000058 00000 n \n0000000115 00000 n \ntrailer\n<<\n/Size 4\n/Root 1 0 R\n>>\nstartxref\n174\n%%EOF"
|