""" Tests unitaires pour le service de génération de rapports. """ import pytest from domain.services import StudentReportService, generate_report_html class TestStudentReportService: """Tests pour le service de génération de rapports.""" @pytest.fixture def report_service(self): """Fixture du service de rapport.""" return StudentReportService() @pytest.fixture def sample_assessment_data(self): """Données d'évaluation de test.""" return { 'title': 'Contrôle de Mathématiques', 'description': 'Chapitre 3 - Fonctions', 'date': '2025-01-15', 'trimester': 2, 'class_name': '6ème A', 'coefficient': 1.0 } @pytest.fixture def sample_exercises_data(self): """Données d'exercices de test.""" return [ { 'id': 1, 'title': 'Exercice 1', 'description': 'Calculs', 'order': 1, 'elements': [ { 'id': 101, 'exercise_id': 1, 'label': 'Question 1a', 'description': 'Calcul simple', 'skill': 'Calculer', 'domain_name': 'Nombres', 'domain_color': '#3b82f6', 'grading_type': 'notes', 'max_points': 2.0 }, { 'id': 102, 'exercise_id': 1, 'label': 'Question 1b', 'description': 'Calcul complexe', 'skill': 'Calculer', 'domain_name': 'Nombres', 'domain_color': '#3b82f6', 'grading_type': 'notes', 'max_points': 3.0 } ] }, { 'id': 2, 'title': 'Exercice 2', 'description': 'Compétences', 'order': 2, 'elements': [ { 'id': 201, 'exercise_id': 2, 'label': 'Compétence graphique', 'description': 'Tracer un graphe', 'skill': 'Représenter', 'domain_name': 'Géométrie', 'domain_color': '#22c55e', 'grading_type': 'score', 'max_points': 3.0 } ] } ] @pytest.fixture def sample_student_data(self): """Données d'élève de test.""" return { 'id': 1, 'first_name': 'Marie', 'last_name': 'Dupont', 'email': 'marie.dupont@test.com' } @pytest.fixture def sample_grades(self): """Notes de test.""" return { 1: [ # Student 1 {'element_id': 101, 'value': '1.5', 'comment': ''}, {'element_id': 102, 'value': '2.5', 'comment': 'Bon travail'}, {'element_id': 201, 'value': '2', 'comment': ''} ], 2: [ # Student 2 {'element_id': 101, 'value': '2', 'comment': ''}, {'element_id': 102, 'value': '1', 'comment': ''}, {'element_id': 201, 'value': '3', 'comment': ''} ] } def test_generate_student_report( self, report_service, sample_assessment_data, sample_student_data, sample_grades, sample_exercises_data ): """Test de génération d'un rapport complet.""" report = report_service.generate_student_report( sample_assessment_data, sample_student_data, sample_grades, sample_exercises_data ) # Vérifier la structure du rapport assert 'assessment' in report assert 'student' in report assert 'results' in report assert 'exercises' in report assert 'competences' in report assert 'domains' in report assert 'class_statistics' in report # Vérifier les données de l'évaluation assert report['assessment']['title'] == 'Contrôle de Mathématiques' assert report['assessment']['trimester'] == 2 # Vérifier les données de l'élève assert report['student']['full_name'] == 'Marie Dupont' assert report['student']['email'] == 'marie.dupont@test.com' # Vérifier les résultats # Score exercice 1: 1.5 + 2.5 = 4.0 # Score exercice 2: 2 (score) -> 2/3 * 3 = 2.0 # Total: 6.0 # Student 2: 2 + 1 + (3/3)*3 = 6.0 (égalité) assert report['results']['total_score'] == 6.0 assert report['results']['total_max_points'] == 8.0 assert report['results']['percentage'] == 75.0 assert report['results']['position'] == 1 # 1er ex-aequo (même score) assert report['results']['total_students'] == 2 def test_generate_report_with_special_values( self, report_service, sample_assessment_data, sample_exercises_data ): """Test avec valeurs spéciales (., d).""" student_data = { 'id': 1, 'first_name': 'Jean', 'last_name': 'Martin', 'email': 'jean@test.com' } grades = { 1: [ {'element_id': 101, 'value': '.', 'comment': 'Pas de réponse'}, {'element_id': 102, 'value': 'd', 'comment': 'Dispensé'}, {'element_id': 201, 'value': '1', 'comment': ''} ] } report = report_service.generate_student_report( sample_assessment_data, student_data, grades, sample_exercises_data ) # . compte comme 0, d ne compte pas # Total: 0 + 1 (score 1/3 * 3) = 1.0 # Max: 2 + 3 = 5.0 (le dispensé ne compte pas) assert report['results']['total_score'] == 1.0 assert report['results']['total_max_points'] == 5.0 def test_competences_performance( self, report_service, sample_assessment_data, sample_student_data, sample_grades, sample_exercises_data ): """Test du calcul des performances par compétence.""" report = report_service.generate_student_report( sample_assessment_data, sample_student_data, sample_grades, sample_exercises_data ) competences = {c['name']: c for c in report['competences']} # Compétence "Calculer": 1.5 + 2.5 = 4.0 / 5.0 assert 'Calculer' in competences assert competences['Calculer']['score'] == 4.0 assert competences['Calculer']['max_points'] == 5.0 assert competences['Calculer']['percentage'] == 80.0 # Compétence "Représenter": 2.0 / 3.0 assert 'Représenter' in competences assert competences['Représenter']['score'] == 2.0 assert competences['Représenter']['max_points'] == 3.0 def test_domains_performance( self, report_service, sample_assessment_data, sample_student_data, sample_grades, sample_exercises_data ): """Test du calcul des performances par domaine.""" report = report_service.generate_student_report( sample_assessment_data, sample_student_data, sample_grades, sample_exercises_data ) domains = {d['name']: d for d in report['domains']} # Domaine "Nombres": 4.0 / 5.0 assert 'Nombres' in domains assert domains['Nombres']['score'] == 4.0 assert domains['Nombres']['color'] == '#3b82f6' # Domaine "Géométrie": 2.0 / 3.0 assert 'Géométrie' in domains assert domains['Géométrie']['score'] == 2.0 assert domains['Géométrie']['color'] == '#22c55e' def test_student_not_in_grades( self, report_service, sample_assessment_data, sample_exercises_data ): """Test avec un élève sans notes.""" student_data = { 'id': 999, 'first_name': 'Unknown', 'last_name': 'Student', 'email': 'unknown@test.com' } grades = { 1: [{'element_id': 101, 'value': '2', 'comment': ''}] } with pytest.raises(ValueError, match="n'a pas de notes"): report_service.generate_student_report( sample_assessment_data, student_data, grades, sample_exercises_data ) class TestGenerateReportHTML: """Tests pour la génération HTML des rapports.""" @pytest.fixture def sample_report_data(self): """Données de rapport pour le HTML.""" return { 'assessment': { 'title': 'Test Évaluation', 'description': 'Description test', 'date': '2025-01-15', 'trimester': 2, 'class_name': '6ème A', 'coefficient': 1.0 }, 'student': { 'full_name': 'Marie Dupont', 'first_name': 'Marie', 'last_name': 'Dupont', 'email': 'marie@test.com' }, 'results': { 'total_score': 15.0, 'total_max_points': 20.0, 'percentage': 75.0, 'position': 5, 'total_students': 25 }, 'exercises': [ { 'title': 'Exercice 1', 'description': 'Test', 'score': 8.0, 'max_points': 10.0, 'percentage': 80.0, 'elements': [ { 'label': 'Q1', 'description': '', 'skill': '', 'domain': '', 'raw_value': '4', 'calculated_score': 4.0, 'max_points': 5.0, 'grading_type': 'notes', 'score_label': '', 'comment': '' } ] } ], 'competences': [], 'domains': [], 'class_statistics': { 'count': 25, 'mean': 12.5, 'median': 13.0, 'min': 5.0, 'max': 19.0, 'std_dev': 3.2 } } def test_generate_html_contains_student_name(self, sample_report_data): """Le HTML contient le prénom de l'élève.""" html = generate_report_html(sample_report_data) assert 'Marie' in html def test_generate_html_contains_assessment_title(self, sample_report_data): """Le HTML contient le titre de l'évaluation.""" html = generate_report_html(sample_report_data) assert 'Test Évaluation' in html def test_generate_html_contains_score(self, sample_report_data): """Le HTML contient le score.""" html = generate_report_html(sample_report_data) assert '15.0' in html or '15' in html assert '20.0' in html or '20' in html assert '75' in html # percentage def test_generate_html_contains_statistics(self, sample_report_data): """Le HTML ne contient plus les statistiques de classe (supprimées).""" html = generate_report_html(sample_report_data) # Les statistiques de classe ont été supprimées du template assert 'Statistiques de la classe' not in html assert 'Position :' not in html def test_generate_html_with_message(self, sample_report_data): """Le HTML inclut le message personnalisé.""" message = "Excellent travail cette année !" html = generate_report_html(sample_report_data, message) assert message in html def test_generate_html_no_dynamic_color(self): """Le HTML n'a plus de couleur dynamique basée sur le score.""" report_data = { 'assessment': {'title': 'Test', 'date': '2025-01-01', 'trimester': 1, 'class_name': 'Test', 'coefficient': 1}, 'student': {'full_name': 'Test', 'first_name': 'Test', 'last_name': 'Test', 'email': 'test@test.com'}, 'results': {'total_score': 18, 'total_max_points': 20, 'percentage': 90.0, 'position': 1, 'total_students': 10}, 'exercises': [], 'competences': [], 'domains': [], 'class_statistics': {'count': 10, 'mean': 12, 'median': 12, 'min': 5, 'max': 18, 'std_dev': 2} } html = generate_report_html(report_data) # Les couleurs dynamiques (vert/jaune/rouge) ont été supprimées assert '#22c55e' not in html # pas de vert assert '#f6d32d' not in html # pas de jaune assert '#ef4444' not in html # pas de rouge # Le score utilise maintenant une couleur neutre assert 'background-color: #f3f4f6' in html # gris neutre