Code/tests/unit/test_ivc.py
Stéphan Peccini 8e2556c2b0
test(unit): +381 tests unitaires — couverture 16%→35%
- 9 nouveaux fichiers de tests (persistance, translations, fiches, indices, IHH)
- Enrichissement des tests existants (graph_utils, gitea, widgets, tickets)
- 67→448 tests, tous passent

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 11:52:21 +01:00

343 lines
13 KiB
Python

"""Tests unitaires pour le module app.fiches.utils.dynamic.indice.ivc.
Ces tests verifient les fonctions de traitement Markdown pour l'indice IVC :
- _synth_ivc
- _ivc_segments
- build_ivc_sections
"""
from app.fiches.utils.dynamic.indice.ivc import (
IVC_RE,
_ivc_segments,
_synth_ivc,
build_ivc_sections,
)
# ──────────────────────────────────────────────
# Helpers
# ──────────────────────────────────────────────
def _yaml_ivc_bloc(minerai: dict) -> str:
"""Construit un bloc YAML markdown pour un minerai IVC."""
lignes = ["```yaml"]
lignes.append("minerai:")
for k, v in minerai.items():
lignes.append(f" {k}: {v}")
lignes.append("```")
return "\n".join(lignes)
def _sample_minerai(**overrides) -> dict:
"""Retourne un dictionnaire minerai avec des valeurs par defaut."""
base = {
"nom": "Lithium",
"ivc": 45,
"vulnerabilite": "Moyenne",
}
base.update(overrides)
return base
def _make_ivc_md(minerais: list[dict], with_markers: bool = True) -> str:
"""Construit un markdown complet pour IVC avec des blocs YAML et des templates Jinja2."""
parts = ["# Indice de Vulnerabilite Complete (IVC)\n"]
parts.append("Introduction de la fiche.\n")
for minerai in minerais:
parts.append(_yaml_ivc_bloc(minerai))
parts.append("\n## Analyse de {{ nom }}\n")
parts.append("L'IVC de {{ nom }} est de {{ ivc }} avec une vulnerabilite {{ vulnerabilite }}.\n")
if with_markers:
parts.append("\n## Tableau de synth\u00e8se\n")
parts.append("<!---- AUTO-BEGIN:TABLEAU-FINAL -->\nancien tableau\n<!---- AUTO-END:TABLEAU-FINAL -->")
return "\n".join(parts)
# ──────────────────────────────────────────────
# IVC_RE (regex)
# ──────────────────────────────────────────────
class TestIvcRegex:
"""Tests pour l'expression reguliere IVC_RE."""
def test_match_bloc_ivc_simple(self):
"""Test la detection d'un bloc yaml IVC simple."""
md = "```yaml\nminerai:\n nom: Lithium\n ivc: 45\n```"
matches = list(IVC_RE.finditer(md))
assert len(matches) == 1
def test_match_blocs_ivc_multiples(self):
"""Test la detection de plusieurs blocs yaml IVC."""
md = (
"```yaml\nminerai:\n nom: Lithium\n ivc: 45\n```\n"
"texte\n"
"```yaml\nminerai:\n nom: Cobalt\n ivc: 62\n```"
)
matches = list(IVC_RE.finditer(md))
assert len(matches) == 2
def test_pas_de_match_sans_minerai(self):
"""Test qu'un bloc yaml sans 'minerai:' ne matche pas."""
md = "```yaml\nautres:\n x: 1\n```"
matches = list(IVC_RE.finditer(md))
assert len(matches) == 0
def test_match_insensible_casse(self):
"""Test que YAML en majuscules est aussi detecte."""
md = "```YAML\nminerai:\n nom: Test\n```"
matches = list(IVC_RE.finditer(md))
assert len(matches) == 1
def test_espaces_entre_yaml_et_minerai(self):
"""Test avec des espaces entre le tag yaml et minerai."""
md = "```yaml\n minerai:\n nom: Test\n```"
matches = list(IVC_RE.finditer(md))
assert len(matches) == 1
# ──────────────────────────────────────────────
# _synth_ivc
# ──────────────────────────────────────────────
class TestSynthIvc:
"""Tests pour la generation du tableau de synthese IVC."""
def test_un_minerai(self):
"""Test la synthese avec un seul minerai."""
minerais = [_sample_minerai()]
result = _synth_ivc(minerais)
assert "| Minerai | IVC | Vuln\u00e9rabilit\u00e9 |" in result
assert "| :-- | :-- | :-- |" in result
assert "Lithium" in result
assert "45" in result
assert "Moyenne" in result
def test_plusieurs_minerais(self):
"""Test la synthese avec plusieurs minerais."""
minerais = [
_sample_minerai(nom="Lithium", ivc=45, vulnerabilite="Moyenne"),
_sample_minerai(nom="Cobalt", ivc=72, vulnerabilite="Elevee"),
_sample_minerai(nom="Cuivre", ivc=18, vulnerabilite="Faible"),
]
result = _synth_ivc(minerais)
assert "Lithium" in result
assert "Cobalt" in result
assert "Cuivre" in result
def test_nombre_lignes(self):
"""Test que le nombre de lignes correspond aux donnees."""
minerais = [
_sample_minerai(nom="A", ivc=10, vulnerabilite="X"),
_sample_minerai(nom="B", ivc=20, vulnerabilite="Y"),
]
result = _synth_ivc(minerais)
lignes = result.strip().split("\n")
# 2 en-tetes + 2 donnees = 4
assert len(lignes) == 4
def test_en_tetes_tableau(self):
"""Test que les en-tetes sont corrects."""
minerais = [_sample_minerai()]
result = _synth_ivc(minerais)
lignes = result.strip().split("\n")
assert lignes[0] == "| Minerai | IVC | Vuln\u00e9rabilit\u00e9 |"
assert lignes[1] == "| :-- | :-- | :-- |"
def test_liste_vide(self):
"""Test avec une liste vide de minerais."""
result = _synth_ivc([])
lignes = result.strip().split("\n")
# Seulement les 2 lignes d'en-tete
assert len(lignes) == 2
# ──────────────────────────────────────────────
# _ivc_segments
# ──────────────────────────────────────────────
class TestIvcSegments:
"""Tests pour l'extraction des segments entre blocs YAML IVC."""
def test_un_segment(self):
"""Test l'extraction d'un seul segment IVC."""
minerai = _sample_minerai()
md = _yaml_ivc_bloc(minerai) + "\nSegment apres le bloc."
segments = list(_ivc_segments(md))
# 1 segment + 1 reste eventuel
assert len(segments) == 2
data, seg = segments[0]
assert data["nom"] == "Lithium"
assert "Segment apres le bloc." in seg
def test_deux_segments(self):
"""Test l'extraction de deux segments entre blocs IVC."""
m1 = _sample_minerai(nom="Lithium", ivc=45, vulnerabilite="Moyenne")
m2 = _sample_minerai(nom="Cobalt", ivc=72, vulnerabilite="Elevee")
md = _yaml_ivc_bloc(m1) + "\nSegment 1\n" + _yaml_ivc_bloc(m2) + "\nSegment 2"
segments = list(_ivc_segments(md))
# 2 segments + 1 reste eventuel
assert len(segments) == 3
assert segments[0][0]["nom"] == "Lithium"
assert "Segment 1" in segments[0][1]
assert segments[1][0]["nom"] == "Cobalt"
assert "Segment 2" in segments[1][1]
def test_reste_final(self):
"""Test que le reste final (apres le dernier bloc) est capture."""
minerai = _sample_minerai()
md = _yaml_ivc_bloc(minerai) + "\nContenu.\nTexte final."
segments = list(_ivc_segments(md))
# Le dernier segment doit etre (None, reste)
dernier = segments[-1]
assert dernier[0] is None
def test_pas_de_bloc_yaml(self):
"""Test avec un markdown sans bloc yaml IVC."""
md = "Texte sans bloc yaml."
segments = list(_ivc_segments(md))
# Seulement le reste (None, texte_entier)
assert len(segments) == 1
assert segments[0][0] is None
assert "Texte sans bloc yaml." in segments[0][1]
# ──────────────────────────────────────────────
# build_ivc_sections
# ──────────────────────────────────────────────
class TestBuildIvcSections:
"""Tests pour la fonction principale de construction des sections dynamiques IVC."""
def test_remplacement_jinja2(self):
"""Test que les templates Jinja2 sont rendus correctement."""
minerais = [_sample_minerai()]
md = _make_ivc_md(minerais)
result = build_ivc_sections(md)
assert "Lithium" in result
assert "45" in result
assert "{{ nom }}" not in result
def test_tableau_synthese_genere(self):
"""Test que le tableau de synthese est genere."""
minerais = [_sample_minerai()]
md = _make_ivc_md(minerais)
result = build_ivc_sections(md)
assert "| Minerai | IVC | Vuln\u00e9rabilit\u00e9 |" in result
def test_marqueurs_preserves(self):
"""Test que les marqueurs AUTO-BEGIN/END sont preserves."""
minerais = [_sample_minerai()]
md = _make_ivc_md(minerais)
result = build_ivc_sections(md)
assert "<!---- AUTO-BEGIN:TABLEAU-FINAL -->" in result
assert "<!---- AUTO-END:TABLEAU-FINAL -->" in result
def test_ancien_contenu_remplace(self):
"""Test que l'ancien contenu entre les marqueurs est remplace."""
minerais = [_sample_minerai()]
md = _make_ivc_md(minerais)
result = build_ivc_sections(md)
assert "ancien tableau" not in result
def test_pas_de_bloc_yaml_retourne_original(self):
"""Test qu'un markdown sans bloc yaml IVC est retourne tel quel."""
md = "# Titre\n\nTexte sans bloc yaml IVC."
result = build_ivc_sections(md)
assert "Texte sans bloc yaml IVC." in result
def test_intro_preservee(self):
"""Test que l'introduction est preservee dans le resultat."""
minerais = [_sample_minerai()]
md = _make_ivc_md(minerais)
result = build_ivc_sections(md)
assert "# Indice de Vulnerabilite Complete (IVC)" in result
assert "Introduction de la fiche." in result
def test_plusieurs_minerais(self):
"""Test avec plusieurs minerais genere un document complet."""
minerais = [
_sample_minerai(nom="Lithium", ivc=45, vulnerabilite="Moyenne"),
_sample_minerai(nom="Cobalt", ivc=72, vulnerabilite="Elevee"),
]
md = _make_ivc_md(minerais)
result = build_ivc_sections(md)
assert "Lithium" in result
assert "Cobalt" in result
assert "45" in result
assert "72" in result
def test_template_jinja2_avec_toutes_variables(self):
"""Test que toutes les variables Jinja2 du minerai sont accessibles."""
minerai = _sample_minerai(nom="Indium", ivc=58, vulnerabilite="Haute")
md = _make_ivc_md([minerai])
result = build_ivc_sections(md)
assert "Indium" in result
assert "58" in result
assert "Haute" in result
def test_md_vide(self):
"""Test avec un markdown vide."""
result = build_ivc_sections("")
assert result == ""
def test_bloc_yaml_sans_marqueurs(self):
"""Test avec un bloc yaml IVC mais sans marqueurs AUTO-BEGIN/END."""
minerais = [_sample_minerai()]
md = _make_ivc_md(minerais, with_markers=False)
result = build_ivc_sections(md)
# Le template doit etre rendu meme sans marqueurs
assert "Lithium" in result
assert "{{ nom }}" not in result
def test_separations_segments(self):
"""Test que les segments sont separes par des doubles retours a la ligne."""
minerais = [
_sample_minerai(nom="A", ivc=10, vulnerabilite="Faible"),
_sample_minerai(nom="B", ivc=20, vulnerabilite="Moyenne"),
]
md = _make_ivc_md(minerais)
result = build_ivc_sections(md)
# Les segments doivent etre joints par "\n\n"
assert "\n\n" in result
def test_ordre_minerais_preserve(self):
"""Test que l'ordre des minerais dans le document est preserve."""
minerais = [
_sample_minerai(nom="Premier", ivc=10, vulnerabilite="X"),
_sample_minerai(nom="Second", ivc=20, vulnerabilite="Y"),
]
md = _make_ivc_md(minerais)
result = build_ivc_sections(md)
idx_premier = result.find("Premier")
idx_second = result.find("Second")
assert idx_premier < idx_second
def test_synthese_contient_tous_minerais(self):
"""Test que le tableau de synthese contient tous les minerais."""
minerais = [
_sample_minerai(nom="Lithium", ivc=45, vulnerabilite="Moyenne"),
_sample_minerai(nom="Cobalt", ivc=72, vulnerabilite="Elevee"),
_sample_minerai(nom="Cuivre", ivc=18, vulnerabilite="Faible"),
]
md = _make_ivc_md(minerais)
result = build_ivc_sections(md)
# Verifier dans la section tableau final
assert "Lithium" in result
assert "Cobalt" in result
assert "Cuivre" in result