diff --git a/pronote_sync/errors.py b/pronote_sync/errors.py index e1f17f0..58d0369 100644 --- a/pronote_sync/errors.py +++ b/pronote_sync/errors.py @@ -31,3 +31,29 @@ class PipelineCriticalError(PronoteSyncError): :param message: Message décrivant la cause de l'erreur critique. """ super().__init__(message) + + +class PipelineWarning(PronoteSyncError): + """Avertissement non bloquant pour une erreur récupérable du pipeline. + + Contrairement à :class:`PipelineCriticalError`, cet avertissement signale + un problème récupérable : le pipeline peut poursuivre son exécution en + mode dégradé. + + Il hérite volontairement de :class:`PronoteSyncError` (et non de la classe + native :class:`Warning`) afin de rester dans la hiérarchie canonique des + erreurs du projet. + + :ivar recoverable: Indique que l'erreur est récupérable (toujours ``True``). + :ivar step: Étape du pipeline ayant produit l'avertissement. + """ + + def __init__(self, message: str, step: str | None = None) -> None: + """Initialise l'avertissement avec un message descriptif. + + :param message: Message décrivant la cause de l'avertissement. + :param step: Étape du pipeline ayant produit l'avertissement. + """ + super().__init__(message) + self.recoverable = True + self.step = step diff --git a/tests/unit/test_errors.py b/tests/unit/test_errors.py new file mode 100644 index 0000000..8056384 --- /dev/null +++ b/tests/unit/test_errors.py @@ -0,0 +1,88 @@ +"""Tests unitaires pour la hiérarchie des erreurs du pipeline. + +Ce module valide les classes d'erreur définies dans pronote_sync.errors. +""" + +import pytest + +from pronote_sync.errors import PipelineWarning, PronoteSyncError + + +def test_pipeline_warning_inherits_pronote_sync_error() -> None: + """Vérifie que PipelineWarning hérite de PronoteSyncError. + + :return: None + :rtype: None + """ + assert isinstance(PipelineWarning("msg"), PronoteSyncError) + + +def test_pipeline_warning_not_warning_builtin() -> None: + """Vérifie que PipelineWarning n'hérite pas de la classe Warning intégrée. + + :return: None + :rtype: None + """ + assert not isinstance(PipelineWarning("msg"), Warning) + + +def test_pipeline_warning_message_stored() -> None: + """Vérifie que le message est stocké et accessible via str(exc). + + :return: None + :rtype: None + """ + exc = PipelineWarning("msg") + assert str(exc) == "msg" + assert exc.args[0] == "msg" + + +def test_pipeline_warning_step_default_none() -> None: + """Vérifie que step est None par défaut. + + :return: None + :rtype: None + """ + assert PipelineWarning("msg").step is None + + +def test_pipeline_warning_step_set() -> None: + """Vérifie que step peut être défini via le constructeur. + + :return: None + :rtype: None + """ + assert PipelineWarning("msg", step="xmpp").step == "xmpp" + + +def test_pipeline_warning_recoverable_true() -> None: + """Vérifie que recoverable est toujours True pour PipelineWarning. + + :return: None + :rtype: None + """ + assert PipelineWarning("msg").recoverable is True + + +def test_pipeline_warning_is_raisable() -> None: + """Vérifie que PipelineWarning peut être levée. + + :return: None + :rtype: None + """ + with pytest.raises(PipelineWarning, match="msg"): + raise PipelineWarning("msg") + + +def test_pipeline_warning_caught_by_pronote_sync_error() -> None: + """Vérifie qu'une PipelineWarning est attrapée par un except PronoteSyncError. + + :return: None + :rtype: None + """ + try: + raise PipelineWarning("msg") + except PronoteSyncError: + assert True + else: + raise AssertionError("PipelineWarning should have been caught by PronoteSyncError")