"""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)