feat: add PipelineWarning for non-blocking pipeline errors (M10-U0)

Add PipelineWarning(PronoteSyncError) to the canonical error hierarchy.
This non-blocking warning type is used by the XMPP channel (and future
channels) to signal recoverable failures without breaking the pipeline.

- PipelineWarning inherits from PronoteSyncError, not Warning builtin
- Constructor: (message, step=None) with recoverable=True
- 8 unit tests covering inheritance, raising, catching, attributes

Co-authored-by: opencode/test-engineer <test-engineer@agents.invalid>
Co-authored-by: opencode/coder <coder@agents.invalid>
This commit is contained in:
2026-09-07 21:01:36 +02:00
parent 58c7fa147f
commit a5a8183663
2 changed files with 114 additions and 0 deletions

View File

@@ -31,3 +31,29 @@ class PipelineCriticalError(PronoteSyncError):
:param message: Message décrivant la cause de l'erreur critique. :param message: Message décrivant la cause de l'erreur critique.
""" """
super().__init__(message) 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

88
tests/unit/test_errors.py Normal file
View File

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