"""Tests unitaires pour le Protocol Channel. Ce module valide la spécification du Protocol ``Channel`` qui sera ajouté à ``pronote_sync.channels.protocol``. Ces tests doivent être ROUGES tant que le Protocol n'est pas implémenté. """ from __future__ import annotations from datetime import date import pytest from pronote_sync.models.xmpp import XmppMessage # Import du Protocol à valider (doit échouer tant qu'il n'existe pas) try: from pronote_sync.channels.protocol import Channel CHANNEL_MODULE_EXISTS = True except ImportError: CHANNEL_MODULE_EXISTS = False class TestChannelProtocol: """Tests pour le Protocol Channel.""" def test_channel_is_protocol(self) -> None: """Vérifie que Channel est un Protocol. :return: None :raises AssertionError: Si Channel n'est pas un Protocol. """ if not CHANNEL_MODULE_EXISTS: pytest.fail( "Le module pronote_sync.channels.protocol n'existe pas encore. " "Ceci est attendu pour l'instant." ) assert hasattr(Channel, "_is_protocol"), "Channel doit être un sous-type de typing.Protocol" def test_channel_has_send_method(self) -> None: """Vérifie que le Protocol Channel définit une méthode send. :return: None :raises AssertionError: Si la méthode send n'est pas dans l'interface. """ if not CHANNEL_MODULE_EXISTS: pytest.fail( "Le module pronote_sync.channels.protocol n'existe pas encore. " "Ceci est attendu pour l'instant." ) assert hasattr(Channel, "send"), "Channel doit définir une méthode 'send'" send_method = Channel.send assert callable(send_method), "La méthode 'send' doit être callable" def test_conforming_class_satisfies_protocol(self) -> None: """Vérifie qu'une classe conforme satisfait le Protocol Channel. :return: None :raises AssertionError: Si la classe conforme n'est pas acceptée. """ if not CHANNEL_MODULE_EXISTS: pytest.fail( "Le module pronote_sync.channels.protocol n'existe pas encore. " "Ceci est attendu pour l'instant." ) # Classe minimale conforme au Protocol class DummyChannel: """Implémentation minimale conforme au Protocol Channel.""" def send(self, message: XmppMessage) -> bool: """Envoie un message XMPP. :param message: Message à envoyer. :return: True si l'envoi a réussi. :rtype: bool """ return True # Création d'une instance de message pour le test test_message = XmppMessage(target_date=date(2025, 9, 7), synthesis=None, external_info=None) # Instanciation et vérification dummy_instance = DummyChannel() assert dummy_instance.send(test_message) is True, "La méthode send doit retourner True" # Vérification que l'instance satisfait le Protocol if hasattr(Channel, "__protocol_attrs__"): # Vérification runtime avec @runtime_checkable assert isinstance(dummy_instance, Channel), ( "Une classe conforme doit satisfaire le Protocol Channel" ) def test_non_conforming_class_does_not_satisfy_protocol(self) -> None: """Vérifie qu'une classe non conforme ne satisfait pas le Protocol Channel. :return: None :raises AssertionError: Si la classe non conforme est acceptée. """ if not CHANNEL_MODULE_EXISTS: pytest.fail( "Le module pronote_sync.channels.protocol n'existe pas encore. " "Ceci est attendu pour l'instant." ) # Classe minimale non conforme (sans méthode send) class NonConformingChannel: """Implémentation minimale non conforme au Protocol Channel.""" pass # Vérification que la classe ne satisfait pas le Protocol non_conforming_instance = NonConformingChannel() if hasattr(Channel, "__protocol_attrs__"): # Vérification runtime avec @runtime_checkable assert not isinstance(non_conforming_instance, Channel), ( "Une classe non conforme ne doit pas satisfaire le Protocol Channel" ) def test_channel_send_returns_bool(self) -> None: """Vérifie que la méthode send retourne un booléen. :return: None :raises AssertionError: Si le retour n'est pas de type bool. """ if not CHANNEL_MODULE_EXISTS: pytest.fail( "Le module pronote_sync.channels.protocol n'existe pas encore. " "Ceci est attendu pour l'instant." ) # Implémentation minimale retournant True class BoolReturningChannel: """Implémentation minimale retournant un booléen.""" def send(self, message: XmppMessage) -> bool: """Envoie un message XMPP. :param message: Message à envoyer. :return: True :rtype: bool """ return True # Création d'une instance de message pour le test test_message = XmppMessage(target_date=date(2025, 9, 7), synthesis=None, external_info=None) # Test du retour channel = BoolReturningChannel() result = channel.send(test_message) assert isinstance(result, bool), "La méthode send doit retourner un booléen" assert result is True, "La méthode send doit retourner True dans cette implémentation" def test_channel_send_signature(self) -> None: """Vérifie la signature déclarée de la méthode Channel.send. :return: None :raises AssertionError: Si la signature ne correspond pas aux attentes. """ if not CHANNEL_MODULE_EXISTS: pytest.fail( "Le module pronote_sync.channels.protocol n'existe pas encore. " "Ceci est attendu pour l'instant." ) import inspect # Vérification de l'existence et de la nature callable de la méthode assert hasattr(Channel, "send"), "Channel doit définir une méthode 'send'" send_method = Channel.send assert callable(send_method), "La méthode 'send' doit être callable" # Introspection de la signature sig = inspect.signature(send_method) params = list(sig.parameters.values()) # Vérification du nombre de paramètres (1 paramètre + self) # On exclut 'self' pour vérifier le paramètre 'message' param_count = len(params) assert param_count == 2, ( f"La méthode send doit avoir exactement 2 paramètres (self + message), " f"trouvé {param_count}" ) # Vérification du nom du paramètre (on ignore 'self') param_names = [p.name for p in params if p.name != "self"] assert len(param_names) == 1, "Doit avoir exactement un paramètre autre que self" param_name = param_names[0] assert param_name == "message", ( f"Le paramètre doit s'appeler 'message', trouvé '{param_name}'" ) # Vérification du type de retour return_annotation = sig.return_annotation # Le type de retour peut être soit la chaîne 'bool' soit le type bool (forward reference) assert return_annotation in (bool, "bool"), ( f"Le type de retour doit être 'bool' ou bool, trouvé {return_annotation}" )