fix(M3): immuabilité et validation des modèles — correction des écarts FIXME_M3
Immuabilité : - Collections des modèles frozen : list → tuple (Lesson, TheoreticalLesson, SchoolEvent, AgendaDiff, Homework, XmppMessage, ExternalInfo) - Gel de HomeworkBlock et ExternalInfo (sous-modèles imbriqués dans contrats) - Valeurs par défaut immuables : Field(default=(), ...) Validation de domaine : - TheoreticalLesson.day_of_week : Field(ge=0, le=6) - Lesson/TheoreticalLesson : start < end (validateur de modèle) - SchoolEvent : from_date <= to_date - AgendaChange : cohérence type/payload (ADDED/MODIFIED → lesson, REMOVED → theoretical_lesson) - CalDAVSyncResult : compteurs Field(ge=0) sur added/updated/removed Tests : 66 tests (construction/sérialisation, immuabilité, invariants) couvrant le gel, la mutabilité légitime, et les cas invalides par invariant. Co-authored-by: opencode/coder <coder@agents.invalid> Co-authored-by: opencode/test-engineer <test-engineer@agents.invalid>
This commit is contained in:
@@ -5,7 +5,7 @@ from __future__ import annotations
|
||||
from datetime import date
|
||||
from enum import StrEnum
|
||||
|
||||
from pydantic import BaseModel, ConfigDict, Field
|
||||
from pydantic import BaseModel, ConfigDict, Field, model_validator
|
||||
|
||||
from pronote_sync.models.agenda import Lesson, TheoreticalLesson
|
||||
|
||||
@@ -30,6 +30,22 @@ class AgendaChange(BaseModel):
|
||||
)
|
||||
details: str = Field(default="", description="Détails du changement")
|
||||
|
||||
@model_validator(mode="after")
|
||||
def _validate_payload_consistency(self) -> AgendaChange:
|
||||
"""Valide la cohérence entre le type de changement et le payload.
|
||||
|
||||
:return: L'instance validée.
|
||||
:rtype: AgendaChange
|
||||
:raises ValueError: Si le payload ne correspond pas au type de changement.
|
||||
"""
|
||||
if self.type in (AgendaChangeType.ADDED, AgendaChangeType.MODIFIED):
|
||||
if self.lesson is None:
|
||||
raise ValueError(f"lesson est requis pour le type {self.type!r}")
|
||||
if self.type == AgendaChangeType.REMOVED:
|
||||
if self.theoretical_lesson is None:
|
||||
raise ValueError(f"theoretical_lesson est requis pour le type {self.type!r}")
|
||||
return self
|
||||
|
||||
|
||||
class AgendaDiff(BaseModel):
|
||||
"""Représente les différences entre l'agenda réel et l'agenda théorique."""
|
||||
@@ -37,4 +53,4 @@ class AgendaDiff(BaseModel):
|
||||
model_config = ConfigDict(frozen=True)
|
||||
|
||||
target_date: date = Field(..., description="Date cible de la comparaison")
|
||||
changes: list[AgendaChange] = Field(default_factory=list, description="Liste des changements")
|
||||
changes: tuple[AgendaChange, ...] = Field(default=(), description="Liste des changements")
|
||||
|
||||
Reference in New Issue
Block a user