✨ Changements majeurs: - Suppression complète du code Flask legacy - Migration backend FastAPI vers racine /backend - Migration frontend Vue.js vers racine /frontend - Suppression de notytex-v2/ (code monté à la racine) ✅ Validations: - Backend démarre correctement (port 8000) - API /api/v2/health répond healthy - 99/99 tests unitaires passent - Frontend configuré avec proxy Vite 📝 Documentation: - README.md réécrit pour v2 - Instructions de démarrage mises à jour - .gitignore adapté pour backend/frontend/ 🎯 Architecture finale: notytex/ ├── backend/ # FastAPI + SQLAlchemy + Pydantic ├── frontend/ # Vue 3 + Vite + TailwindCSS ├── docs/ # Documentation └── school_management.db # Base de données (inchangée) Jalon 6 complété: Application v2 prête pour utilisation!
228 lines
7.3 KiB
Python
228 lines
7.3 KiB
Python
"""
|
|
Tests unitaires pour le StatisticsService.
|
|
"""
|
|
import pytest
|
|
from domain.services.statistics_service import StatisticsService
|
|
|
|
|
|
class TestStatisticsServiceBasic:
|
|
"""Tests basiques pour le service de statistiques."""
|
|
|
|
def test_calculate_statistics_empty(self):
|
|
service = StatisticsService()
|
|
result = service.calculate_statistics([])
|
|
|
|
assert result.count == 0
|
|
assert result.mean == 0.0
|
|
assert result.median == 0.0
|
|
assert result.min == 0.0
|
|
assert result.max == 0.0
|
|
assert result.std_dev == 0.0
|
|
|
|
def test_calculate_statistics_single_value(self):
|
|
service = StatisticsService()
|
|
result = service.calculate_statistics([15.0])
|
|
|
|
assert result.count == 1
|
|
assert result.mean == 15.0
|
|
assert result.median == 15.0
|
|
assert result.min == 15.0
|
|
assert result.max == 15.0
|
|
assert result.std_dev == 0.0
|
|
|
|
def test_calculate_statistics_multiple_values(self):
|
|
service = StatisticsService()
|
|
scores = [10.0, 12.0, 14.0, 16.0, 18.0]
|
|
result = service.calculate_statistics(scores)
|
|
|
|
assert result.count == 5
|
|
assert result.mean == 14.0
|
|
assert result.median == 14.0
|
|
assert result.min == 10.0
|
|
assert result.max == 18.0
|
|
# std_dev ≈ 3.16
|
|
assert 3.1 < result.std_dev < 3.2
|
|
|
|
def test_calculate_statistics_even_count(self):
|
|
service = StatisticsService()
|
|
scores = [10.0, 12.0, 14.0, 16.0]
|
|
result = service.calculate_statistics(scores)
|
|
|
|
assert result.count == 4
|
|
assert result.median == 13.0 # (12 + 14) / 2
|
|
|
|
def test_calculate_statistics_rounding(self):
|
|
service = StatisticsService()
|
|
scores = [10.333, 12.666, 14.999]
|
|
result = service.calculate_statistics(scores)
|
|
|
|
# Vérifier que les valeurs sont arrondies à 2 décimales
|
|
assert result.mean == 12.67
|
|
assert result.min == 10.33
|
|
assert result.max == 15.0
|
|
|
|
|
|
class TestHistogramCreation:
|
|
"""Tests pour la création d'histogrammes."""
|
|
|
|
def test_create_histogram_empty(self):
|
|
service = StatisticsService()
|
|
result = service.create_histogram([], 20)
|
|
|
|
assert len(result.bins) == 0
|
|
|
|
def test_create_histogram_zero_max(self):
|
|
service = StatisticsService()
|
|
result = service.create_histogram([5, 10], 0)
|
|
|
|
assert len(result.bins) == 0
|
|
|
|
def test_create_histogram_single_value(self):
|
|
service = StatisticsService()
|
|
result = service.create_histogram([10.0], 20)
|
|
|
|
assert len(result.bins) == 21
|
|
assert result.bins[10].count == 1
|
|
assert result.total_count == 1
|
|
|
|
def test_create_histogram_distribution(self):
|
|
service = StatisticsService()
|
|
# 5 valeurs: 0-1, 5-6, 10-11, 15-16, 20
|
|
scores = [0.5, 5.5, 10.5, 15.5, 20.0]
|
|
result = service.create_histogram(scores, 20)
|
|
|
|
assert result.bins[0].count == 1 # 0-1
|
|
assert result.bins[5].count == 1 # 5-6
|
|
assert result.bins[10].count == 1 # 10-11
|
|
assert result.bins[15].count == 1 # 15-16
|
|
assert result.bins[20].count == 1 # 20+
|
|
assert result.total_count == 5
|
|
|
|
def test_create_histogram_max_count(self):
|
|
service = StatisticsService()
|
|
scores = [10.0, 10.5, 10.8, 5.0] # 3 dans bin 10, 1 dans bin 5
|
|
result = service.create_histogram(scores, 20)
|
|
|
|
assert result.max_count == 3
|
|
|
|
def test_create_simple_histogram(self):
|
|
service = StatisticsService()
|
|
scores = [0.5, 5.5, 10.5]
|
|
result = service.create_simple_histogram(scores, 20)
|
|
|
|
assert len(result) == 21
|
|
assert result[0] == 1
|
|
assert result[5] == 1
|
|
assert result[10] == 1
|
|
assert sum(result) == 3
|
|
|
|
|
|
class TestPercentileCalculations:
|
|
"""Tests pour les calculs de percentiles."""
|
|
|
|
def test_calculate_percentile_empty(self):
|
|
service = StatisticsService()
|
|
assert service.calculate_percentile([], 50) == 0.0
|
|
|
|
def test_calculate_percentile_median(self):
|
|
service = StatisticsService()
|
|
scores = [1, 2, 3, 4, 5]
|
|
assert service.calculate_percentile(scores, 50) == 3.0
|
|
|
|
def test_calculate_percentile_25(self):
|
|
service = StatisticsService()
|
|
scores = [1, 2, 3, 4, 5]
|
|
# Q1 = 2
|
|
assert service.calculate_percentile(scores, 25) == 2.0
|
|
|
|
def test_calculate_percentile_75(self):
|
|
service = StatisticsService()
|
|
scores = [1, 2, 3, 4, 5]
|
|
# Q3 = 4
|
|
assert service.calculate_percentile(scores, 75) == 4.0
|
|
|
|
def test_calculate_quartiles(self):
|
|
service = StatisticsService()
|
|
scores = [1, 2, 3, 4, 5, 6, 7, 8, 9]
|
|
quartiles = service.calculate_quartiles(scores)
|
|
|
|
assert quartiles["q1"] == 3.0
|
|
assert quartiles["q2"] == 5.0 # Médiane
|
|
assert quartiles["q3"] == 7.0
|
|
|
|
|
|
class TestScoreNormalization:
|
|
"""Tests pour la normalisation des scores."""
|
|
|
|
def test_normalize_scores_to_20(self):
|
|
service = StatisticsService()
|
|
scores = [5, 10, 15] # sur 30
|
|
normalized = service.normalize_scores(scores, 30, 20)
|
|
|
|
assert normalized == [3.33, 6.67, 10.0]
|
|
|
|
def test_normalize_scores_zero_max(self):
|
|
service = StatisticsService()
|
|
scores = [5, 10]
|
|
normalized = service.normalize_scores(scores, 0, 20)
|
|
|
|
assert normalized == [0.0, 0.0]
|
|
|
|
def test_normalize_scores_same_scale(self):
|
|
service = StatisticsService()
|
|
scores = [10, 15, 20]
|
|
normalized = service.normalize_scores(scores, 20, 20)
|
|
|
|
assert normalized == [10.0, 15.0, 20.0]
|
|
|
|
def test_normalize_scores_custom_target(self):
|
|
service = StatisticsService()
|
|
scores = [10, 20] # sur 20
|
|
normalized = service.normalize_scores(scores, 20, 100) # vers 100
|
|
|
|
assert normalized == [50.0, 100.0]
|
|
|
|
|
|
class TestStatisticsResultObject:
|
|
"""Tests pour l'objet StatisticsResult."""
|
|
|
|
def test_to_dict(self):
|
|
service = StatisticsService()
|
|
result = service.calculate_statistics([10, 15, 20])
|
|
|
|
result_dict = result.to_dict()
|
|
|
|
assert "count" in result_dict
|
|
assert "mean" in result_dict
|
|
assert "median" in result_dict
|
|
assert "min" in result_dict
|
|
assert "max" in result_dict
|
|
assert "std_dev" in result_dict
|
|
|
|
assert result_dict["count"] == 3
|
|
assert result_dict["mean"] == 15.0
|
|
assert result_dict["median"] == 15.0
|
|
|
|
|
|
class TestHistogramDataObject:
|
|
"""Tests pour l'objet HistogramData."""
|
|
|
|
def test_to_list(self):
|
|
service = StatisticsService()
|
|
scores = [5.5, 10.5]
|
|
result = service.create_histogram(scores, 20)
|
|
|
|
counts = result.to_list()
|
|
assert isinstance(counts, list)
|
|
assert len(counts) == 21
|
|
assert sum(counts) == 2
|
|
|
|
def test_to_dict_list(self):
|
|
service = StatisticsService()
|
|
scores = [5.5]
|
|
result = service.create_histogram(scores, 10)
|
|
|
|
dict_list = result.to_dict_list()
|
|
assert isinstance(dict_list, list)
|
|
assert all("range" in d and "count" in d for d in dict_list)
|