From e7882a3755cf086139619b6170aa4445b8bda723 Mon Sep 17 00:00:00 2001 From: Andrew Mirsky Date: Thu, 12 Jun 2025 10:52:44 -0400 Subject: [PATCH 1/2] fixes amqtt/Yakifo#210 : when reconnect is false, authentication failure causes NoDataError instead of ConnectError --- amqtt/mqtt/protocol/client_handler.py | 8 +++-- tests/plugins/mocks.py | 18 ++++++++++++ tests/test_client.py | 42 +++++++++++++++++++++++++++ 3 files changed, 65 insertions(+), 3 deletions(-) create mode 100644 tests/plugins/mocks.py diff --git a/amqtt/mqtt/protocol/client_handler.py b/amqtt/mqtt/protocol/client_handler.py index 32755f8..9b7d9d8 100644 --- a/amqtt/mqtt/protocol/client_handler.py +++ b/amqtt/mqtt/protocol/client_handler.py @@ -1,7 +1,7 @@ import asyncio from typing import Any -from amqtt.errors import AMQTTError +from amqtt.errors import AMQTTError, NoDataError from amqtt.mqtt.connack import ConnackPacket from amqtt.mqtt.connect import ConnectPacket, ConnectPayload, ConnectVariableHeader from amqtt.mqtt.disconnect import DisconnectPacket @@ -87,8 +87,10 @@ class ClientProtocolHandler(ProtocolHandler): if self.reader is None: msg = "Reader is not initialized." raise AMQTTError(msg) - - connack = await ConnackPacket.from_stream(self.reader) + try: + connack = await ConnackPacket.from_stream(self.reader) + except NoDataError as e: + raise ConnectionError from e await self.plugins_manager.fire_event(EVENT_MQTT_PACKET_RECEIVED, packet=connack, session=self.session) return connack.return_code diff --git a/tests/plugins/mocks.py b/tests/plugins/mocks.py new file mode 100644 index 0000000..3d80ca2 --- /dev/null +++ b/tests/plugins/mocks.py @@ -0,0 +1,18 @@ +import logging + +from amqtt.plugins.authentication import BaseAuthPlugin +from amqtt.session import Session + +logger = logging.getLogger(__name__) + + +class NoAuthPlugin(BaseAuthPlugin): + + async def authenticate(self, *, session: Session) -> bool | None: + return False + +class AuthPlugin(BaseAuthPlugin): + + async def authenticate(self, *, session: Session) -> bool | None: + return True + diff --git a/tests/test_client.py b/tests/test_client.py index 59547ce..ad0a5cc 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -1,8 +1,11 @@ import asyncio import logging +from importlib.metadata import EntryPoint +from unittest.mock import patch import pytest +from amqtt.broker import Broker from amqtt.client import MQTTClient from amqtt.errors import ConnectError from amqtt.mqtt.constants import QOS_0, QOS_1, QOS_2 @@ -295,3 +298,42 @@ async def test_client_publish_will_with_retain(broker_fixture, client_config): assert message3.topic == 'test/will/topic' assert message3.data == b'client ABC has disconnected' await client3.disconnect() + + +@pytest.mark.asyncio +async def test_client_no_auth(): + + + class MockEntryPoints: + + def select(self, group) -> list[EntryPoint]: + match group: + case 'tests.mock_plugins': + return [ + EntryPoint(name='auth_plugin', group='tests.mock_plugins', value='tests.plugins.mocks:NoAuthPlugin'), + ] + case _: + return list() + + + with patch("amqtt.plugins.manager.entry_points", side_effect=MockEntryPoints) as mocked_mqtt_publish: + + config = { + "listeners": { + "default": {"type": "tcp", "bind": "127.0.0.1:1883", "max_connections": 10}, + }, + 'sys_interval': 1, + 'auth': { + 'plugins': ['auth_plugin', ] + } + } + + client = MQTTClient(client_id="client1", config={'auto_reconnect': False}) + + broker = Broker(plugin_namespace='tests.mock_plugins', config=config) + await broker.start() + + + with pytest.raises(ConnectError): + await client.connect("mqtt://127.0.0.1:1883/") + From fed5b8eb082aae81f63d2cfd760827f6cc3247a8 Mon Sep 17 00:00:00 2001 From: Andrew Mirsky Date: Thu, 12 Jun 2025 11:03:28 -0400 Subject: [PATCH 2/2] shutdown broker when test completes --- tests/test_client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_client.py b/tests/test_client.py index ad0a5cc..8d49035 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -333,7 +333,7 @@ async def test_client_no_auth(): broker = Broker(plugin_namespace='tests.mock_plugins', config=config) await broker.start() - with pytest.raises(ConnectError): await client.connect("mqtt://127.0.0.1:1883/") + await broker.shutdown()