"""Tests P1-lite — LOGIC-2 (CPAM dégradé), LOGIC-3 (modèles identiques). Sans mocks : manipulation directe des structures de données et env vars. """ from __future__ import annotations from src.config import ( ControleCPAM, DossierMedical, OLLAMA_MODELS, Sejour, check_adversarial_model_config, ) # ============================================================ # LOGIC-2 — CPAM passe 1 échoue → mode dégradé tracé # ============================================================ class TestCpamDegradedMode: """Vérifie que le mode dégradé passe 1 est correctement tracé.""" def test_degraded_sets_alertes_codage(self): """Si extraction est None, alertes_codage doit contenir le message.""" dossier = DossierMedical(sejour=Sejour()) # Simule le comportement de generate_cpam_response quand extraction = None extraction = None degraded_pass1 = extraction is None if degraded_pass1: dossier.alertes_codage.append( "CPAM: passe 1 (extraction structurée) échouée → mode dégradé" ) assert any("passe 1" in a for a in dossier.alertes_codage) assert any("dégradé" in a for a in dossier.alertes_codage) def test_degraded_sets_quality_flags_on_result(self): """quality_flags ajouté au résultat quand dégradé.""" result = {"conclusion": "test"} degraded_pass1 = True if degraded_pass1: result.setdefault("quality_flags", {}) result["quality_flags"]["cpam_pass1_failed"] = True result["quality_flags"]["degraded_mode"] = True assert result["quality_flags"]["cpam_pass1_failed"] is True assert result["quality_flags"]["degraded_mode"] is True def test_non_degraded_no_quality_flags(self): """Pas de quality_flags quand extraction réussit.""" result = {"conclusion": "test"} extraction = {"comprehension_contestation": "ok"} degraded_pass1 = extraction is None assert degraded_pass1 is False assert "quality_flags" not in result def test_quality_flags_format_matches_spec(self): """Format quality_flags conforme au spec.""" result: dict = {} result.setdefault("quality_flags", {}) result["quality_flags"]["cpam_pass1_failed"] = True result["quality_flags"]["degraded_mode"] = True flags = result["quality_flags"] assert isinstance(flags, dict) assert "cpam_pass1_failed" in flags assert "degraded_mode" in flags # ============================================================ # LOGIC-3 — Modèles CPAM et validation identiques # ============================================================ class TestAdversarialModelCheck: """Vérifie la détection de modèles identiques CPAM/validation.""" def test_same_model_detected(self): """Modèles identiques → (True, message).""" old_cpam = OLLAMA_MODELS["cpam"] old_val = OLLAMA_MODELS["validation"] OLLAMA_MODELS["cpam"] = "test-same-model" OLLAMA_MODELS["validation"] = "test-same-model" try: same, msg = check_adversarial_model_config() assert same is True assert "identiques" in msg assert "test-same-model" in msg finally: OLLAMA_MODELS["cpam"] = old_cpam OLLAMA_MODELS["validation"] = old_val def test_different_models_ok(self): """Modèles différents → (False, '').""" old_cpam = OLLAMA_MODELS["cpam"] old_val = OLLAMA_MODELS["validation"] OLLAMA_MODELS["cpam"] = "model-a" OLLAMA_MODELS["validation"] = "model-b" try: same, msg = check_adversarial_model_config() assert same is False assert msg == "" finally: OLLAMA_MODELS["cpam"] = old_cpam OLLAMA_MODELS["validation"] = old_val def test_adversarial_skip_returns_degraded_result(self): """Si même modèle, la validation adversariale retourne un résultat dégradé.""" old_cpam = OLLAMA_MODELS["cpam"] old_val = OLLAMA_MODELS["validation"] OLLAMA_MODELS["cpam"] = "same-model" OLLAMA_MODELS["validation"] = "same-model" try: same, msg = check_adversarial_model_config() assert same is True # Simule le comportement de _validate_adversarial quand same_model degraded = { "coherent": True, "erreurs": [f"Validation adversariale dégradée : {msg}"], "score_confiance": 0, } assert degraded["score_confiance"] == 0 assert "dégradée" in degraded["erreurs"][0] finally: OLLAMA_MODELS["cpam"] = old_cpam OLLAMA_MODELS["validation"] = old_val def test_empty_model_not_flagged(self): """Modèles vides ne déclenchent pas le flag.""" old_cpam = OLLAMA_MODELS["cpam"] old_val = OLLAMA_MODELS["validation"] OLLAMA_MODELS["cpam"] = "" OLLAMA_MODELS["validation"] = "" try: same, msg = check_adversarial_model_config() assert same is False finally: OLLAMA_MODELS["cpam"] = old_cpam OLLAMA_MODELS["validation"] = old_val # ============================================================ # LOGIC-2 & LOGIC-3 — quality_flags + alertes visibles output # ============================================================ class TestQualityFlagsOutput: """Vérifie que les quality_flags et alertes sont visibles dans l'output.""" def test_cpam_pass1_failure_sets_quality_flags_and_alert(self): """LOGIC-2 — passe 1 échouée → quality_flags + alerte dans dossier.""" dossier = DossierMedical(sejour=Sejour()) result: dict = {"conclusion": "test argument"} # Simule le flow exact de generate_cpam_response (lines 122-165) extraction = None # passe 1 échouée degraded_pass1 = extraction is None if degraded_pass1: dossier.alertes_codage.append( "CPAM: passe 1 (extraction structurée) échouée → mode dégradé" ) if degraded_pass1: result.setdefault("quality_flags", {}) result["quality_flags"]["cpam_pass1_failed"] = True result["quality_flags"]["degraded_mode"] = True # Vérifications assert result["quality_flags"]["cpam_pass1_failed"] is True assert result["quality_flags"]["degraded_mode"] is True assert any("passe 1" in a and "dégradé" in a for a in dossier.alertes_codage) def test_adversarial_same_model_sets_quality_flag_and_alert(self): """LOGIC-3 — modèles identiques → quality_flags + alerte dans dossier.""" dossier = DossierMedical(sejour=Sejour()) result: dict = {"conclusion": "test argument"} old_cpam = OLLAMA_MODELS["cpam"] old_val = OLLAMA_MODELS["validation"] OLLAMA_MODELS["cpam"] = "same-test-model" OLLAMA_MODELS["validation"] = "same-test-model" try: # Simule le flow exact de generate_cpam_response (lines 192-199) same_model, model_msg = check_adversarial_model_config() if same_model: result.setdefault("quality_flags", {}) result["quality_flags"]["adversarial_disabled_same_model"] = True dossier.alertes_codage.append( "Validation adversariale désactivée (modèles identiques)" ) assert same_model is True assert result["quality_flags"]["adversarial_disabled_same_model"] is True assert any("adversariale" in a and "identiques" in a for a in dossier.alertes_codage) finally: OLLAMA_MODELS["cpam"] = old_cpam OLLAMA_MODELS["validation"] = old_val def test_no_flags_when_all_ok(self): """Pas de quality_flags quand tout fonctionne correctement.""" dossier = DossierMedical(sejour=Sejour()) result: dict = {"conclusion": "test argument"} # Passe 1 OK extraction = {"comprehension_contestation": "ok"} degraded_pass1 = extraction is None assert degraded_pass1 is False # Modèles différents old_cpam = OLLAMA_MODELS["cpam"] old_val = OLLAMA_MODELS["validation"] OLLAMA_MODELS["cpam"] = "model-a" OLLAMA_MODELS["validation"] = "model-b" try: same_model, _ = check_adversarial_model_config() assert same_model is False finally: OLLAMA_MODELS["cpam"] = old_cpam OLLAMA_MODELS["validation"] = old_val assert "quality_flags" not in result assert len(dossier.alertes_codage) == 0