Files
anonymisation/tests/unit/test_synthetic_review.py
Domi31tls c7e71072e7 fix(detect): RPPS avec qualificateur (RPPS prescripteur :, RPPS de garde :…) (#1)
Étend `RE_RPPS` pour tolérer 0 à 3 mots qualificateurs entre `RPPS`
et le séparateur `:` ou `-`. Couvre les variantes observées :
- RPPS prescripteur :
- RPPS du médecin signataire :
- RPPS de garde -
- N° RPPS :

Si un qualificateur est présent, le séparateur (`:` ou `-`) devient
obligatoire pour éviter d'aspirer du narratif (faux positif type
"Le RPPS est consulté pour vérifier 12345678901 dans la base").

La lambda `_repl_rpps` reconstruit `RPPS : [RPPS]` en sortie : le
qualificateur est consommé mais perdu (pas de fuite, choix cosmétique).

Cas 005_bacterio_complete passe désormais (retiré de KNOWN_FAILURES).
La fuite `10101010101` derrière `RPPS prescripteur :` est masquée.

Cohérent avec le cadrage section 10.1 (règle cœur générique
applicable à tout établissement de santé français — pas de
spécificité locale).

Tests : 72 passed, 1 xfailed (avant : 71 passed, 2 xfailed).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 22:33:01 +02:00

77 lines
2.3 KiB
Python

#!/usr/bin/env python3
"""
Gate pytest sur le corpus synthétique de revue humaine (couche 2).
Chaque cas dans tests/synthetic_review/cases/ doit produire un texte
identique à expected.txt, satisfaire ses expectations.json et ne révéler
aucune fuite via le LeakScanner.
Les cas listés dans KNOWN_FAILURES sont marqués xfail(strict=True) :
ils sont attendus en échec aujourd'hui car ils révèlent des bugs réels
du moteur. Quand un bug est fixé, le cas correspondant passe → pytest
signale le xpass strict, ce qui force à retirer son entrée de
KNOWN_FAILURES.
"""
from __future__ import annotations
import sys
from pathlib import Path
import pytest
ROOT = Path(__file__).resolve().parents[2]
if str(ROOT) not in sys.path:
sys.path.insert(0, str(ROOT))
from tools.run_synthetic_review_corpus import ( # noqa: E402
CASES_DIR,
run_case,
)
KNOWN_FAILURES: dict[str, str] = {
"009_multi_etablissements": (
"Fuites résiduelles : suffixe `de Bordeaux` après "
"[ETABLISSEMENT], CHCB en fin de phrase. À traiter via "
"admin_rules (étape B suivante)."
),
}
def _case_dirs() -> list[Path]:
if not CASES_DIR.exists():
return []
return sorted(path for path in CASES_DIR.iterdir() if path.is_dir())
def _make_param(case_dir: Path) -> "pytest.ParameterSet":
if case_dir.name in KNOWN_FAILURES:
return pytest.param(
case_dir,
id=case_dir.name,
marks=pytest.mark.xfail(
strict=True,
reason=KNOWN_FAILURES[case_dir.name],
),
)
return pytest.param(case_dir, id=case_dir.name)
def test_synthetic_review_inventory():
"""Le corpus doit contenir 10 cas (cible de cadrage produit)."""
assert CASES_DIR.exists(), f"Dossier corpus introuvable : {CASES_DIR}"
case_dirs = _case_dirs()
assert len(case_dirs) == 10, (
f"Attendu 10 cas dans synthetic_review/cases, trouvé {len(case_dirs)} : "
f"{[c.name for c in case_dirs]}"
)
@pytest.mark.parametrize("case_dir", [_make_param(c) for c in _case_dirs()])
def test_synthetic_review_case(case_dir: Path):
result = run_case(case_dir)
assert not result["failures"], (
f"{case_dir.name} : {', '.join(result['failures'])}\n"
f"Diff disponible dans {result['output_dir']}/diff.txt"
)