Initial commit
This commit is contained in:
363
tests/test_pipeline_error_handling.py
Normal file
363
tests/test_pipeline_error_handling.py
Normal file
@@ -0,0 +1,363 @@
|
||||
"""
|
||||
Tests pour la gestion des erreurs du Pipeline.
|
||||
|
||||
Ce module teste le retry, le timeout et le mode résultats partiels.
|
||||
"""
|
||||
|
||||
import pytest
|
||||
from datetime import datetime, timedelta
|
||||
from unittest.mock import Mock, patch
|
||||
|
||||
from pipeline_mco_pmsi.models.clinical import ClinicalDocument
|
||||
from pipeline_mco_pmsi.models.metadata import StayMetadata
|
||||
from pipeline_mco_pmsi.pipeline import Pipeline
|
||||
|
||||
|
||||
def test_pipeline_retry_on_failure(db_session, rag_engine):
|
||||
"""
|
||||
Test le retry avec exponential backoff en cas d'échec.
|
||||
|
||||
Exigences: 14.1, 14.6
|
||||
"""
|
||||
from pipeline_mco_pmsi.database.models import StayDB
|
||||
|
||||
pipeline = Pipeline(
|
||||
db_session=db_session,
|
||||
rag_engine=rag_engine,
|
||||
max_retries=3,
|
||||
retry_delay=0.1, # Court délai pour les tests
|
||||
)
|
||||
|
||||
# Créer le séjour dans la base de données
|
||||
stay = StayDB(
|
||||
stay_id="stay_retry_001",
|
||||
admission_date=datetime.now() - timedelta(days=1),
|
||||
discharge_date=datetime.now(),
|
||||
specialty="gastro-enterologie",
|
||||
unit="medecine",
|
||||
age=45,
|
||||
sex="M",
|
||||
)
|
||||
db_session.add(stay)
|
||||
db_session.commit()
|
||||
|
||||
documents = [
|
||||
ClinicalDocument(
|
||||
document_id="doc_retry_001",
|
||||
document_type="cr_medical",
|
||||
content="Diagnostic: Gastrite aiguë.",
|
||||
creation_date=datetime.now(),
|
||||
author="Dr. Test",
|
||||
priority=2,
|
||||
)
|
||||
]
|
||||
|
||||
stay_metadata = StayMetadata(
|
||||
stay_id="stay_retry_001",
|
||||
admission_date=datetime.now() - timedelta(days=1),
|
||||
discharge_date=datetime.now(),
|
||||
specialty="gastro-enterologie",
|
||||
unit="medecine",
|
||||
age=45,
|
||||
sex="M",
|
||||
)
|
||||
|
||||
# Mock pour simuler des échecs puis succès
|
||||
call_count = 0
|
||||
original_extract = pipeline.clinical_facts_extractor.extract_facts
|
||||
|
||||
def mock_extract_with_retry(structured_stay):
|
||||
nonlocal call_count
|
||||
call_count += 1
|
||||
if call_count < 2: # Échoue la première fois
|
||||
raise RuntimeError("Erreur temporaire")
|
||||
return original_extract(structured_stay)
|
||||
|
||||
with patch.object(
|
||||
pipeline.clinical_facts_extractor,
|
||||
"extract_facts",
|
||||
side_effect=mock_extract_with_retry,
|
||||
):
|
||||
result = pipeline.process_stay(documents, stay_metadata)
|
||||
|
||||
# Vérifications
|
||||
assert call_count == 2 # 1 échec + 1 succès
|
||||
assert result.success is True
|
||||
|
||||
|
||||
def test_pipeline_timeout_with_partial_results(db_session, rag_engine):
|
||||
"""
|
||||
Test le timeout avec mode résultats partiels activé.
|
||||
|
||||
Exigences: 14.6
|
||||
"""
|
||||
from pipeline_mco_pmsi.database.models import StayDB
|
||||
|
||||
pipeline = Pipeline(
|
||||
db_session=db_session,
|
||||
rag_engine=rag_engine,
|
||||
timeout=0.01, # Timeout très court pour forcer l'erreur
|
||||
partial_results_mode=True,
|
||||
)
|
||||
|
||||
# Créer le séjour dans la base de données
|
||||
stay = StayDB(
|
||||
stay_id="stay_timeout_001",
|
||||
admission_date=datetime.now() - timedelta(days=1),
|
||||
discharge_date=datetime.now(),
|
||||
specialty="gastro-enterologie",
|
||||
unit="medecine",
|
||||
age=45,
|
||||
sex="M",
|
||||
)
|
||||
db_session.add(stay)
|
||||
db_session.commit()
|
||||
|
||||
documents = [
|
||||
ClinicalDocument(
|
||||
document_id="doc_timeout_001",
|
||||
document_type="cr_medical",
|
||||
content="Diagnostic: Gastrite aiguë.",
|
||||
creation_date=datetime.now(),
|
||||
author="Dr. Test",
|
||||
priority=2,
|
||||
)
|
||||
]
|
||||
|
||||
stay_metadata = StayMetadata(
|
||||
stay_id="stay_timeout_001",
|
||||
admission_date=datetime.now() - timedelta(days=1),
|
||||
discharge_date=datetime.now(),
|
||||
specialty="gastro-enterologie",
|
||||
unit="medecine",
|
||||
age=45,
|
||||
sex="M",
|
||||
)
|
||||
|
||||
# Mock pour ralentir le traitement
|
||||
import time
|
||||
|
||||
def slow_extract(structured_stay):
|
||||
time.sleep(0.1) # Plus long que le timeout
|
||||
return []
|
||||
|
||||
with patch.object(
|
||||
pipeline.clinical_facts_extractor,
|
||||
"extract_facts",
|
||||
side_effect=slow_extract,
|
||||
):
|
||||
result = pipeline.process_stay(documents, stay_metadata)
|
||||
|
||||
# Vérifications
|
||||
assert result.success is False
|
||||
assert "timeout" in result.error_message.lower()
|
||||
|
||||
# Vérifier qu'un problème de validation timeout a été ajouté
|
||||
timeout_issues = [
|
||||
issue for issue in result.validation_issues
|
||||
if "timeout" in issue.message.lower()
|
||||
]
|
||||
assert len(timeout_issues) > 0
|
||||
|
||||
|
||||
def test_pipeline_timeout_without_partial_results(db_session, rag_engine):
|
||||
"""
|
||||
Test le timeout sans mode résultats partiels (doit lever une exception).
|
||||
|
||||
Exigences: 14.6
|
||||
"""
|
||||
from pipeline_mco_pmsi.database.models import StayDB
|
||||
|
||||
pipeline = Pipeline(
|
||||
db_session=db_session,
|
||||
rag_engine=rag_engine,
|
||||
timeout=0.01, # Timeout très court
|
||||
partial_results_mode=False, # Mode désactivé
|
||||
)
|
||||
|
||||
# Créer le séjour dans la base de données
|
||||
stay = StayDB(
|
||||
stay_id="stay_timeout_002",
|
||||
admission_date=datetime.now() - timedelta(days=1),
|
||||
discharge_date=datetime.now(),
|
||||
specialty="gastro-enterologie",
|
||||
unit="medecine",
|
||||
age=45,
|
||||
sex="M",
|
||||
)
|
||||
db_session.add(stay)
|
||||
db_session.commit()
|
||||
|
||||
documents = [
|
||||
ClinicalDocument(
|
||||
document_id="doc_timeout_002",
|
||||
document_type="cr_medical",
|
||||
content="Diagnostic: Gastrite aiguë.",
|
||||
creation_date=datetime.now(),
|
||||
author="Dr. Test",
|
||||
priority=2,
|
||||
)
|
||||
]
|
||||
|
||||
stay_metadata = StayMetadata(
|
||||
stay_id="stay_timeout_002",
|
||||
admission_date=datetime.now() - timedelta(days=1),
|
||||
discharge_date=datetime.now(),
|
||||
specialty="gastro-enterologie",
|
||||
unit="medecine",
|
||||
age=45,
|
||||
sex="M",
|
||||
)
|
||||
|
||||
# Mock pour ralentir le traitement
|
||||
import time
|
||||
|
||||
def slow_extract(structured_stay):
|
||||
time.sleep(0.1)
|
||||
return []
|
||||
|
||||
with patch.object(
|
||||
pipeline.clinical_facts_extractor,
|
||||
"extract_facts",
|
||||
side_effect=slow_extract,
|
||||
):
|
||||
result = pipeline.process_stay(documents, stay_metadata)
|
||||
|
||||
# Vérifications - devrait retourner un résultat d'erreur
|
||||
assert result.success is False
|
||||
assert result.error_message is not None
|
||||
|
||||
|
||||
def test_pipeline_max_retries_exceeded(db_session, rag_engine):
|
||||
"""
|
||||
Test que le pipeline échoue après avoir dépassé le nombre maximum de retries.
|
||||
|
||||
Exigences: 14.1
|
||||
"""
|
||||
from pipeline_mco_pmsi.database.models import StayDB
|
||||
|
||||
pipeline = Pipeline(
|
||||
db_session=db_session,
|
||||
rag_engine=rag_engine,
|
||||
max_retries=2,
|
||||
retry_delay=0.1,
|
||||
)
|
||||
|
||||
# Créer le séjour dans la base de données
|
||||
stay = StayDB(
|
||||
stay_id="stay_max_retry_001",
|
||||
admission_date=datetime.now() - timedelta(days=1),
|
||||
discharge_date=datetime.now(),
|
||||
specialty="gastro-enterologie",
|
||||
unit="medecine",
|
||||
age=45,
|
||||
sex="M",
|
||||
)
|
||||
db_session.add(stay)
|
||||
db_session.commit()
|
||||
|
||||
documents = [
|
||||
ClinicalDocument(
|
||||
document_id="doc_max_retry_001",
|
||||
document_type="cr_medical",
|
||||
content="Diagnostic: Gastrite aiguë.",
|
||||
creation_date=datetime.now(),
|
||||
author="Dr. Test",
|
||||
priority=2,
|
||||
)
|
||||
]
|
||||
|
||||
stay_metadata = StayMetadata(
|
||||
stay_id="stay_max_retry_001",
|
||||
admission_date=datetime.now() - timedelta(days=1),
|
||||
discharge_date=datetime.now(),
|
||||
specialty="gastro-enterologie",
|
||||
unit="medecine",
|
||||
age=45,
|
||||
sex="M",
|
||||
)
|
||||
|
||||
# Mock pour toujours échouer
|
||||
def always_fail(structured_stay):
|
||||
raise RuntimeError("Erreur persistante")
|
||||
|
||||
with patch.object(
|
||||
pipeline.clinical_facts_extractor,
|
||||
"extract_facts",
|
||||
side_effect=always_fail,
|
||||
):
|
||||
result = pipeline.process_stay(documents, stay_metadata)
|
||||
|
||||
# Vérifications
|
||||
assert result.success is False
|
||||
assert result.error_message is not None
|
||||
assert "Erreur persistante" in result.error_message
|
||||
|
||||
|
||||
def test_pipeline_error_handling_with_partial_data(db_session, rag_engine):
|
||||
"""
|
||||
Test que les données partielles sont préservées en cas d'erreur.
|
||||
|
||||
Exigences: 14.6
|
||||
"""
|
||||
from pipeline_mco_pmsi.database.models import StayDB
|
||||
|
||||
pipeline = Pipeline(
|
||||
db_session=db_session,
|
||||
rag_engine=rag_engine,
|
||||
partial_results_mode=True,
|
||||
)
|
||||
|
||||
# Créer le séjour dans la base de données
|
||||
stay = StayDB(
|
||||
stay_id="stay_partial_001",
|
||||
admission_date=datetime.now() - timedelta(days=1),
|
||||
discharge_date=datetime.now(),
|
||||
specialty="gastro-enterologie",
|
||||
unit="medecine",
|
||||
age=45,
|
||||
sex="M",
|
||||
)
|
||||
db_session.add(stay)
|
||||
db_session.commit()
|
||||
|
||||
documents = [
|
||||
ClinicalDocument(
|
||||
document_id="doc_partial_001",
|
||||
document_type="cr_medical",
|
||||
content="Diagnostic: Gastrite aiguë.",
|
||||
creation_date=datetime.now(),
|
||||
author="Dr. Test",
|
||||
priority=2,
|
||||
)
|
||||
]
|
||||
|
||||
stay_metadata = StayMetadata(
|
||||
stay_id="stay_partial_001",
|
||||
admission_date=datetime.now() - timedelta(days=1),
|
||||
discharge_date=datetime.now(),
|
||||
specialty="gastro-enterologie",
|
||||
unit="medecine",
|
||||
age=45,
|
||||
sex="M",
|
||||
)
|
||||
|
||||
# Mock pour échouer après l'extraction des faits
|
||||
def fail_after_facts(proposal, facts, cim10_version, ccam_version):
|
||||
raise RuntimeError("Erreur lors de la vérification")
|
||||
|
||||
with patch.object(
|
||||
pipeline.verificateur,
|
||||
"verify_proposal",
|
||||
side_effect=fail_after_facts,
|
||||
):
|
||||
result = pipeline.process_stay(documents, stay_metadata)
|
||||
|
||||
# Vérifications
|
||||
assert result.success is False
|
||||
# Les données partielles doivent être présentes
|
||||
assert result.structured_stay is not None
|
||||
# Le coding_proposal peut être None si l'erreur survient avant ou pendant le codage
|
||||
# On vérifie juste que le résultat est bien un échec avec un message d'erreur
|
||||
assert result.error_message is not None
|
||||
|
||||
Reference in New Issue
Block a user