Add unit tests #39

Merged
bmartin5692 merged 9 commits from add-tests into master 2019-06-05 05:51:23 +02:00
2 changed files with 155 additions and 103 deletions
Showing only changes of commit 3060e7db20 - Show all commits

View file

@ -402,22 +402,20 @@ class XMPPAsyncClient:
if self.TLSUpgraded == False:
# With STARTTLS #https://xmpp.org/rfcs/rfc3920.html
self.send(
'<stream:features><starttls xmlns="urn:ietf:params:xml:ns:xmpp-tls"><required/></starttls><auth xmlns="http://jabber.org/features/iq-auth"/><mechanisms xmlns="urn:ietf:params:xml:ns:xmpp-sasl"><mechanism>PLAIN</mechanism></mechanisms></stream:features>'
'<stream:features><starttls xmlns="urn:ietf:params:xml:ns:xmpp-tls"><required/></starttls><mechanisms xmlns="urn:ietf:params:xml:ns:xmpp-sasl"><mechanism>PLAIN</mechanism></mechanisms></stream:features>'
)
else:
# Already using TLS send authentication support for iq-auth (fallback) and SASL
# Already using TLS send authentication support for SASL
self.send(
'<stream:features><auth xmlns="http://jabber.org/features/iq-auth"/><mechanisms xmlns="urn:ietf:params:xml:ns:xmpp-sasl"><mechanism>PLAIN</mechanism></mechanisms></stream:features>'
'<stream:features><mechanisms xmlns="urn:ietf:params:xml:ns:xmpp-sasl"><mechanism>PLAIN</mechanism></mechanisms></stream:features>'
)
else:
self.send("</stream>")
else:
if "jabber:iq:auth" in xml.tag: # Handle iq-auth
self._handle_iq_auth(xml)
elif (
if (
"urn:ietf:params:xml:ns:xmpp-sasl" in xml.tag
): # Handle SASL Auth
self._handle_sasl_auth(xml)
@ -486,98 +484,6 @@ class XMPPAsyncClient:
except Exception as e:
xmppserverlog.exception("{}".format(e))
def _handle_iq_auth(self, data):
try:
xml = ET.fromstring(data.decode("utf-8"))
ctl = xml[0][0]
xmppserverlog.info("IQ AUTH XML: {}".format(xml))
# Received username and auth tag, send username/password requirement
if (
xml.get("type") == "get"
and "auth}username" in ctl.tag
and self.type == self.UNKNOWN
):
self.send(
'<iq type="result" id="{}"><query xmlns="jabber:iq:auth"><username/><password/></query></iq>'.format(
xml.get("id")
)
)
# Received username, password, resource - Handle auth here and return pass or fail
if (
xml.get("type") == "set"
and "auth}username" in ctl.tag
and self.type == self.UNKNOWN
):
xmlauth = xml[0].getchildren()
# uid = ""
password = ""
authcode = ""
resource = ""
for aitem in xmlauth:
if "username" in aitem.tag:
self.uid = aitem.text
elif "password" in aitem.tag:
password = aitem.text.split("/")[2]
authcode = password
elif "resource" in aitem.tag:
self.clientresource = aitem.text
resource = self.clientresource
if self.devclass: # if there is a devclass it is a bot
bumper.bot_add("", self.uid, "", resource, "eco-legacy")
xmppserverlog.debug("bot authenticated {}".format(self.uid))
# Client authenticated, move to next state
self._set_state("INIT")
# Successful auth
self.send('<iq type="result" id="{}"/>'.format(xml.get("id")))
else:
auth = False
if bumper.check_authcode(self.uid, authcode):
auth = True
elif bumper.use_auth == False:
auth = True
if auth:
bumper.client_add(self.uid, "bumper", self.clientresource)
xmppserverlog.debug("client authenticated {}".format(self.uid))
# Client authenticated, move to next state
self._set_state("INIT")
# Successful auth
self.send('<iq type="result" id="{}"/>'.format(xml.get("id")))
else:
# Failed auth
self.send(
'<iq type="error" id="{}"><error code="401" type="auth"><not-authorized xmlns="urn:ietf:params:xml:ns:xmpp-stanzas"/></error></iq>'.format(
xml.get("id")
)
)
except ET.ParseError as e:
if "no element found" in e.msg:
xmppserverlog.debug(
"xml parse error - {} - {}".format(data.decode("utf-8"), e)
)
elif "not well-formed (invalid token)" in e.msg:
xmppserverlog.debug(
"xml parse error - {} - {}".format(data.decode("utf-8"), e)
)
else:
xmppserverlog.debug(
"xml parse error - {} - {}".format(data.decode("utf-8"), e)
)
except Exception as e:
xmppserverlog.exception("{}".format(e))
def _handle_sasl_auth(self, xml):
try:

View file

@ -41,7 +41,7 @@ async def test_client_connect_no_starttls(*args, **kwargs):
# Server tells client available features
assert (
mock_send.mock_calls[1].args[0]
== '<stream:features><starttls xmlns="urn:ietf:params:xml:ns:xmpp-tls"><required/></starttls><auth xmlns="http://jabber.org/features/iq-auth"/><mechanisms xmlns="urn:ietf:params:xml:ns:xmpp-sasl"><mechanism>PLAIN</mechanism></mechanisms></stream:features>'
== '<stream:features><starttls xmlns="urn:ietf:params:xml:ns:xmpp-tls"><required/></starttls><mechanisms xmlns="urn:ietf:params:xml:ns:xmpp-sasl"><mechanism>PLAIN</mechanism></mechanisms></stream:features>'
)
# Reset mock calls
@ -60,6 +60,38 @@ async def test_client_connect_no_starttls(*args, **kwargs):
assert xmppclient.state == xmppclient.INIT # Client moved to INIT state
async def test_client_end_stream(*args, **kwargs):
test_transport = asyncio.Transport()
test_transport.get_extra_info = mock.Mock(return_value=mock_transport_extra_info())
test_transport.write = mock.Mock(return_value=return_send_data)
xmppclient = bumper.xmppserver.XMPPAsyncClient(test_transport)
xmppclient.state = xmppclient.CONNECT # Set client state to CONNECT
mock_send = xmppclient.send = mock.Mock(side_effect=return_send_data)
# Send end stream from "client"
test_data = "</stream:stream>".encode("utf-8")
xmppclient._parse_data(test_data)
# Expect 2 calls to send
assert mock_send.call_count == 1
# Server opens stream
assert mock_send.mock_calls[0].args[0] == "</stream:stream>"
# Reset mock calls
mock_send.reset_mock()
# Send abnormal stream from "client"
test_data = "<badstr />".encode("utf-8")
xmppclient._parse_data(test_data)
# Reset mock calls
mock_send.reset_mock()
# Send blank from "client"
test_data = "".encode("utf-8")
xmppclient._parse_data(test_data)
async def test_client_connect_starttls_called(*args, **kwargs):
test_transport = asyncio.Transport()
test_transport.get_extra_info = mock.Mock(return_value=mock_transport_extra_info())
@ -84,7 +116,7 @@ async def test_client_connect_starttls_called(*args, **kwargs):
# Server tells client available features
assert (
mock_send.mock_calls[1].args[0]
== '<stream:features><starttls xmlns="urn:ietf:params:xml:ns:xmpp-tls"><required/></starttls><auth xmlns="http://jabber.org/features/iq-auth"/><mechanisms xmlns="urn:ietf:params:xml:ns:xmpp-sasl"><mechanism>PLAIN</mechanism></mechanisms></stream:features>'
== '<stream:features><starttls xmlns="urn:ietf:params:xml:ns:xmpp-tls"><required/></starttls><mechanisms xmlns="urn:ietf:params:xml:ns:xmpp-sasl"><mechanism>PLAIN</mechanism></mechanisms></stream:features>'
)
# Reset mock calls
@ -117,7 +149,7 @@ async def test_client_connect_starttls_called(*args, **kwargs):
# Server tells client available features (without STARTTLS)
assert (
mock_send.mock_calls[1].args[0]
== '<stream:features><auth xmlns="http://jabber.org/features/iq-auth"/><mechanisms xmlns="urn:ietf:params:xml:ns:xmpp-sasl"><mechanism>PLAIN</mechanism></mechanisms></stream:features>'
== '<stream:features><mechanisms xmlns="urn:ietf:params:xml:ns:xmpp-sasl"><mechanism>PLAIN</mechanism></mechanisms></stream:features>'
)
# Reset mock calls
mock_send.reset_mock()
@ -135,6 +167,80 @@ async def test_client_connect_starttls_called(*args, **kwargs):
assert xmppclient.state == xmppclient.INIT # Client moved to INIT state
async def test_client_init(*args, **kwargs):
test_transport = asyncio.Transport()
test_transport.get_extra_info = mock.Mock(return_value=mock_transport_extra_info())
test_transport.write = mock.Mock(return_value=return_send_data)
xmppclient = bumper.xmppserver.XMPPAsyncClient(test_transport)
xmppclient.state = xmppclient.INIT # Set client state to INIT
xmppclient.uid = "fuid_tmpuser"
xmppclient.resource = "IOSF53D07BA"
xmppclient.bumper_jid = "fuid_tmpuser@ecouser.net/IOSF53D07BA"
xmppclient.type = xmppclient.CONTROLLER
mock_send = xmppclient.send = mock.Mock(side_effect=return_send_data)
# Send connect stream from "client"
test_data = "<stream:stream xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams' version='1.0' to='ecouser.net'>".encode(
"utf-8"
)
xmppclient._parse_data(test_data)
# Expect 2 calls to send
assert mock_send.call_count == 2
# Server opens stream
assert (
mock_send.mock_calls[0].args[0]
== '<stream:stream xmlns:stream="http://etherx.jabber.org/streams" xmlns="jabber:client" version="1.0" id="1" from="ecouser.net">'
)
# Server tells client binds
assert (
mock_send.mock_calls[1].args[0]
== '<stream:features><bind xmlns="urn:ietf:params:xml:ns:xmpp-bind"/><session xmlns="urn:ietf:params:xml:ns:xmpp-session"/></stream:features>'
)
# Reset mock calls
mock_send.reset_mock()
# Send bind from "client"
test_data = '<iq type="set" id="5E9872D5-547E-49AF-AE51-9EFAA282F952"><bind xmlns="urn:ietf:params:xml:ns:xmpp-bind"><resource>IOSF53D07BA</resource></bind></iq>'.encode(
"utf-8"
)
xmppclient._parse_data(test_data)
assert (
mock_send.mock_calls[0].args[0]
== '<iq type="result" id="5E9872D5-547E-49AF-AE51-9EFAA282F952"><bind xmlns="urn:ietf:params:xml:ns:xmpp-bind"><jid>fuid_tmpuser@ecouser.net/IOSF53D07BA</jid></bind></iq>'
) # client successfully binded
assert xmppclient.state == xmppclient.BIND # client moved to BIND state
# Reset mock calls
mock_send.reset_mock()
# Send set session from client
test_data = '<iq type="set" id="FA1041E7-AA27-43DD-BAA3-64DE2DE56AA3"><session xmlns="urn:ietf:params:xml:ns:xmpp-session"/></iq>'.encode(
"utf-8"
)
xmppclient._parse_data(test_data)
assert xmppclient.state == xmppclient.READY # client moved to READY state
assert (
mock_send.mock_calls[0].args[0]
== '<iq type="result" id="FA1041E7-AA27-43DD-BAA3-64DE2DE56AA3" />'
) # client ready
# Reset mock calls
mock_send.reset_mock()
# Send presense from client
test_data = '<presence type="available"/>'.encode("utf-8")
xmppclient._parse_data(test_data)
assert (
mock_send.mock_calls[0].args[0]
== '<presence to="fuid_tmpuser@ecouser.net/IOSF53D07BA"> dummy </presence>'
) # client presence - dummy response
async def test_bot_connect(*args, **kwargs):
test_transport = asyncio.Transport()
test_transport.get_extra_info = mock.Mock(return_value=mock_transport_extra_info())
@ -159,7 +265,7 @@ async def test_bot_connect(*args, **kwargs):
# Server tells client available features
assert (
mock_send.mock_calls[1].args[0]
== '<stream:features><starttls xmlns="urn:ietf:params:xml:ns:xmpp-tls"><required/></starttls><auth xmlns="http://jabber.org/features/iq-auth"/><mechanisms xmlns="urn:ietf:params:xml:ns:xmpp-sasl"><mechanism>PLAIN</mechanism></mechanisms></stream:features>'
== '<stream:features><starttls xmlns="urn:ietf:params:xml:ns:xmpp-tls"><required/></starttls><mechanisms xmlns="urn:ietf:params:xml:ns:xmpp-sasl"><mechanism>PLAIN</mechanism></mechanisms></stream:features>'
)
# Reset mock calls
@ -187,6 +293,7 @@ async def test_bot_init(*args, **kwargs):
xmppclient.state = xmppclient.INIT # Set client state to INIT
xmppclient.uid = "E0000000000000001234"
xmppclient.devclass = "159"
xmppclient.type = xmppclient.BOT
mock_send = xmppclient.send = mock.Mock(side_effect=return_send_data)
# Send connect stream from "bot"
@ -302,12 +409,22 @@ async def test_ping_client_to_client(*args, **kwargs):
)
xmppclient._parse_data(test_data)
assert (
mock_send2.mock_calls[0].args[0]
== '<iq from="E0000000000000001234@159.ecorobot.net/atom" id="104934615" to="fuid_tmpuser@ecouser.net/IOSF53D07BA" type="get"><ping xmlns="urn:xmpp:ping" /></iq>'
) # ping response
# Ping response from bot to user
test_data = "<iq type='result' to='E0000000000000001234@159.ecorobot.net/atom' id='104934615'/>".encode(
"utf-8"
)
xmppclient2._parse_data(test_data)
assert (
mock_send.mock_calls[0].args[0]
== '<iq from="fuid_tmpuser@ecouser.net/IOSF53D07BA" id="104934615" to="E0000000000000001234@159.ecorobot.net/atom" type="result" />'
) # ping response
async def test_client_send_iq(*args, **kwargs):
test_transport = asyncio.Transport()
@ -341,7 +458,7 @@ async def test_client_send_iq(*args, **kwargs):
assert (
mock_send.mock_calls[0].args[0]
== '<iq type="error" id="EE0XQ-2"><error type="cancel" code="501"><feature-not-implemented xmlns="urn:ietf:params:xml:ns:xmpp-stanzas"/></error></iq>'
) # ping response
) # feature not implemented response
# Reset mock calls
mock_send.reset_mock()
@ -373,3 +490,32 @@ async def test_client_send_iq(*args, **kwargs):
# Reset mock calls
mock_send.reset_mock()
# Bot result
test_data = "<iq type='result' from='E0000000000000001234@159.ecorobot.net/atom' to='ecouser.net' id='s2c1'/>".encode(
"utf-8"
)
xmppclient2._parse_data(test_data)
assert (
mock_send.mock_calls[0].args[0]
== '<iq from="E0000000000000001234@159.ecorobot.net/atom" id="s2c1" to="ecouser.net" type="result" />'
) # result sent to ecouser.net
# Reset mock calls
mock_send.reset_mock()
# Bot iq set
test_data = "<iq to='fuid_tmpuser@ecouser.net/IOSF53D07BA' type='set' id='2700'><query xmlns='com:ctl'><ctl td='BatteryInfo'><battery power='100'/></ctl></query></iq>".encode(
"utf-8"
)
xmppclient2._parse_data(test_data)
assert (
mock_send.mock_calls[0].args[0]
== '<iq from="E0000000000000001234@159.ecorobot.net/atom" id="2700" to="fuid_tmpuser@ecouser.net/IOSF53D07BA" type="set"><query xmlns="com:ctl"><ctl td="BatteryInfo"><battery power="100" /></ctl></query></iq>'
) # result sent to ecouser.net
# Reset mock calls
mock_send.reset_mock()