Merge branch 'first-integration-tests' into 'master'
First integration tests See merge request poezio/slixmpp!75
This commit is contained in:
commit
73cc2a4008
7 changed files with 269 additions and 1 deletions
|
@ -13,6 +13,21 @@ test:
|
|||
- pip3 install emoji aiohttp
|
||||
- ./run_tests.py
|
||||
|
||||
test_integration:
|
||||
stage: test
|
||||
tags:
|
||||
- docker
|
||||
image: ubuntu:latest
|
||||
only:
|
||||
variables:
|
||||
- $CI_ACCOUNT1
|
||||
- $CI_ACCOUNT2
|
||||
script:
|
||||
- apt update
|
||||
- apt install -y python3 python3-pip cython3 gpg
|
||||
- pip3 install emoji aiohttp aiodns
|
||||
- ./run_integration_tests.py
|
||||
|
||||
trigger_poezio:
|
||||
stage: trigger
|
||||
tags:
|
||||
|
|
0
itests/__init__.py
Normal file
0
itests/__init__.py
Normal file
28
itests/test_basic_connect_and_message.py
Normal file
28
itests/test_basic_connect_and_message.py
Normal file
|
@ -0,0 +1,28 @@
|
|||
import unittest
|
||||
from slixmpp.test.integration import SlixIntegration
|
||||
|
||||
|
||||
class TestConnect(SlixIntegration):
|
||||
async def asyncSetUp(self):
|
||||
await super().asyncSetUp()
|
||||
self.add_client(
|
||||
self.envjid('CI_ACCOUNT1'),
|
||||
self.envstr('CI_ACCOUNT1_PASSWORD'),
|
||||
)
|
||||
self.add_client(
|
||||
self.envjid('CI_ACCOUNT2'),
|
||||
self.envstr('CI_ACCOUNT2_PASSWORD'),
|
||||
)
|
||||
await self.connect_clients()
|
||||
|
||||
async def test_send_message(self):
|
||||
"""Make sure we can send and receive messages"""
|
||||
msg = self.clients[0].make_message(
|
||||
mto=self.clients[1].boundjid, mbody='Msg body',
|
||||
)
|
||||
msg.send()
|
||||
message = await self.clients[1].wait_until('message')
|
||||
self.assertEqual(message['body'], msg['body'])
|
||||
|
||||
|
||||
suite = unittest.TestLoader().loadTestsFromTestCase(TestConnect)
|
78
itests/test_muc.py
Normal file
78
itests/test_muc.py
Normal file
|
@ -0,0 +1,78 @@
|
|||
import asyncio
|
||||
import unittest
|
||||
from uuid import uuid4
|
||||
from slixmpp import JID
|
||||
from slixmpp.test.integration import SlixIntegration
|
||||
|
||||
UNIQUE = uuid4().hex
|
||||
|
||||
|
||||
class TestConnect(SlixIntegration):
|
||||
|
||||
async def asyncSetUp(self):
|
||||
self.mucserver = self.envjid('CI_MUC_SERVER')
|
||||
self.muc = JID('%s@%s' % (UNIQUE, self.mucserver))
|
||||
self.add_client(
|
||||
self.envjid('CI_ACCOUNT1'),
|
||||
self.envstr('CI_ACCOUNT1_PASSWORD'),
|
||||
)
|
||||
self.add_client(
|
||||
self.envjid('CI_ACCOUNT2'),
|
||||
self.envstr('CI_ACCOUNT2_PASSWORD'),
|
||||
)
|
||||
self.register_plugins(['xep_0045'])
|
||||
await self.connect_clients()
|
||||
|
||||
async def test_initial_join(self):
|
||||
"""Check that we can connect to a new muc"""
|
||||
self.clients[0]['xep_0045'].join_muc(self.muc, 'client1')
|
||||
presence = await self.clients[0].wait_until('muc::%s::got_online' % self.muc)
|
||||
self.assertEqual(presence['muc']['affiliation'], 'owner')
|
||||
|
||||
async def test_setup_muc(self):
|
||||
"""Check that sending the initial room config and affiliation list works"""
|
||||
self.clients[0]['xep_0045'].join_muc(self.muc, 'client1')
|
||||
presence = await self.clients[0].wait_until('muc::%s::got_online' % self.muc)
|
||||
self.assertEqual(presence['muc']['affiliation'], 'owner')
|
||||
# Send initial configuration
|
||||
config = await self.clients[0]['xep_0045'].get_room_config(self.muc)
|
||||
values = config.get_values()
|
||||
values['muc#roomconfig_persistentroom'] = False
|
||||
values['muc#roomconfig_membersonly'] = True
|
||||
config['values'] = values
|
||||
config.reply()
|
||||
config = await self.clients[0]['xep_0045'].set_room_config(self.muc, config)
|
||||
|
||||
# Send affiliation list including client 2
|
||||
await self.clients[0]['xep_0045'].send_affiliation_list(
|
||||
self.muc,
|
||||
[
|
||||
(self.clients[1].boundjid.bare, 'member'),
|
||||
],
|
||||
)
|
||||
|
||||
async def test_join_after_config(self):
|
||||
"""Join a room after being added to the affiliation list"""
|
||||
await self.test_setup_muc()
|
||||
self.clients[1]['xep_0045'].join_muc(self.muc, 'client2')
|
||||
await self.clients[1].wait_until('muc::%s::got_online' % self.muc)
|
||||
|
||||
async def test_leave(self):
|
||||
"""Check that we leave properly"""
|
||||
await self.test_join_after_config()
|
||||
self.clients[0]['xep_0045'].leave_muc(self.muc, 'client1', 'boooring')
|
||||
pres = await self.clients[1].wait_until('muc::%s::got_offline' % self.muc)
|
||||
self.assertEqual(pres['status'], 'boooring')
|
||||
self.assertEqual(pres['type'], 'unavailable')
|
||||
|
||||
|
||||
async def test_kick(self):
|
||||
"""Test kicking a user"""
|
||||
await self.test_join_after_config()
|
||||
await asyncio.gather(
|
||||
self.clients[0].wait_until('muc::%s::got_offline' % self.muc),
|
||||
self.clients[0]['xep_0045'].set_role(self.muc, 'client2', 'none')
|
||||
)
|
||||
|
||||
|
||||
suite = unittest.TestLoader().loadTestsFromTestCase(TestConnect)
|
71
run_integration_tests.py
Executable file
71
run_integration_tests.py
Executable file
|
@ -0,0 +1,71 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
import sys
|
||||
import logging
|
||||
import unittest
|
||||
|
||||
from argparse import ArgumentParser
|
||||
from distutils.core import Command
|
||||
from importlib import import_module
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def run_tests(filenames=None):
|
||||
"""
|
||||
Find and run all tests in the tests/ directory.
|
||||
|
||||
Excludes live tests (tests/live_*).
|
||||
"""
|
||||
if sys.version_info < (3, 8):
|
||||
raise ValueError('Your python version is too old to run these tests')
|
||||
if not filenames:
|
||||
filenames = [i for i in Path('itests').glob('test_*')]
|
||||
else:
|
||||
filenames = [Path(i) for i in filenames]
|
||||
|
||||
modules = ['.'.join(test.parts[:-1] + (test.stem,)) for test in filenames]
|
||||
|
||||
suites = []
|
||||
for filename in modules:
|
||||
module = import_module(filename)
|
||||
suites.append(module.suite)
|
||||
|
||||
tests = unittest.TestSuite(suites)
|
||||
runner = unittest.TextTestRunner(verbosity=2)
|
||||
|
||||
# Disable logging output
|
||||
logging.basicConfig(level=100)
|
||||
logging.disable(100)
|
||||
|
||||
result = runner.run(tests)
|
||||
return result
|
||||
|
||||
|
||||
# Add a 'test' command for setup.py
|
||||
|
||||
class TestCommand(Command):
|
||||
|
||||
user_options = []
|
||||
|
||||
def initialize_options(self):
|
||||
pass
|
||||
|
||||
def finalize_options(self):
|
||||
pass
|
||||
|
||||
def run(self):
|
||||
run_tests()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
parser = ArgumentParser(description='Run unit tests.')
|
||||
parser.add_argument('tests', metavar='TEST', nargs='*', help='list of tests to run, or nothing to run them all')
|
||||
args = parser.parse_args()
|
||||
|
||||
result = run_tests(args.tests)
|
||||
print("<tests %s ran='%s' errors='%s' fails='%s' success='%s'/>" % (
|
||||
"xmlns='http//andyet.net/protocol/tests'",
|
||||
result.testsRun, len(result.errors),
|
||||
len(result.failures), result.wasSuccessful()))
|
||||
|
||||
sys.exit(not result.wasSuccessful())
|
61
slixmpp/test/integration.py
Normal file
61
slixmpp/test/integration.py
Normal file
|
@ -0,0 +1,61 @@
|
|||
"""
|
||||
Slixmpp: The Slick XMPP Library
|
||||
Copyright (C) 2020 Mathieu Pasquet
|
||||
This file is part of Slixmpp.
|
||||
|
||||
See the file LICENSE for copying permission.
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import os
|
||||
try:
|
||||
from unittest import IsolatedAsyncioTestCase
|
||||
except ImportError:
|
||||
# Python < 3.8
|
||||
# just to make sure the imports do not break, but
|
||||
# not usable.
|
||||
from unittest import TestCase as IsolatedAsyncioTestCase
|
||||
from typing import (
|
||||
List,
|
||||
)
|
||||
|
||||
from slixmpp import JID
|
||||
from slixmpp.clientxmpp import ClientXMPP
|
||||
|
||||
|
||||
class SlixIntegration(IsolatedAsyncioTestCase):
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.clients = []
|
||||
self.addAsyncCleanup(self._destroy)
|
||||
|
||||
def envjid(self, name):
|
||||
"""Get a JID from an env var"""
|
||||
value = os.getenv(name)
|
||||
return JID(value)
|
||||
|
||||
def envstr(self, name):
|
||||
"""get a str from an env var"""
|
||||
return os.getenv(name)
|
||||
|
||||
def register_plugins(self, plugins: List[str]):
|
||||
"""Register plugins on all known clients"""
|
||||
for plugin in plugins:
|
||||
for client in self.clients:
|
||||
client.register_plugin(plugin)
|
||||
|
||||
def add_client(self, jid: JID, password: str):
|
||||
"""Register a new client"""
|
||||
self.clients.append(ClientXMPP(jid, password))
|
||||
|
||||
async def connect_clients(self):
|
||||
"""Connect all clients"""
|
||||
for client in self.clients:
|
||||
client.connect()
|
||||
await client.wait_until('session_start')
|
||||
|
||||
async def _destroy(self):
|
||||
"""Kill all clients"""
|
||||
for client in self.clients:
|
||||
client.abort()
|
|
@ -12,7 +12,7 @@
|
|||
:license: MIT, see LICENSE for more details
|
||||
"""
|
||||
|
||||
from typing import Optional, Set, Callable
|
||||
from typing import Optional, Set, Callable, Any
|
||||
|
||||
import functools
|
||||
import logging
|
||||
|
@ -1130,3 +1130,18 @@ class XMLStream(asyncio.BaseProtocol):
|
|||
:param exception: An unhandled exception object.
|
||||
"""
|
||||
pass
|
||||
|
||||
async def wait_until(self, event: str, timeout=30) -> Any:
|
||||
"""Utility method to wake on the next firing of an event.
|
||||
(Registers a disposable handler on it)
|
||||
|
||||
:param str event: Event to wait on.
|
||||
:param int timeout: Timeout
|
||||
"""
|
||||
fut = asyncio.Future()
|
||||
self.add_event_handler(
|
||||
event,
|
||||
fut.set_result,
|
||||
disposable=True,
|
||||
)
|
||||
return await asyncio.wait_for(fut, timeout)
|
||||
|
|
Loading…
Reference in a new issue