improve code quality for mqtt server

This commit is contained in:
Robert Resch 2022-03-03 14:10:36 +01:00
parent e65446515d
commit 1e2d87efda
7 changed files with 145 additions and 128 deletions

View file

@ -266,7 +266,7 @@ def get_disconnected_xmpp_clients():
return clients.search(Client.xmpp_connection == False) return clients.search(Client.xmpp_connection == False)
def check_authcode(uid, authcode): def check_authcode(uid: str, authcode: str) -> bool:
bumperlog.debug(f"Checking for authcode: {authcode}") bumperlog.debug(f"Checking for authcode: {authcode}")
tokens = db_get().table("tokens") tokens = db_get().table("tokens")
tmpauth = tokens.get( tmpauth = tokens.get(
@ -323,7 +323,7 @@ def revoke_expired_tokens():
db_get().table("tokens").remove(doc_ids=[i.doc_id]) db_get().table("tokens").remove(doc_ids=[i.doc_id])
def bot_add(sn, did, devclass, resource, company): def bot_add(sn: str, did: str, devclass: str, resource: str, company: str) -> None:
newbot = VacBotDevice() newbot = VacBotDevice()
newbot.did = did newbot.did = did
newbot.name = sn newbot.name = sn
@ -347,11 +347,12 @@ def bot_remove(did):
bots.remove(doc_ids=[bot.doc_id]) bots.remove(doc_ids=[bot.doc_id])
def bot_get(did): def bot_get(did: str):
bots = db_get().table("bots") bots = db_get().table("bots")
Bot = Query() Bot = Query()
return bots.get(Bot.did == did) return bots.get(Bot.did == did)
def bot_full_upsert(vacbot): def bot_full_upsert(vacbot):
bots = db_get().table("bots") bots = db_get().table("bots")
Bot = Query() Bot = Query()
@ -367,7 +368,7 @@ def bot_set_nick(did, nick):
bots.upsert({"nick": nick}, Bot.did == did) bots.upsert({"nick": nick}, Bot.did == did)
def bot_set_mqtt(did, mqtt): def bot_set_mqtt(did: str, mqtt: bool) -> None:
bots = db_get().table("bots") bots = db_get().table("bots")
Bot = Query() Bot = Query()
bots.upsert({"mqtt_connection": mqtt}, Bot.did == did) bots.upsert({"mqtt_connection": mqtt}, Bot.did == did)
@ -379,7 +380,7 @@ def bot_set_xmpp(did, xmpp):
bots.upsert({"xmpp_connection": xmpp}, Bot.did == did) bots.upsert({"xmpp_connection": xmpp}, Bot.did == did)
def client_add(userid, realm, resource): def client_add(userid: str, realm: str, resource: str) -> None:
newclient = VacBotClient() newclient = VacBotClient()
newclient.userid = userid newclient.userid = userid
newclient.realm = realm newclient.realm = realm
@ -398,7 +399,7 @@ def client_remove(resource):
clients.remove(doc_ids=[client.doc_id]) clients.remove(doc_ids=[client.doc_id])
def client_get(resource): def client_get(resource: str):
clients = db_get().table("clients") clients = db_get().table("clients")
Client = Query() Client = Query()
return clients.get(Client.resource == resource) return clients.get(Client.resource == resource)
@ -410,7 +411,7 @@ def client_full_upsert(client):
clients.upsert(client, Client.resource == client["resource"]) clients.upsert(client, Client.resource == client["resource"])
def client_set_mqtt(resource, mqtt): def client_set_mqtt(resource: str, mqtt: bool) -> None:
clients = db_get().table("clients") clients = db_get().table("clients")
Client = Query() Client = Query()
clients.upsert({"mqtt_connection": mqtt}, Client.resource == resource) clients.upsert({"mqtt_connection": mqtt}, Client.resource == resource)

View file

@ -3,7 +3,7 @@ import copy
import json import json
import uuid import uuid
from datetime import datetime, timedelta from datetime import datetime, timedelta
from typing import Dict, Any from typing import Any
import bumper import bumper
@ -149,7 +149,9 @@ def include_EcoVacsHomeProducts_info(bot) -> dict[str, Any]:
# } # }
# todo refactor it # todo refactor it
result["status"] = 1 if bot["mqtt_connection"] or bot["xmpp_connection"] else 0 result["status"] = (
1 if bot["mqtt_connection"] or bot["xmpp_connection"] else 0
)
# mqtt_connection is not always set correctly, therefore workaround until fixed properly # mqtt_connection is not always set correctly, therefore workaround until fixed properly
for session in bumper.mqtt_server.broker._sessions: for session in bumper.mqtt_server.broker._sessions:
@ -159,6 +161,7 @@ def include_EcoVacsHomeProducts_info(bot) -> dict[str, Any]:
return result return result
# EcoVacs Home Product IOT Map - 2021-04-15 # EcoVacs Home Product IOT Map - 2021-04-15
# https://portal-ww.ecouser.net/api/pim/product/getProductIotMap # https://portal-ww.ecouser.net/api/pim/product/getProductIotMap
EcoVacsHomeProducts = [ EcoVacsHomeProducts = [

View file

@ -1,10 +1,11 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
"""Mqtt module."""
import asyncio import asyncio
import json import json
import os import os
from asyncio import Task from asyncio import Task
from typing import Any, Dict, MutableMapping, Optional, Union from typing import Any, MutableMapping, Optional, Union
import amqtt import amqtt
import pkg_resources import pkg_resources
@ -24,24 +25,30 @@ boterrorlog = get_logger("boterror")
class CommandDto: class CommandDto:
"""Command DTO."""
def __init__(self, payload_type: str) -> None: def __init__(self, payload_type: str) -> None:
self._payload_type = payload_type self._payload_type = payload_type
self._event = asyncio.Event() self._event = asyncio.Event()
self._response: Union[str, bytes] self._response: Union[str, bytes]
async def wait_for_response(self) -> Union[str, dict[str, Any]]: async def wait_for_response(self) -> Union[str, dict[str, Any]]:
"""Wait for the response to be received."""
await self._event.wait() await self._event.wait()
if self._payload_type == "j": if self._payload_type == "j":
return json.loads(self._response) return json.loads(self._response) # type:ignore[no-any-return]
else:
return str(self._response) return str(self._response)
def add_response(self, response: Union[str, bytes]) -> None: def add_response(self, response: Union[str, bytes]) -> None:
"""Add received response."""
self._response = response self._response = response
self._event.set() self._event.set()
class MQTTHelperBot: class MQTTHelperBot:
"""Helper bot, which converts commands from the rest api to mqtt ones."""
def __init__(self, host: str, port: int, timeout: float = 60): def __init__(self, host: str, port: int, timeout: float = 60):
self._commands: MutableMapping[str, CommandDto] = TTLCache( self._commands: MutableMapping[str, CommandDto] = TTLCache(
maxsize=timeout * 60, ttl=timeout * 1.1 maxsize=timeout * 60, ttl=timeout * 1.1
@ -62,6 +69,7 @@ class MQTTHelperBot:
) )
async def start(self) -> None: async def start(self) -> None:
"""Connect and subscribe helper bot."""
try: try:
if self._client is None: if self._client is None:
self._client = MQTTClient( self._client = MQTTClient(
@ -80,16 +88,19 @@ class MQTTHelperBot:
self._new_messages_task = asyncio.create_task( self._new_messages_task = asyncio.create_task(
self._check_for_new_messages() self._check_for_new_messages()
) )
except Exception as e: except Exception:
helperbotlog.exception(f"{e}") mqttserverlog.exception(
raise e "An exception occurred during startup", exc_info=True
)
raise
async def _check_for_new_messages(self): async def _check_for_new_messages(self) -> None:
assert self._client is not None
while True: while True:
try: try:
message: IncomingApplicationMessage = ( message: Optional[
await self._client.deliver_message() IncomingApplicationMessage
) ] = await self._client.deliver_message()
if message is not None: if message is not None:
topic_split = str(message.topic).split("/") topic_split = str(message.topic).split("/")
data_decoded = str(message.data.decode("utf-8")) data_decoded = str(message.data.decode("utf-8"))
@ -97,7 +108,7 @@ class MQTTHelperBot:
self._commands[topic_split[10]].add_response(data_decoded) self._commands[topic_split[10]].add_response(data_decoded)
except asyncio.CancelledError: except asyncio.CancelledError:
pass pass
except Exception as ex: # pylint: disable=broad-except except Exception: # pylint: disable=broad-except
helperbotlog.error( helperbotlog.error(
"An exception occurred during handling new messages", exc_info=True "An exception occurred during handling new messages", exc_info=True
) )
@ -112,10 +123,10 @@ class MQTTHelperBot:
return {"id": request_id, "ret": "ok", "resp": payload} return {"id": request_id, "ret": "ok", "resp": payload}
except asyncio.TimeoutError: except asyncio.TimeoutError:
helperbotlog.debug("wait_for_resp timeout reached") helperbotlog.debug("wait_for_resp timeout reached")
except asyncio.CancelledError as e: except asyncio.CancelledError:
helperbotlog.debug("wait_for_resp cancelled by asyncio", e, exc_info=True) helperbotlog.debug("wait_for_resp cancelled by asyncio", exc_info=True)
except Exception as e: except Exception: # pylint: disable=broad-except
helperbotlog.exception(f"{e}") helperbotlog.exception("An unknown error occurred", exc_info=True)
return { return {
"id": request_id, "id": request_id,
@ -127,19 +138,13 @@ class MQTTHelperBot:
async def send_command( async def send_command(
self, cmdjson: dict[str, Any], request_id: str self, cmdjson: dict[str, Any], request_id: str
) -> dict[str, Any]: ) -> dict[str, Any]:
"""Send command over MQTT."""
if self._client is None: if self._client is None:
await self.start() await self.start()
assert self._client is not None assert self._client is not None
try: try:
topic = "iot/p2p/{}/helperbot/bumper/helperbot/{}/{}/{}/q/{}/{}".format( topic = f"iot/p2p/{cmdjson['cmdName']}/helperbot/bumper/helperbot/{cmdjson['toId']}/{cmdjson['toType']}/{cmdjson['toRes']}/q/{request_id}/{cmdjson['payloadType']}"
cmdjson["cmdName"],
cmdjson["toId"],
cmdjson["toType"],
cmdjson["toRes"],
request_id,
cmdjson["payloadType"],
)
command_dto = CommandDto(cmdjson["payloadType"]) command_dto = CommandDto(cmdjson["payloadType"])
self._commands[request_id] = command_dto self._commands[request_id] = command_dto
@ -152,8 +157,8 @@ class MQTTHelperBot:
resp = await self._wait_for_resp(command_dto, request_id) resp = await self._wait_for_resp(command_dto, request_id)
return resp return resp
except Exception as e: except Exception: # pylint: disable=broad-except
helperbotlog.exception(f"{e}") helperbotlog.exception("Could not send command.", exc_info=True)
return { return {
"id": request_id, "id": request_id,
"errno": 500, "errno": 500,
@ -163,7 +168,8 @@ class MQTTHelperBot:
finally: finally:
self._commands.pop(request_id, None) self._commands.pop(request_id, None)
async def disconnect(self): async def disconnect(self) -> None:
"""Disconnect client."""
if self._new_messages_task is not None: if self._new_messages_task is not None:
self._new_messages_task.cancel() self._new_messages_task.cancel()
self._new_messages_task = None self._new_messages_task = None
@ -174,6 +180,8 @@ class MQTTHelperBot:
class MQTTServer: class MQTTServer:
"""Mqtt server."""
def __init__(self, host: str, port: int, **kwargs: dict[str, Any]) -> None: def __init__(self, host: str, port: int, **kwargs: dict[str, Any]) -> None:
try: try:
self._host = host self._host = host
@ -189,9 +197,9 @@ class MQTTServer:
# The below adds a plugin to the amqtt.broker.plugins without having to futz with setup.py # The below adds a plugin to the amqtt.broker.plugins without having to futz with setup.py
distribution = pkg_resources.Distribution("amqtt.broker.plugins") distribution = pkg_resources.Distribution("amqtt.broker.plugins")
bumper_plugin = pkg_resources.EntryPoint.parse( bumper_plugin = pkg_resources.EntryPoint.parse(
"bumper = bumper.mqttserver:BumperMQTTServer_Plugin", dist=distribution "bumper = bumper.mqttserver:BumperMQTTServerPlugin", dist=distribution
) )
distribution._ep_map = {"amqtt.broker.plugins": {"bumper": bumper_plugin}} distribution._ep_map = {"amqtt.broker.plugins": {"bumper": bumper_plugin}} # type: ignore[attr-defined]
pkg_resources.working_set.add(distribution) pkg_resources.working_set.add(distribution)
# Initialize bot server # Initialize bot server
@ -221,8 +229,11 @@ class MQTTServer:
self._broker = amqtt.broker.Broker(config=config) self._broker = amqtt.broker.Broker(config=config)
except Exception as e: except Exception:
mqttserverlog.exception(f"{e}") mqttserverlog.exception(
"An exception occurred during initialize", exc_info=True
)
raise
@property @property
def state(self) -> Broker.states: def state(self) -> Broker.states:
@ -231,22 +242,32 @@ class MQTTServer:
@property @property
def broker(self) -> Broker: def broker(self) -> Broker:
"""Get MQTT broker."""
return self._broker return self._broker
async def start(self) -> None: async def start(self) -> None:
mqttserverlog.info(f"Starting MQTT Server at {self._host}:{self._port}") """Start MQTT server."""
mqttserverlog.info("Starting MQTT Server at %s:%d", self._host, self._port)
try: try:
await self._broker.start() await self._broker.start()
except Exception as e: except Exception:
mqttserverlog.exception(f"{e}") mqttserverlog.exception(
raise e "An exception occurred during startup", exc_info=True
)
raise
async def shutdown(self): async def shutdown(self) -> None:
"""Shutdown server."""
await self._broker.shutdown() await self._broker.shutdown()
class BumperMQTTServer_Plugin: def _log__helperbot_message(custom_log_message: str, topic: str, data: str) -> None:
helperbotlog.debug("%s - Topic: %s - Message: %s", custom_log_message, topic, data)
class BumperMQTTServerPlugin:
"""MQTT Server plugin which handles the authentication."""
def __init__(self, context: BrokerContext) -> None: def __init__(self, context: BrokerContext) -> None:
self.context = context self.context = context
try: try:
@ -257,11 +278,14 @@ class BumperMQTTServer_Plugin:
self.context.logger.warning( self.context.logger.warning(
"'bumper' section not found in context configuration" "'bumper' section not found in context configuration"
) )
except Exception as e: except Exception:
mqttserverlog.exception(f"{e}") mqttserverlog.exception(
"An exception occurred during plugin initialization", exc_info=True
)
raise
async def authenticate(self, session: Session, **kwargs: dict[str, Any]) -> bool: async def authenticate(self, session: Session, **kwargs: dict[str, Any]) -> bool:
authenticated = False """Authenticate session."""
username = session.username username = session.username
password = session.password password = session.password
client_id = session.client_id client_id = session.client_id
@ -281,84 +305,75 @@ class BumperMQTTServer_Plugin:
"eco-ng", "eco-ng",
) )
mqttserverlog.info( mqttserverlog.info(
f"Bumper Authentication Success - Bot - SN: {username} - DID: {didsplit[0]}" "Bumper Authentication Success - Bot - SN: %s - DID: %s - Class: %s",
f" - Class: {tmpbotdetail[0]}" username,
didsplit[0],
tmpbotdetail[0],
) )
authenticated = True return True
else:
tmpclientdetail = str(didsplit[1]).split("/")
userid = didsplit[0]
realm = tmpclientdetail[0]
resource = tmpclientdetail[1]
if userid == "helperbot": tmpclientdetail = str(didsplit[1]).split("/")
mqttserverlog.info( userid = didsplit[0]
f"Bumper Authentication Success - Helperbot: {client_id}" realm = tmpclientdetail[0]
) resource = tmpclientdetail[1]
authenticated = True
elif ( if userid == "helperbot":
bumper.check_authcode(didsplit[0], password) mqttserverlog.info(
or not bumper.use_auth "Bumper Authentication Success - Helperbot: %s", client_id
): )
bumper.client_add(userid, realm, resource) return True
mqttserverlog.info( if bumper.check_authcode(didsplit[0], password) or not bumper.use_auth:
f"Bumper Authentication Success - Client - Username: {username} - " bumper.client_add(userid, realm, resource)
f"ClientID: {client_id}" mqttserverlog.info(
) "Bumper Authentication Success - Client - Username: %s - ClientID: %s",
authenticated = True username,
client_id,
)
return True
# Check for File Auth # Check for File Auth
if ( if username:
username and not authenticated # If there is a username and it isn't already authenticated
): # If there is a username and it isn't already authenticated password_hash = self._users.get(username, None)
hash = self._users.get(username, None) message_suffix = f"- Username: {username} - ClientID: {client_id}"
if hash: # If there is a matching entry in passwd, check hash if password_hash: # If there is a matching entry in passwd, check hash
authenticated = pwd_context.verify(password, hash) if pwd_context.verify(password, password_hash):
if authenticated:
mqttserverlog.info( mqttserverlog.info(
f"File Authentication Success - Username: {username} - ClientID: {client_id}" "File Authentication Success %s", message_suffix
)
else:
mqttserverlog.info(
f"File Authentication Failed - Username: {username} - ClientID: {client_id}"
) )
return True
mqttserverlog.info("File Authentication Failed %s", message_suffix)
else: else:
mqttserverlog.info( mqttserverlog.info(
f"File Authentication Failed - No Entry for Username: {username} - ClientID: {client_id}" "File Authentication Failed - No Entry %s", message_suffix
) )
except Exception as e: except Exception: # pylint: disable=broad-except
mqttserverlog.exception( mqttserverlog.exception(
"Session: {} - {}".format((kwargs.get("session", None)), e) "Session: %s", kwargs.get("session", ""), exc_info=True
) )
authenticated = False
# Check for allow anonymous # Check for allow anonymous
allow_anonymous = self.auth_config.get("allow-anonymous", True) if self.auth_config.get("allow-anonymous", True):
if ( message = f"Anonymous Authentication Success: config allows anonymous - Username: {username}"
allow_anonymous and not authenticated self.context.logger.debug(message)
): # If anonymous auth is allowed and it isn't already authenticated mqttserverlog.info(message)
authenticated = True return True
self.context.logger.debug(
f"Anonymous Authentication Success: config allows anonymous - Username: {username}"
)
mqttserverlog.info(
f"Anonymous Authentication Success: config allows anonymous - Username: {username}"
)
return authenticated return False
def _read_password_file(self) -> dict[str, str]: def _read_password_file(self) -> dict[str, str]:
password_file = self.auth_config.get("password-file", None) password_file = self.auth_config.get("password-file", None)
users: dict[str, str] = {} users: dict[str, str] = {}
if password_file: if password_file:
try: try:
with open(password_file) as f: with open(password_file, encoding="utf-8") as file:
self.context.logger.debug( self.context.logger.debug(
f"Reading user database from {password_file}" f"Reading user database from {password_file}"
) )
for l in f: for line in file:
line = l.strip() line = line.strip()
if not line.startswith("#"): # Allow comments in files if not line.startswith("#"): # Allow comments in files
(username, pwd_hash) = line.split(sep=":", maxsplit=3) (username, pwd_hash) = line.split(sep=":", maxsplit=3)
if username: if username:
@ -375,9 +390,12 @@ class BumperMQTTServer_Plugin:
return users return users
async def on_broker_client_connected(self, client_id: str) -> None: async def on_broker_client_connected(self, client_id: str) -> None:
"""On client connected."""
self._set_client_connected(client_id, True) self._set_client_connected(client_id, True)
def _set_client_connected(self, client_id: str, connected: bool) -> None: def _set_client_connected( # pylint: disable=no-self-use
self, client_id: str, connected: bool
) -> None:
didsplit = str(client_id).split("@") didsplit = str(client_id).split("@")
bot = bumper.bot_get(didsplit[0]) bot = bumper.bot_get(didsplit[0])
@ -390,36 +408,30 @@ class BumperMQTTServer_Plugin:
if client: if client:
bumper.client_set_mqtt(client["resource"], connected) bumper.client_set_mqtt(client["resource"], connected)
async def on_broker_message_received( async def on_broker_message_received( # pylint: disable=no-self-use
self, message: IncomingApplicationMessage, **kwargs: dict[str, Any] self, message: IncomingApplicationMessage, **_: dict[str, Any]
) -> None: ) -> None:
"""On message received."""
topic = message.topic topic = message.topic
topic_split = str(topic).split("/") topic_split = str(topic).split("/")
data_decoded = str(message.data.decode("utf-8")) data_decoded = str(message.data.decode("utf-8"))
if topic_split[6] == "helperbot": if topic_split[6] == "helperbot":
# Response to command # Response to command
helperbotlog.debug( _log__helperbot_message("Received Response", topic, data_decoded)
f"Received Response - Topic: {topic} - Message: {data_decoded}"
)
elif topic_split[3] == "helperbot": elif topic_split[3] == "helperbot":
# Helperbot sending command # Helperbot sending command
helperbotlog.debug( _log__helperbot_message("Send Command", topic, data_decoded)
f"Send Command - Topic: {topic} - Message: {data_decoded}"
)
elif topic_split[1] == "atr": elif topic_split[1] == "atr":
# Broadcast message received on atr # Broadcast message received on atr
if topic_split[2] == "errors": if topic_split[2] == "errors":
boterrorlog.error( boterrorlog.error(
f"Received Error - Topic: {topic} - Message: {data_decoded}" "Received Error - Topic: %s - Message: %s", topic, data_decoded
) )
else: else:
helperbotlog.debug( _log__helperbot_message("Received Broadcast", topic, data_decoded)
f"Received Broadcast - Topic: {topic} - Message: {data_decoded}"
)
else: else:
helperbotlog.debug( _log__helperbot_message("Received Message", topic, data_decoded)
f"Received Message - Topic: {topic} - Message: {data_decoded}"
)
async def on_broker_client_disconnected(self, client_id: str) -> None: async def on_broker_client_disconnected(self, client_id: str) -> None:
"""On client disconnect."""
self._set_client_connected(client_id, False) self._set_client_connected(client_id, False)

View file

@ -54,7 +54,8 @@ class portal_api_appsvr(plugins.ConfServerApp):
for bot in bots: for bot in bots:
if bot["class"] != "": if bot["class"] != "":
b = bumper.include_EcoVacsHomeProducts_info(bot) b = bumper.include_EcoVacsHomeProducts_info(bot)
if b is not None: # Happens if the bot isn't on the EcoVacs Home list # Happens if the bot isn't on the EcoVacs Home list
if b is not None:
botlist.append(b) botlist.append(b)
body = { body = {

View file

@ -1,6 +1,6 @@
aiohttp==3.8.1 aiohttp==3.8.1
aiohttp-jinja2==1.5 aiohttp-jinja2==1.5
cachetools==5.0.0
amqtt==0.10.0 amqtt==0.10.0
cachetools==5.0.0
Jinja2==3.0.3 Jinja2==3.0.3
tinydb==4.6.1 tinydb==4.6.1

View file

@ -376,6 +376,8 @@ async def test_mqttserver():
), ),
order_matters=False, order_matters=False,
) )
l.clear()
# no username in file # no username in file
await client.connect( await client.connect(
f"mqtts://test-client-noexist:notvalid!@{HOST}:{MQTT_PORT}/", f"mqtts://test-client-noexist:notvalid!@{HOST}:{MQTT_PORT}/",
@ -387,7 +389,7 @@ async def test_mqttserver():
( (
"mqttserver", "mqttserver",
"INFO", "INFO",
"File Authentication Failed - No Entry for Username: test-client-noexist - ClientID: test-file-auth", "File Authentication Failed - No Entry - Username: test-client-noexist - ClientID: test-file-auth",
), ),
order_matters=False, order_matters=False,
) )

View file

@ -199,7 +199,6 @@ async def test_client_connect_starttls_called():
assert xmppclient.state == xmppclient.INIT # Client moved to INIT state assert xmppclient.state == xmppclient.INIT # Client moved to INIT state
async def test_client_init(): async def test_client_init():
test_transport = mock.Mock() test_transport = mock.Mock()
test_transport.get_extra_info = mock.Mock(return_value=mock_transport_extra_info()) test_transport.get_extra_info = mock.Mock(return_value=mock_transport_extra_info())
@ -430,7 +429,7 @@ async def test_ping_client_to_client():
assert ( assert (
mock_send.mock_calls[0][1][0] mock_send.mock_calls[0][1][0]
== '<iq type="result" to="E0000000000000001234@159.ecorobot.net/atom" id="104934615" from="fuid_tmpuser@ecouser.net/IOSF53D07BA" />' == '<iq type="result" to="E0000000000000001234@159.ecorobot.net/atom" id="104934615" from="fuid_tmpuser@ecouser.net/IOSF53D07BA" />'
) # ping response ) # ping response
@ -534,9 +533,8 @@ async def test_client_send_iq():
# Bot "DorpError" to all # Bot "DorpError" to all
test_data = b"<iq to='rl.ecorobot.net' type='set' id='1234'><query xmlns='com:sf'><sf td='pub' t='log' ts='1559893796000' tp='p' k='DeviceAlert' v='DorpError' f='E0000000000000001234@159.ecorobot.net' g='fuid_tmpuser@ecouser.net'/></query></iq>" test_data = b"<iq to='rl.ecorobot.net' type='set' id='1234'><query xmlns='com:sf'><sf td='pub' t='log' ts='1559893796000' tp='p' k='DeviceAlert' v='DorpError' f='E0000000000000001234@159.ecorobot.net' g='fuid_tmpuser@ecouser.net'/></query></iq>"
xmppclient2._parse_data(test_data) xmppclient2._parse_data(test_data)
assert ( assert mock_send.mock_calls[0][1][0] == (
mock_send.mock_calls[0][1][0] '<iq xmlns="com:sf" to="rl.ecorobot.net" type="set" id="1234" from="E0000000000000001234@159.ecorobot.net/atom"><query xmlns="com:ctl"><sf td="pub" t="log" ts="1559893796000" tp="p" k="DeviceAlert" v="DorpError" f="E0000000000000001234@159.ecorobot.net" g="fuid_tmpuser@ecouser.net" /></query></iq>'
== ('<iq xmlns="com:sf" to="rl.ecorobot.net" type="set" id="1234" from="E0000000000000001234@159.ecorobot.net/atom"><query xmlns="com:ctl"><sf td="pub" t="log" ts="1559893796000" tp="p" k="DeviceAlert" v="DorpError" f="E0000000000000001234@159.ecorobot.net" g="fuid_tmpuser@ecouser.net" /></query></iq>')
) # result sent to ecouser.net ) # result sent to ecouser.net
# Reset mock calls # Reset mock calls