#!/usr/bin/env python3 """ Tests — chargement du gazetteer médicaments edsnlp depuis data/ (torch-free). Contexte : le build Windows torch-free (Plan 3) retire torch. Or edsnlp importe torch en dur → en frozen, `import edsnlp` échoue et l'ancienne `_load_edsnlp_drug_names()` retournait silencieusement set() → whitelist médicaments amputée de ~4206 noms → sur-masquage de médicaments pris pour des personnes. Correctif (Option A+B) : A. charger d'abord depuis data/edsnlp/drugs.json (versionné, 0 dépendance) ; fallback sur le package edsnlp (dev). B. log.warning explicite si NI le fichier data NI le package ne sont dispo. Aucun mock du contenu du gazetteer : on utilise le VRAI fichier data extrait. """ from __future__ import annotations from pathlib import Path import pytest import anonymizer_core_refactored_onnx as core ROOT_DIR = Path(__file__).resolve().parents[2] DATA_DRUGS = ROOT_DIR / "data" / "edsnlp" / "drugs.json" # Nombre exact de noms mono-mot (len>=4, lowercase) issus de drugs.json 0.20.0. # C'est aussi le compte historique produit par l'ancienne fonction en dev : # la garantie de non-régression est que la whitelist n'est PAS réduite. EXPECTED_COUNT = 4206 # Noms de médicaments réellement présents dans le gazetteer extrait et qui # entrent en conflit avec des noms/prénoms INSEE (vérifiés, pas inventés). CONFLICT_NAMES = ["elisor", "kessar", "panos", "muse", "sirop"] def test_data_file_present_and_parses(): """Le fichier data doit exister et contenir 1968 codes ATC.""" import json assert DATA_DRUGS.exists(), f"fichier data manquant : {DATA_DRUGS}" data = json.loads(DATA_DRUGS.read_text(encoding="utf-8")) assert len(data) == 1968 def test_load_from_data_exact_count(): """Chargement depuis data/edsnlp/drugs.json → set de 4206 noms exactement.""" result = core._load_edsnlp_drug_names() assert isinstance(result, set) assert len(result) == EXPECTED_COUNT def test_load_contains_conflict_names(): """Les noms-conflits INSEE vérifiés doivent être dans le set (anti-sur-masquage).""" result = core._load_edsnlp_drug_names() for name in CONFLICT_NAMES: assert name in result, f"{name!r} absent du gazetteer médicaments" def test_fallback_to_package_when_data_absent(monkeypatch, tmp_path): """Si le fichier data est absent mais edsnlp importable → fallback package, même résultat (4206).""" pytest.importorskip("edsnlp") # Pointer la constante de chemin data vers un dossier vide → fichier absent. missing = tmp_path / "drugs.json" monkeypatch.setattr(core, "_EDSNLP_DRUGS_DATA_PATH", missing) assert not missing.exists() result = core._load_edsnlp_drug_names() assert len(result) == EXPECTED_COUNT for name in CONFLICT_NAMES: assert name in result def test_warning_when_both_sources_absent(monkeypatch, tmp_path, caplog): """Si le fichier data est absent ET edsnlp non importable → set() + log.warning.""" import builtins missing = tmp_path / "drugs.json" monkeypatch.setattr(core, "_EDSNLP_DRUGS_DATA_PATH", missing) _real_import = builtins.__import__ def _fake_import(name, *args, **kwargs): if name == "edsnlp" or name.startswith("edsnlp."): raise ImportError("edsnlp indisponible (torch-free)") return _real_import(name, *args, **kwargs) monkeypatch.setattr(builtins, "__import__", _fake_import) with caplog.at_level("WARNING", logger=core.log.name): result = core._load_edsnlp_drug_names() assert result == set() assert any( "edsnlp" in rec.message.lower() and rec.levelname == "WARNING" for rec in caplog.records ), "aucun log.warning émis lors de l'échec total" def test_data_source_matches_package_source(monkeypatch, tmp_path): """Le set chargé depuis data doit être IDENTIQUE à celui du fallback package (garantie que l'extraction n'altère pas le gazetteer).""" pytest.importorskip("edsnlp") from_data = core._load_edsnlp_drug_names() missing = tmp_path / "drugs.json" monkeypatch.setattr(core, "_EDSNLP_DRUGS_DATA_PATH", missing) from_package = core._load_edsnlp_drug_names() assert from_data == from_package