Cette mise a jour complete ameliore significativement la qualite et la maintenabilite du projet. 1. Extension de la couverture de tests Couverture globale passee de 8% a 16% (+100%) - Ajout de 25 nouveaux tests (total: 67 tests, 100% passent) - Nouveaux fichiers de tests: * tests/unit/test_gitea.py (17 tests) * tests/unit/test_fiches_tickets.py (8 tests) Etat de la couverture par module: - utils/gitea.py: 100% - utils/widgets.py: 100% - utils/logger.py: 94% - app/fiches/utils/tickets/core.py: 77% - utils/graph_utils.py: 59% 2. Documentation d'architecture complete Creation de 3 nouveaux documents (30 Ko total): - docs/ARCHITECTURE.md (15 Ko) * Architecture complete du projet * Flux de donnees detailles * Indices de vulnerabilite (IHH, ISG, ICS, IVC) * Structure du graphe NetworkX - docs/MODULES.md (15 Ko) * Guide des 11 modules principaux * Exemples de code (15+ snippets) * Bonnes pratiques * Guide de depannage - docs/README.md (4 Ko) * Index de toute la documentation Contenu documente: - 5 modules applicatifs - 6 modules utilitaires - 4 indices de vulnerabilite avec formules et seuils - Conventions de code 3. Reorganisation de la documentation Structure finale optimisee: - Racine: README.md (mis a jour) + Instructions.md - docs/: 11 documents organises par categorie Fichiers deplaces vers docs/: - README_connexion.md -> docs/CONNEXION.md - GUIDE_LOGS.md -> docs/ - GUIDE_RUFF.md -> docs/ - RAPPORT_RUFF.md -> docs/ - RAPPORT_CORRECTIONS_AUTO.md -> docs/ - REFACTORING_REPORT.md -> docs/ - VERIFICATION_LOGS.md -> docs/ - TODO_IA_BATCH.md -> docs/ 4. Ajout de docstrings 52 fonctions documentees en style Google (100%) Documentation en francais avec Args, Returns, Raises 5. Corrections automatiques Ruff Application de 347 corrections automatiques: - Formatage du code (line-length: 120) - Organisation des imports - Simplifications syntaxiques - Suppressions de code mort - Ameliorations de performance 6. Configuration qualite du code Nouveaux fichiers: - pyproject.toml: configuration Ruff complete - .vscode/settings.json: integration Ruff avec formatOnSave - GUIDE_RUFF.md: documentation du linter - GUIDE_LOGS.md: documentation du logging - .gitignore: ajout htmlcov/ pour rapports de couverture Etat final du projet: - Linter: Ruff configure (15 regles actives) - Tests: 67 tests (100% passent) - Couverture de code: 16% - Docstrings: 52/52 (100%) - Documentation: 11 fichiers organises Impact: - Tests plus robustes et maintenables - Documentation technique complete - Meilleure organisation des fichiers - Workflow optimise avec Ruff - Code pret pour integration continue References: - Architecture: docs/ARCHITECTURE.md - Guide modules: docs/MODULES.md - Tests: tests/unit/ - Configuration: pyproject.toml Co-Authored-By: Claude <noreply@anthropic.com>
195 lines
6.6 KiB
Python
195 lines
6.6 KiB
Python
"""
|
|
Tests unitaires pour le module utils.widgets.
|
|
|
|
Ces tests vérifient le fonctionnement des widgets HTML personnalisés.
|
|
"""
|
|
|
|
import pytest
|
|
from unittest.mock import patch, MagicMock
|
|
from utils.widgets import html_expander
|
|
|
|
|
|
class TestHtmlExpander:
|
|
"""Tests pour la fonction html_expander."""
|
|
|
|
@patch('utils.widgets.st')
|
|
@patch('utils.widgets.markdown')
|
|
def test_expander_basic(self, mock_markdown, mock_st):
|
|
"""Test la création basique d'un expander."""
|
|
mock_markdown.markdown.return_value = "<p>Test content</p>"
|
|
|
|
html_expander("Test Title", "Test content")
|
|
|
|
# Vérifier que markdown.markdown a été appelé
|
|
mock_markdown.markdown.assert_called_once_with("Test content")
|
|
|
|
# Vérifier que st.markdown a été appelé
|
|
assert mock_st.markdown.called
|
|
call_args = mock_st.markdown.call_args
|
|
html_output = call_args[0][0]
|
|
|
|
assert "Test Title" in html_output
|
|
assert "<p>Test content</p>" in html_output
|
|
assert "<details" in html_output
|
|
assert "<summary" in html_output
|
|
|
|
@patch('utils.widgets.st')
|
|
@patch('utils.widgets.markdown')
|
|
def test_expander_open_by_default(self, mock_markdown, mock_st):
|
|
"""Test un expander ouvert par défaut."""
|
|
mock_markdown.markdown.return_value = "<p>Content</p>"
|
|
|
|
html_expander("Title", "Content", open_by_default=True)
|
|
|
|
call_args = mock_st.markdown.call_args
|
|
html_output = call_args[0][0]
|
|
|
|
assert 'open' in html_output
|
|
|
|
@patch('utils.widgets.st')
|
|
@patch('utils.widgets.markdown')
|
|
def test_expander_closed_by_default(self, mock_markdown, mock_st):
|
|
"""Test un expander fermé par défaut."""
|
|
mock_markdown.markdown.return_value = "<p>Content</p>"
|
|
|
|
html_expander("Title", "Content", open_by_default=False)
|
|
|
|
call_args = mock_st.markdown.call_args
|
|
html_output = call_args[0][0]
|
|
|
|
# 'open' ne doit pas être dans les attributs si fermé
|
|
# Note: vérifie la logique, pas juste la présence du mot 'open'
|
|
assert '<details id=' in html_output
|
|
|
|
@patch('utils.widgets.st')
|
|
@patch('utils.widgets.markdown')
|
|
def test_expander_with_css_classes(self, mock_markdown, mock_st):
|
|
"""Test avec des classes CSS personnalisées."""
|
|
mock_markdown.markdown.return_value = "<p>Content</p>"
|
|
|
|
html_expander(
|
|
"Title",
|
|
"Content",
|
|
details_class="custom-details",
|
|
summary_class="custom-summary"
|
|
)
|
|
|
|
call_args = mock_st.markdown.call_args
|
|
html_output = call_args[0][0]
|
|
|
|
assert 'class="custom-details"' in html_output
|
|
assert 'class="custom-summary"' in html_output
|
|
|
|
@patch('utils.widgets.st')
|
|
@patch('utils.widgets.markdown')
|
|
@patch('utils.widgets.logger')
|
|
def test_expander_markdown_import_error(self, mock_logger, mock_markdown, mock_st):
|
|
"""Test le fallback si markdown n'est pas disponible."""
|
|
# Simuler une ImportError
|
|
mock_markdown.markdown.side_effect = ImportError("Module not found")
|
|
|
|
html_expander("Title", "Test\ncontent")
|
|
|
|
# Vérifier que le logger a été appelé
|
|
mock_logger.warning.assert_called_once()
|
|
assert "markdown" in mock_logger.warning.call_args[0][0].lower()
|
|
|
|
# Vérifier que le fallback HTML a été utilisé
|
|
call_args = mock_st.markdown.call_args
|
|
html_output = call_args[0][0]
|
|
|
|
# Le contenu doit être échappé avec <br>
|
|
assert "Test<br>content" in html_output or "Test\ncontent" in html_output
|
|
|
|
@patch('utils.widgets.st')
|
|
@patch('utils.widgets.markdown')
|
|
@patch('utils.widgets.logger')
|
|
def test_expander_markdown_other_error(self, mock_logger, mock_markdown, mock_st):
|
|
"""Test la gestion d'autres erreurs lors de la conversion markdown."""
|
|
# Simuler une autre exception
|
|
mock_markdown.markdown.side_effect = ValueError("Invalid markdown")
|
|
|
|
html_expander("Title", "Content")
|
|
|
|
# Vérifier que l'erreur a été loggée
|
|
mock_logger.error.assert_called_once()
|
|
assert "erreur" in mock_logger.error.call_args[0][0].lower()
|
|
|
|
@patch('utils.widgets.st')
|
|
@patch('utils.widgets.markdown')
|
|
def test_expander_unique_ids(self, mock_markdown, mock_st):
|
|
"""Test que chaque expander a un ID unique."""
|
|
mock_markdown.markdown.return_value = "<p>Content</p>"
|
|
|
|
# Créer deux expanders
|
|
html_expander("Title 1", "Content 1")
|
|
call_1 = mock_st.markdown.call_args[0][0]
|
|
|
|
html_expander("Title 2", "Content 2")
|
|
call_2 = mock_st.markdown.call_args[0][0]
|
|
|
|
# Extraire les IDs
|
|
import re
|
|
id_pattern = r'id="(expander_[a-f0-9]+)"'
|
|
id_1 = re.search(id_pattern, call_1).group(1)
|
|
id_2 = re.search(id_pattern, call_2).group(1)
|
|
|
|
# Les IDs doivent être différents
|
|
assert id_1 != id_2
|
|
|
|
@patch('utils.widgets.st')
|
|
@patch('utils.widgets.markdown')
|
|
def test_expander_unsafe_html_enabled(self, mock_markdown, mock_st):
|
|
"""Test que unsafe_allow_html est activé."""
|
|
mock_markdown.markdown.return_value = "<p>Content</p>"
|
|
|
|
html_expander("Title", "Content")
|
|
|
|
# Vérifier que unsafe_allow_html=True
|
|
call_kwargs = mock_st.markdown.call_args[1]
|
|
assert call_kwargs.get("unsafe_allow_html") is True
|
|
|
|
@patch('utils.widgets.st')
|
|
@patch('utils.widgets.markdown')
|
|
def test_expander_with_special_characters(self, mock_markdown, mock_st):
|
|
"""Test avec des caractères spéciaux dans le titre et le contenu."""
|
|
mock_markdown.markdown.return_value = "<p>Content <></p>"
|
|
|
|
html_expander("Title <>&", "Content <>&")
|
|
|
|
call_args = mock_st.markdown.call_args
|
|
html_output = call_args[0][0]
|
|
|
|
# Le titre doit être présent
|
|
assert "Title <>&" in html_output
|
|
|
|
@patch('utils.widgets.st')
|
|
@patch('utils.widgets.markdown')
|
|
def test_expander_empty_content(self, mock_markdown, mock_st):
|
|
"""Test avec un contenu vide."""
|
|
mock_markdown.markdown.return_value = ""
|
|
|
|
html_expander("Title", "")
|
|
|
|
# Ne doit pas crasher
|
|
assert mock_st.markdown.called
|
|
|
|
@patch('utils.widgets.st')
|
|
@patch('utils.widgets.markdown')
|
|
def test_expander_multiline_content(self, mock_markdown, mock_st):
|
|
"""Test avec du contenu multiligne."""
|
|
content = """
|
|
# Titre
|
|
Paragraphe 1
|
|
|
|
Paragraphe 2
|
|
"""
|
|
mock_markdown.markdown.return_value = "<h1>Titre</h1><p>Paragraphe 1</p><p>Paragraphe 2</p>"
|
|
|
|
html_expander("Title", content)
|
|
|
|
call_args = mock_st.markdown.call_args
|
|
html_output = call_args[0][0]
|
|
|
|
assert "<h1>Titre</h1>" in html_output
|