feat(M6): agenda théorique JSON avec parité des semaines et vacances scolaires
Implémentation complète de la source d'agenda théorique : - model.py : modèles Pydantic de parsing JSON (TheoreticalLessonEntry, TheoreticalAgendaFile) avec validation des formats d'heure et de l'ordre début/fin. - parity.py : WeekParityService déterministe calculant la parité d'une semaine (paire/impaire) à partir d'une date de référence. - holidays.py : SchoolHolidayCalendar lisant un fichier JSON de vacances scolaires (zone A) avec bornes inclusives. - provider.py : protocole TheoreticalAgendaProvider (get_lessons, get_lessons_for_range). - file.py : JsonTheoreticalAgendaProvider implémentant le protocole : filtrage par parité et vacances, génération d'IDs déterministes incluant le type de semaine, validation de l'unicité des IDs, tri stable par identifiant. - __init__.py : factory get_theoretical_provider câblant la configuration (None si désactivé, erreur si config de parité partielle). - Fixtures : theoretical.json (9 leçons all/even/odd) et school_holidays.json (zone A, 4 périodes). - 57 tests unitaires couvrant parsing, parité, vacances, provider, factory, déduplication de range, collisions d'IDs. - Guide : §8 et §12 alignés avec le format JSON. Co-authored-by: opencode/coder <coder@agents.invalid> Co-authored-by: opencode/test-engineer <test-engineer@agents.invalid>
This commit is contained in:
116
tests/unit/test_theoretical_parity.py
Normal file
116
tests/unit/test_theoretical_parity.py
Normal file
@@ -0,0 +1,116 @@
|
||||
"""Tests unitaires pour le service de parité des semaines.
|
||||
|
||||
Ce module contient les tests pour la classe :class:`WeekParityService`
|
||||
du module :mod:`pronote_sync.sources.theoretical.parity`.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import date
|
||||
|
||||
from pronote_sync.sources.theoretical.parity import WeekParityService
|
||||
|
||||
|
||||
class TestWeekParityService:
|
||||
"""Tests pour la classe WeekParityService."""
|
||||
|
||||
def test_same_week_as_anchor_even(self) -> None:
|
||||
"""Teste qu'une date dans la même semaine que l'ancrage pair retourne "even".
|
||||
|
||||
:assert: parity_for retourne "even" pour une date dans la même semaine.
|
||||
"""
|
||||
anchor_date = date(2026, 9, 1) # Mardi
|
||||
service = WeekParityService(anchor_date, "even")
|
||||
# Même semaine (lundi 31 août 2026)
|
||||
target_date = date(2026, 9, 1)
|
||||
assert service.parity_for(target_date) == "even"
|
||||
|
||||
def test_one_week_after_anchor_even(self) -> None:
|
||||
"""Teste qu'une date une semaine après l'ancrage pair retourne "odd".
|
||||
|
||||
:assert: parity_for retourne "odd" pour une date une semaine après.
|
||||
"""
|
||||
anchor_date = date(2026, 9, 1) # Mardi
|
||||
service = WeekParityService(anchor_date, "even")
|
||||
# Une semaine après (mardi 8 septembre 2026)
|
||||
target_date = date(2026, 9, 8)
|
||||
assert service.parity_for(target_date) == "odd"
|
||||
|
||||
def test_two_weeks_after_anchor_even(self) -> None:
|
||||
"""Teste qu'une date deux semaines après l'ancrage pair retourne "even".
|
||||
|
||||
:assert: parity_for retourne "even" pour une date deux semaines après.
|
||||
"""
|
||||
anchor_date = date(2026, 9, 1) # Mardi
|
||||
service = WeekParityService(anchor_date, "even")
|
||||
# Deux semaines après (mardi 15 septembre 2026)
|
||||
target_date = date(2026, 9, 15)
|
||||
assert service.parity_for(target_date) == "even"
|
||||
|
||||
def test_same_week_as_anchor_odd(self) -> None:
|
||||
"""Teste qu'une date dans la même semaine que l'ancrage impair retourne "odd".
|
||||
|
||||
:assert: parity_for retourne "odd" pour une date dans la même semaine.
|
||||
"""
|
||||
anchor_date = date(2026, 9, 1) # Mardi
|
||||
service = WeekParityService(anchor_date, "odd")
|
||||
target_date = date(2026, 9, 1)
|
||||
assert service.parity_for(target_date) == "odd"
|
||||
|
||||
def test_one_week_before_anchor(self) -> None:
|
||||
"""Teste qu'une date une semaine avant l'ancrage retourne la parité opposée.
|
||||
|
||||
:assert: parity_for retourne la parité opposée pour une date une semaine avant.
|
||||
"""
|
||||
anchor_date = date(2026, 9, 8) # Mardi
|
||||
service = WeekParityService(anchor_date, "even")
|
||||
# Une semaine avant (mardi 1er septembre 2026)
|
||||
target_date = date(2026, 9, 1)
|
||||
assert service.parity_for(target_date) == "odd"
|
||||
|
||||
def test_many_weeks_later(self) -> None:
|
||||
"""Teste qu'une date 10 semaines après l'ancrage pair retourne "even".
|
||||
|
||||
:assert: parity_for retourne "even" pour une date 10 semaines après.
|
||||
"""
|
||||
anchor_date = date(2026, 9, 1) # Mardi
|
||||
service = WeekParityService(anchor_date, "even")
|
||||
# 10 semaines après (70 jours)
|
||||
target_date = date(2026, 11, 10)
|
||||
assert service.parity_for(target_date) == "even"
|
||||
|
||||
def test_negative_offset(self) -> None:
|
||||
"""Teste qu'une date loin avant l'ancrage calcule correctement la parité.
|
||||
|
||||
:assert: parity_for retourne la parité correcte pour une date loin dans le passé.
|
||||
"""
|
||||
anchor_date = date(2026, 9, 1) # Mardi
|
||||
service = WeekParityService(anchor_date, "even")
|
||||
# 10 semaines avant (70 jours)
|
||||
target_date = date(2026, 6, 23)
|
||||
assert service.parity_for(target_date) == "even"
|
||||
|
||||
def test_different_day_in_same_week(self) -> None:
|
||||
"""Teste que lundi et vendredi de la même semaine ont la même parité.
|
||||
|
||||
:assert: parity_for retourne la même parité pour lundi et vendredi de la même semaine.
|
||||
"""
|
||||
anchor_date = date(2026, 9, 1) # Mardi
|
||||
service = WeekParityService(anchor_date, "even")
|
||||
# Lundi de la même semaine
|
||||
monday = date(2026, 8, 31)
|
||||
# Vendredi de la même semaine
|
||||
friday = date(2026, 9, 4)
|
||||
assert service.parity_for(monday) == "even"
|
||||
assert service.parity_for(friday) == "even"
|
||||
|
||||
def test_crosses_year_boundary(self) -> None:
|
||||
"""Teste que le calcul de parité fonctionne à cheval sur une année.
|
||||
|
||||
:assert: parity_for retourne la parité correcte à cheval sur une année.
|
||||
"""
|
||||
anchor_date = date(2026, 12, 29) # Mardi (semaine 53)
|
||||
service = WeekParityService(anchor_date, "even")
|
||||
# Date dans la semaine suivante (année 2027)
|
||||
target_date = date(2027, 1, 5) # Mardi de la semaine suivante
|
||||
assert service.parity_for(target_date) == "odd"
|
||||
Reference in New Issue
Block a user