✨ 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!
251 lines
8.2 KiB
Python
251 lines
8.2 KiB
Python
"""
|
|
Tests unitaires pour le GradingCalculator.
|
|
"""
|
|
import pytest
|
|
from domain.services.grading_calculator import (
|
|
GradingCalculator,
|
|
NotesStrategy,
|
|
ScoreStrategy,
|
|
GradingStrategyFactory,
|
|
)
|
|
|
|
|
|
class TestNotesStrategy:
|
|
"""Tests pour la stratégie de notation par points."""
|
|
|
|
def test_calculate_integer(self):
|
|
strategy = NotesStrategy()
|
|
assert strategy.calculate_score("15", 20) == 15.0
|
|
|
|
def test_calculate_decimal_point(self):
|
|
strategy = NotesStrategy()
|
|
assert strategy.calculate_score("15.5", 20) == 15.5
|
|
|
|
def test_calculate_decimal_comma(self):
|
|
strategy = NotesStrategy()
|
|
assert strategy.calculate_score("15,5", 20) == 15.5
|
|
|
|
def test_calculate_zero(self):
|
|
strategy = NotesStrategy()
|
|
assert strategy.calculate_score("0", 20) == 0.0
|
|
|
|
def test_calculate_invalid(self):
|
|
strategy = NotesStrategy()
|
|
assert strategy.calculate_score("invalid", 20) == 0.0
|
|
|
|
def test_grading_type(self):
|
|
strategy = NotesStrategy()
|
|
assert strategy.get_grading_type() == "notes"
|
|
|
|
|
|
class TestScoreStrategy:
|
|
"""Tests pour la stratégie de notation par compétences."""
|
|
|
|
def test_calculate_score_0(self):
|
|
strategy = ScoreStrategy()
|
|
# 0/3 * 6 = 0
|
|
assert strategy.calculate_score("0", 6) == 0.0
|
|
|
|
def test_calculate_score_1(self):
|
|
strategy = ScoreStrategy()
|
|
# 1/3 * 6 = 2
|
|
assert strategy.calculate_score("1", 6) == 2.0
|
|
|
|
def test_calculate_score_2(self):
|
|
strategy = ScoreStrategy()
|
|
# 2/3 * 6 = 4
|
|
assert strategy.calculate_score("2", 6) == 4.0
|
|
|
|
def test_calculate_score_3(self):
|
|
strategy = ScoreStrategy()
|
|
# 3/3 * 6 = 6
|
|
assert strategy.calculate_score("3", 6) == 6.0
|
|
|
|
def test_calculate_score_3_with_max_3(self):
|
|
strategy = ScoreStrategy()
|
|
# 3/3 * 3 = 3
|
|
assert strategy.calculate_score("3", 3) == 3.0
|
|
|
|
def test_calculate_score_2_with_max_3(self):
|
|
strategy = ScoreStrategy()
|
|
# 2/3 * 3 = 2
|
|
assert strategy.calculate_score("2", 3) == 2.0
|
|
|
|
def test_calculate_invalid_score(self):
|
|
strategy = ScoreStrategy()
|
|
assert strategy.calculate_score("4", 6) == 0.0
|
|
|
|
def test_calculate_negative_score(self):
|
|
strategy = ScoreStrategy()
|
|
assert strategy.calculate_score("-1", 6) == 0.0
|
|
|
|
def test_calculate_non_numeric(self):
|
|
strategy = ScoreStrategy()
|
|
assert strategy.calculate_score("abc", 6) == 0.0
|
|
|
|
def test_grading_type(self):
|
|
strategy = ScoreStrategy()
|
|
assert strategy.get_grading_type() == "score"
|
|
|
|
|
|
class TestGradingStrategyFactory:
|
|
"""Tests pour la factory de stratégies."""
|
|
|
|
def test_create_notes_strategy(self):
|
|
strategy = GradingStrategyFactory.create("notes")
|
|
assert isinstance(strategy, NotesStrategy)
|
|
|
|
def test_create_score_strategy(self):
|
|
strategy = GradingStrategyFactory.create("score")
|
|
assert isinstance(strategy, ScoreStrategy)
|
|
|
|
def test_create_unknown_raises(self):
|
|
with pytest.raises(ValueError, match="Type de notation non supporté"):
|
|
GradingStrategyFactory.create("unknown")
|
|
|
|
|
|
class TestGradingCalculator:
|
|
"""Tests pour le calculateur unifié."""
|
|
|
|
def test_calculate_notes_score(self):
|
|
calc = GradingCalculator()
|
|
assert calc.calculate_score("15.5", "notes", 20) == 15.5
|
|
|
|
def test_calculate_competence_score(self):
|
|
calc = GradingCalculator()
|
|
# 2/3 * 3 = 2
|
|
assert calc.calculate_score("2", "score", 3) == 2.0
|
|
|
|
def test_special_value_dot(self):
|
|
calc = GradingCalculator()
|
|
# "." = pas de réponse = 0
|
|
assert calc.calculate_score(".", "notes", 20) == 0.0
|
|
|
|
def test_special_value_d(self):
|
|
calc = GradingCalculator()
|
|
# "d" = dispensé = None
|
|
assert calc.calculate_score("d", "notes", 20) is None
|
|
|
|
def test_special_value_a(self):
|
|
calc = GradingCalculator()
|
|
# "a" = absent = 0
|
|
assert calc.calculate_score("a", "notes", 20) == 0.0
|
|
|
|
def test_empty_value(self):
|
|
calc = GradingCalculator()
|
|
assert calc.calculate_score("", "notes", 20) is None
|
|
|
|
def test_whitespace_value(self):
|
|
calc = GradingCalculator()
|
|
assert calc.calculate_score(" ", "notes", 20) is None
|
|
|
|
def test_is_special_value_dot(self):
|
|
calc = GradingCalculator()
|
|
assert calc.is_special_value(".") is True
|
|
|
|
def test_is_special_value_d(self):
|
|
calc = GradingCalculator()
|
|
assert calc.is_special_value("d") is True
|
|
|
|
def test_is_special_value_a(self):
|
|
calc = GradingCalculator()
|
|
assert calc.is_special_value("a") is True
|
|
|
|
def test_is_special_value_number(self):
|
|
calc = GradingCalculator()
|
|
assert calc.is_special_value("15") is False
|
|
|
|
def test_is_counted_in_total_dot(self):
|
|
calc = GradingCalculator()
|
|
# "." compte dans le total (comme 0)
|
|
assert calc.is_counted_in_total(".") is True
|
|
|
|
def test_is_counted_in_total_d(self):
|
|
calc = GradingCalculator()
|
|
# "d" ne compte pas (dispensé)
|
|
assert calc.is_counted_in_total("d") is False
|
|
|
|
def test_is_counted_in_total_a(self):
|
|
calc = GradingCalculator()
|
|
# "a" compte dans le total (comme 0)
|
|
assert calc.is_counted_in_total("a") is True
|
|
|
|
def test_is_counted_in_total_normal(self):
|
|
calc = GradingCalculator()
|
|
assert calc.is_counted_in_total("15") is True
|
|
|
|
def test_is_counted_in_total_empty(self):
|
|
calc = GradingCalculator()
|
|
assert calc.is_counted_in_total("") is False
|
|
|
|
|
|
class TestGradingCalculatorValidation:
|
|
"""Tests pour la validation des notes."""
|
|
|
|
def test_validate_notes_integer(self):
|
|
calc = GradingCalculator()
|
|
assert calc.validate_grade_value("15", "notes") is True
|
|
|
|
def test_validate_notes_decimal(self):
|
|
calc = GradingCalculator()
|
|
assert calc.validate_grade_value("15.5", "notes") is True
|
|
|
|
def test_validate_notes_comma(self):
|
|
calc = GradingCalculator()
|
|
assert calc.validate_grade_value("15,5", "notes") is True
|
|
|
|
def test_validate_notes_negative(self):
|
|
calc = GradingCalculator()
|
|
assert calc.validate_grade_value("-5", "notes") is False
|
|
|
|
def test_validate_notes_exceeds_max(self):
|
|
calc = GradingCalculator()
|
|
assert calc.validate_grade_value("25", "notes", max_points=20) is False
|
|
|
|
def test_validate_notes_within_max(self):
|
|
calc = GradingCalculator()
|
|
assert calc.validate_grade_value("18", "notes", max_points=20) is True
|
|
|
|
def test_validate_score_valid(self):
|
|
calc = GradingCalculator()
|
|
for score in ["0", "1", "2", "3"]:
|
|
assert calc.validate_grade_value(score, "score") is True
|
|
|
|
def test_validate_score_invalid(self):
|
|
calc = GradingCalculator()
|
|
assert calc.validate_grade_value("4", "score") is False
|
|
assert calc.validate_grade_value("-1", "score") is False
|
|
assert calc.validate_grade_value("abc", "score") is False
|
|
|
|
def test_validate_special_values(self):
|
|
calc = GradingCalculator()
|
|
assert calc.validate_grade_value(".", "notes") is True
|
|
assert calc.validate_grade_value("d", "score") is True
|
|
assert calc.validate_grade_value("a", "notes") is True
|
|
|
|
def test_validate_empty(self):
|
|
calc = GradingCalculator()
|
|
assert calc.validate_grade_value("", "notes") is True
|
|
assert calc.validate_grade_value(" ", "score") is True
|
|
|
|
|
|
class TestGradingCalculatorCustomConfig:
|
|
"""Tests avec configuration personnalisée."""
|
|
|
|
def test_custom_special_values(self):
|
|
custom_config = {
|
|
"x": {
|
|
"label": "Excusé",
|
|
"value": 0,
|
|
"counts": False
|
|
}
|
|
}
|
|
calc = GradingCalculator(special_values=custom_config)
|
|
|
|
assert calc.is_special_value("x") is True
|
|
assert calc.calculate_score("x", "notes", 20) == 0.0
|
|
assert calc.is_counted_in_total("x") is False
|
|
|
|
# Les valeurs par défaut ne sont plus disponibles
|
|
assert calc.is_special_value(".") is False
|