Files
t2a_v2/tests/test_viewer.py
dom 40934fdc39 feat: traçabilité source systématique + viewer interactif
Ajoute source_page/source_excerpt à tous les types (biologie, imagerie,
traitements, actes CCAM, antécédents, complications). Convertit antecedents
et complications en types structurés (Antecedent/Complication) avec
validators backward-compat pour les vieux JSON. Étend _apply_source_tracking
à tous les éléments du dossier. Ajoute un endpoint /api/source-text/ et un
modal interactif dans le viewer avec surlignage du texte source.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 20:59:50 +01:00

158 lines
4.7 KiB
Python

"""Tests pour le viewer Flask."""
import pytest
from src.viewer.app import create_app, compute_group_stats, severity_badge, format_duration, format_cpam_text
from src.config import DossierMedical, Diagnostic, ActeCCAM
@pytest.fixture
def app():
app = create_app()
app.config["TESTING"] = True
return app
@pytest.fixture
def client(app):
return app.test_client()
class TestGroupStats:
def test_group_stats(self):
items = [
{
"dossier": DossierMedical(
diagnostics_associes=[
Diagnostic(texte="HTA", cim10_suggestion="I10"),
Diagnostic(texte="Diabète", cim10_suggestion="E11.9", est_cma=True),
],
actes_ccam=[
ActeCCAM(texte="Cholé", code_ccam_suggestion="HMFC004"),
],
alertes_codage=["Alerte 1", "Alerte 2"],
),
},
{
"dossier": DossierMedical(
diagnostics_associes=[
Diagnostic(texte="Obésité", cim10_suggestion="E66.0"),
],
actes_ccam=[
ActeCCAM(texte="TDM", code_ccam_suggestion="ZCQK002"),
],
alertes_codage=[],
),
},
]
stats = compute_group_stats(items)
assert stats["das_count"] == 3
assert stats["actes_count"] == 2
assert stats["alertes_count"] == 2
assert stats["cma_count"] == 1
def test_group_stats_empty(self):
stats = compute_group_stats([])
assert stats["das_count"] == 0
assert stats["alertes_count"] == 0
class TestSeverityBadgeFilter:
def test_severe(self):
result = severity_badge("severe")
assert "Sévère" in result
assert "#dc2626" in result
def test_modere(self):
result = severity_badge("modere")
assert "Modéré" in result
def test_leger(self):
result = severity_badge("leger")
assert "Léger" in result
def test_none(self):
result = severity_badge(None)
assert result == ""
def test_unknown(self):
result = severity_badge("inconnu")
assert result == ""
class TestFormatDuration:
def test_none(self):
assert format_duration(None) == ""
def test_seconds_only(self):
assert format_duration(45.3) == "45.3s"
def test_minutes(self):
assert format_duration(150.0) == "2min 30s"
def test_exact_minutes(self):
assert format_duration(120.0) == "2min"
def test_large_duration(self):
assert format_duration(1257.65) == "20min 57s"
class TestIndexPageLoads:
def test_index_page_loads(self, client):
response = client.get("/")
assert response.status_code == 200
assert b"Dossiers" in response.data
class TestFormatCpamText:
def test_plain_text(self):
result = format_cpam_text("Un simple paragraphe.")
assert "<p" in result
assert "Un simple paragraphe." in result
def test_bullet_list(self):
result = format_cpam_text("- Premier argument\n- Deuxième argument")
assert "<ul" in result
assert "<li>Premier argument</li>" in result
assert "<li>Deuxième argument</li>" in result
def test_mixed_text_and_bullets(self):
text = "Introduction\n- Point A\n- Point B\nConclusion"
result = format_cpam_text(text)
assert "<p" in result
assert "<ul" in result
assert "<li>Point A</li>" in result
assert "Conclusion" in result
def test_none_input(self):
result = format_cpam_text(None)
assert result == ""
def test_empty_input(self):
result = format_cpam_text("")
assert result == ""
def test_html_escaping(self):
result = format_cpam_text("Test <script>alert('xss')</script>")
assert "<script>" not in result
assert "&lt;script&gt;" in result
class TestDetailPageLoads:
def test_detail_page_404(self, client):
"""Un fichier inexistant retourne 404."""
response = client.get("/dossier/nonexistent.json")
assert response.status_code == 404
class TestSourceTextEndpoint:
def test_source_text_404_nonexistent(self, client):
"""Un dossier inexistant retourne 404."""
response = client.get("/api/source-text/nonexistent_dossier")
assert response.status_code == 404
def test_source_text_security_path_traversal(self, client):
"""Path traversal bloqué."""
response = client.get("/api/source-text/../../etc")
assert response.status_code in (403, 404)