Files
t2a_v2/tests/test_p1_lite.py
2026-03-05 00:37:41 +01:00

227 lines
8.7 KiB
Python

"""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