"""Tests pour le module de découpage multi-dossiers.""" from unittest.mock import patch, MagicMock import pytest from src.extraction.document_splitter import split_documents, _split_trackare, _split_crh # --- Données de test --- TRACKARE_PATIENT_BLOCK = """CENTRE HOSPITALIER COTE BASQUE Dossier Patient Détails des patients Nom de naissance: DUPONT IPP: 01234567 Nom et Prénom: DUPONT JEAN Date de naissance: 15/03/1960 Sexe: Masculin Lieu de naissance: BAYONNE Adresse: 12 RUE DU PORT Ville de résidence: BAYONNE Code Postal: 64100""" TRACKARE_EPISODE_1 = """Détails épisode Episode No: 23001111 Date d'admission: 10/01/2023 Heure d'admission: 14:30 Date de sortie: 10/01/2023 Heure de sortie: 18:00 Localisation: URG-ADULTE Médecin courant: DR MARTIN Observations médicales Note d'évolution DR MARTIN 10/01/2023 15:00 Douleur abdominale. Bilan normal. Diagnostic aux urgences Principal actif R10.4 Douleurs abdominales 10/01/2023 15:30""" TRACKARE_EPISODE_2 = """Détails épisode Episode No: 23002222 Date d'admission: 15/03/2023 Heure d'admission: 09:15 Date de sortie: 15/03/2023 Heure de sortie: 13:00 Localisation: URG-ADULTE Médecin courant: DR DURAND Observations médicales Note d'évolution DR DURAND 15/03/2023 10:00 Chute mécanique. Radio cheville gauche normale. Diagnostic aux urgences Principal actif S93.4 Entorse de la cheville 15/03/2023 10:30""" TRACKARE_SINGLE = TRACKARE_PATIENT_BLOCK + "\n" + TRACKARE_EPISODE_1 TRACKARE_MULTI = TRACKARE_PATIENT_BLOCK + "\n" + TRACKARE_EPISODE_1 + "\n" + TRACKARE_EPISODE_2 CRH_SINGLE = """MME MARTIN SOPHIE 12 RUE DES FLEURS 64100 BAYONNE Mon cher confrère, Votre patiente MARTIN Sophie née le 05/06/1975 a été hospitalisée du 20/01/2023 au 25/01/2023 pour le motif suivant: Cholécystite aiguë lithiasique. Cordialement, Dr DURAND""" CRH_DOC_1 = """MME MARTIN SOPHIE 12 RUE DES FLEURS 64100 BAYONNE Mon cher confrère, Votre patiente MARTIN Sophie née le 05/06/1975 a été hospitalisée du 20/01/2023 au 25/01/2023 pour le motif suivant: Cholécystite aiguë lithiasique. Cordialement, Dr DURAND """ CRH_DOC_2 = """M. BERNARD PIERRE 5 AVENUE DU MARÉCHAL 64200 BIARRITZ Mon cher confrère, Votre patient BERNARD Pierre né le 12/11/1950 a été hospitalisé du 02/02/2023 au 08/02/2023 pour le motif suivant: Décompensation cardiaque. Cordialement, Dr PUJOS """ CRH_MULTI = CRH_DOC_1 + CRH_DOC_2 # --- Tests Trackare --- class TestSplitTrackare: def test_single_episode_returns_unchanged(self): result = _split_trackare(TRACKARE_SINGLE) assert len(result) == 1 assert result[0] == TRACKARE_SINGLE def test_multi_episode_returns_two_chunks(self): result = _split_trackare(TRACKARE_MULTI) assert len(result) == 2 def test_each_chunk_has_patient_block(self): result = _split_trackare(TRACKARE_MULTI) for chunk in result: assert "DUPONT JEAN" in chunk assert "IPP: 01234567" in chunk def test_each_chunk_has_own_episode(self): result = _split_trackare(TRACKARE_MULTI) assert "Episode No: 23001111" in result[0] assert "Episode No: 23002222" in result[1] def test_episodes_are_separated(self): result = _split_trackare(TRACKARE_MULTI) # Le premier chunk ne doit PAS contenir l'épisode 2 assert "Episode No: 23002222" not in result[0] # Le second chunk ne doit PAS contenir l'épisode 1 assert "Episode No: 23001111" not in result[1] def test_no_episode_returns_unchanged(self): text = "Un texte sans épisode du tout." result = _split_trackare(text) assert len(result) == 1 assert result[0] == text def test_fallback_without_details_episode(self): """Si pas de 'Détails épisode', coupe sur 'Episode No:'.""" text = ( "Nom de naissance: DUPONT IPP: 01234567\n" "Episode No: 111\nContenu épisode 1\n" "Episode No: 222\nContenu épisode 2" ) result = _split_trackare(text) assert len(result) == 2 assert "Episode No: 111" in result[0] assert "Episode No: 222" in result[1] # --- Tests CRH --- class TestSplitCRH: def test_single_crh_returns_unchanged(self): result = _split_crh(CRH_SINGLE) assert len(result) == 1 assert result[0] == CRH_SINGLE def test_multi_crh_returns_two_chunks(self): result = _split_crh(CRH_MULTI) assert len(result) == 2 def test_each_chunk_is_independent(self): result = _split_crh(CRH_MULTI) assert "MARTIN SOPHIE" in result[0] assert "BERNARD PIERRE" in result[1] def test_chunks_contain_medical_content(self): result = _split_crh(CRH_MULTI) assert "Cholécystite" in result[0] assert "Décompensation cardiaque" in result[1] def test_no_header_returns_unchanged(self): text = "Un texte médical sans header patient." result = _split_crh(text) assert len(result) == 1 def test_header_without_crh_markers_no_split(self): """Un header patient sans patterns CRH ne déclenche pas de split.""" text = "MME DUPONT MARIE\nTexte quelconque\nMME MARTIN SOPHIE\nAutre texte" result = _split_crh(text) assert len(result) == 1 # --- Tests dispatch --- class TestSplitDocuments: def test_dispatch_trackare(self): result = split_documents(TRACKARE_MULTI, "trackare") assert len(result) == 2 def test_dispatch_crh(self): result = split_documents(CRH_MULTI, "crh") assert len(result) == 2 def test_dispatch_unknown_type(self): result = split_documents("some text", "unknown") assert len(result) == 1 assert result[0] == "some text" def test_single_doc_no_split(self): result = split_documents(TRACKARE_SINGLE, "trackare") assert len(result) == 1 # --- Test intégration process_pdf --- class TestProcessPdfMulti: @patch("src.main.extract_document_with_pages") @patch("src.main.extract_medical_info") @patch("src.main._run_edsnlp", return_value=None) @patch("src.main._use_edsnlp", False) def test_multi_episode_returns_multiple_results( self, mock_edsnlp, mock_medical, mock_extract ): from pathlib import Path from src.main import process_pdf from src.config import DossierMedical, Diagnostic from src.extraction.page_tracker import PageTracker from src.extraction.pdf_extractor import ExtractionStats # Mock extract_document_with_pages retournant un texte multi-épisodes Trackare mock_extract.return_value = ( TRACKARE_MULTI, PageTracker([(0, len(TRACKARE_MULTI))]), ExtractionStats(total_pages=1, chars_per_page=[len(TRACKARE_MULTI)], total_chars=len(TRACKARE_MULTI)), ) # Mock extract_medical_info retournant un DossierMedical minimal mock_medical.return_value = DossierMedical( diagnostic_principal=Diagnostic(texte="test"), ) results = process_pdf(Path("fake.pdf")) assert len(results) == 2 # Chaque résultat est un tuple (text, dossier, report) for anon_text, dossier, report in results: assert isinstance(dossier, DossierMedical)