2017-08-06 22:46:15 +00:00
|
|
|
import asyncio
|
|
|
|
import logging
|
2023-02-11 14:53:06 +00:00
|
|
|
import socket
|
2025-04-06 17:03:30 +00:00
|
|
|
from unittest.mock import MagicMock, call, patch
|
2017-08-06 22:46:15 +00:00
|
|
|
|
2023-02-11 14:53:06 +00:00
|
|
|
import psutil
|
2021-03-06 15:20:48 +00:00
|
|
|
import pytest
|
|
|
|
|
2021-03-27 12:16:42 +00:00
|
|
|
from amqtt.adapters import StreamReaderAdapter, StreamWriterAdapter
|
|
|
|
from amqtt.broker import (
|
2017-08-06 22:46:15 +00:00
|
|
|
EVENT_BROKER_CLIENT_CONNECTED,
|
|
|
|
EVENT_BROKER_CLIENT_DISCONNECTED,
|
|
|
|
EVENT_BROKER_CLIENT_SUBSCRIBED,
|
|
|
|
EVENT_BROKER_CLIENT_UNSUBSCRIBED,
|
|
|
|
EVENT_BROKER_MESSAGE_RECEIVED,
|
2024-12-21 10:52:26 +00:00
|
|
|
EVENT_BROKER_POST_SHUTDOWN,
|
|
|
|
EVENT_BROKER_POST_START,
|
|
|
|
EVENT_BROKER_PRE_SHUTDOWN,
|
|
|
|
EVENT_BROKER_PRE_START,
|
2025-01-12 20:28:42 +00:00
|
|
|
Broker,
|
2021-03-14 20:44:41 +00:00
|
|
|
)
|
2024-12-21 10:52:26 +00:00
|
|
|
from amqtt.client import MQTTClient
|
2025-01-12 19:52:50 +00:00
|
|
|
from amqtt.errors import ConnectError
|
2024-12-21 10:52:26 +00:00
|
|
|
from amqtt.mqtt.connack import ConnackPacket
|
|
|
|
from amqtt.mqtt.connect import ConnectPacket, ConnectPayload, ConnectVariableHeader
|
2021-03-27 12:16:42 +00:00
|
|
|
from amqtt.mqtt.constants import QOS_0, QOS_1, QOS_2
|
2024-12-21 10:52:26 +00:00
|
|
|
from amqtt.mqtt.disconnect import DisconnectPacket
|
2025-04-06 17:03:30 +00:00
|
|
|
from amqtt.mqtt.protocol.broker_handler import BrokerProtocolHandler
|
2024-12-21 10:52:26 +00:00
|
|
|
from amqtt.mqtt.pubcomp import PubcompPacket
|
|
|
|
from amqtt.mqtt.publish import PublishPacket
|
|
|
|
from amqtt.mqtt.pubrec import PubrecPacket
|
|
|
|
from amqtt.mqtt.pubrel import PubrelPacket
|
|
|
|
from amqtt.session import OutgoingApplicationMessage
|
|
|
|
|
|
|
|
formatter = "[%(asctime)s] %(name)s {%(filename)s:%(lineno)d} %(levelname)s - %(message)s"
|
2015-09-30 19:34:21 +00:00
|
|
|
logging.basicConfig(level=logging.DEBUG, format=formatter)
|
|
|
|
log = logging.getLogger(__name__)
|
|
|
|
|
2021-03-06 17:58:34 +00:00
|
|
|
|
2021-01-09 19:38:41 +00:00
|
|
|
# monkey patch MagicMock
|
|
|
|
# taken from https://stackoverflow.com/questions/51394411/python-object-magicmock-cant-be-used-in-await-expression
|
|
|
|
async def async_magic():
|
|
|
|
pass
|
|
|
|
|
2015-09-30 19:34:21 +00:00
|
|
|
|
2024-12-21 10:52:26 +00:00
|
|
|
MagicMock.__await__ = lambda _: async_magic().__await__()
|
2015-10-16 20:13:37 +00:00
|
|
|
|
2021-03-06 16:01:28 +00:00
|
|
|
|
2025-01-12 20:28:42 +00:00
|
|
|
@pytest.mark.parametrize(
|
|
|
|
"input_str, output_addr, output_port",
|
|
|
|
[
|
|
|
|
("1234", None, 1234),
|
|
|
|
(":1234", None, 1234),
|
|
|
|
("0.0.0.0:1234", "0.0.0.0", 1234),
|
|
|
|
("[::]:1234", "[::]", 1234),
|
|
|
|
("0.0.0.0", "0.0.0.0", 5678),
|
|
|
|
("[::]", "[::]", 5678),
|
|
|
|
("localhost", "localhost", 5678),
|
|
|
|
("localhost:1234", "localhost", 1234),
|
|
|
|
],
|
|
|
|
)
|
|
|
|
def test_split_bindaddr_port(input_str, output_addr, output_port):
|
|
|
|
assert Broker._split_bindaddr_port(input_str, 5678) == (output_addr, output_port)
|
|
|
|
|
|
|
|
|
2021-03-06 16:01:28 +00:00
|
|
|
@pytest.mark.asyncio
|
2021-03-06 17:58:34 +00:00
|
|
|
async def test_start_stop(broker, mock_plugin_manager):
|
|
|
|
mock_plugin_manager.assert_has_calls(
|
2021-03-14 21:16:51 +00:00
|
|
|
[
|
|
|
|
call().fire_event(EVENT_BROKER_PRE_START),
|
|
|
|
call().fire_event(EVENT_BROKER_POST_START),
|
|
|
|
],
|
2021-03-14 20:44:41 +00:00
|
|
|
any_order=True,
|
|
|
|
)
|
2021-03-06 17:58:34 +00:00
|
|
|
mock_plugin_manager.reset_mock()
|
2021-03-06 16:01:28 +00:00
|
|
|
await broker.shutdown()
|
2021-03-06 17:58:34 +00:00
|
|
|
mock_plugin_manager.assert_has_calls(
|
2021-03-14 20:44:41 +00:00
|
|
|
[
|
|
|
|
call().fire_event(EVENT_BROKER_PRE_SHUTDOWN),
|
|
|
|
call().fire_event(EVENT_BROKER_POST_SHUTDOWN),
|
|
|
|
],
|
|
|
|
any_order=True,
|
|
|
|
)
|
2021-03-06 16:01:28 +00:00
|
|
|
assert broker.transitions.is_stopped()
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.asyncio
|
2021-03-06 17:58:34 +00:00
|
|
|
async def test_client_connect(broker, mock_plugin_manager):
|
2021-03-06 16:01:28 +00:00
|
|
|
client = MQTTClient()
|
2021-03-14 20:44:41 +00:00
|
|
|
ret = await client.connect("mqtt://127.0.0.1/")
|
2021-03-06 16:01:28 +00:00
|
|
|
assert ret == 0
|
2024-12-21 10:52:26 +00:00
|
|
|
assert client.session is not None
|
2021-03-06 16:01:28 +00:00
|
|
|
assert client.session.client_id in broker._sessions
|
|
|
|
await client.disconnect()
|
2021-03-06 17:58:34 +00:00
|
|
|
|
|
|
|
await asyncio.sleep(0.01)
|
|
|
|
|
|
|
|
mock_plugin_manager.assert_has_calls(
|
2021-03-14 20:44:41 +00:00
|
|
|
[
|
|
|
|
call().fire_event(
|
2024-12-21 10:52:26 +00:00
|
|
|
EVENT_BROKER_CLIENT_CONNECTED,
|
|
|
|
client_id=client.session.client_id,
|
2021-03-14 20:44:41 +00:00
|
|
|
),
|
|
|
|
call().fire_event(
|
2024-12-21 10:52:26 +00:00
|
|
|
EVENT_BROKER_CLIENT_DISCONNECTED,
|
|
|
|
client_id=client.session.client_id,
|
2021-03-14 20:44:41 +00:00
|
|
|
),
|
|
|
|
],
|
|
|
|
any_order=True,
|
|
|
|
)
|
2021-03-06 16:01:28 +00:00
|
|
|
|
|
|
|
|
2023-02-11 14:53:06 +00:00
|
|
|
@pytest.mark.asyncio
|
|
|
|
async def test_connect_tcp(broker):
|
|
|
|
process = psutil.Process()
|
|
|
|
connections_number = 10
|
2025-05-19 16:45:45 +00:00
|
|
|
|
|
|
|
# mqtt 3.1 requires a connect packet, otherwise the socket connection is rejected
|
|
|
|
static_connect_packet = b'\x10\x1b\x00\x04MQTT\x04\x02\x00<\x00\x0ftest-client-123'
|
|
|
|
|
|
|
|
sockets = []
|
|
|
|
for i in range(connections_number):
|
|
|
|
s = socket.create_connection(("127.0.0.1", 1883))
|
|
|
|
s.send(static_connect_packet)
|
|
|
|
sockets.append(s)
|
2024-12-21 10:52:26 +00:00
|
|
|
|
|
|
|
# Wait for a brief moment to ensure connections are established
|
2023-02-11 14:53:06 +00:00
|
|
|
await asyncio.sleep(0.1)
|
|
|
|
|
2025-05-19 16:45:45 +00:00
|
|
|
# # Get the current number of TCP connections
|
2024-12-21 10:52:26 +00:00
|
|
|
connections = process.net_connections()
|
|
|
|
|
|
|
|
# max number of connections on the TCP listener is 10
|
2023-02-11 14:53:06 +00:00
|
|
|
assert broker._servers["default"].conn_count == connections_number
|
2024-12-21 10:52:26 +00:00
|
|
|
|
|
|
|
# Ensure connections are only on the TCP listener (port 1883)
|
|
|
|
tcp_connections = [conn for conn in connections if conn.laddr.port == 1883]
|
|
|
|
assert len(tcp_connections) == connections_number + 1 # Including the Broker's listening socket
|
|
|
|
|
2023-02-11 14:53:06 +00:00
|
|
|
for conn in connections:
|
2024-12-21 10:52:26 +00:00
|
|
|
assert conn.status in ("ESTABLISHED", "LISTEN")
|
2023-02-11 14:53:06 +00:00
|
|
|
|
|
|
|
# close all connections
|
|
|
|
for s in sockets:
|
|
|
|
s.close()
|
2024-12-21 10:52:26 +00:00
|
|
|
|
|
|
|
# Wait a moment for connections to be closed
|
2023-02-11 14:53:06 +00:00
|
|
|
await asyncio.sleep(0.1)
|
2024-12-21 10:52:26 +00:00
|
|
|
|
|
|
|
# Recheck connections after closing
|
|
|
|
connections = process.net_connections()
|
|
|
|
tcp_connections = [conn for conn in connections if conn.laddr.port == 1883]
|
|
|
|
|
|
|
|
for conn in tcp_connections:
|
|
|
|
assert conn.status in ("CLOSE_WAIT", "LISTEN")
|
|
|
|
|
|
|
|
# Ensure no active connections for the default listener
|
2023-02-11 14:53:06 +00:00
|
|
|
assert broker._servers["default"].conn_count == 0
|
|
|
|
|
2024-12-21 10:52:26 +00:00
|
|
|
# Add one more connection to the TCP listener
|
2023-02-11 14:53:06 +00:00
|
|
|
s = socket.create_connection(("127.0.0.1", 1883))
|
2025-05-19 16:45:45 +00:00
|
|
|
s.send(static_connect_packet)
|
|
|
|
|
2023-02-11 14:53:06 +00:00
|
|
|
open_connections = []
|
2024-12-21 10:52:26 +00:00
|
|
|
open_connections = [conn for conn in process.net_connections() if conn.status == "ESTABLISHED"]
|
|
|
|
|
|
|
|
# Ensure that only one TCP connection is active now
|
2023-02-11 14:53:06 +00:00
|
|
|
assert len(open_connections) == 1
|
2022-04-28 09:49:37 +00:00
|
|
|
await asyncio.sleep(0.1)
|
2023-02-11 14:53:06 +00:00
|
|
|
assert broker._servers["default"].conn_count == 1
|
|
|
|
|
|
|
|
|
2021-03-06 16:01:28 +00:00
|
|
|
@pytest.mark.asyncio
|
2021-12-31 10:12:59 +00:00
|
|
|
async def test_client_connect_will_flag(broker):
|
|
|
|
conn_reader, conn_writer = await asyncio.open_connection("127.0.0.1", 1883)
|
2021-03-06 16:01:28 +00:00
|
|
|
reader = StreamReaderAdapter(conn_reader)
|
|
|
|
writer = StreamWriterAdapter(conn_writer)
|
|
|
|
|
|
|
|
vh = ConnectVariableHeader()
|
|
|
|
payload = ConnectPayload()
|
|
|
|
|
|
|
|
vh.keep_alive = 10
|
|
|
|
vh.clean_session_flag = False
|
|
|
|
vh.will_retain_flag = False
|
|
|
|
vh.will_flag = True
|
|
|
|
vh.will_qos = QOS_0
|
2021-03-14 20:44:41 +00:00
|
|
|
payload.client_id = "test_id"
|
|
|
|
payload.will_message = b"test"
|
|
|
|
payload.will_topic = "/topic"
|
2024-12-29 18:23:27 +00:00
|
|
|
connect = ConnectPacket(variable_header=vh, payload=payload)
|
2021-03-06 16:01:28 +00:00
|
|
|
await connect.to_stream(writer)
|
|
|
|
await ConnackPacket.from_stream(reader)
|
|
|
|
|
|
|
|
await asyncio.sleep(0.1)
|
|
|
|
|
|
|
|
disconnect = DisconnectPacket()
|
|
|
|
await disconnect.to_stream(writer)
|
|
|
|
|
|
|
|
await asyncio.sleep(0.1)
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.asyncio
|
2021-03-06 17:58:34 +00:00
|
|
|
async def test_client_connect_clean_session_false(broker):
|
2021-03-14 20:44:41 +00:00
|
|
|
client = MQTTClient(client_id="", config={"auto_reconnect": False})
|
2021-03-06 16:01:28 +00:00
|
|
|
return_code = None
|
|
|
|
try:
|
2021-03-14 20:44:41 +00:00
|
|
|
await client.connect("mqtt://127.0.0.1/", cleansession=False)
|
2025-01-12 19:52:50 +00:00
|
|
|
except ConnectError as ce:
|
2021-03-06 16:01:28 +00:00
|
|
|
return_code = ce.return_code
|
|
|
|
assert return_code == 0x02
|
2024-12-21 10:52:26 +00:00
|
|
|
assert client.session is not None
|
2021-03-06 16:01:28 +00:00
|
|
|
assert client.session.client_id not in broker._sessions
|
|
|
|
await client.disconnect()
|
|
|
|
await asyncio.sleep(0.1)
|
2021-03-06 17:58:34 +00:00
|
|
|
|
2021-03-06 16:01:28 +00:00
|
|
|
|
|
|
|
@pytest.mark.asyncio
|
2021-03-06 17:58:34 +00:00
|
|
|
async def test_client_subscribe(broker, mock_plugin_manager):
|
2021-03-06 16:01:28 +00:00
|
|
|
client = MQTTClient()
|
2021-03-14 20:44:41 +00:00
|
|
|
ret = await client.connect("mqtt://127.0.0.1/")
|
2021-03-06 16:01:28 +00:00
|
|
|
assert ret == 0
|
2021-03-14 20:44:41 +00:00
|
|
|
await client.subscribe([("/topic", QOS_0)])
|
2021-03-06 16:01:28 +00:00
|
|
|
|
|
|
|
# Test if the client test client subscription is registered
|
2021-03-14 20:44:41 +00:00
|
|
|
assert "/topic" in broker._subscriptions
|
|
|
|
subs = broker._subscriptions["/topic"]
|
2021-03-06 16:01:28 +00:00
|
|
|
assert len(subs) == 1
|
|
|
|
(s, qos) = subs[0]
|
|
|
|
assert s == client.session
|
|
|
|
assert qos == QOS_0
|
|
|
|
|
|
|
|
await client.disconnect()
|
|
|
|
await asyncio.sleep(0.1)
|
2021-03-06 17:58:34 +00:00
|
|
|
|
|
|
|
mock_plugin_manager.assert_has_calls(
|
2021-03-14 20:44:41 +00:00
|
|
|
[
|
|
|
|
call().fire_event(
|
|
|
|
EVENT_BROKER_CLIENT_SUBSCRIBED,
|
|
|
|
client_id=client.session.client_id,
|
|
|
|
topic="/topic",
|
|
|
|
qos=QOS_0,
|
2024-12-21 10:52:26 +00:00
|
|
|
),
|
2021-03-14 20:44:41 +00:00
|
|
|
],
|
|
|
|
any_order=True,
|
|
|
|
)
|
|
|
|
|
2021-03-06 16:01:28 +00:00
|
|
|
|
|
|
|
@pytest.mark.asyncio
|
2021-03-06 17:58:34 +00:00
|
|
|
async def test_client_subscribe_twice(broker, mock_plugin_manager):
|
2021-03-06 16:01:28 +00:00
|
|
|
client = MQTTClient()
|
2021-03-14 20:44:41 +00:00
|
|
|
ret = await client.connect("mqtt://127.0.0.1/")
|
2021-03-06 16:01:28 +00:00
|
|
|
assert ret == 0
|
2021-03-14 20:44:41 +00:00
|
|
|
await client.subscribe([("/topic", QOS_0)])
|
2021-03-06 16:01:28 +00:00
|
|
|
|
|
|
|
# Test if the client test client subscription is registered
|
2021-03-14 20:44:41 +00:00
|
|
|
assert "/topic" in broker._subscriptions
|
|
|
|
subs = broker._subscriptions["/topic"]
|
2021-03-06 16:01:28 +00:00
|
|
|
assert len(subs) == 1
|
|
|
|
(s, qos) = subs[0]
|
|
|
|
assert s == client.session
|
|
|
|
assert qos == QOS_0
|
|
|
|
|
2021-03-14 20:44:41 +00:00
|
|
|
await client.subscribe([("/topic", QOS_0)])
|
2021-03-06 16:01:28 +00:00
|
|
|
assert len(subs) == 1
|
|
|
|
(s, qos) = subs[0]
|
|
|
|
assert s == client.session
|
|
|
|
assert qos == QOS_0
|
|
|
|
|
|
|
|
await client.disconnect()
|
|
|
|
await asyncio.sleep(0.1)
|
2021-03-06 17:58:34 +00:00
|
|
|
|
|
|
|
mock_plugin_manager.assert_has_calls(
|
2021-03-14 20:44:41 +00:00
|
|
|
[
|
|
|
|
call().fire_event(
|
|
|
|
EVENT_BROKER_CLIENT_SUBSCRIBED,
|
|
|
|
client_id=client.session.client_id,
|
|
|
|
topic="/topic",
|
|
|
|
qos=QOS_0,
|
2024-12-21 10:52:26 +00:00
|
|
|
),
|
2021-03-14 20:44:41 +00:00
|
|
|
],
|
|
|
|
any_order=True,
|
|
|
|
)
|
2021-03-06 16:01:28 +00:00
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.asyncio
|
2021-03-06 17:58:34 +00:00
|
|
|
async def test_client_unsubscribe(broker, mock_plugin_manager):
|
2021-03-06 16:01:28 +00:00
|
|
|
client = MQTTClient()
|
2021-03-14 20:44:41 +00:00
|
|
|
ret = await client.connect("mqtt://127.0.0.1/")
|
2021-03-06 16:01:28 +00:00
|
|
|
assert ret == 0
|
2021-03-14 20:44:41 +00:00
|
|
|
await client.subscribe([("/topic", QOS_0)])
|
2021-03-06 16:01:28 +00:00
|
|
|
|
|
|
|
# Test if the client test client subscription is registered
|
2021-03-14 20:44:41 +00:00
|
|
|
assert "/topic" in broker._subscriptions
|
|
|
|
subs = broker._subscriptions["/topic"]
|
2021-03-06 16:01:28 +00:00
|
|
|
assert len(subs) == 1
|
|
|
|
(s, qos) = subs[0]
|
|
|
|
assert s == client.session
|
|
|
|
assert qos == QOS_0
|
|
|
|
|
2021-03-14 20:44:41 +00:00
|
|
|
await client.unsubscribe(["/topic"])
|
2021-03-06 16:01:28 +00:00
|
|
|
await asyncio.sleep(0.1)
|
2021-03-14 20:44:41 +00:00
|
|
|
assert broker._subscriptions["/topic"] == []
|
2021-03-06 16:01:28 +00:00
|
|
|
await client.disconnect()
|
|
|
|
await asyncio.sleep(0.1)
|
2021-03-06 17:58:34 +00:00
|
|
|
|
|
|
|
mock_plugin_manager.assert_has_calls(
|
2021-03-06 16:01:28 +00:00
|
|
|
[
|
2021-03-14 20:44:41 +00:00
|
|
|
call().fire_event(
|
|
|
|
EVENT_BROKER_CLIENT_SUBSCRIBED,
|
|
|
|
client_id=client.session.client_id,
|
|
|
|
topic="/topic",
|
|
|
|
qos=QOS_0,
|
|
|
|
),
|
|
|
|
call().fire_event(
|
|
|
|
EVENT_BROKER_CLIENT_UNSUBSCRIBED,
|
|
|
|
client_id=client.session.client_id,
|
|
|
|
topic="/topic",
|
|
|
|
),
|
|
|
|
],
|
|
|
|
any_order=True,
|
|
|
|
)
|
2021-03-06 16:01:28 +00:00
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.asyncio
|
2021-03-06 17:58:34 +00:00
|
|
|
async def test_client_publish(broker, mock_plugin_manager):
|
2021-03-06 16:01:28 +00:00
|
|
|
pub_client = MQTTClient()
|
2021-03-14 20:44:41 +00:00
|
|
|
ret = await pub_client.connect("mqtt://127.0.0.1/")
|
2021-03-06 16:01:28 +00:00
|
|
|
assert ret == 0
|
|
|
|
|
2021-03-14 20:44:41 +00:00
|
|
|
ret_message = await pub_client.publish("/topic", b"data", QOS_0)
|
2021-03-06 16:01:28 +00:00
|
|
|
await pub_client.disconnect()
|
|
|
|
assert broker._retained_messages == {}
|
|
|
|
|
|
|
|
await asyncio.sleep(0.1)
|
2024-12-21 10:52:26 +00:00
|
|
|
assert pub_client.session is not None
|
2021-03-06 17:58:34 +00:00
|
|
|
|
|
|
|
mock_plugin_manager.assert_has_calls(
|
2021-03-06 16:01:28 +00:00
|
|
|
[
|
2021-03-14 20:44:41 +00:00
|
|
|
call().fire_event(
|
|
|
|
EVENT_BROKER_MESSAGE_RECEIVED,
|
|
|
|
client_id=pub_client.session.client_id,
|
|
|
|
message=ret_message,
|
|
|
|
),
|
|
|
|
],
|
|
|
|
any_order=True,
|
|
|
|
)
|
|
|
|
|
2021-03-06 16:01:28 +00:00
|
|
|
|
2021-06-06 22:22:05 +00:00
|
|
|
@pytest.mark.asyncio
|
|
|
|
async def test_client_publish_acl_permitted(acl_broker):
|
|
|
|
sub_client = MQTTClient()
|
2024-12-21 10:52:26 +00:00
|
|
|
ret_conn = await sub_client.connect("mqtt://user2:user2password@127.0.0.1:1884/")
|
|
|
|
assert ret_conn == 0
|
2021-06-06 22:22:05 +00:00
|
|
|
|
2024-12-21 10:52:26 +00:00
|
|
|
ret_sub = await sub_client.subscribe([("public/subtopic/test", QOS_0)])
|
|
|
|
assert ret_sub == [QOS_0]
|
2021-06-06 22:22:05 +00:00
|
|
|
|
|
|
|
pub_client = MQTTClient()
|
2024-12-21 10:52:26 +00:00
|
|
|
ret_conn = await pub_client.connect("mqtt://user1:user1password@127.0.0.1:1884/")
|
|
|
|
assert ret_conn == 0
|
2021-06-06 22:22:05 +00:00
|
|
|
|
2021-07-08 00:39:07 +00:00
|
|
|
await pub_client.publish("public/subtopic/test", b"data", QOS_0)
|
2021-06-06 22:22:05 +00:00
|
|
|
|
2024-12-21 10:52:26 +00:00
|
|
|
message = await sub_client.deliver_message(timeout_duration=1)
|
2021-06-06 22:22:05 +00:00
|
|
|
await pub_client.disconnect()
|
|
|
|
await sub_client.disconnect()
|
|
|
|
|
|
|
|
assert message is not None
|
|
|
|
assert message.topic == "public/subtopic/test"
|
|
|
|
assert message.data == b"data"
|
|
|
|
assert message.qos == QOS_0
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
|
|
async def test_client_publish_acl_forbidden(acl_broker):
|
|
|
|
sub_client = MQTTClient()
|
2024-12-21 10:52:26 +00:00
|
|
|
ret_conn = await sub_client.connect("mqtt://user2:user2password@127.0.0.1:1884/")
|
|
|
|
assert ret_conn == 0
|
2021-06-06 22:22:05 +00:00
|
|
|
|
2024-12-21 10:52:26 +00:00
|
|
|
ret_sub = await sub_client.subscribe([("public/forbidden/test", QOS_0)])
|
|
|
|
assert ret_sub == [QOS_0]
|
2021-06-06 22:22:05 +00:00
|
|
|
|
|
|
|
pub_client = MQTTClient()
|
2024-12-21 10:52:26 +00:00
|
|
|
ret_conn = await pub_client.connect("mqtt://user1:user1password@127.0.0.1:1884/")
|
|
|
|
assert ret_conn == 0
|
2021-06-06 22:22:05 +00:00
|
|
|
|
2021-07-08 00:39:07 +00:00
|
|
|
await pub_client.publish("public/forbidden/test", b"data", QOS_0)
|
2021-06-06 22:22:05 +00:00
|
|
|
|
|
|
|
try:
|
2024-12-21 10:52:26 +00:00
|
|
|
await sub_client.deliver_message(timeout_duration=1)
|
|
|
|
msg = "Should not have worked"
|
|
|
|
raise AssertionError(msg)
|
2025-01-12 19:52:50 +00:00
|
|
|
except Exception:
|
2021-06-06 22:22:05 +00:00
|
|
|
pass
|
|
|
|
|
|
|
|
await pub_client.disconnect()
|
|
|
|
await sub_client.disconnect()
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
|
|
async def test_client_publish_acl_permitted_sub_forbidden(acl_broker):
|
|
|
|
sub_client1 = MQTTClient()
|
2024-12-21 10:52:26 +00:00
|
|
|
ret_conn = await sub_client1.connect("mqtt://user2:user2password@127.0.0.1:1884/")
|
|
|
|
assert ret_conn == 0
|
2021-06-06 22:22:05 +00:00
|
|
|
|
|
|
|
sub_client2 = MQTTClient()
|
2024-12-21 10:52:26 +00:00
|
|
|
ret_conn = await sub_client2.connect("mqtt://user3:user3password@127.0.0.1:1884/")
|
|
|
|
assert ret_conn == 0
|
2021-06-06 22:22:05 +00:00
|
|
|
|
2024-12-21 10:52:26 +00:00
|
|
|
ret_sub = await sub_client1.subscribe([("public/subtopic/test", QOS_0)])
|
|
|
|
assert ret_sub == [QOS_0]
|
2021-06-06 22:22:05 +00:00
|
|
|
|
2024-12-21 10:52:26 +00:00
|
|
|
ret_sub = await sub_client2.subscribe([("public/subtopic/test", QOS_0)])
|
|
|
|
assert ret_sub == [128]
|
2021-06-06 22:22:05 +00:00
|
|
|
|
|
|
|
pub_client = MQTTClient()
|
2024-12-21 10:52:26 +00:00
|
|
|
ret_conn = await pub_client.connect("mqtt://user1:user1password@127.0.0.1:1884/")
|
|
|
|
assert ret_conn == 0
|
2021-06-06 22:22:05 +00:00
|
|
|
|
2021-07-08 00:39:07 +00:00
|
|
|
await pub_client.publish("public/subtopic/test", b"data", QOS_0)
|
2021-06-06 22:22:05 +00:00
|
|
|
|
2024-12-21 10:52:26 +00:00
|
|
|
message = await sub_client1.deliver_message(timeout_duration=1)
|
2021-06-06 22:22:05 +00:00
|
|
|
|
|
|
|
try:
|
2024-12-21 10:52:26 +00:00
|
|
|
await sub_client2.deliver_message(timeout_duration=1)
|
|
|
|
msg = "Should not have worked"
|
|
|
|
raise AssertionError(msg)
|
2025-01-12 19:52:50 +00:00
|
|
|
except Exception:
|
2021-06-06 22:22:05 +00:00
|
|
|
pass
|
|
|
|
|
|
|
|
await pub_client.disconnect()
|
|
|
|
await sub_client1.disconnect()
|
|
|
|
await sub_client2.disconnect()
|
|
|
|
|
|
|
|
assert message is not None
|
|
|
|
assert message.topic == "public/subtopic/test"
|
|
|
|
assert message.data == b"data"
|
|
|
|
assert message.qos == QOS_0
|
|
|
|
|
|
|
|
|
2021-03-06 16:01:28 +00:00
|
|
|
@pytest.mark.asyncio
|
2021-12-31 10:12:59 +00:00
|
|
|
async def test_client_publish_dup(broker):
|
|
|
|
conn_reader, conn_writer = await asyncio.open_connection("127.0.0.1", 1883)
|
2021-03-06 16:01:28 +00:00
|
|
|
reader = StreamReaderAdapter(conn_reader)
|
|
|
|
writer = StreamWriterAdapter(conn_writer)
|
|
|
|
|
|
|
|
vh = ConnectVariableHeader()
|
|
|
|
payload = ConnectPayload()
|
|
|
|
|
|
|
|
vh.keep_alive = 10
|
|
|
|
vh.clean_session_flag = False
|
|
|
|
vh.will_retain_flag = False
|
2021-03-14 20:44:41 +00:00
|
|
|
payload.client_id = "test_id"
|
2024-12-29 18:23:27 +00:00
|
|
|
connect = ConnectPacket(variable_header=vh, payload=payload)
|
2021-03-06 16:01:28 +00:00
|
|
|
await connect.to_stream(writer)
|
|
|
|
await ConnackPacket.from_stream(reader)
|
|
|
|
|
2021-03-14 20:44:41 +00:00
|
|
|
publish_1 = PublishPacket.build("/test", b"data", 1, False, QOS_2, False)
|
2021-03-06 16:01:28 +00:00
|
|
|
await publish_1.to_stream(writer)
|
2024-12-21 10:52:26 +00:00
|
|
|
|
|
|
|
# Store the future of PubrecPacket.from_stream() in a variable
|
|
|
|
pubrec_task_1 = asyncio.ensure_future(PubrecPacket.from_stream(reader))
|
2021-03-06 16:01:28 +00:00
|
|
|
|
|
|
|
await asyncio.sleep(2)
|
|
|
|
|
2021-03-14 20:44:41 +00:00
|
|
|
publish_dup = PublishPacket.build("/test", b"data", 1, True, QOS_2, False)
|
2021-03-06 16:01:28 +00:00
|
|
|
await publish_dup.to_stream(writer)
|
|
|
|
await PubrecPacket.from_stream(reader)
|
|
|
|
pubrel = PubrelPacket.build(1)
|
|
|
|
await pubrel.to_stream(writer)
|
|
|
|
await PubcompPacket.from_stream(reader)
|
|
|
|
|
2024-12-21 10:52:26 +00:00
|
|
|
# Ensure we wait for the Pubrec packets to be processed
|
|
|
|
await pubrec_task_1
|
|
|
|
|
2021-03-06 16:01:28 +00:00
|
|
|
disconnect = DisconnectPacket()
|
|
|
|
await disconnect.to_stream(writer)
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.asyncio
|
2021-03-06 17:58:34 +00:00
|
|
|
async def test_client_publish_invalid_topic(broker):
|
2021-03-06 16:01:28 +00:00
|
|
|
assert broker.transitions.is_started()
|
|
|
|
pub_client = MQTTClient()
|
2021-03-14 20:44:41 +00:00
|
|
|
ret = await pub_client.connect("mqtt://127.0.0.1/")
|
2021-03-06 16:01:28 +00:00
|
|
|
assert ret == 0
|
|
|
|
|
2021-03-14 20:44:41 +00:00
|
|
|
await pub_client.publish("/+", b"data", QOS_0)
|
2021-03-06 16:01:28 +00:00
|
|
|
await asyncio.sleep(0.1)
|
|
|
|
await pub_client.disconnect()
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.asyncio
|
2021-03-06 17:58:34 +00:00
|
|
|
async def test_client_publish_big(broker, mock_plugin_manager):
|
2021-03-06 16:01:28 +00:00
|
|
|
pub_client = MQTTClient()
|
2021-03-14 20:44:41 +00:00
|
|
|
ret = await pub_client.connect("mqtt://127.0.0.1/")
|
2021-03-06 16:01:28 +00:00
|
|
|
assert ret == 0
|
|
|
|
|
2021-03-14 21:16:51 +00:00
|
|
|
ret_message = await pub_client.publish(
|
2024-12-21 10:52:26 +00:00
|
|
|
"/topic",
|
|
|
|
bytearray(b"\x99" * 256 * 1024),
|
|
|
|
QOS_2,
|
2021-03-14 21:16:51 +00:00
|
|
|
)
|
2021-03-06 16:01:28 +00:00
|
|
|
await pub_client.disconnect()
|
|
|
|
assert broker._retained_messages == {}
|
|
|
|
|
|
|
|
await asyncio.sleep(0.1)
|
2024-12-21 10:52:26 +00:00
|
|
|
assert pub_client.session is not None
|
2021-03-06 17:58:34 +00:00
|
|
|
|
|
|
|
mock_plugin_manager.assert_has_calls(
|
2021-03-06 16:01:28 +00:00
|
|
|
[
|
2021-03-14 20:44:41 +00:00
|
|
|
call().fire_event(
|
|
|
|
EVENT_BROKER_MESSAGE_RECEIVED,
|
|
|
|
client_id=pub_client.session.client_id,
|
|
|
|
message=ret_message,
|
|
|
|
),
|
|
|
|
],
|
|
|
|
any_order=True,
|
|
|
|
)
|
|
|
|
|
2021-03-06 16:01:28 +00:00
|
|
|
|
|
|
|
@pytest.mark.asyncio
|
2021-03-06 17:58:34 +00:00
|
|
|
async def test_client_publish_retain(broker):
|
2021-03-06 16:01:28 +00:00
|
|
|
pub_client = MQTTClient()
|
2021-03-14 20:44:41 +00:00
|
|
|
ret = await pub_client.connect("mqtt://127.0.0.1/")
|
2021-03-06 16:01:28 +00:00
|
|
|
assert ret == 0
|
2021-03-14 20:44:41 +00:00
|
|
|
await pub_client.publish("/topic", b"data", QOS_0, retain=True)
|
2021-03-06 16:01:28 +00:00
|
|
|
await pub_client.disconnect()
|
|
|
|
await asyncio.sleep(0.1)
|
2021-03-14 20:44:41 +00:00
|
|
|
assert "/topic" in broker._retained_messages
|
|
|
|
retained_message = broker._retained_messages["/topic"]
|
2021-03-06 16:01:28 +00:00
|
|
|
assert retained_message.source_session == pub_client.session
|
2021-03-14 20:44:41 +00:00
|
|
|
assert retained_message.topic == "/topic"
|
|
|
|
assert retained_message.data == b"data"
|
2021-03-06 16:01:28 +00:00
|
|
|
assert retained_message.qos == QOS_0
|
|
|
|
|
|
|
|
|
2021-03-06 17:58:34 +00:00
|
|
|
@pytest.mark.asyncio
|
|
|
|
async def test_client_publish_retain_delete(broker):
|
2021-03-06 16:01:28 +00:00
|
|
|
pub_client = MQTTClient()
|
2021-03-14 20:44:41 +00:00
|
|
|
ret = await pub_client.connect("mqtt://127.0.0.1/")
|
2021-03-06 16:01:28 +00:00
|
|
|
assert ret == 0
|
2021-03-14 20:44:41 +00:00
|
|
|
await pub_client.publish("/topic", b"", QOS_0, retain=True)
|
2021-03-06 16:01:28 +00:00
|
|
|
await pub_client.disconnect()
|
|
|
|
await asyncio.sleep(0.1)
|
2021-03-14 20:44:41 +00:00
|
|
|
assert "/topic" not in broker._retained_messages
|
2021-03-06 17:58:34 +00:00
|
|
|
|
2021-03-06 16:01:28 +00:00
|
|
|
|
|
|
|
@pytest.mark.asyncio
|
2021-03-06 17:58:34 +00:00
|
|
|
async def test_client_subscribe_publish(broker):
|
2021-03-06 16:01:28 +00:00
|
|
|
sub_client = MQTTClient()
|
2021-03-14 20:44:41 +00:00
|
|
|
await sub_client.connect("mqtt://127.0.0.1")
|
2021-03-14 21:16:51 +00:00
|
|
|
ret = await sub_client.subscribe(
|
2024-12-21 10:52:26 +00:00
|
|
|
[("/qos0", QOS_0), ("/qos1", QOS_1), ("/qos2", QOS_2)],
|
2021-03-14 21:16:51 +00:00
|
|
|
)
|
2021-03-06 16:01:28 +00:00
|
|
|
assert ret == [QOS_0, QOS_1, QOS_2]
|
|
|
|
|
2021-03-14 20:44:41 +00:00
|
|
|
await _client_publish("/qos0", b"data", QOS_0)
|
|
|
|
await _client_publish("/qos1", b"data", QOS_1)
|
|
|
|
await _client_publish("/qos2", b"data", QOS_2)
|
2021-03-06 16:01:28 +00:00
|
|
|
await asyncio.sleep(0.1)
|
|
|
|
for qos in [QOS_0, QOS_1, QOS_2]:
|
|
|
|
message = await sub_client.deliver_message()
|
|
|
|
assert message is not None
|
2024-12-21 10:52:26 +00:00
|
|
|
assert message.topic == f"/qos{qos}"
|
2021-03-14 20:44:41 +00:00
|
|
|
assert message.data == b"data"
|
2021-03-06 16:01:28 +00:00
|
|
|
assert message.qos == qos
|
|
|
|
await sub_client.disconnect()
|
|
|
|
await asyncio.sleep(0.1)
|
|
|
|
|
2021-03-14 20:44:41 +00:00
|
|
|
|
2021-03-06 16:01:28 +00:00
|
|
|
@pytest.mark.asyncio
|
2021-03-06 17:58:34 +00:00
|
|
|
async def test_client_subscribe_invalid(broker):
|
2021-03-06 16:01:28 +00:00
|
|
|
sub_client = MQTTClient()
|
2021-03-14 20:44:41 +00:00
|
|
|
await sub_client.connect("mqtt://127.0.0.1")
|
2021-03-06 16:01:28 +00:00
|
|
|
ret = await sub_client.subscribe(
|
2021-03-14 21:16:51 +00:00
|
|
|
[
|
|
|
|
("+", QOS_0),
|
|
|
|
("+/tennis/#", QOS_0),
|
|
|
|
("sport+", QOS_0),
|
|
|
|
("sport/+/player1", QOS_0),
|
2024-12-21 10:52:26 +00:00
|
|
|
],
|
2021-03-14 20:44:41 +00:00
|
|
|
)
|
2021-03-06 16:01:28 +00:00
|
|
|
assert ret == [QOS_0, QOS_0, 0x80, QOS_0]
|
|
|
|
|
|
|
|
await asyncio.sleep(0.1)
|
|
|
|
await sub_client.disconnect()
|
|
|
|
await asyncio.sleep(0.1)
|
2021-03-06 17:58:34 +00:00
|
|
|
|
2021-03-06 16:01:28 +00:00
|
|
|
|
|
|
|
@pytest.mark.asyncio
|
2021-03-06 17:58:34 +00:00
|
|
|
async def test_client_subscribe_publish_dollar_topic_1(broker):
|
2021-03-06 16:01:28 +00:00
|
|
|
assert broker.transitions.is_started()
|
|
|
|
sub_client = MQTTClient()
|
2021-03-14 20:44:41 +00:00
|
|
|
await sub_client.connect("mqtt://127.0.0.1")
|
|
|
|
ret = await sub_client.subscribe([("#", QOS_0)])
|
2021-03-06 16:01:28 +00:00
|
|
|
assert ret == [QOS_0]
|
|
|
|
|
2021-03-14 20:44:41 +00:00
|
|
|
await _client_publish("/topic", b"data", QOS_0)
|
2021-03-06 16:01:28 +00:00
|
|
|
message = await sub_client.deliver_message()
|
|
|
|
assert message is not None
|
|
|
|
|
2021-03-14 20:44:41 +00:00
|
|
|
await _client_publish("$topic", b"data", QOS_0)
|
2021-03-06 16:01:28 +00:00
|
|
|
await asyncio.sleep(0.1)
|
|
|
|
message = None
|
|
|
|
try:
|
2024-12-21 10:52:26 +00:00
|
|
|
message = await sub_client.deliver_message(timeout_duration=2)
|
2025-01-12 19:52:50 +00:00
|
|
|
except Exception:
|
2021-03-06 16:01:28 +00:00
|
|
|
pass
|
2021-03-15 09:59:13 +00:00
|
|
|
except RuntimeError as e:
|
|
|
|
# The loop is closed with pending tasks. Needs fine tuning.
|
|
|
|
log.warning(e)
|
2021-03-06 16:01:28 +00:00
|
|
|
assert message is None
|
|
|
|
await sub_client.disconnect()
|
|
|
|
await asyncio.sleep(0.1)
|
2021-03-06 17:58:34 +00:00
|
|
|
|
2021-03-06 16:01:28 +00:00
|
|
|
|
|
|
|
@pytest.mark.asyncio
|
2021-03-06 17:58:34 +00:00
|
|
|
async def test_client_subscribe_publish_dollar_topic_2(broker):
|
2021-03-06 16:01:28 +00:00
|
|
|
sub_client = MQTTClient()
|
2021-03-14 20:44:41 +00:00
|
|
|
await sub_client.connect("mqtt://127.0.0.1")
|
|
|
|
ret = await sub_client.subscribe([("+/monitor/Clients", QOS_0)])
|
2021-03-06 16:01:28 +00:00
|
|
|
assert ret == [QOS_0]
|
|
|
|
|
2020-10-08 18:58:26 +00:00
|
|
|
await _client_publish("test/monitor/Clients", b"data", QOS_0)
|
2021-03-06 16:01:28 +00:00
|
|
|
message = await sub_client.deliver_message()
|
|
|
|
assert message is not None
|
|
|
|
|
2021-03-14 20:44:41 +00:00
|
|
|
await _client_publish("$SYS/monitor/Clients", b"data", QOS_0)
|
2021-03-06 16:01:28 +00:00
|
|
|
await asyncio.sleep(0.1)
|
|
|
|
message = None
|
|
|
|
try:
|
2024-12-21 10:52:26 +00:00
|
|
|
message = await sub_client.deliver_message(timeout_duration=2)
|
2025-01-12 19:52:50 +00:00
|
|
|
except Exception:
|
2021-03-06 16:01:28 +00:00
|
|
|
pass
|
2021-03-15 09:59:13 +00:00
|
|
|
except RuntimeError as e:
|
|
|
|
# The loop is closed with pending tasks. Needs fine tuning.
|
|
|
|
log.warning(e)
|
2021-03-06 16:01:28 +00:00
|
|
|
assert message is None
|
|
|
|
await sub_client.disconnect()
|
|
|
|
await asyncio.sleep(0.1)
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.asyncio
|
2021-03-14 21:16:51 +00:00
|
|
|
@pytest.mark.xfail(
|
2024-12-21 10:52:26 +00:00
|
|
|
reason="see https://github.com/Yakifo/aio-amqtt/issues/16",
|
|
|
|
strict=False,
|
2021-03-14 21:16:51 +00:00
|
|
|
)
|
2021-03-06 17:58:34 +00:00
|
|
|
async def test_client_publish_retain_subscribe(broker):
|
2021-03-06 16:01:28 +00:00
|
|
|
sub_client = MQTTClient()
|
2021-03-14 20:44:41 +00:00
|
|
|
await sub_client.connect("mqtt://127.0.0.1", cleansession=False)
|
2021-03-14 21:16:51 +00:00
|
|
|
ret = await sub_client.subscribe(
|
2024-12-21 10:52:26 +00:00
|
|
|
[("/qos0", QOS_0), ("/qos1", QOS_1), ("/qos2", QOS_2)],
|
2021-03-14 21:16:51 +00:00
|
|
|
)
|
2021-03-06 16:01:28 +00:00
|
|
|
assert ret == [QOS_0, QOS_1, QOS_2]
|
|
|
|
await sub_client.disconnect()
|
|
|
|
await asyncio.sleep(0.1)
|
|
|
|
|
2021-03-14 20:44:41 +00:00
|
|
|
await _client_publish("/qos0", b"data", QOS_0, retain=True)
|
|
|
|
await _client_publish("/qos1", b"data", QOS_1, retain=True)
|
|
|
|
await _client_publish("/qos2", b"data", QOS_2, retain=True)
|
2021-03-06 16:01:28 +00:00
|
|
|
await sub_client.reconnect()
|
|
|
|
for qos in [QOS_0, QOS_1, QOS_2]:
|
2024-12-21 10:52:26 +00:00
|
|
|
log.debug(f"TEST QOS: {qos}")
|
2021-03-06 16:01:28 +00:00
|
|
|
message = await sub_client.deliver_message()
|
2024-12-21 10:52:26 +00:00
|
|
|
log.debug(f"Message: {message.publish_packet if message else None!r}")
|
2021-03-06 16:01:28 +00:00
|
|
|
assert message is not None
|
2024-12-21 10:52:26 +00:00
|
|
|
assert message.topic == f"/qos{qos}"
|
2021-03-14 20:44:41 +00:00
|
|
|
assert message.data == b"data"
|
2021-03-06 16:01:28 +00:00
|
|
|
assert message.qos == qos
|
|
|
|
await sub_client.disconnect()
|
|
|
|
await asyncio.sleep(0.1)
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.asyncio
|
2024-12-21 10:52:26 +00:00
|
|
|
async def _client_publish(topic, data, qos, retain=False) -> int | OutgoingApplicationMessage:
|
2021-03-06 16:01:28 +00:00
|
|
|
pub_client = MQTTClient()
|
2024-12-21 10:52:26 +00:00
|
|
|
ret: int | OutgoingApplicationMessage = await pub_client.connect("mqtt://127.0.0.1/")
|
2021-03-06 16:01:28 +00:00
|
|
|
assert ret == 0
|
|
|
|
ret = await pub_client.publish(topic, data, qos, retain)
|
|
|
|
await pub_client.disconnect()
|
|
|
|
return ret
|
2020-10-08 18:58:26 +00:00
|
|
|
|
|
|
|
|
|
|
|
def test_matches_multi_level_wildcard(broker):
|
|
|
|
test_filter = "sport/tennis/player1/#"
|
|
|
|
|
|
|
|
for bad_topic in [
|
|
|
|
"sport/tennis",
|
|
|
|
"sport/tennis/",
|
|
|
|
]:
|
2025-04-06 17:03:30 +00:00
|
|
|
assert not broker._matches(bad_topic, test_filter)
|
2020-10-08 18:58:26 +00:00
|
|
|
|
|
|
|
for good_topic in [
|
|
|
|
"sport/tennis/player1",
|
|
|
|
"sport/tennis/player1/",
|
|
|
|
"sport/tennis/player1/ranking",
|
|
|
|
"sport/tennis/player1/score/wimbledon",
|
|
|
|
]:
|
2025-04-06 17:03:30 +00:00
|
|
|
assert broker._matches(good_topic, test_filter)
|
2020-10-08 18:58:26 +00:00
|
|
|
|
|
|
|
|
|
|
|
def test_matches_single_level_wildcard(broker):
|
|
|
|
test_filter = "sport/tennis/+"
|
|
|
|
|
|
|
|
for bad_topic in [
|
|
|
|
"sport/tennis",
|
|
|
|
"sport/tennis/player1/",
|
|
|
|
"sport/tennis/player1/ranking",
|
|
|
|
]:
|
2025-04-06 17:03:30 +00:00
|
|
|
assert not broker._matches(bad_topic, test_filter)
|
2020-10-08 18:58:26 +00:00
|
|
|
|
|
|
|
for good_topic in [
|
|
|
|
"sport/tennis/",
|
|
|
|
"sport/tennis/player1",
|
|
|
|
"sport/tennis/player2",
|
|
|
|
]:
|
2025-04-06 17:03:30 +00:00
|
|
|
assert broker._matches(good_topic, test_filter)
|
2023-02-07 12:44:33 +00:00
|
|
|
|
|
|
|
|
2025-04-06 17:03:30 +00:00
|
|
|
@pytest.mark.asyncio
|
|
|
|
async def test_broker_broadcast_cancellation(broker):
|
|
|
|
topic = "test"
|
|
|
|
data = b"data"
|
|
|
|
qos = QOS_0
|
2023-02-07 12:44:33 +00:00
|
|
|
|
2025-04-06 17:03:30 +00:00
|
|
|
sub_client = MQTTClient()
|
|
|
|
await sub_client.connect("mqtt://127.0.0.1")
|
|
|
|
await sub_client.subscribe([(topic, qos)])
|
2024-12-19 19:34:09 +00:00
|
|
|
|
2025-04-06 17:03:30 +00:00
|
|
|
with patch.object(BrokerProtocolHandler, "mqtt_publish", side_effect=asyncio.CancelledError) as mocked_mqtt_publish:
|
|
|
|
await _client_publish(topic, data, qos)
|
2024-12-19 19:34:09 +00:00
|
|
|
|
2025-04-06 17:03:30 +00:00
|
|
|
# Second publish triggers the awaiting of first `mqtt_publish` task
|
|
|
|
await _client_publish(topic, data, qos)
|
|
|
|
await asyncio.sleep(0.01)
|
2024-12-19 19:34:09 +00:00
|
|
|
|
2025-04-06 17:03:30 +00:00
|
|
|
mocked_mqtt_publish.assert_awaited()
|
2024-12-19 19:34:09 +00:00
|
|
|
|
2025-04-06 17:03:30 +00:00
|
|
|
# Ensure broadcast loop is still functional and can deliver the message
|
|
|
|
await _client_publish(topic, data, qos)
|
|
|
|
message = await asyncio.wait_for(sub_client.deliver_message(), timeout=1)
|
|
|
|
assert message
|
2025-06-09 02:42:49 +00:00
|
|
|
|
|
|
|
|
|
|
|
def test_broker_socket_open_close(broker):
|
|
|
|
|
|
|
|
# check that https://github.com/Yakifo/amqtt/issues/86 is fixed
|
|
|
|
|
|
|
|
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
|
|
server = ('localhost', 1883)
|
|
|
|
sock.connect(server)
|
|
|
|
sock.close()
|
|
|
|
|