Files
college-infos/tests/unit/test_fetch_target_date.py
Codex 573705a3d0 chore(integration): intégrer les PR Pronote critiques dans main
Revue Codex validée. Intégration dans main de la chaîne des PR #31, #32 et #33.

Co-authored-by: Codex <codex@antoineve.me>
Co-committed-by: Codex <codex@antoineve.me>
2026-09-12 15:00:32 +02:00

87 lines
2.5 KiB
Python

"""Tests de sélection de la date cible du digest Pronote."""
from __future__ import annotations
from datetime import date, datetime
from pronote_sync.models.agenda import Lesson, LessonStatus, SchoolEvent, SchoolEventKind
from pronote_sync.pipeline.steps.fetch import resolve_target_date
def _lesson(day: date, status: LessonStatus = LessonStatus.NORMAL) -> Lesson:
"""Construit un cours minimal à une date donnée.
:param day: Date du cours.
:param status: Statut du cours.
:return: Cours de test.
:rtype: Lesson
"""
return Lesson(
id=day.isoformat(),
start=datetime.combine(day, datetime.min.time()),
end=datetime.combine(day, datetime.min.time().replace(hour=1)),
subject="Mathématiques",
group=None,
status=status,
content=None,
)
def _event(from_date: date, to_date: date) -> SchoolEvent:
"""Construit un événement scolaire à borne de fin exclusive.
:param from_date: Début inclusif.
:param to_date: Fin exclusive.
:return: Événement de vacances de test.
:rtype: SchoolEvent
"""
return SchoolEvent(
kind=SchoolEventKind.HOLIDAY,
label="Vacances anonymisées",
from_date=from_date,
to_date=to_date,
)
def test_target_date_prefers_tomorrow_effective_lesson() -> None:
"""Un cours effectif demain est prioritaire.
:return: None
"""
today = date(2026, 9, 11)
assert resolve_target_date(today, [_lesson(date(2026, 9, 12))], []) == date(2026, 9, 12)
def test_target_date_skips_empty_days_and_cancelled_lessons() -> None:
"""Le prochain cours effectif remplace un lendemain vide ou annulé.
:return: None
"""
today = date(2026, 9, 11)
lessons = [
_lesson(date(2026, 9, 12), LessonStatus.CANCELLED),
_lesson(date(2026, 9, 14)),
]
assert resolve_target_date(today, lessons, []) == date(2026, 9, 14)
def test_target_date_uses_exclusive_holiday_end_when_no_reprise_is_visible() -> None:
"""La fin exclusive d'une période couvrant demain devient la reprise connue.
:return: None
"""
today = date(2026, 10, 19)
holiday = _event(date(2026, 10, 20), date(2026, 11, 2))
assert resolve_target_date(today, [], [holiday]) == date(2026, 11, 2)
def test_target_date_falls_back_to_tomorrow_without_data() -> None:
"""Une liste vide valide sans événement conserve J+1 explicitement.
:return: None
"""
assert resolve_target_date(date(2026, 9, 11), [], []) == date(2026, 9, 12)