From 51a34f83f9cae36f65b021e379e411cacf84c054 Mon Sep 17 00:00:00 2001 From: Florent Le Coz Date: Tue, 23 Feb 2016 19:20:00 +0100 Subject: Add a basic integration test in python --- tests/end_to_end/__main__.py | 217 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 217 insertions(+) create mode 100644 tests/end_to_end/__main__.py (limited to 'tests/end_to_end') diff --git a/tests/end_to_end/__main__.py b/tests/end_to_end/__main__.py new file mode 100644 index 0000000..96bbbf4 --- /dev/null +++ b/tests/end_to_end/__main__.py @@ -0,0 +1,217 @@ +#!/usr/bin/env python3 + +import slixmpp +import asyncio +import logging +import signal +import atexit +import sys +from functools import partial + + +class MatchAll(slixmpp.xmlstream.matcher.base.MatcherBase): + """match everything""" + + def match(self, xml): + return True + + +class XMPPComponent(slixmpp.BaseXMPP): + """ + XMPPComponent sending a “scenario” of stanzas, checking that the responses + match the expected results. + """ + + def __init__(self, scenario, biboumi): + super().__init__(jid="biboumi.localhost", default_ns="jabber:component:accept") + self.is_component = True + self.stream_header = '' % ( + 'xmlns="jabber:component:accept"', + 'xmlns:stream="%s"' % self.stream_ns, + self.boundjid, self.get_id()) + self.stream_footer = "" + + self.register_handler(slixmpp.Callback('Match All', + MatchAll(None), + self.handle_incoming_stanza)) + + self.add_event_handler("session_end", self.on_end_session) + + asyncio.async(self.accept_routine()) + + self.scenario = scenario + self.biboumi = biboumi + self.expected_xpath = None + self.failed = False + self.accepting_server = None + + def error(self, message): + print("Failure: %s" % (message,)) + self.scenario.steps = [] + self.failed = True + + def on_end_session(self, event): + self.loop.stop() + + def handle_incoming_stanza(self, stanza): + if self.expected_xpath: + matched = slixmpp.xmlstream.matcher.xpath.MatchXPath(self.expected_xpath).match(stanza) + if not matched: + self.error("Received stanza “%s” did not match expected xpath “%s”" % (stanza, self.expected_xpath)) + self.expected_xpath = None + self.run_scenario() + + def run_scenario(self): + if scenario.steps: + step = scenario.steps.pop(0) + step(self, self.biboumi) + else: + self.biboumi.stop() + + @asyncio.coroutine + def accept_routine(self): + self.accepting_server = yield from self.loop.create_server(lambda: self, + "127.0.0.1", "8811", reuse_address=True) + + +class Scenario: + """Defines a list of actions that are executed in sequence, until one of + them throws an exception, or until the end. An action can be something + like “send a stanza”, “receive the next stanza and check that it matches + the given XPath”, “send a signal”, “wait for the end of the process”, + etc + """ + + def __init__(self, name, steps): + """ + Steps is a list of 2-tuple: + [(action, answer), (action, answer)] + """ + self.name = name + self.steps = steps + + +class BiboumiRunner: + def __init__(self, name, with_valgrind): + self.name = name + self.fd = open("biboumi_%s_output.txt" % (name,), "w") + if with_valgrind: + self.create = asyncio.create_subprocess_exec("valgrind", "--leak-check=full", "--show-leak-kinds=all", "--errors-for-leak-kinds=all", "--error-exitcode=16", "./biboumi", "test.conf", stdin=None, stdout=self.fd, + stderr=self.fd, loop=None, limit=None) + else: + self.create = asyncio.create_subprocess_exec("./biboumi", "test.conf", stdin=None, stdout=self.fd, + stderr=self.fd, loop=None, limit=None) + self.process = None + + self.signal_sent = False + + @asyncio.coroutine + def start(self): + self.process = yield from self.create + + @asyncio.coroutine + def wait(self): + code = yield from self.process.wait() + return code + + def stop(self): + if not self.signal_sent: + self.signal_sent = True + if self.process: + self.process.send_signal(signal.SIGINT) + + +def send_stanza(stanza, xmpp, biboumi): + xmpp.send_raw(stanza) + asyncio.get_event_loop().call_soon(xmpp.run_scenario) + + +def expect_stanza(xpath, xmpp, biboumi): + xmpp.expected_xpath = xpath + + +class BiboumiTest: + """ + Spawns a biboumi process and a fake XMPP Component that will run a + Scenario. It redirects the outputs of the subprocess into separated + files, and detects any failure in the running of the scenario. + """ + + def __init__(self, scenario, expected_code=0): + self.scenario = scenario + self.expected_code = 0 + + def run(self, with_valgrind=True): + print("Running scenario: %s%s" % (self.scenario.name, " (with valgrind)" if with_valgrind else '')) + # Redirect the slixmpp logging into a specific file + output_filename = "slixmpp_%s_output.txt" % (self.scenario.name,) + with open(output_filename, "w"): + pass + logging.basicConfig(level=logging.DEBUG, + format='%(levelname)-8s %(message)s', + filename=output_filename) + + # Start the XMPP component and biboumi + biboumi = BiboumiRunner(scenario.name, with_valgrind) + xmpp = XMPPComponent(self.scenario, biboumi) + asyncio.get_event_loop().run_until_complete(biboumi.start()) + + asyncio.get_event_loop().call_soon(xmpp.run_scenario) + + xmpp.process() + + code = asyncio.get_event_loop().run_until_complete(biboumi.wait()) + failed = False + if not xmpp.failed: + if code != self.expected_code: + xmpp.error("Wrong return code from biboumi's process: %d" % (code,)) + failed = True + else: + print("Success!") + else: + failed = True + + if xmpp.server: + xmpp.accepting_server.close() + + return not failed + + +if __name__ == '__main__': + + atexit.register(asyncio.get_event_loop().close) + + # Start the test component, accepting connections on the configured + # port. + scenarios = ( + Scenario("basic_handshake_success", + [ + partial(expect_stanza, "{jabber:component:accept}handshake"), + partial(send_stanza, ""), + ]), + Scenario("channel_join", + [ + partial(expect_stanza, "{jabber:component:accept}handshake"), + partial(send_stanza, ""), + partial(send_stanza, ""), + partial(expect_stanza, "{jabber:component:accept}message/body"), + ]), + ) + + failures = 0 + + print("Running %s checks for biboumi." % (len(scenarios))) + + for scenario in scenarios: + test = BiboumiTest(scenario) + if not test.run(False): + print("You can check the files slixmpp_%s_output.txt and biboumi_%s_output.txt to help you debug." % + (scenario.name, scenario.name)) + failures += 1 + + if failures: + print("%d test%s failed, please fix %s." % (failures, 's' if failures > 1 else '', + 'them' if failures > 1 else 'it')) + sys.exit(1) + else: + print("All tests passed successfully") -- cgit v1.2.3 From 1ee5f8e01a932b73628ed3f89e8c77c5fa25f1b0 Mon Sep 17 00:00:00 2001 From: Florent Le Coz Date: Tue, 23 Feb 2016 19:40:17 +0100 Subject: end_to_end creates a config file before running biboumi --- tests/end_to_end/__main__.py | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'tests/end_to_end') diff --git a/tests/end_to_end/__main__.py b/tests/end_to_end/__main__.py index 96bbbf4..8e9c46e 100644 --- a/tests/end_to_end/__main__.py +++ b/tests/end_to_end/__main__.py @@ -151,6 +151,9 @@ class BiboumiTest: format='%(levelname)-8s %(message)s', filename=output_filename) + with open("test.conf", "w") as fd: + fd.write(confs['basic']) + # Start the XMPP component and biboumi biboumi = BiboumiRunner(scenario.name, with_valgrind) xmpp = XMPPComponent(self.scenario, biboumi) @@ -176,6 +179,11 @@ class BiboumiTest: return not failed +confs = {'basic': +"""hostname=biboumi.localhost +password=coucou +db_name=biboumi.sqlite +port=8811"""} if __name__ == '__main__': -- cgit v1.2.3 From 55d1d817719646d6be3cac200e4ff8b7b113e136 Mon Sep 17 00:00:00 2001 From: Florent Le Coz Date: Mon, 29 Feb 2016 17:59:47 +0100 Subject: Provide a better way to check stanzas at each step of the end_to_end test --- tests/end_to_end/__main__.py | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) (limited to 'tests/end_to_end') diff --git a/tests/end_to_end/__main__.py b/tests/end_to_end/__main__.py index 8e9c46e..dbc7c66 100644 --- a/tests/end_to_end/__main__.py +++ b/tests/end_to_end/__main__.py @@ -16,6 +16,10 @@ class MatchAll(slixmpp.xmlstream.matcher.base.MatcherBase): return True +class StanzaError(Exception): + pass + + class XMPPComponent(slixmpp.BaseXMPP): """ XMPPComponent sending a “scenario” of stanzas, checking that the responses @@ -41,7 +45,9 @@ class XMPPComponent(slixmpp.BaseXMPP): self.scenario = scenario self.biboumi = biboumi - self.expected_xpath = None + # A callable, taking a stanza as argument and raising a StanzaError + # exception if the test should fail. + self.stanza_checker = None self.failed = False self.accepting_server = None @@ -54,11 +60,12 @@ class XMPPComponent(slixmpp.BaseXMPP): self.loop.stop() def handle_incoming_stanza(self, stanza): - if self.expected_xpath: - matched = slixmpp.xmlstream.matcher.xpath.MatchXPath(self.expected_xpath).match(stanza) - if not matched: - self.error("Received stanza “%s” did not match expected xpath “%s”" % (stanza, self.expected_xpath)) - self.expected_xpath = None + if self.stanza_checker: + try: + self.stanza_checker(stanza) + except StanzaError as e: + self.error(e) + self.stanza_checker = None self.run_scenario() def run_scenario(self): @@ -73,6 +80,10 @@ class XMPPComponent(slixmpp.BaseXMPP): self.accepting_server = yield from self.loop.create_server(lambda: self, "127.0.0.1", "8811", reuse_address=True) +def check_xpath(xpath, stanza): + matched = slixmpp.xmlstream.matcher.xpath.MatchXPath(xpath).match(stanza) + if not matched: + raise StanzaError("Received stanza “%s” did not match expected xpath “%s”" % (stanza, self.expected_xpath)) class Scenario: """Defines a list of actions that are executed in sequence, until one of @@ -127,7 +138,7 @@ def send_stanza(stanza, xmpp, biboumi): def expect_stanza(xpath, xmpp, biboumi): - xmpp.expected_xpath = xpath + xmpp.stanza_checker = partial(check_xpath, xpath) class BiboumiTest: -- cgit v1.2.3 From d57b8bb8c3edc4271a9be3d052a220db09250a7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Thu, 17 Mar 2016 19:18:24 +0100 Subject: Trivial formatting --- tests/end_to_end/__main__.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) (limited to 'tests/end_to_end') diff --git a/tests/end_to_end/__main__.py b/tests/end_to_end/__main__.py index dbc7c66..7b01a39 100644 --- a/tests/end_to_end/__main__.py +++ b/tests/end_to_end/__main__.py @@ -7,9 +7,10 @@ import signal import atexit import sys from functools import partial +from slixmpp.xmlstream.matcher.base import MatcherBase -class MatchAll(slixmpp.xmlstream.matcher.base.MatcherBase): +class MatchAll(MatcherBase): """match everything""" def match(self, xml): @@ -80,11 +81,13 @@ class XMPPComponent(slixmpp.BaseXMPP): self.accepting_server = yield from self.loop.create_server(lambda: self, "127.0.0.1", "8811", reuse_address=True) + def check_xpath(xpath, stanza): matched = slixmpp.xmlstream.matcher.xpath.MatchXPath(xpath).match(stanza) if not matched: raise StanzaError("Received stanza “%s” did not match expected xpath “%s”" % (stanza, self.expected_xpath)) + class Scenario: """Defines a list of actions that are executed in sequence, until one of them throws an exception, or until the end. An action can be something @@ -107,7 +110,9 @@ class BiboumiRunner: self.name = name self.fd = open("biboumi_%s_output.txt" % (name,), "w") if with_valgrind: - self.create = asyncio.create_subprocess_exec("valgrind", "--leak-check=full", "--show-leak-kinds=all", "--errors-for-leak-kinds=all", "--error-exitcode=16", "./biboumi", "test.conf", stdin=None, stdout=self.fd, + self.create = asyncio.create_subprocess_exec("valgrind", "--leak-check=full", "--show-leak-kinds=all", + "--errors-for-leak-kinds=all", "--error-exitcode=16", + "./biboumi", "test.conf", stdin=None, stdout=self.fd, stderr=self.fd, loop=None, limit=None) else: self.create = asyncio.create_subprocess_exec("./biboumi", "test.conf", stdin=None, stdout=self.fd, @@ -190,6 +195,7 @@ class BiboumiTest: return not failed + confs = {'basic': """hostname=biboumi.localhost password=coucou @@ -212,7 +218,8 @@ if __name__ == '__main__': [ partial(expect_stanza, "{jabber:component:accept}handshake"), partial(send_stanza, ""), - partial(send_stanza, ""), + partial(send_stanza, + ""), partial(expect_stanza, "{jabber:component:accept}message/body"), ]), ) -- cgit v1.2.3 From 606700f4c5c2e76762b6c5d1ba360e99932b3309 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Mon, 11 Apr 2016 17:18:07 +0200 Subject: Improve e2e test, start mammond ourself, etc --- tests/end_to_end/__main__.py | 182 ++++++++++++++++++++++++++++++++++++------- 1 file changed, 152 insertions(+), 30 deletions(-) (limited to 'tests/end_to_end') diff --git a/tests/end_to_end/__main__.py b/tests/end_to_end/__main__.py index 7b01a39..da9e80c 100644 --- a/tests/end_to_end/__main__.py +++ b/tests/end_to_end/__main__.py @@ -1,11 +1,14 @@ #!/usr/bin/env python3 +import collections import slixmpp import asyncio import logging import signal import atexit +import lxml.etree import sys +import io from functools import partial from slixmpp.xmlstream.matcher.base import MatcherBase @@ -81,11 +84,16 @@ class XMPPComponent(slixmpp.BaseXMPP): self.accepting_server = yield from self.loop.create_server(lambda: self, "127.0.0.1", "8811", reuse_address=True) + def check_stanza_against_all_expected_xpaths(self): + pass -def check_xpath(xpath, stanza): - matched = slixmpp.xmlstream.matcher.xpath.MatchXPath(xpath).match(stanza) - if not matched: - raise StanzaError("Received stanza “%s” did not match expected xpath “%s”" % (stanza, self.expected_xpath)) +def check_xpath(xpaths, stanza): + for xpath in xpaths: + tree = lxml.etree.parse(io.StringIO(str(stanza))) + matched = tree.xpath(xpath, namespaces={'re': 'http://exslt.org/regular-expressions', + 'muc_user': 'http://jabber.org/protocol/muc#user'}) + if not matched: + raise StanzaError("Received stanza “%s” did not match expected xpath “%s”" % (stanza, xpath)) class Scenario: @@ -102,23 +110,17 @@ class Scenario: [(action, answer), (action, answer)] """ self.name = name - self.steps = steps - + self.steps = [] + for elem in steps: + if isinstance(elem, collections.Iterable): + for step in elem: + self.steps.append(step) + else: + self.steps.append(elem) -class BiboumiRunner: - def __init__(self, name, with_valgrind): - self.name = name - self.fd = open("biboumi_%s_output.txt" % (name,), "w") - if with_valgrind: - self.create = asyncio.create_subprocess_exec("valgrind", "--leak-check=full", "--show-leak-kinds=all", - "--errors-for-leak-kinds=all", "--error-exitcode=16", - "./biboumi", "test.conf", stdin=None, stdout=self.fd, - stderr=self.fd, loop=None, limit=None) - else: - self.create = asyncio.create_subprocess_exec("./biboumi", "test.conf", stdin=None, stdout=self.fd, - stderr=self.fd, loop=None, limit=None) +class ProcessRunner: + def __init__(self): self.process = None - self.signal_sent = False @asyncio.coroutine @@ -136,15 +138,42 @@ class BiboumiRunner: if self.process: self.process.send_signal(signal.SIGINT) + def __del__(self): + self.stop() + +class BiboumiRunner(ProcessRunner): + def __init__(self, name, with_valgrind): + super().__init__() + self.name = name + self.fd = open("biboumi_%s_output.txt" % (name,), "w") + if with_valgrind: + self.create = asyncio.create_subprocess_exec("valgrind", "--leak-check=full", "--show-leak-kinds=all", + "--errors-for-leak-kinds=all", "--error-exitcode=16", + "./biboumi", "test.conf", stdin=None, stdout=self.fd, + stderr=self.fd, loop=None, limit=None) + else: + self.create = asyncio.create_subprocess_exec("./biboumi", "test.conf", stdin=None, stdout=self.fd, + stderr=self.fd, loop=None, limit=None) + +class IrcServerRunner(ProcessRunner): + def __init__(self): + super().__init__() + self.create = asyncio.create_subprocess_exec("mammond", "--debug", "--nofork", + "--config", "../tests/end_to_end/mammond.conf", + stderr=asyncio.subprocess.PIPE) def send_stanza(stanza, xmpp, biboumi): - xmpp.send_raw(stanza) + xmpp.send_raw(stanza.format_map(common_replacements)) asyncio.get_event_loop().call_soon(xmpp.run_scenario) -def expect_stanza(xpath, xmpp, biboumi): - xmpp.stanza_checker = partial(check_xpath, xpath) - +def expect_stanza(xpaths, xmpp, biboumi): + if isinstance(xpaths, str): + xmpp.stanza_checker = partial(check_xpath, [xpaths.format_map(common_replacements)]) + elif isinstance(xpaths, tuple): + xmpp.stanza_checker = partial(check_xpath, [xpath.format_map(common_replacements) for xpath in xpaths]) + else: + print("Warning, from argument type passed to expect_stanza: %s" % (type(xpaths))) class BiboumiTest: """ @@ -202,6 +231,59 @@ password=coucou db_name=biboumi.sqlite port=8811"""} +common_replacements = { + 'irc_server_one': 'irc.localhost@biboumi.localhost', + 'irc_host_one': 'irc.localhost', + 'resource_one': 'resource1', + 'nick_one': 'Nick', + 'jid_one': 'first@example.com', + 'jid_two': 'second@example.com', + 'nick_two': 'Bobby', +} + + +def handshake_sequence(): + return (partial(expect_stanza, "//handshake"), + partial(send_stanza, "")) + + +def connection_sequence(irc_host, jid): + jid = jid.format_map(common_replacements) + xpath = "/message[@to='" + jid + "'][@from='irc.localhost@biboumi.localhost']/body[text()='%s']" + xpath_re = "/message[@to='" + jid + "'][@from='irc.localhost@biboumi.localhost']/body[re:test(text(), '%s')]" + return ( + partial(expect_stanza, + xpath % ('Connecting to %s:6697 (encrypted)' % irc_host)), + partial(expect_stanza, + xpath % ('Connection failed: Connection refused')), + partial(expect_stanza, + xpath % ('Connecting to %s:6670 (encrypted)' % irc_host)), + partial(expect_stanza, + xpath % ('Connection failed: Connection refused')), + partial(expect_stanza, + xpath % ('Connecting to %s:6667 (not encrypted)' % irc_host)), + partial(expect_stanza, + xpath % ('Connected to IRC server.')), + partial(expect_stanza, + xpath % ('%s: *** Looking up your hostname...' % irc_host)), + partial(expect_stanza, + xpath % ('%s: *** Checking Ident' % irc_host)), + # These three messages can be received in any order + partial(expect_stanza, + xpath_re % (r'^%s: (\*\*\* Found your hostname: .*|NAK multi-prefix |\*\*\* No Ident response)$' % irc_host)), + partial(expect_stanza, + xpath_re % (r'^%s: (\*\*\* Found your hostname: .*|NAK multi-prefix |\*\*\* No Ident response)$' % irc_host)), + partial(expect_stanza, + xpath_re % (r'^%s: (\*\*\* Found your hostname: .*|NAK multi-prefix |\*\*\* No Ident response)$' % irc_host)), + partial(expect_stanza, + xpath_re % (r'^%s: Your host is .*$' % irc_host)), + partial(expect_stanza, + xpath_re % (r'^%s: This server was started at .*$' % irc_host)), + partial(expect_stanza, + xpath % ("- Default MOTD\n")), + ) + + if __name__ == '__main__': atexit.register(asyncio.get_event_loop().close) @@ -211,21 +293,57 @@ if __name__ == '__main__': scenarios = ( Scenario("basic_handshake_success", [ - partial(expect_stanza, "{jabber:component:accept}handshake"), - partial(send_stanza, ""), + handshake_sequence() + ]), + Scenario("irc_server_connection", + [ + handshake_sequence(), + partial(send_stanza, + ""), + connection_sequence("irc.localhost", '{jid_one}/{resource_one}'), + ]), + Scenario("simple_channel_join", + [ + handshake_sequence(), + partial(send_stanza, + ""), + connection_sequence("irc.localhost", '{jid_one}/{resource_one}'), + partial(expect_stanza, + ("/presence[@to='{jid_one}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_one}']/muc_user:x/muc_user:item[@affiliation='none'][@jid='~nick@localhost.localdomain'][@role='participant']", + "/presence/muc_user:x/muc_user:status[@code='110']") + ), + partial(expect_stanza, "/message[@from='#foo%{irc_server_one}'][@type='groupchat']/subject[not(text())]"), ]), - Scenario("channel_join", + Scenario("channel_join_with_two_users", [ - partial(expect_stanza, "{jabber:component:accept}handshake"), - partial(send_stanza, ""), + handshake_sequence(), + # First user joins partial(send_stanza, - ""), - partial(expect_stanza, "{jabber:component:accept}message/body"), + ""), + connection_sequence("irc.localhost", '{jid_one}/{resource_one}'), + partial(expect_stanza, + ("/presence[@to='{jid_one}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_one}']/muc_user:x/muc_user:item[@affiliation='none'][@jid='~nick@localhost.localdomain'][@role='participant']", + "/presence/muc_user:x/muc_user:status[@code='110']") + ), + partial(expect_stanza, "/message[@from='#foo%{irc_server_one}'][@type='groupchat']/subject[not(text())]"), + + # Second user joins + partial(send_stanza, + ""), + # connection_sequence("irc.localhost", '{jid_two}/{resource_one}'), ]), ) failures = 0 + irc = IrcServerRunner() + print("Starting mammond server…") + asyncio.get_event_loop().run_until_complete(irc.start()) + while True: + res = asyncio.get_event_loop().run_until_complete(irc.process.stderr.readline()) + if b"init finished..." in res: + break + print("mammond server started.") print("Running %s checks for biboumi." % (len(scenarios))) for scenario in scenarios: @@ -235,6 +353,10 @@ if __name__ == '__main__': (scenario.name, scenario.name)) failures += 1 + print("Waiting for mammond to exit…") + irc.stop() + code = asyncio.get_event_loop().run_until_complete(irc.wait()) + if failures: print("%d test%s failed, please fix %s." % (failures, 's' if failures > 1 else '', 'them' if failures > 1 else 'it')) -- cgit v1.2.3 From 065f52a0b32d5355da58e200a7e9a301693dcb55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Mon, 11 Apr 2016 17:29:54 +0200 Subject: Add mammond conf --- tests/end_to_end/mammond.conf | 269 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 269 insertions(+) create mode 100644 tests/end_to_end/mammond.conf (limited to 'tests/end_to_end') diff --git a/tests/end_to_end/mammond.conf b/tests/end_to_end/mammond.conf new file mode 100644 index 0000000..a9c7d43 --- /dev/null +++ b/tests/end_to_end/mammond.conf @@ -0,0 +1,269 @@ +# The server object defines the server information parameters. +server: + # name - the server name + name: "irc.localhost" + + # description - the description of the server on the network + description: "Test server for biboumi" + + # network - the NETWORK= name in 005 for rfc1459 clients + network: "irc.localhost" + + # recvq_len - the maximum number of lines that can be in a client's recvq + recvq_len: 20 + + # motd - the motd content (will later be file) + motd: + - "Default MOTD" + + +# The clients object defines client parameters +clients: + # ping_frequency - client ping frequency + ping_frequency: + minutes: 1 + + # ping_timeout - ping timeout length + ping_timeout: + minutes: 2 + + +# The data object defines the data store parameters +data: + + ## JSON should only be considered for testing + # format - data store type + format: "json" + + # filename - data store filename + filename: ".mammon.data.json" + + # save_frequency - save the database every this amount of time + save_frequency: + minutes: 5 + + +# The listeners object is a list of listeners. +listeners: +- {"host": "0.0.0.0", "port": 6667, "ssl": false, "certfile": "~/workspace/biboumi/cert.pem", "keyfile": "~/workspace/biboumi/key.pem", } + + +# The logs section is a list of logs. +logs: +- { + # path - the path of the logfile + "path": "mammond.log", + + # level - the log level of the file + "level": "debug" + } + + +# Limits define maximum lengths for various commands and objects +# to remove a limit, simply comment it out +limits: + # user - maximum length of usernames + user: 10 + + # nick - maximum length of nicknames + nick: 50 + + # channel - maximum length of channel names + channel: 200 + + # topic - maximum length of channel topics + topic: 400 + + # line - maximum length of lines in and out + line: 2048 + + +# The register object defines registration information +register: + + # verify_timeout - length of time a user has to verify their newly-created + # account before it can be re-registered + verify_timeout: + days: 5 + + # enabled_callbacks - callbacks that we allow + enabled_callbacks: + # - mailto + # - none # no verification required, will instantly register successfully + + # callbacks - types and details for various callback methods + callbacks: + + # mailto - email using sendmail + mailto: + # from - address our messages get sent from + from: mammon@example.com + + # sendmail - location of the sendmail binary + sendmail: /usr/sbin/sendmail + + # verify_message_subject - subject of the verify message + verify_message_subject: "{network_name} Account Registration" + + # verify_message - message sent to users to verify their account + verify_message: | + Hi, + + You have requested to register the account {account}. + + Your verification code is {auth_code} + + Please type "/quote REG VERIFY {account} {auth_code}" to complete registration + + Thank you, + {network_name} +# Roles define the capabilities an oper may have, as well as role-specific +# metadata. + + # mammon capability names: + # oper:local_kill allows /KILLing local users + # oper:global_kill allows /KILLing local and remote users + # oper:routing allows remote SQUIT and CONNECT + # oper:kline allows KLINE and DLINE + # oper:unkline allows UNKLINE and UNDLINE + # oper:remote_ban allows remote klines + # oper:rehash allows REHASH of server config + # oper:die allows DIE and RESTART + +roles: + # name - the name of the privilege set + "local_op": + # capabilities - a list of qualified capability names + capabilities: + - oper:local_kill + - oper:kline + - oper:unkline + - metadata:set_local + + # metakeys_get - metadata keys this role can view + metakeys_get: + # - spammer_probability + + # title - metadata identifying the specific role + title: "IRC Operator" + + # name - the name of the privilege set + "global_op": + # extends - inherets this role's capabilities + extends: "local_op" + + # capabilities - a list of qualified capability names + capabilities: + - oper:global_kill + - oper:remote_ban + - metadata:set_global + + # title - metadata identifying the specific role + title: "IRC Operator" + + # name - the name of the privilege set + "network_admin": + # capabilities - a list of qualified capability names + capabilities: + - oper:global_kill + - oper:routing + + # title - metadata identifying the specific role + title: "Network Administrator" + + # name - the name of the privilege set + "server_admin": + # extends - inherets this role's capabilities + extends: "local_op" + + # capabilities - a list of qualified capability names + capabilities: + - oper:rehash + - oper:die + + # title - metadata identifying the specific role + title: "Server Administrator" + + # example metadata-specific roles + # name - the name of the privilege set + "spam_detection_bot": + # capabilities - a list of qualified capability names + capabilities: + - metadata:set_global + + # metakeys_access - metadata keys this role can view and set + metakeys_access: + - spammer_probability + + # title - metadata identifying the specific role + title: "Spam Detection Bot" + + +# Metadata defines the metadata users are allowed to set for themselves +metadata: + # limit - max number of metadata each target is allowed to have + # comment out to remove limit + limit: 20 + + # whitelist - if defined, a list of lowercase keys that are checked when users set data + whitelist: + - avatar + - info + - source + - url + - version + + # restricted_keys - keys that unprived users cannot see / edit, and require a + # special entry in a ROLE to see / edit + restricted_keys: + - away + # - spammer_probability + + +# Monitor defines the monitoring users are allowed to do on other users +monitor: + # limit - max number of 'monitors' each target is allowed to have + # comment out to remove limit + limit: 20 + + +# Operator credentials allow a user to transition from a typical user role +# to a privileged role. +opers: + # name - the name of the operator + "nobody": + # password - the plaintext oper password + password: "nothing" + + # hostmask - if defined, the hostmask the oper must connect from + hostmask: "*@localhost" + + # role - the role that the credentials allow transition to + role: "local_op" + + # name - the name of the operator + "somebody": + # password - the hashed oper password + # created by: mammond --mkpasswd + password: "$6$rounds=100000$KkEHFBhWHV3BHCCS$YuOdlikJHdeIv2YpwvyLAtYCslDlsnUUnrfeKJiBh4SeVhkSU6pQqHWWDjr6lnalkkf1KLDD1wgSQH5AObILQ1" + + # hash - the hashing algorithm to use + hash: "sha512_crypt" + + # hostmask - if defined, the hostmask the oper must connect from + hostmask: "somebody!*@localhost" + + # role - the role that the credentials allow transition to + role: "local_op" + + +# The extensions section is a list of extension modules to load. +extensions: +- mammon.ext.rfc1459.42 +- mammon.ext.rfc1459.ident +- mammon.ext.ircv3.account_notify +- mammon.ext.ircv3.server_time +- mammon.ext.ircv3.echo_message +- mammon.ext.ircv3.register +- mammon.ext.ircv3.sasl +- mammon.ext.misc.nopost \ No newline at end of file -- cgit v1.2.3 From e0ee881f10ddc29d2e633df62c4dc1879a39b342 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Mon, 11 Apr 2016 17:33:54 +0200 Subject: Remove localdomain --- tests/end_to_end/__main__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'tests/end_to_end') diff --git a/tests/end_to_end/__main__.py b/tests/end_to_end/__main__.py index da9e80c..3d907ca 100644 --- a/tests/end_to_end/__main__.py +++ b/tests/end_to_end/__main__.py @@ -309,7 +309,7 @@ if __name__ == '__main__': ""), connection_sequence("irc.localhost", '{jid_one}/{resource_one}'), partial(expect_stanza, - ("/presence[@to='{jid_one}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_one}']/muc_user:x/muc_user:item[@affiliation='none'][@jid='~nick@localhost.localdomain'][@role='participant']", + ("/presence[@to='{jid_one}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_one}']/muc_user:x/muc_user:item[@affiliation='none'][@role='participant']", "/presence/muc_user:x/muc_user:status[@code='110']") ), partial(expect_stanza, "/message[@from='#foo%{irc_server_one}'][@type='groupchat']/subject[not(text())]"), @@ -322,7 +322,7 @@ if __name__ == '__main__': ""), connection_sequence("irc.localhost", '{jid_one}/{resource_one}'), partial(expect_stanza, - ("/presence[@to='{jid_one}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_one}']/muc_user:x/muc_user:item[@affiliation='none'][@jid='~nick@localhost.localdomain'][@role='participant']", + ("/presence[@to='{jid_one}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_one}']/muc_user:x/muc_user:item[@affiliation='none'][@jid='~nick@localhost'][@role='participant']", "/presence/muc_user:x/muc_user:status[@code='110']") ), partial(expect_stanza, "/message[@from='#foo%{irc_server_one}'][@type='groupchat']/subject[not(text())]"), -- cgit v1.2.3 From 45f442b67d0331e82e25d8cafe3b6f2c87d1d611 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Sun, 17 Apr 2016 17:01:28 +0200 Subject: More tests --- tests/end_to_end/__main__.py | 65 +++++++++++++++++++++++++++++++++++++++---- tests/end_to_end/mammond.conf | 2 +- 2 files changed, 60 insertions(+), 7 deletions(-) (limited to 'tests/end_to_end') diff --git a/tests/end_to_end/__main__.py b/tests/end_to_end/__main__.py index 3d907ca..f498585 100644 --- a/tests/end_to_end/__main__.py +++ b/tests/end_to_end/__main__.py @@ -87,6 +87,7 @@ class XMPPComponent(slixmpp.BaseXMPP): def check_stanza_against_all_expected_xpaths(self): pass + def check_xpath(xpaths, stanza): for xpath in xpaths: tree = lxml.etree.parse(io.StringIO(str(stanza))) @@ -118,6 +119,7 @@ class Scenario: else: self.steps.append(elem) + class ProcessRunner: def __init__(self): self.process = None @@ -155,13 +157,14 @@ class BiboumiRunner(ProcessRunner): self.create = asyncio.create_subprocess_exec("./biboumi", "test.conf", stdin=None, stdout=self.fd, stderr=self.fd, loop=None, limit=None) + class IrcServerRunner(ProcessRunner): def __init__(self): super().__init__() - self.create = asyncio.create_subprocess_exec("mammond", "--debug", "--nofork", - "--config", "../tests/end_to_end/mammond.conf", + self.create = asyncio.create_subprocess_exec("/home/louiz/sources/charybdis/ircd/charybdis", "-foreground", stderr=asyncio.subprocess.PIPE) + def send_stanza(stanza, xmpp, biboumi): xmpp.send_raw(stanza.format_map(common_replacements)) asyncio.get_event_loop().call_soon(xmpp.run_scenario) @@ -175,6 +178,7 @@ def expect_stanza(xpaths, xmpp, biboumi): else: print("Warning, from argument type passed to expect_stanza: %s" % (type(xpaths))) + class BiboumiTest: """ Spawns a biboumi process and a fake XMPP Component that will run a @@ -330,20 +334,69 @@ if __name__ == '__main__': # Second user joins partial(send_stanza, ""), + connection_sequence("irc.localhost", '{jid_two}/{resource_one}'), + # Our presence, sent to the other user + partial(expect_stanza, + ("/presence[@to='{jid_one}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_two}']/muc_user:x/muc_user:item[@affiliation='none'][@jid='~bobby@localhost'][@role='participant']",)), + # The other user presence + partial(expect_stanza, + ("/presence[@to='{jid_second}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_one}']/muc_user:x/muc_user:item[@affiliation='none'][@jid='~nick@localhost'][@role='participant']") + ), + # Our own presence + partial(expect_stanza, + ("/presence[@to='{jid_two}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_two}']/muc_user:x/muc_user:item[@affiliation='none'][@jid='~bobby@localhost'][@role='participant']", + "/presence/muc_user:x/muc_user:status[@code='110']") + ), + partial(expect_stanza, "/message[@from='#foo%{irc_server_one}'][@type='groupchat']/subject[not(text())]"), + ]), + Scenario("channel_custom_topic", + [ + handshake_sequence(), + # First user joins + partial(send_stanza, + ""), + connection_sequence("irc.localhost", '{jid_one}/{resource_one}'), + partial(expect_stanza, + ("/presence[@to='{jid_one}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_one}']/muc_user:x/muc_user:item[@affiliation='none'][@jid='~nick@localhost'][@role='participant']", + "/presence/muc_user:x/muc_user:status[@code='110']") + ), + partial(expect_stanza, "/message[@from='#foo%{irc_server_one}'][@type='groupchat']/subject[not(text())]"), + + # First user sets the topic + partial(send_stanza, + "TOPIC\nTEST"), + partial(expect_stanza, "/message[@from='#foo%{irc_server_one}/{nick_one}'][@type='groupchat'][@to='{jid_one}/{resource_one}']/subject[text()='TOPIC\nTEST']"), + + # # Second user joins + # partial(send_stanza, + # ""), # connection_sequence("irc.localhost", '{jid_two}/{resource_one}'), + # # Our presence, sent to the other user + # partial(expect_stanza, + # ("/presence[@to='{jid_one}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_two}']/muc_user:x/muc_user:item[@affiliation='none'][@jid='~bobby@localhost'][@role='participant']",)), + # # The other user presence + # partial(expect_stanza, + # ("/presence[@to='{jid_second}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_one}']/muc_user:x/muc_user:item[@affiliation='none'][@jid='~nick@localhost'][@role='participant']") + # ), + # # Our own presence + # partial(expect_stanza, + # ("/presence[@to='{jid_two}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_two}']/muc_user:x/muc_user:item[@affiliation='none'][@jid='~bobby@localhost'][@role='participant']", + # "/presence/muc_user:x/muc_user:status[@code='110']") + # ), + # partial(expect_stanza, "/message[@from='#foo%{irc_server_one}'][@type='groupchat']/subject[not(text())]"), ]), ) failures = 0 irc = IrcServerRunner() - print("Starting mammond server…") + print("Starting irc server…") asyncio.get_event_loop().run_until_complete(irc.start()) while True: res = asyncio.get_event_loop().run_until_complete(irc.process.stderr.readline()) - if b"init finished..." in res: + if b"now running in foreground mode" in res: break - print("mammond server started.") + print("irc server started.") print("Running %s checks for biboumi." % (len(scenarios))) for scenario in scenarios: @@ -353,7 +406,7 @@ if __name__ == '__main__': (scenario.name, scenario.name)) failures += 1 - print("Waiting for mammond to exit…") + print("Waiting for irc server to exit…") irc.stop() code = asyncio.get_event_loop().run_until_complete(irc.wait()) diff --git a/tests/end_to_end/mammond.conf b/tests/end_to_end/mammond.conf index a9c7d43..99db2bc 100644 --- a/tests/end_to_end/mammond.conf +++ b/tests/end_to_end/mammond.conf @@ -233,7 +233,7 @@ opers: # name - the name of the operator "nobody": # password - the plaintext oper password - password: "nothing" + # password: "nothing" # hostmask - if defined, the hostmask the oper must connect from hostmask: "*@localhost" -- cgit v1.2.3 From bd625aa59bcd5194dd5ee5eea03e7f465d555ba5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Tue, 19 Apr 2016 02:36:45 +0200 Subject: e2etests: we are able to receive optional stanzas --- tests/end_to_end/__main__.py | 29 ++++++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) (limited to 'tests/end_to_end') diff --git a/tests/end_to_end/__main__.py b/tests/end_to_end/__main__.py index f498585..c45e8b9 100644 --- a/tests/end_to_end/__main__.py +++ b/tests/end_to_end/__main__.py @@ -21,6 +21,17 @@ class MatchAll(MatcherBase): class StanzaError(Exception): + """ + Raised when a step fails. + """ + pass + + +class SkipStepError(Exception): + """ + Raised by a step when it needs to be skiped, by running + the next available step immediately. + """ pass @@ -69,6 +80,10 @@ class XMPPComponent(slixmpp.BaseXMPP): self.stanza_checker(stanza) except StanzaError as e: self.error(e) + except SkipStepError: + # Run the next step and then re-handle this same stanza + self.run_scenario() + return self.handle_incoming_stanza(stanza) self.stanza_checker = None self.run_scenario() @@ -97,6 +112,13 @@ def check_xpath(xpaths, stanza): raise StanzaError("Received stanza “%s” did not match expected xpath “%s”" % (stanza, xpath)) +def check_xpath_optional(xpaths, stanza): + try: + check_xpath(xpaths, stanza) + except StanzaError: + raise SkipStepError() + + class Scenario: """Defines a list of actions that are executed in sequence, until one of them throws an exception, or until the end. An action can be something @@ -170,11 +192,12 @@ def send_stanza(stanza, xmpp, biboumi): asyncio.get_event_loop().call_soon(xmpp.run_scenario) -def expect_stanza(xpaths, xmpp, biboumi): +def expect_stanza(xpaths, xmpp, biboumi, optional=False): + check_func = check_xpath if not optional else check_xpath_optional if isinstance(xpaths, str): - xmpp.stanza_checker = partial(check_xpath, [xpaths.format_map(common_replacements)]) + xmpp.stanza_checker = partial(check_func, [xpaths.format_map(common_replacements)]) elif isinstance(xpaths, tuple): - xmpp.stanza_checker = partial(check_xpath, [xpath.format_map(common_replacements) for xpath in xpaths]) + xmpp.stanza_checker = partial(check_func, [xpath.format_map(common_replacements) for xpath in xpaths]) else: print("Warning, from argument type passed to expect_stanza: %s" % (type(xpaths))) -- cgit v1.2.3 From 64f341ee80b0d53f0a6e128a1ccc38205361c3bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Tue, 19 Apr 2016 02:39:27 +0200 Subject: e2etests: Use charybdis and update the scenarios accordingly --- tests/end_to_end/__main__.py | 96 +++++++++------ tests/end_to_end/mammond.conf | 269 ------------------------------------------ 2 files changed, 58 insertions(+), 307 deletions(-) delete mode 100644 tests/end_to_end/mammond.conf (limited to 'tests/end_to_end') diff --git a/tests/end_to_end/__main__.py b/tests/end_to_end/__main__.py index c45e8b9..b027bd9 100644 --- a/tests/end_to_end/__main__.py +++ b/tests/end_to_end/__main__.py @@ -165,6 +165,7 @@ class ProcessRunner: def __del__(self): self.stop() + class BiboumiRunner(ProcessRunner): def __init__(self, name, with_valgrind): super().__init__() @@ -183,7 +184,7 @@ class BiboumiRunner(ProcessRunner): class IrcServerRunner(ProcessRunner): def __init__(self): super().__init__() - self.create = asyncio.create_subprocess_exec("/home/louiz/sources/charybdis/ircd/charybdis", "-foreground", + self.create = asyncio.create_subprocess_exec("charybdis", "-foreground", stderr=asyncio.subprocess.PIPE) @@ -271,7 +272,7 @@ common_replacements = { def handshake_sequence(): return (partial(expect_stanza, "//handshake"), - partial(send_stanza, "")) + partial(send_stanza, "")) def connection_sequence(irc_host, jid): @@ -282,32 +283,47 @@ def connection_sequence(irc_host, jid): partial(expect_stanza, xpath % ('Connecting to %s:6697 (encrypted)' % irc_host)), partial(expect_stanza, - xpath % ('Connection failed: Connection refused')), + xpath % 'Connection failed: Connection refused'), partial(expect_stanza, xpath % ('Connecting to %s:6670 (encrypted)' % irc_host)), partial(expect_stanza, - xpath % ('Connection failed: Connection refused')), + xpath % 'Connection failed: Connection refused'), partial(expect_stanza, xpath % ('Connecting to %s:6667 (not encrypted)' % irc_host)), partial(expect_stanza, - xpath % ('Connected to IRC server.')), + xpath % 'Connected to IRC server.'), + # These two messages can be receive in any order partial(expect_stanza, - xpath % ('%s: *** Looking up your hostname...' % irc_host)), + xpath_re % (r'^%s: \*\*\* (Checking Ident|Looking up your hostname...)$' % irc_host)), partial(expect_stanza, - xpath % ('%s: *** Checking Ident' % irc_host)), + xpath_re % (r'^%s: \*\*\* (Checking Ident|Looking up your hostname...)$' % irc_host)), # These three messages can be received in any order partial(expect_stanza, - xpath_re % (r'^%s: (\*\*\* Found your hostname: .*|NAK multi-prefix |\*\*\* No Ident response)$' % irc_host)), + xpath_re % (r'^%s: (\*\*\* Found your hostname: .*|ACK multi-prefix|\*\*\* No Ident response)$' % irc_host)), partial(expect_stanza, - xpath_re % (r'^%s: (\*\*\* Found your hostname: .*|NAK multi-prefix |\*\*\* No Ident response)$' % irc_host)), + xpath_re % (r'^%s: (\*\*\* Found your hostname: .*|ACK multi-prefix|\*\*\* No Ident response)$' % irc_host)), partial(expect_stanza, - xpath_re % (r'^%s: (\*\*\* Found your hostname: .*|NAK multi-prefix |\*\*\* No Ident response)$' % irc_host)), + xpath_re % (r'^%s: (\*\*\* Found your hostname: .*|ACK multi-prefix|\*\*\* No Ident response)$' % irc_host)), partial(expect_stanza, xpath_re % (r'^%s: Your host is .*$' % irc_host)), partial(expect_stanza, - xpath_re % (r'^%s: This server was started at .*$' % irc_host)), + xpath_re % (r'^%s: This server was created .*$' % irc_host)), + partial(expect_stanza, + xpath_re % (r'^%s: There are \d+ users and \d+ invisible on \d+ servers$' % irc_host)), + partial(expect_stanza, + xpath_re % (r'^%s: \d+ channels formed$' % irc_host), optional=True), + partial(expect_stanza, + xpath_re % (r'^%s: I have \d+ clients and \d+ servers$' % irc_host)), + partial(expect_stanza, + xpath_re % (r'^%s: \d+ \d+ Current local users \d+, max \d+$' % irc_host)), + partial(expect_stanza, + xpath_re % (r'^%s: \d+ \d+ Current global users \d+, max \d+$' % irc_host)), + partial(expect_stanza, + xpath_re % (r'^%s: Highest connection count: \d+ \(\d+ clients\) \(\d+ connections received\)$' % irc_host)), + partial(expect_stanza, + xpath % "- This is charybdis MOTD you might replace it, but if not your friends will\n- laugh at you.\n"), partial(expect_stanza, - xpath % ("- Default MOTD\n")), + xpath_re % r'^User mode for \w+ is \[\+i\]$'), ) @@ -336,7 +352,9 @@ if __name__ == '__main__': ""), connection_sequence("irc.localhost", '{jid_one}/{resource_one}'), partial(expect_stanza, - ("/presence[@to='{jid_one}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_one}']/muc_user:x/muc_user:item[@affiliation='none'][@role='participant']", + "/message/body[text()='Mode #foo [+nt] by {irc_host_one}']"), + partial(expect_stanza, + ("/presence[@to='{jid_one}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_one}']/muc_user:x/muc_user:item[@affiliation='admin'][@role='moderator']", "/presence/muc_user:x/muc_user:status[@code='110']") ), partial(expect_stanza, "/message[@from='#foo%{irc_server_one}'][@type='groupchat']/subject[not(text())]"), @@ -349,7 +367,9 @@ if __name__ == '__main__': ""), connection_sequence("irc.localhost", '{jid_one}/{resource_one}'), partial(expect_stanza, - ("/presence[@to='{jid_one}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_one}']/muc_user:x/muc_user:item[@affiliation='none'][@jid='~nick@localhost'][@role='participant']", + "/message/body[text()='Mode #foo [+nt] by {irc_host_one}']"), + partial(expect_stanza, + ("/presence[@to='{jid_one}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_one}']/muc_user:x/muc_user:item[@affiliation='admin'][@jid='~nick@localhost'][@role='moderator']", "/presence/muc_user:x/muc_user:status[@code='110']") ), partial(expect_stanza, "/message[@from='#foo%{irc_server_one}'][@type='groupchat']/subject[not(text())]"), @@ -363,8 +383,7 @@ if __name__ == '__main__': ("/presence[@to='{jid_one}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_two}']/muc_user:x/muc_user:item[@affiliation='none'][@jid='~bobby@localhost'][@role='participant']",)), # The other user presence partial(expect_stanza, - ("/presence[@to='{jid_second}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_one}']/muc_user:x/muc_user:item[@affiliation='none'][@jid='~nick@localhost'][@role='participant']") - ), + "/presence[@to='{jid_second}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_one}']/muc_user:x/muc_user:item[@affiliation='none'][@jid='~nick@localhost'][@role='participant']"), # Our own presence partial(expect_stanza, ("/presence[@to='{jid_two}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_two}']/muc_user:x/muc_user:item[@affiliation='none'][@jid='~bobby@localhost'][@role='participant']", @@ -380,33 +399,34 @@ if __name__ == '__main__': ""), connection_sequence("irc.localhost", '{jid_one}/{resource_one}'), partial(expect_stanza, - ("/presence[@to='{jid_one}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_one}']/muc_user:x/muc_user:item[@affiliation='none'][@jid='~nick@localhost'][@role='participant']", - "/presence/muc_user:x/muc_user:status[@code='110']") + "/message/body[text()='Mode #foo [+nt] by {irc_host_one}']"), + partial(expect_stanza, + ("/presence[@to='{jid_one}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_one}']/muc_user:x/muc_user:item[@affiliation='admin'][@jid='~nick@localhost'][@role='moderator']", + "/presence/muc_user:x/muc_user:status[@code='110']") ), partial(expect_stanza, "/message[@from='#foo%{irc_server_one}'][@type='groupchat']/subject[not(text())]"), # First user sets the topic partial(send_stanza, - "TOPIC\nTEST"), - partial(expect_stanza, "/message[@from='#foo%{irc_server_one}/{nick_one}'][@type='groupchat'][@to='{jid_one}/{resource_one}']/subject[text()='TOPIC\nTEST']"), - - # # Second user joins - # partial(send_stanza, - # ""), - # connection_sequence("irc.localhost", '{jid_two}/{resource_one}'), - # # Our presence, sent to the other user - # partial(expect_stanza, - # ("/presence[@to='{jid_one}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_two}']/muc_user:x/muc_user:item[@affiliation='none'][@jid='~bobby@localhost'][@role='participant']",)), - # # The other user presence - # partial(expect_stanza, - # ("/presence[@to='{jid_second}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_one}']/muc_user:x/muc_user:item[@affiliation='none'][@jid='~nick@localhost'][@role='participant']") - # ), - # # Our own presence - # partial(expect_stanza, - # ("/presence[@to='{jid_two}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_two}']/muc_user:x/muc_user:item[@affiliation='none'][@jid='~bobby@localhost'][@role='participant']", - # "/presence/muc_user:x/muc_user:status[@code='110']") - # ), - # partial(expect_stanza, "/message[@from='#foo%{irc_server_one}'][@type='groupchat']/subject[not(text())]"), + "TOPIC TEST"), + partial(expect_stanza, "/message[@from='#foo%{irc_server_one}'][@type='groupchat'][@to='{jid_one}/{resource_one}']/subject[text()='TOPIC TEST']"), + + # Second user joins + partial(send_stanza, + ""), + connection_sequence("irc.localhost", '{jid_two}/{resource_one}'), + # Our presence, sent to the other user + partial(expect_stanza, + ("/presence[@to='{jid_one}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_two}']/muc_user:x/muc_user:item[@affiliation='none'][@jid='~bobby@localhost'][@role='participant']",)), + # The other user presence + partial(expect_stanza, + "/presence[@to='{jid_second}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_one}']/muc_user:x/muc_user:item[@affiliation='admin'][@jid='~nick@localhost'][@role='moderator']"), + # Our own presence + partial(expect_stanza, + ("/presence[@to='{jid_two}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_two}']/muc_user:x/muc_user:item[@affiliation='none'][@jid='~bobby@localhost'][@role='participant']", + "/presence/muc_user:x/muc_user:status[@code='110']") + ), + partial(expect_stanza, "/message[@from='#foo%{irc_server_one}'][@type='groupchat']/subject[text()='TOPIC TEST']"), ]), ) diff --git a/tests/end_to_end/mammond.conf b/tests/end_to_end/mammond.conf deleted file mode 100644 index 99db2bc..0000000 --- a/tests/end_to_end/mammond.conf +++ /dev/null @@ -1,269 +0,0 @@ -# The server object defines the server information parameters. -server: - # name - the server name - name: "irc.localhost" - - # description - the description of the server on the network - description: "Test server for biboumi" - - # network - the NETWORK= name in 005 for rfc1459 clients - network: "irc.localhost" - - # recvq_len - the maximum number of lines that can be in a client's recvq - recvq_len: 20 - - # motd - the motd content (will later be file) - motd: - - "Default MOTD" - - -# The clients object defines client parameters -clients: - # ping_frequency - client ping frequency - ping_frequency: - minutes: 1 - - # ping_timeout - ping timeout length - ping_timeout: - minutes: 2 - - -# The data object defines the data store parameters -data: - - ## JSON should only be considered for testing - # format - data store type - format: "json" - - # filename - data store filename - filename: ".mammon.data.json" - - # save_frequency - save the database every this amount of time - save_frequency: - minutes: 5 - - -# The listeners object is a list of listeners. -listeners: -- {"host": "0.0.0.0", "port": 6667, "ssl": false, "certfile": "~/workspace/biboumi/cert.pem", "keyfile": "~/workspace/biboumi/key.pem", } - - -# The logs section is a list of logs. -logs: -- { - # path - the path of the logfile - "path": "mammond.log", - - # level - the log level of the file - "level": "debug" - } - - -# Limits define maximum lengths for various commands and objects -# to remove a limit, simply comment it out -limits: - # user - maximum length of usernames - user: 10 - - # nick - maximum length of nicknames - nick: 50 - - # channel - maximum length of channel names - channel: 200 - - # topic - maximum length of channel topics - topic: 400 - - # line - maximum length of lines in and out - line: 2048 - - -# The register object defines registration information -register: - - # verify_timeout - length of time a user has to verify their newly-created - # account before it can be re-registered - verify_timeout: - days: 5 - - # enabled_callbacks - callbacks that we allow - enabled_callbacks: - # - mailto - # - none # no verification required, will instantly register successfully - - # callbacks - types and details for various callback methods - callbacks: - - # mailto - email using sendmail - mailto: - # from - address our messages get sent from - from: mammon@example.com - - # sendmail - location of the sendmail binary - sendmail: /usr/sbin/sendmail - - # verify_message_subject - subject of the verify message - verify_message_subject: "{network_name} Account Registration" - - # verify_message - message sent to users to verify their account - verify_message: | - Hi, - - You have requested to register the account {account}. - - Your verification code is {auth_code} - - Please type "/quote REG VERIFY {account} {auth_code}" to complete registration - - Thank you, - {network_name} -# Roles define the capabilities an oper may have, as well as role-specific -# metadata. - - # mammon capability names: - # oper:local_kill allows /KILLing local users - # oper:global_kill allows /KILLing local and remote users - # oper:routing allows remote SQUIT and CONNECT - # oper:kline allows KLINE and DLINE - # oper:unkline allows UNKLINE and UNDLINE - # oper:remote_ban allows remote klines - # oper:rehash allows REHASH of server config - # oper:die allows DIE and RESTART - -roles: - # name - the name of the privilege set - "local_op": - # capabilities - a list of qualified capability names - capabilities: - - oper:local_kill - - oper:kline - - oper:unkline - - metadata:set_local - - # metakeys_get - metadata keys this role can view - metakeys_get: - # - spammer_probability - - # title - metadata identifying the specific role - title: "IRC Operator" - - # name - the name of the privilege set - "global_op": - # extends - inherets this role's capabilities - extends: "local_op" - - # capabilities - a list of qualified capability names - capabilities: - - oper:global_kill - - oper:remote_ban - - metadata:set_global - - # title - metadata identifying the specific role - title: "IRC Operator" - - # name - the name of the privilege set - "network_admin": - # capabilities - a list of qualified capability names - capabilities: - - oper:global_kill - - oper:routing - - # title - metadata identifying the specific role - title: "Network Administrator" - - # name - the name of the privilege set - "server_admin": - # extends - inherets this role's capabilities - extends: "local_op" - - # capabilities - a list of qualified capability names - capabilities: - - oper:rehash - - oper:die - - # title - metadata identifying the specific role - title: "Server Administrator" - - # example metadata-specific roles - # name - the name of the privilege set - "spam_detection_bot": - # capabilities - a list of qualified capability names - capabilities: - - metadata:set_global - - # metakeys_access - metadata keys this role can view and set - metakeys_access: - - spammer_probability - - # title - metadata identifying the specific role - title: "Spam Detection Bot" - - -# Metadata defines the metadata users are allowed to set for themselves -metadata: - # limit - max number of metadata each target is allowed to have - # comment out to remove limit - limit: 20 - - # whitelist - if defined, a list of lowercase keys that are checked when users set data - whitelist: - - avatar - - info - - source - - url - - version - - # restricted_keys - keys that unprived users cannot see / edit, and require a - # special entry in a ROLE to see / edit - restricted_keys: - - away - # - spammer_probability - - -# Monitor defines the monitoring users are allowed to do on other users -monitor: - # limit - max number of 'monitors' each target is allowed to have - # comment out to remove limit - limit: 20 - - -# Operator credentials allow a user to transition from a typical user role -# to a privileged role. -opers: - # name - the name of the operator - "nobody": - # password - the plaintext oper password - # password: "nothing" - - # hostmask - if defined, the hostmask the oper must connect from - hostmask: "*@localhost" - - # role - the role that the credentials allow transition to - role: "local_op" - - # name - the name of the operator - "somebody": - # password - the hashed oper password - # created by: mammond --mkpasswd - password: "$6$rounds=100000$KkEHFBhWHV3BHCCS$YuOdlikJHdeIv2YpwvyLAtYCslDlsnUUnrfeKJiBh4SeVhkSU6pQqHWWDjr6lnalkkf1KLDD1wgSQH5AObILQ1" - - # hash - the hashing algorithm to use - hash: "sha512_crypt" - - # hostmask - if defined, the hostmask the oper must connect from - hostmask: "somebody!*@localhost" - - # role - the role that the credentials allow transition to - role: "local_op" - - -# The extensions section is a list of extension modules to load. -extensions: -- mammon.ext.rfc1459.42 -- mammon.ext.rfc1459.ident -- mammon.ext.ircv3.account_notify -- mammon.ext.ircv3.server_time -- mammon.ext.ircv3.echo_message -- mammon.ext.ircv3.register -- mammon.ext.ircv3.sasl -- mammon.ext.misc.nopost \ No newline at end of file -- cgit v1.2.3 From 04d28f968b227067e77e365d317fc251d3c965f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Tue, 19 Apr 2016 02:43:26 +0200 Subject: Forward the topic authors, handle the author from 333 messages fix #2 --- tests/end_to_end/__main__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'tests/end_to_end') diff --git a/tests/end_to_end/__main__.py b/tests/end_to_end/__main__.py index b027bd9..f9d0447 100644 --- a/tests/end_to_end/__main__.py +++ b/tests/end_to_end/__main__.py @@ -409,7 +409,7 @@ if __name__ == '__main__': # First user sets the topic partial(send_stanza, "TOPIC TEST"), - partial(expect_stanza, "/message[@from='#foo%{irc_server_one}'][@type='groupchat'][@to='{jid_one}/{resource_one}']/subject[text()='TOPIC TEST']"), + partial(expect_stanza, "/message[@from='#foo%{irc_server_one}/{nick_one}'][@type='groupchat'][@to='{jid_one}/{resource_one}']/subject[text()='TOPIC TEST']"), # Second user joins partial(send_stanza, @@ -426,7 +426,7 @@ if __name__ == '__main__': ("/presence[@to='{jid_two}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_two}']/muc_user:x/muc_user:item[@affiliation='none'][@jid='~bobby@localhost'][@role='participant']", "/presence/muc_user:x/muc_user:status[@code='110']") ), - partial(expect_stanza, "/message[@from='#foo%{irc_server_one}'][@type='groupchat']/subject[text()='TOPIC TEST']"), + partial(expect_stanza, "/message[@from='#foo%{irc_server_one}/{nick_one}'][@type='groupchat']/subject[text()='TOPIC TEST']"), ]), ) -- cgit v1.2.3 From 04facc515ce2bc32249fa4cf2cdadb75a8bcb3ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Wed, 20 Apr 2016 22:57:27 +0200 Subject: Provide a conf for charybdis --- tests/end_to_end/__main__.py | 2 +- tests/end_to_end/ircd.conf | 510 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 511 insertions(+), 1 deletion(-) create mode 100644 tests/end_to_end/ircd.conf (limited to 'tests/end_to_end') diff --git a/tests/end_to_end/__main__.py b/tests/end_to_end/__main__.py index f9d0447..58165d9 100644 --- a/tests/end_to_end/__main__.py +++ b/tests/end_to_end/__main__.py @@ -184,7 +184,7 @@ class BiboumiRunner(ProcessRunner): class IrcServerRunner(ProcessRunner): def __init__(self): super().__init__() - self.create = asyncio.create_subprocess_exec("charybdis", "-foreground", + self.create = asyncio.create_subprocess_exec("charybdis", "-foreground", "-configfile", "../tests/end_to_end/ircd.conf", stderr=asyncio.subprocess.PIPE) diff --git a/tests/end_to_end/ircd.conf b/tests/end_to_end/ircd.conf new file mode 100644 index 0000000..7edb3a8 --- /dev/null +++ b/tests/end_to_end/ircd.conf @@ -0,0 +1,510 @@ +/* doc/ircd.conf.example - brief example configuration file + * + * Copyright (C) 2000-2002 Hybrid Development Team + * Copyright (C) 2002-2005 ircd-ratbox development team + * Copyright (C) 2005-2006 charybdis development team + * + * See reference.conf for more information. + */ + +/* Extensions */ +#loadmodule "extensions/chm_operonly_compat"; +#loadmodule "extensions/chm_quietunreg_compat"; +#loadmodule "extensions/chm_sslonly_compat"; +#loadmodule "extensions/chm_operpeace"; +#loadmodule "extensions/createauthonly"; +#loadmodule "extensions/extb_account"; +#loadmodule "extensions/extb_canjoin"; +#loadmodule "extensions/extb_channel"; +#loadmodule "extensions/extb_combi"; +#loadmodule "extensions/extb_extgecos"; +#loadmodule "extensions/extb_hostmask"; +#loadmodule "extensions/extb_oper"; +#loadmodule "extensions/extb_realname"; +#loadmodule "extensions/extb_server"; +#loadmodule "extensions/extb_ssl"; +#loadmodule "extensions/extb_usermode"; +#loadmodule "extensions/hurt"; +#loadmodule "extensions/m_extendchans"; +#loadmodule "extensions/m_findforwards"; +#loadmodule "extensions/m_identify"; +#loadmodule "extensions/m_locops"; +#loadmodule "extensions/no_oper_invis"; +#loadmodule "extensions/sno_farconnect"; +#loadmodule "extensions/sno_globalkline"; +#loadmodule "extensions/sno_globalnickchange"; +#loadmodule "extensions/sno_globaloper"; +#loadmodule "extensions/sno_whois"; +#loadmodule "extensions/override"; +#loadmodule "extensions/no_kill_services"; + +/* + * IP cloaking extensions: use ip_cloaking_4.0 + * if you're linking 3.2 and later, otherwise use + * ip_cloaking, for compatibility with older 3.x + * releases. + */ + +#loadmodule "extensions/ip_cloaking_4.0"; +#loadmodule "extensions/ip_cloaking"; + +serverinfo { + name = "irc.localhost"; + sid = "42X"; + description = "charybdis test server"; + network_name = "StaticBox"; + + /* On multi-homed hosts you may need the following. These define + * the addresses we connect from to other servers. */ + /* for IPv4 */ + #vhost = "192.0.2.6"; + /* for IPv6 */ + #vhost6 = "2001:db8:2::6"; + + /* ssl_private_key: our ssl private key */ + ssl_private_key = "etc/ssl.key"; + + /* ssl_cert: certificate for our ssl server */ + ssl_cert = "etc/ssl.pem"; + + /* ssl_dh_params: DH parameters, generate with openssl dhparam -out dh.pem 2048 + * In general, the DH parameters size should be the same as your key's size. + * However it has been reported that some clients have broken TLS implementations which may + * choke on keysizes larger than 2048-bit, so we would recommend using 2048-bit DH parameters + * for now if your keys are larger than 2048-bit. + */ + ssl_dh_params = "etc/dh.pem"; + + /* ssld_count: number of ssld processes you want to start, if you + * have a really busy server, using N-1 where N is the number of + * cpu/cpu cores you have might be useful. A number greater than one + * can also be useful in case of bugs in ssld and because ssld needs + * two file descriptors per SSL connection. + */ + ssld_count = 1; + + /* default max clients: the default maximum number of clients + * allowed to connect. This can be changed once ircd has started by + * issuing: + * /quote set maxclients + */ + default_max_clients = 1024; + + /* nicklen: enforced nickname length (for this server only; must not + * be longer than the maximum length set while building). + */ + nicklen = 30; +}; + +admin { + name = "Lazy admin (lazya)"; + description = "StaticBox client server"; + email = "nobody@127.0.0.1"; +}; + +log { + fname_userlog = "logs/userlog"; + #fname_fuserlog = "logs/fuserlog"; + fname_operlog = "logs/operlog"; + #fname_foperlog = "logs/foperlog"; + fname_serverlog = "logs/serverlog"; + #fname_klinelog = "logs/klinelog"; + fname_killlog = "logs/killlog"; + fname_operspylog = "logs/operspylog"; + #fname_ioerrorlog = "logs/ioerror"; +}; + +/* class {} blocks MUST be specified before anything that uses them. That + * means they must be defined before auth {} and before connect {}. + */ +class "users" { + ping_time = 2 minutes; + number_per_ident = 10; + number_per_ip = 10; + number_per_ip_global = 50; + cidr_ipv4_bitlen = 24; + cidr_ipv6_bitlen = 64; + number_per_cidr = 200; + max_number = 3000; + sendq = 400 kbytes; +}; + +class "opers" { + ping_time = 5 minutes; + number_per_ip = 10; + max_number = 1000; + sendq = 1 megabyte; +}; + +class "server" { + ping_time = 5 minutes; + connectfreq = 5 minutes; + max_number = 1; + sendq = 4 megabytes; +}; + +listen { + /* defer_accept: wait for clients to send IRC handshake data before + * accepting them. if you intend to use software which depends on the + * server replying first, such as BOPM, you should disable this feature. + * otherwise, you probably want to leave it on. + */ + defer_accept = yes; + + /* If you want to listen on a specific IP only, specify host. + * host definitions apply only to the following port line. + */ + #host = "192.0.2.6"; + port = 5000, 6665 .. 6669; + # sslport = 6697; + + /* Listen on IPv6 (if you used host= above). */ + #host = "2001:db8:2::6"; + #port = 5000, 6665 .. 6669; + #sslport = 9999; +}; + +/* auth {}: allow users to connect to the ircd (OLD I:) + * auth {} blocks MUST be specified in order of precedence. The first one + * that matches a user will be used. So place spoofs first, then specials, + * then general access, then restricted. + */ +auth { + /* user: the user@host allowed to connect. Multiple IPv4/IPv6 user + * lines are permitted per auth block. This is matched against the + * hostname and IP address (using :: shortening for IPv6 and + * prepending a 0 if it starts with a colon) and can also use CIDR + * masks. + */ + user = "*@198.51.100.0/24"; + user = "*test@2001:db8:1:*"; + + /* password: an optional password that is required to use this block. + * By default this is not encrypted, specify the flag "encrypted" in + * flags = ...; below if it is. + */ + password = "letmein"; + + /* spoof: fake the users user@host to be be this. You may either + * specify a host or a user@host to spoof to. This is free-form, + * just do everyone a favour and dont abuse it. (OLD I: = flag) + */ + spoof = "I.still.hate.packets"; + + /* Possible flags in auth: + * + * encrypted | password is encrypted with mkpasswd + * spoof_notice | give a notice when spoofing hosts + * exceed_limit (old > flag) | allow user to exceed class user limits + * kline_exempt (old ^ flag) | exempt this user from k/g/xlines, + * | dnsbls, and proxies + * proxy_exempt | exempt this user from proxies + * dnsbl_exempt | exempt this user from dnsbls + * spambot_exempt | exempt this user from spambot checks + * shide_exempt | exempt this user from serverhiding + * jupe_exempt | exempt this user from generating + * warnings joining juped channels + * resv_exempt | exempt this user from resvs + * flood_exempt | exempt this user from flood limits + * USE WITH CAUTION. + * no_tilde (old - flag) | don't prefix ~ to username if no ident + * need_ident (old + flag) | require ident for user in this class + * need_ssl | require SSL/TLS for user in this class + * need_sasl | require SASL id for user in this class + */ + flags = kline_exempt, exceed_limit; + + /* class: the class the user is placed in */ + class = "opers"; +}; + +auth { + user = "*@*"; + class = "users"; +}; + +/* privset {} blocks MUST be specified before anything that uses them. That + * means they must be defined before operator {}. + */ +privset "local_op" { + privs = oper:local_kill, oper:operwall; +}; + +privset "server_bot" { + extends = "local_op"; + privs = oper:kline, oper:remoteban, snomask:nick_changes; +}; + +privset "global_op" { + extends = "local_op"; + privs = oper:global_kill, oper:routing, oper:kline, oper:unkline, oper:xline, + oper:resv, oper:mass_notice, oper:remoteban; +}; + +privset "admin" { + extends = "global_op"; + privs = oper:admin, oper:die, oper:rehash, oper:spy, oper:grant; +}; + +operator "god" { + /* name: the name of the oper must go above */ + + /* user: the user@host required for this operator. CIDR *is* + * supported now. auth{} spoofs work here, other spoofs do not. + * multiple user="" lines are supported. + */ + user = "*god@127.0.0.1"; + + /* password: the password required to oper. Unless ~encrypted is + * contained in flags = ...; this will need to be encrypted using + * mkpasswd, MD5 is supported + */ + password = "etcnjl8juSU1E"; + + /* rsa key: the public key for this oper when using Challenge. + * A password should not be defined when this is used, see + * doc/challenge.txt for more information. + */ + #rsa_public_key_file = "/usr/local/ircd/etc/oper.pub"; + + /* umodes: the specific umodes this oper gets when they oper. + * If this is specified an oper will not be given oper_umodes + * These are described above oper_only_umodes in general {}; + */ + #umodes = locops, servnotice, operwall, wallop; + + /* fingerprint: if specified, the oper's client certificate + * fingerprint will be checked against the specified fingerprint + * below. + */ + #fingerprint = "c77106576abf7f9f90cca0f63874a60f2e40a64b"; + + /* snomask: specific server notice mask on oper up. + * If this is specified an oper will not be given oper_snomask. + */ + snomask = "+Zbfkrsuy"; + + /* flags: misc options for the operator. You may prefix an option + * with ~ to disable it, e.g. ~encrypted. + * + * Default flags are encrypted. + * + * Available options: + * + * encrypted: the password above is encrypted [DEFAULT] + * need_ssl: must be using SSL/TLS to oper up + */ + flags = encrypted; + + /* privset: privileges set to grant */ + privset = "admin"; +}; + +connect "irc.uplink.com" { + host = "203.0.113.3"; + send_password = "password"; + accept_password = "anotherpassword"; + port = 6666; + hub_mask = "*"; + class = "server"; + flags = compressed, topicburst; + + #fingerprint = "c77106576abf7f9f90cca0f63874a60f2e40a64b"; + + /* If the connection is IPv6, uncomment below. + * Use 0::1, not ::1, for IPv6 localhost. */ + #aftype = ipv6; +}; + +connect "ssl.uplink.com" { + host = "203.0.113.129"; + send_password = "password"; + accept_password = "anotherpassword"; + port = 9999; + hub_mask = "*"; + class = "server"; + flags = ssl, topicburst; +}; + +service { + name = "services.int"; +}; + +cluster { + name = "*"; + flags = kline, tkline, unkline, xline, txline, unxline, resv, tresv, unresv; +}; + +shared { + oper = "*@*", "*"; + flags = all, rehash; +}; + +/* exempt {}: IPs that are exempt from Dlines and rejectcache. (OLD d:) */ +exempt { + ip = "127.0.0.1"; +}; + +channel { + use_invex = yes; + use_except = yes; + use_forward = yes; + use_knock = yes; + knock_delay = 5 minutes; + knock_delay_channel = 1 minute; + max_chans_per_user = 15; + max_chans_per_user_large = 60; + max_bans = 100; + max_bans_large = 500; + default_split_user_count = 0; + default_split_server_count = 0; + no_create_on_split = no; + no_join_on_split = no; + burst_topicwho = yes; + kick_on_split_riding = no; + only_ascii_channels = no; + resv_forcepart = yes; + channel_target_change = yes; + disable_local_channels = no; + autochanmodes = "+nt"; + displayed_usercount = 3; + strip_topic_colors = no; +}; + +serverhide { + flatten_links = yes; + links_delay = 5 minutes; + hidden = no; + disable_hidden = no; +}; + +alias "NickServ" { + target = "NickServ"; +}; + +alias "ChanServ" { + target = "ChanServ"; +}; + +alias "OperServ" { + target = "OperServ"; +}; + +alias "MemoServ" { + target = "MemoServ"; +}; + +alias "NS" { + target = "NickServ"; +}; + +alias "CS" { + target = "ChanServ"; +}; + +alias "OS" { + target = "OperServ"; +}; + +alias "MS" { + target = "MemoServ"; +}; + +general { + hide_error_messages = opers; + hide_spoof_ips = yes; + + /* + * default_umodes: umodes to enable on connect. + * If you have enabled the new ip_cloaking_4.0 module, and you want + * to make use of it, add +x to this option, i.e.: + * default_umodes = "+ix"; + * + * If you have enabled the old ip_cloaking module, and you want + * to make use of it, add +h to this option, i.e.: + * default_umodes = "+ih"; + */ + default_umodes = "+i"; + + default_operstring = "is an IRC Operator"; + default_adminstring = "is a Server Administrator"; + servicestring = "is a Network Service"; + + /* + * Nick of the network's SASL agent. Used to check whether services are here, + * SASL credentials are only sent to its server. Needs to be a service. + * + * Defaults to SaslServ if unspecified. + */ + sasl_service = "SaslServ"; + disable_fake_channels = no; + tkline_expire_notices = no; + default_floodcount = 10; + failed_oper_notice = yes; + dots_in_ident=2; + min_nonwildcard = 4; + min_nonwildcard_simple = 3; + max_accept = 100; + max_monitor = 100; + anti_nick_flood = yes; + max_nick_time = 20 seconds; + max_nick_changes = 5; + anti_spam_exit_message_time = 5 minutes; + ts_warn_delta = 30 seconds; + ts_max_delta = 5 minutes; + client_exit = yes; + collision_fnc = yes; + resv_fnc = yes; + global_snotices = yes; + dline_with_reason = yes; + kline_delay = 0 seconds; + kline_with_reason = yes; + kline_reason = "K-Lined"; + identify_service = "NickServ@services.int"; + identify_command = "IDENTIFY"; + non_redundant_klines = yes; + warn_no_nline = yes; + use_propagated_bans = yes; + stats_e_disabled = yes; + stats_c_oper_only=no; + stats_h_oper_only=no; + stats_y_oper_only=no; + stats_o_oper_only=yes; + stats_P_oper_only=no; + stats_i_oper_only=masked; + stats_k_oper_only=masked; + map_oper_only = no; + operspy_admin_only = no; + operspy_dont_care_user_info = no; + caller_id_wait = 1 minute; + pace_wait_simple = 1 second; + pace_wait = 10 seconds; + short_motd = no; + ping_cookie = no; + connect_timeout = 30 seconds; + default_ident_timeout = 5; + disable_auth = no; + no_oper_flood = yes; + max_targets = 4; + client_flood_max_lines = 20; + use_whois_actually = no; + oper_only_umodes = operwall, locops, servnotice; + oper_umodes = locops, servnotice, operwall, wallop; + oper_snomask = "+s"; + burst_away = yes; + nick_delay = 0 seconds; # 15 minutes if you want to enable this + reject_ban_time = 1 minute; + reject_after_count = 3; + reject_duration = 5 minutes; + throttle_duration = 60; + throttle_count = 4; + max_ratelimit_tokens = 30; + away_interval = 30; + certfp_method = sha1; + hide_opers_in_whois = no; +}; + +modules { + path = "modules"; + path = "modules/autoload"; +}; -- cgit v1.2.3 From 71b3f8f50b2831142ec50c725f07333ab948c169 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Thu, 21 Apr 2016 10:14:17 +0200 Subject: Correct charybdis config file path --- tests/end_to_end/__main__.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'tests/end_to_end') diff --git a/tests/end_to_end/__main__.py b/tests/end_to_end/__main__.py index 58165d9..2efbf24 100644 --- a/tests/end_to_end/__main__.py +++ b/tests/end_to_end/__main__.py @@ -9,6 +9,7 @@ import atexit import lxml.etree import sys import io +import os from functools import partial from slixmpp.xmlstream.matcher.base import MatcherBase @@ -184,7 +185,7 @@ class BiboumiRunner(ProcessRunner): class IrcServerRunner(ProcessRunner): def __init__(self): super().__init__() - self.create = asyncio.create_subprocess_exec("charybdis", "-foreground", "-configfile", "../tests/end_to_end/ircd.conf", + self.create = asyncio.create_subprocess_exec("charybdis", "-foreground", "-configfile", os.getcwd() + "/../tests/end_to_end/ircd.conf", stderr=asyncio.subprocess.PIPE) -- cgit v1.2.3 From 3ddca86851786b5d2bb24a29ba0f73567e0c2bbc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Thu, 21 Apr 2016 17:48:39 +0200 Subject: Debug and exit if IRC server startup fails --- tests/end_to_end/__main__.py | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'tests/end_to_end') diff --git a/tests/end_to_end/__main__.py b/tests/end_to_end/__main__.py index 2efbf24..0eba231 100644 --- a/tests/end_to_end/__main__.py +++ b/tests/end_to_end/__main__.py @@ -438,6 +438,10 @@ if __name__ == '__main__': asyncio.get_event_loop().run_until_complete(irc.start()) while True: res = asyncio.get_event_loop().run_until_complete(irc.process.stderr.readline()) + print(res) + if not res: + print("IRC server failed to start, exiting") + sys.exit(1) if b"now running in foreground mode" in res: break print("irc server started.") -- cgit v1.2.3 From 327c821f4630b283e08a0b0875a6e7073af2423d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Fri, 22 Apr 2016 10:23:25 +0200 Subject: Write irc output into a file --- tests/end_to_end/__main__.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'tests/end_to_end') diff --git a/tests/end_to_end/__main__.py b/tests/end_to_end/__main__.py index 0eba231..1043a95 100644 --- a/tests/end_to_end/__main__.py +++ b/tests/end_to_end/__main__.py @@ -433,14 +433,15 @@ if __name__ == '__main__': failures = 0 + irc_output = open("irc_output.txt", "w") irc = IrcServerRunner() print("Starting irc server…") asyncio.get_event_loop().run_until_complete(irc.start()) while True: res = asyncio.get_event_loop().run_until_complete(irc.process.stderr.readline()) - print(res) + irc_output.write(res.decode()) if not res: - print("IRC server failed to start, exiting") + print("IRC server failed to start, see irc_output.txt for more details. Exiting…") sys.exit(1) if b"now running in foreground mode" in res: break -- cgit v1.2.3 From ea35a12d5b20c50a1405a6eed5149f44aee59a9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Fri, 22 Apr 2016 11:57:32 +0200 Subject: Each e2e scenario can provide its own config name --- tests/end_to_end/__main__.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'tests/end_to_end') diff --git a/tests/end_to_end/__main__.py b/tests/end_to_end/__main__.py index 1043a95..c0c8542 100644 --- a/tests/end_to_end/__main__.py +++ b/tests/end_to_end/__main__.py @@ -128,13 +128,14 @@ class Scenario: etc """ - def __init__(self, name, steps): + def __init__(self, name, steps, conf="basic"): """ Steps is a list of 2-tuple: [(action, answer), (action, answer)] """ self.name = name self.steps = [] + self.conf = conf for elem in steps: if isinstance(elem, collections.Iterable): for step in elem: @@ -226,7 +227,7 @@ class BiboumiTest: filename=output_filename) with open("test.conf", "w") as fd: - fd.write(confs['basic']) + fd.write(confs[scenario.conf]) # Start the XMPP component and biboumi biboumi = BiboumiRunner(scenario.name, with_valgrind) @@ -254,7 +255,8 @@ class BiboumiTest: return not failed -confs = {'basic': +confs = { +'basic': """hostname=biboumi.localhost password=coucou db_name=biboumi.sqlite -- cgit v1.2.3 From 439ea262237f6c33342bf62c05de7f305a63ff2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Fri, 22 Apr 2016 11:55:08 +0200 Subject: Add a basic fixed_server e2e scenario --- tests/end_to_end/__main__.py | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) (limited to 'tests/end_to_end') diff --git a/tests/end_to_end/__main__.py b/tests/end_to_end/__main__.py index c0c8542..ef8c7aa 100644 --- a/tests/end_to_end/__main__.py +++ b/tests/end_to_end/__main__.py @@ -260,11 +260,20 @@ confs = { """hostname=biboumi.localhost password=coucou db_name=biboumi.sqlite -port=8811"""} +port=8811""", + +'fixed_server': +"""hostname=biboumi.localhost +password=coucou +db_name=biboumi.sqlite +port=8811 +fixed_irc_server=irc.localhost +"""} common_replacements = { 'irc_server_one': 'irc.localhost@biboumi.localhost', 'irc_host_one': 'irc.localhost', + 'biboumi_host': 'biboumi.localhost', 'resource_one': 'resource1', 'nick_one': 'Nick', 'jid_one': 'first@example.com', @@ -431,6 +440,21 @@ if __name__ == '__main__': ), partial(expect_stanza, "/message[@from='#foo%{irc_server_one}/{nick_one}'][@type='groupchat']/subject[text()='TOPIC TEST']"), ]), + Scenario("channel_basic_join_on_fixed_irc_server", + [ + handshake_sequence(), + partial(send_stanza, + ""), + connection_sequence("irc.localhost", '{jid_one}/{resource_one}'), + partial(expect_stanza, + "/message/body[text()='Mode #zgeg [+nt] by {irc_host_one}']"), + partial(expect_stanza, + ("/presence[@to='{jid_one}/{resource_one}'][@from='#zgeg@{biboumi_host}/{nick_one}']/muc_user:x/muc_user:item[@affiliation='admin'][@role='moderator']", + "/presence/muc_user:x/muc_user:status[@code='110']") + ), + partial(expect_stanza, "/message[@from='#zgeg@{biboumi_host}'][@type='groupchat']/subject[not(text())]"), + ], conf='fixed_server' + ), ) failures = 0 -- cgit v1.2.3 From 3fe55ab0b3935339e77c86c55cd81434c29edec8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Fri, 22 Apr 2016 11:55:30 +0200 Subject: Remove an unused variable --- tests/end_to_end/__main__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'tests/end_to_end') diff --git a/tests/end_to_end/__main__.py b/tests/end_to_end/__main__.py index ef8c7aa..3811d2b 100644 --- a/tests/end_to_end/__main__.py +++ b/tests/end_to_end/__main__.py @@ -483,7 +483,7 @@ if __name__ == '__main__': print("Waiting for irc server to exit…") irc.stop() - code = asyncio.get_event_loop().run_until_complete(irc.wait()) + asyncio.get_event_loop().run_until_complete(irc.wait()) if failures: print("%d test%s failed, please fix %s." % (failures, 's' if failures > 1 else '', -- cgit v1.2.3 From 376b28172b94d3950d4a20d447a9b9e49dad9ada Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Fri, 22 Apr 2016 15:02:28 +0200 Subject: tests: Split the connection sequence into two We can now insert steps between the two parts --- tests/end_to_end/__main__.py | 46 ++++++++++++++++++++++++++++---------------- 1 file changed, 29 insertions(+), 17 deletions(-) (limited to 'tests/end_to_end') diff --git a/tests/end_to_end/__main__.py b/tests/end_to_end/__main__.py index 3811d2b..d1fbb29 100644 --- a/tests/end_to_end/__main__.py +++ b/tests/end_to_end/__main__.py @@ -287,51 +287,59 @@ def handshake_sequence(): partial(send_stanza, "")) -def connection_sequence(irc_host, jid): +def connection_begin_sequence(irc_host, jid): jid = jid.format_map(common_replacements) xpath = "/message[@to='" + jid + "'][@from='irc.localhost@biboumi.localhost']/body[text()='%s']" xpath_re = "/message[@to='" + jid + "'][@from='irc.localhost@biboumi.localhost']/body[re:test(text(), '%s')]" return ( partial(expect_stanza, - xpath % ('Connecting to %s:6697 (encrypted)' % irc_host)), + xpath % ('Connecting to %s:6697 (encrypted)' % irc_host)), partial(expect_stanza, xpath % 'Connection failed: Connection refused'), partial(expect_stanza, - xpath % ('Connecting to %s:6670 (encrypted)' % irc_host)), + xpath % ('Connecting to %s:6670 (encrypted)' % irc_host)), partial(expect_stanza, xpath % 'Connection failed: Connection refused'), partial(expect_stanza, - xpath % ('Connecting to %s:6667 (not encrypted)' % irc_host)), + xpath % ('Connecting to %s:6667 (not encrypted)' % irc_host)), partial(expect_stanza, xpath % 'Connected to IRC server.'), # These two messages can be receive in any order partial(expect_stanza, - xpath_re % (r'^%s: \*\*\* (Checking Ident|Looking up your hostname...)$' % irc_host)), + xpath_re % (r'^%s: \*\*\* (Checking Ident|Looking up your hostname...)$' % irc_host)), partial(expect_stanza, - xpath_re % (r'^%s: \*\*\* (Checking Ident|Looking up your hostname...)$' % irc_host)), + xpath_re % (r'^%s: \*\*\* (Checking Ident|Looking up your hostname...)$' % irc_host)), # These three messages can be received in any order partial(expect_stanza, - xpath_re % (r'^%s: (\*\*\* Found your hostname: .*|ACK multi-prefix|\*\*\* No Ident response)$' % irc_host)), + xpath_re % (r'^%s: (\*\*\* Found your hostname: .*|ACK multi-prefix|\*\*\* No Ident response)$' % irc_host)), partial(expect_stanza, - xpath_re % (r'^%s: (\*\*\* Found your hostname: .*|ACK multi-prefix|\*\*\* No Ident response)$' % irc_host)), + xpath_re % (r'^%s: (\*\*\* Found your hostname: .*|ACK multi-prefix|\*\*\* No Ident response)$' % irc_host)), partial(expect_stanza, - xpath_re % (r'^%s: (\*\*\* Found your hostname: .*|ACK multi-prefix|\*\*\* No Ident response)$' % irc_host)), + xpath_re % (r'^%s: (\*\*\* Found your hostname: .*|ACK multi-prefix|\*\*\* No Ident response)$' % irc_host)), + ) + + +def connection_end_sequence(irc_host, jid): + jid = jid.format_map(common_replacements) + xpath = "/message[@to='" + jid + "'][@from='irc.localhost@biboumi.localhost']/body[text()='%s']" + xpath_re = "/message[@to='" + jid + "'][@from='irc.localhost@biboumi.localhost']/body[re:test(text(), '%s')]" + return ( partial(expect_stanza, - xpath_re % (r'^%s: Your host is .*$' % irc_host)), + xpath_re % (r'^%s: Your host is .*$' % irc_host)), partial(expect_stanza, - xpath_re % (r'^%s: This server was created .*$' % irc_host)), + xpath_re % (r'^%s: This server was created .*$' % irc_host)), partial(expect_stanza, - xpath_re % (r'^%s: There are \d+ users and \d+ invisible on \d+ servers$' % irc_host)), + xpath_re % (r'^%s: There are \d+ users and \d+ invisible on \d+ servers$' % irc_host)), partial(expect_stanza, - xpath_re % (r'^%s: \d+ channels formed$' % irc_host), optional=True), + xpath_re % (r'^%s: \d+ channels formed$' % irc_host), optional=True), partial(expect_stanza, - xpath_re % (r'^%s: I have \d+ clients and \d+ servers$' % irc_host)), + xpath_re % (r'^%s: I have \d+ clients and \d+ servers$' % irc_host)), partial(expect_stanza, - xpath_re % (r'^%s: \d+ \d+ Current local users \d+, max \d+$' % irc_host)), + xpath_re % (r'^%s: \d+ \d+ Current local users \d+, max \d+$' % irc_host)), partial(expect_stanza, - xpath_re % (r'^%s: \d+ \d+ Current global users \d+, max \d+$' % irc_host)), + xpath_re % (r'^%s: \d+ \d+ Current global users \d+, max \d+$' % irc_host)), partial(expect_stanza, - xpath_re % (r'^%s: Highest connection count: \d+ \(\d+ clients\) \(\d+ connections received\)$' % irc_host)), + xpath_re % (r'^%s: Highest connection count: \d+ \(\d+ clients\) \(\d+ connections received\)$' % irc_host)), partial(expect_stanza, xpath % "- This is charybdis MOTD you might replace it, but if not your friends will\n- laugh at you.\n"), partial(expect_stanza, @@ -339,6 +347,10 @@ def connection_sequence(irc_host, jid): ) +def connection_sequence(irc_host, jid): + return connection_begin_sequence(irc_host, jid) + connection_end_sequence(irc_host, jid) + + if __name__ == '__main__': atexit.register(asyncio.get_event_loop().close) -- cgit v1.2.3 From 254249dd7848020fea48dab5dd3405ae56c85b57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Fri, 22 Apr 2016 15:03:29 +0200 Subject: Test the the virtual channel --- tests/end_to_end/__main__.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) (limited to 'tests/end_to_end') diff --git a/tests/end_to_end/__main__.py b/tests/end_to_end/__main__.py index d1fbb29..4c07661 100644 --- a/tests/end_to_end/__main__.py +++ b/tests/end_to_end/__main__.py @@ -383,6 +383,22 @@ if __name__ == '__main__': ), partial(expect_stanza, "/message[@from='#foo%{irc_server_one}'][@type='groupchat']/subject[not(text())]"), ]), + Scenario("virtual_channel_join", + [ + handshake_sequence(), + partial(send_stanza, + ""), + connection_begin_sequence("irc.localhost", '{jid_one}/{resource_one}'), + # partial(expect_stanza, + # "/message/body[text()='Mode #foo [+nt] by {irc_host_one}']"), + partial(expect_stanza, + ("/presence[@to='{jid_one}/{resource_one}'][@from='%{irc_server_one}/{nick_one}']/muc_user:x/muc_user:item[@affiliation='none'][@role='participant']", + "/presence/muc_user:x/muc_user:status[@code='110']") + ), + partial(expect_stanza, "/message[@from='%{irc_server_one}'][@type='groupchat']/subject[re:test(text(), '^This is a virtual channel.*$')]"), + # partial(expect_stanza, "/message[@from='#foo%{irc_server_one}'][@type='groupchat']/subject[not(text())]"), + connection_end_sequence("irc.localhost", '{jid_one}/{resource_one}'), + ]), Scenario("channel_join_with_two_users", [ handshake_sequence(), -- cgit v1.2.3 From b98402c972e1d27656af2303754d233a23868fa5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Fri, 22 Apr 2016 15:06:45 +0200 Subject: Remove a forgotten comment [ci skip] --- tests/end_to_end/__main__.py | 1 - 1 file changed, 1 deletion(-) (limited to 'tests/end_to_end') diff --git a/tests/end_to_end/__main__.py b/tests/end_to_end/__main__.py index 4c07661..3b2e9ee 100644 --- a/tests/end_to_end/__main__.py +++ b/tests/end_to_end/__main__.py @@ -396,7 +396,6 @@ if __name__ == '__main__': "/presence/muc_user:x/muc_user:status[@code='110']") ), partial(expect_stanza, "/message[@from='%{irc_server_one}'][@type='groupchat']/subject[re:test(text(), '^This is a virtual channel.*$')]"), - # partial(expect_stanza, "/message[@from='#foo%{irc_server_one}'][@type='groupchat']/subject[not(text())]"), connection_end_sequence("irc.localhost", '{jid_one}/{resource_one}'), ]), Scenario("channel_join_with_two_users", -- cgit v1.2.3 From d452c2ff1897d5f5c519d6a821598864d8207933 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Mon, 25 Apr 2016 10:28:53 +0200 Subject: Add e2e tests for the ad-hoc listing on the server jid --- tests/end_to_end/__main__.py | 36 ++++++++++++++++++++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-) (limited to 'tests/end_to_end') diff --git a/tests/end_to_end/__main__.py b/tests/end_to_end/__main__.py index 3b2e9ee..4f8e35b 100644 --- a/tests/end_to_end/__main__.py +++ b/tests/end_to_end/__main__.py @@ -108,7 +108,8 @@ def check_xpath(xpaths, stanza): for xpath in xpaths: tree = lxml.etree.parse(io.StringIO(str(stanza))) matched = tree.xpath(xpath, namespaces={'re': 'http://exslt.org/regular-expressions', - 'muc_user': 'http://jabber.org/protocol/muc#user'}) + 'muc_user': 'http://jabber.org/protocol/muc#user', + 'disco_items': 'http://jabber.org/protocol/disco#items'}) if not matched: raise StanzaError("Received stanza “%s” did not match expected xpath “%s”" % (stanza, xpath)) @@ -260,7 +261,8 @@ confs = { """hostname=biboumi.localhost password=coucou db_name=biboumi.sqlite -port=8811""", +port=8811 +admin=admin@example.com""", 'fixed_server': """hostname=biboumi.localhost @@ -268,6 +270,7 @@ password=coucou db_name=biboumi.sqlite port=8811 fixed_irc_server=irc.localhost +admin=admin@example.com """} common_replacements = { @@ -278,6 +281,7 @@ common_replacements = { 'nick_one': 'Nick', 'jid_one': 'first@example.com', 'jid_two': 'second@example.com', + 'jid_admin': 'admin@example.com', 'nick_two': 'Bobby', } @@ -482,6 +486,34 @@ if __name__ == '__main__': partial(expect_stanza, "/message[@from='#zgeg@{biboumi_host}'][@type='groupchat']/subject[not(text())]"), ], conf='fixed_server' ), + Scenario("list_adhoc", + [ + handshake_sequence(), + partial(send_stanza, ""), + partial(expect_stanza, ("/iq[@type='result']/disco_items:query[@node='http://jabber.org/protocol/commands']", + "/iq/disco_items:query/disco_items:item[3]")), + ]), + Scenario("list_admin_adhoc", + [ + handshake_sequence(), + partial(send_stanza, ""), + partial(expect_stanza, ("/iq[@type='result']/disco_items:query[@node='http://jabber.org/protocol/commands']", + "/iq/disco_items:query/disco_items:item[5]")), + ]), + Scenario("list_adhoc_fixed_server", + [ + handshake_sequence(), + partial(send_stanza, ""), + partial(expect_stanza, ("/iq[@type='result']/disco_items:query[@node='http://jabber.org/protocol/commands']", + "/iq/disco_items:query/disco_items:item[3]")), + ], conf='fixed_server'), + Scenario("list_admin_adhoc_fixed_server", + [ + handshake_sequence(), + partial(send_stanza, ""), + partial(expect_stanza, ("/iq[@type='result']/disco_items:query[@node='http://jabber.org/protocol/commands']", + "/iq/disco_items:query/disco_items:item[5]")), + ], conf='fixed_server'), ) failures = 0 -- cgit v1.2.3 From 82e0cf99b0dd0ba48f31060656d03a9586b939c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Mon, 25 Apr 2016 10:29:55 +0200 Subject: Trivial cleanup --- tests/end_to_end/__main__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'tests/end_to_end') diff --git a/tests/end_to_end/__main__.py b/tests/end_to_end/__main__.py index 4f8e35b..786c4e9 100644 --- a/tests/end_to_end/__main__.py +++ b/tests/end_to_end/__main__.py @@ -240,6 +240,8 @@ class BiboumiTest: xmpp.process() code = asyncio.get_event_loop().run_until_complete(biboumi.wait()) + xmpp.biboumi = None + scenario.steps.clear() failed = False if not xmpp.failed: if code != self.expected_code: @@ -393,8 +395,6 @@ if __name__ == '__main__': partial(send_stanza, ""), connection_begin_sequence("irc.localhost", '{jid_one}/{resource_one}'), - # partial(expect_stanza, - # "/message/body[text()='Mode #foo [+nt] by {irc_host_one}']"), partial(expect_stanza, ("/presence[@to='{jid_one}/{resource_one}'][@from='%{irc_server_one}/{nick_one}']/muc_user:x/muc_user:item[@affiliation='none'][@role='participant']", "/presence/muc_user:x/muc_user:status[@code='110']") -- cgit v1.2.3 From 1e56c59e8241dbfc6a2526c371cc2e894f9d0f18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Mon, 25 Apr 2016 11:29:52 +0200 Subject: Include the Configure ad-hoc command on biboumi's JID for fixed_irc_server MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Because a jid like “freenode.example.org” is both the JID for the configured IRC server, and biboumi’s JID. fix #3175 --- tests/end_to_end/__main__.py | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) (limited to 'tests/end_to_end') diff --git a/tests/end_to_end/__main__.py b/tests/end_to_end/__main__.py index 786c4e9..fbfd3ce 100644 --- a/tests/end_to_end/__main__.py +++ b/tests/end_to_end/__main__.py @@ -514,6 +514,31 @@ if __name__ == '__main__': partial(expect_stanza, ("/iq[@type='result']/disco_items:query[@node='http://jabber.org/protocol/commands']", "/iq/disco_items:query/disco_items:item[5]")), ], conf='fixed_server'), + + + Scenario("list_adhoc_irc", + [ + handshake_sequence(), + partial(send_stanza, ""), + partial(expect_stanza, ("/iq[@type='result']/disco_items:query[@node='http://jabber.org/protocol/commands']", + "/iq/disco_items:query/disco_items:item[1]")), + ]), + Scenario("list_adhoc_irc_fixed_server", + [ + handshake_sequence(), + partial(send_stanza, ""), + partial(expect_stanza, ("/iq[@type='result']/disco_items:query[@node='http://jabber.org/protocol/commands']", + "/iq/disco_items:query/disco_items:item[4]")), + ], conf='fixed_server'), + Scenario("list_admin_adhoc_irc_fixed_server", + [ + handshake_sequence(), + partial(send_stanza, ""), + partial(expect_stanza, ("/iq[@type='result']/disco_items:query[@node='http://jabber.org/protocol/commands']", + "/iq/disco_items:query/disco_items:item[6]")), + ], conf='fixed_server'), + + ) failures = 0 -- cgit v1.2.3 From 98517e9a7b7873c15a5bde081a29be92c3dcfac3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Wed, 27 Apr 2016 10:22:43 +0200 Subject: Change the name of the DB in the e2e tests --- tests/end_to_end/__main__.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'tests/end_to_end') diff --git a/tests/end_to_end/__main__.py b/tests/end_to_end/__main__.py index fbfd3ce..147e132 100644 --- a/tests/end_to_end/__main__.py +++ b/tests/end_to_end/__main__.py @@ -262,14 +262,14 @@ confs = { 'basic': """hostname=biboumi.localhost password=coucou -db_name=biboumi.sqlite +db_name=e2e_test.sqlite port=8811 admin=admin@example.com""", 'fixed_server': """hostname=biboumi.localhost password=coucou -db_name=biboumi.sqlite +db_name=e2e_test.sqlite port=8811 fixed_irc_server=irc.localhost admin=admin@example.com @@ -537,8 +537,6 @@ if __name__ == '__main__': partial(expect_stanza, ("/iq[@type='result']/disco_items:query[@node='http://jabber.org/protocol/commands']", "/iq/disco_items:query/disco_items:item[6]")), ], conf='fixed_server'), - - ) failures = 0 -- cgit v1.2.3 From 0b1bf92479f7086f04404538b6813106ce7733bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Fri, 13 May 2016 15:14:29 +0200 Subject: e2e: possibility to extract a value from a stanza and reuse it in send_stanza --- tests/end_to_end/__main__.py | 48 +++++++++++++++++++++++++++++++++----------- 1 file changed, 36 insertions(+), 12 deletions(-) (limited to 'tests/end_to_end') diff --git a/tests/end_to_end/__main__.py b/tests/end_to_end/__main__.py index 147e132..815093e 100644 --- a/tests/end_to_end/__main__.py +++ b/tests/end_to_end/__main__.py @@ -67,6 +67,8 @@ class XMPPComponent(slixmpp.BaseXMPP): self.failed = False self.accepting_server = None + self.saved_values = {} + def error(self, message): print("Failure: %s" % (message,)) self.scenario.steps = [] @@ -104,19 +106,27 @@ class XMPPComponent(slixmpp.BaseXMPP): pass -def check_xpath(xpaths, stanza): - for xpath in xpaths: - tree = lxml.etree.parse(io.StringIO(str(stanza))) - matched = tree.xpath(xpath, namespaces={'re': 'http://exslt.org/regular-expressions', - 'muc_user': 'http://jabber.org/protocol/muc#user', - 'disco_items': 'http://jabber.org/protocol/disco#items'}) +def match(stanza, xpath): + tree = lxml.etree.parse(io.StringIO(str(stanza))) + matched = tree.xpath(xpath, namespaces={'re': 'http://exslt.org/regular-expressions', + 'muc_user': 'http://jabber.org/protocol/muc#user', + 'disco_items': 'http://jabber.org/protocol/disco#items', + 'dataform': 'jabber:x:data'}) + return matched + + +def check_xpath(xpaths, xmpp, after, stanza): + for i, xpath in enumerate(xpaths): + matched = match(stanza, xpath) if not matched: raise StanzaError("Received stanza “%s” did not match expected xpath “%s”" % (stanza, xpath)) + if after: + after(stanza, xmpp) -def check_xpath_optional(xpaths, stanza): +def check_xpath_optional(xpaths, xmpp, after, stanza): try: - check_xpath(xpaths, stanza) + check_xpath(xpaths, xmpp, after, stanza) except StanzaError: raise SkipStepError() @@ -149,6 +159,7 @@ class ProcessRunner: def __init__(self): self.process = None self.signal_sent = False + self.create = None @asyncio.coroutine def start(self): @@ -192,16 +203,18 @@ class IrcServerRunner(ProcessRunner): def send_stanza(stanza, xmpp, biboumi): - xmpp.send_raw(stanza.format_map(common_replacements)) + replacements = common_replacements + replacements.update(xmpp.saved_values) + xmpp.send_raw(stanza.format_map(replacements)) asyncio.get_event_loop().call_soon(xmpp.run_scenario) -def expect_stanza(xpaths, xmpp, biboumi, optional=False): +def expect_stanza(xpaths, xmpp, biboumi, optional=False, after=None): check_func = check_xpath if not optional else check_xpath_optional if isinstance(xpaths, str): - xmpp.stanza_checker = partial(check_func, [xpaths.format_map(common_replacements)]) + xmpp.stanza_checker = partial(check_func, [xpaths.format_map(common_replacements)], xmpp, after) elif isinstance(xpaths, tuple): - xmpp.stanza_checker = partial(check_func, [xpath.format_map(common_replacements) for xpath in xpaths]) + xmpp.stanza_checker = partial(check_func, [xpath.format_map(common_replacements) for xpath in xpaths], xmpp, after) else: print("Warning, from argument type passed to expect_stanza: %s" % (type(xpaths))) @@ -252,6 +265,8 @@ class BiboumiTest: else: failed = True + xmpp.saved_values.clear() + if xmpp.server: xmpp.accepting_server.close() @@ -357,6 +372,15 @@ def connection_sequence(irc_host, jid): return connection_begin_sequence(irc_host, jid) + connection_end_sequence(irc_host, jid) +def extract_attribute(xpath, name, stanza): + matched = match(stanza, xpath) + return matched[0].get(name) + + +def save_value(name, func, stanza, xmpp): + xmpp.saved_values[name] = func(stanza) + + if __name__ == '__main__': atexit.register(asyncio.get_event_loop().close) -- cgit v1.2.3 From 8f80b9c53011561f8eee9875504e526c174317a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Fri, 13 May 2016 15:15:10 +0200 Subject: Test the execution of the hello command --- tests/end_to_end/__main__.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) (limited to 'tests/end_to_end') diff --git a/tests/end_to_end/__main__.py b/tests/end_to_end/__main__.py index 815093e..706d177 100644 --- a/tests/end_to_end/__main__.py +++ b/tests/end_to_end/__main__.py @@ -111,6 +111,7 @@ def match(stanza, xpath): matched = tree.xpath(xpath, namespaces={'re': 'http://exslt.org/regular-expressions', 'muc_user': 'http://jabber.org/protocol/muc#user', 'disco_items': 'http://jabber.org/protocol/disco#items', + 'commands': 'http://jabber.org/protocol/commands', 'dataform': 'jabber:x:data'}) return matched @@ -561,6 +562,23 @@ if __name__ == '__main__': partial(expect_stanza, ("/iq[@type='result']/disco_items:query[@node='http://jabber.org/protocol/commands']", "/iq/disco_items:query/disco_items:item[6]")), ], conf='fixed_server'), + + Scenario("execute_hello_adhoc_command", + [ + handshake_sequence(), + partial(send_stanza, ""), + partial(expect_stanza, ("/iq[@type='result']/commands:command[@node='hello'][@sessionid][@status='executing']", + "/iq/commands:command/dataform:x[@type='form']/dataform:title[text()='Configure your name.']", + "/iq/commands:command/dataform:x[@type='form']/dataform:instructions[text()='Please provide your name.']", + "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='text-single']/dataform:required", + "/iq/commands:command/commands:actions/commands:next", + ), + after = partial(save_value, "sessionid", partial(extract_attribute, "/iq[@type='result']/commands:command[@node='hello']", "sessionid")) + + ), + partial(send_stanza, "COUCOU"), + partial(expect_stanza, "/iq[@type='result']/commands:command[@node='hello'][@status='completed']/commands:note[@type='info'][text()='Hello COUCOU!']") + ]), ) failures = 0 -- cgit v1.2.3 From 367de4826c3c7298a88e80c92e325081a3c3d794 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Wed, 18 May 2016 22:53:17 +0200 Subject: Associate a bridge with a bare JID instead of a full JID ref #2556 --- tests/end_to_end/__main__.py | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) (limited to 'tests/end_to_end') diff --git a/tests/end_to_end/__main__.py b/tests/end_to_end/__main__.py index 706d177..630f59a 100644 --- a/tests/end_to_end/__main__.py +++ b/tests/end_to_end/__main__.py @@ -296,6 +296,7 @@ common_replacements = { 'irc_host_one': 'irc.localhost', 'biboumi_host': 'biboumi.localhost', 'resource_one': 'resource1', + 'resource_two': 'resource2', 'nick_one': 'Nick', 'jid_one': 'first@example.com', 'jid_two': 'second@example.com', @@ -579,6 +580,31 @@ if __name__ == '__main__': partial(send_stanza, "COUCOU"), partial(expect_stanza, "/iq[@type='result']/commands:command[@node='hello'][@status='completed']/commands:note[@type='info'][text()='Hello COUCOU!']") ]), + Scenario("simple_multisessionnick", + [ + handshake_sequence(), + partial(send_stanza, + ""), + connection_sequence("irc.localhost", '{jid_one}/{resource_one}'), + partial(expect_stanza, + "/message/body[text()='Mode #foo [+nt] by {irc_host_one}']"), + partial(expect_stanza, + ("/presence[@to='{jid_one}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_one}']/muc_user:x/muc_user:item[@affiliation='admin'][@role='moderator']", + "/presence/muc_user:x/muc_user:status[@code='110']") + ), + partial(expect_stanza, "/message[@from='#foo%{irc_server_one}'][@type='groupchat']/subject[not(text())]"), + + # The other resources joins the same room, with the same nick + partial(send_stanza, + ""), + # We receive our own join + partial(expect_stanza, + ("/presence[@to='{jid_one}/{resource_two}'][@from='#foo%{irc_server_one}/{nick_one}']/muc_user:x/muc_user:item[@affiliation='admin'][@role='moderator']", + "/presence/muc_user:x/muc_user:status[@code='110']") + ), + partial(expect_stanza, "/message[@from='#foo%{irc_server_one}'][@type='groupchat']/subject[not(text())]"), + ]), + ) failures = 0 -- cgit v1.2.3 From 199f010f523b1613afcc760f2b9aedc1888ab7a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Wed, 8 Jun 2016 11:49:44 +0200 Subject: Add a basic e2e test for channel and private messages --- tests/end_to_end/__main__.py | 53 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 51 insertions(+), 2 deletions(-) (limited to 'tests/end_to_end') diff --git a/tests/end_to_end/__main__.py b/tests/end_to_end/__main__.py index 630f59a..186ada1 100644 --- a/tests/end_to_end/__main__.py +++ b/tests/end_to_end/__main__.py @@ -302,6 +302,7 @@ common_replacements = { 'jid_two': 'second@example.com', 'jid_admin': 'admin@example.com', 'nick_two': 'Bobby', + 'lower_nick_one': 'nick', } @@ -592,7 +593,7 @@ if __name__ == '__main__': ("/presence[@to='{jid_one}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_one}']/muc_user:x/muc_user:item[@affiliation='admin'][@role='moderator']", "/presence/muc_user:x/muc_user:status[@code='110']") ), - partial(expect_stanza, "/message[@from='#foo%{irc_server_one}'][@type='groupchat']/subject[not(text())]"), + partial(expect_stanza, "/message[@from='#foo%{irc_server_one}'][@type='groupchat'][@to='{jid_one}/{resource_one}']/subject[not(text())]"), # The other resources joins the same room, with the same nick partial(send_stanza, @@ -602,9 +603,57 @@ if __name__ == '__main__': ("/presence[@to='{jid_one}/{resource_two}'][@from='#foo%{irc_server_one}/{nick_one}']/muc_user:x/muc_user:item[@affiliation='admin'][@role='moderator']", "/presence/muc_user:x/muc_user:status[@code='110']") ), - partial(expect_stanza, "/message[@from='#foo%{irc_server_one}'][@type='groupchat']/subject[not(text())]"), + partial(expect_stanza, "/message[@from='#foo%{irc_server_one}'][@type='groupchat'][@to='{jid_one}/{resource_two}']/subject[not(text())]"), ]), + Scenario("channel_messages", + [ + handshake_sequence(), + # First user joins + partial(send_stanza, + ""), + connection_sequence("irc.localhost", '{jid_one}/{resource_one}'), + partial(expect_stanza, + "/message/body[text()='Mode #foo [+nt] by {irc_host_one}']"), + partial(expect_stanza, + ("/presence[@to='{jid_one}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_one}']/muc_user:x/muc_user:item[@affiliation='admin'][@jid='~nick@localhost'][@role='moderator']", + "/presence/muc_user:x/muc_user:status[@code='110']") + ), + partial(expect_stanza, "/message[@from='#foo%{irc_server_one}'][@type='groupchat']/subject[not(text())]"), + + # Second user joins + partial(send_stanza, + ""), + connection_sequence("irc.localhost", '{jid_two}/{resource_one}'), + # Our presence, sent to the other user + partial(expect_stanza, + ("/presence[@to='{jid_one}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_two}']/muc_user:x/muc_user:item[@affiliation='none'][@jid='~bobby@localhost'][@role='participant']",)), + # The other user presence + partial(expect_stanza, + "/presence[@to='{jid_second}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_one}']/muc_user:x/muc_user:item[@affiliation='none'][@jid='~nick@localhost'][@role='participant']"), + # Our own presence + partial(expect_stanza, + ("/presence[@to='{jid_two}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_two}']/muc_user:x/muc_user:item[@affiliation='none'][@jid='~bobby@localhost'][@role='participant']", + "/presence/muc_user:x/muc_user:status[@code='110']") + ), + partial(expect_stanza, "/message[@from='#foo%{irc_server_one}'][@type='groupchat']/subject[not(text())]"), + # Send a channel message + partial(send_stanza, "coucou"), + # Receive the message, forwarded to the two users + partial(expect_stanza, "/message[@from='#foo%{irc_server_one}/{nick_one}'][@to='{jid_one}/{resource_one}'][@type='groupchat']/body[text()='coucou']"), + partial(expect_stanza, "/message[@from='#foo%{irc_server_one}/{nick_one}'][@to='{jid_two}/{resource_one}'][@type='groupchat']/body[text()='coucou']"), + + # Send a private message, to a in-room JID + partial(send_stanza, "coucou in private"), + # Message is received with a server-wide JID + partial(expect_stanza, "/message[@from='{lower_nick_one}!{irc_server_one}'][@to='{jid_two}/{resource_one}'][@type='chat']/body[text()='coucou in private']"), + + # Respond to the message, to the server-wide JID + partial(send_stanza, "yes"), + # The response is received from the in-room JID + partial(expect_stanza, "/message[@from='#foo%{irc_server_one}/{nick_two}'][@to='{jid_one}/{resource_one}'][@type='chat']/body[text()='yes']"), + ] + ) ) failures = 0 -- cgit v1.2.3 From 7540f6793342aeb1e9cec53208b347ca8a346c36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Thu, 9 Jun 2016 16:32:23 +0200 Subject: Add some e2e tests to check that private messages come from the right JIDs --- tests/end_to_end/__main__.py | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) (limited to 'tests/end_to_end') diff --git a/tests/end_to_end/__main__.py b/tests/end_to_end/__main__.py index 186ada1..1408ea9 100644 --- a/tests/end_to_end/__main__.py +++ b/tests/end_to_end/__main__.py @@ -610,7 +610,7 @@ if __name__ == '__main__': handshake_sequence(), # First user joins partial(send_stanza, - ""), + ""), connection_sequence("irc.localhost", '{jid_one}/{resource_one}'), partial(expect_stanza, "/message/body[text()='Mode #foo [+nt] by {irc_host_one}']"), @@ -652,6 +652,27 @@ if __name__ == '__main__': partial(send_stanza, "yes"), # The response is received from the in-room JID partial(expect_stanza, "/message[@from='#foo%{irc_server_one}/{nick_two}'][@to='{jid_one}/{resource_one}'][@type='chat']/body[text()='yes']"), + + ## Do the exact same thing, from a different chan, + # to check if the response comes from the right JID + + # Join the virtual channel + partial(send_stanza, + ""), + partial(expect_stanza, + "/presence/muc_user:x/muc_user:status[@code='110']"), + partial(expect_stanza, "/message[@from='%{irc_server_one}'][@type='groupchat']/subject"), + + + # Send a private message, to a in-room JID + partial(send_stanza, "re in private"), + # Message is received with a server-wide JID + partial(expect_stanza, "/message[@from='{lower_nick_one}!{irc_server_one}'][@to='{jid_two}/{resource_one}'][@type='chat']/body[text()='re in private']"), + + # Respond to the message, to the server-wide JID + partial(send_stanza, "re"), + # The response is received from the in-room JID + partial(expect_stanza, "/message[@from='%{irc_server_one}/{nick_two}'][@to='{jid_one}/{resource_one}'][@type='chat']/body[text()='re']"), ] ) ) -- cgit v1.2.3 From 272c0e4995f2fe94fb2366c15453fdada341861a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Fri, 10 Jun 2016 10:00:48 +0200 Subject: Reset the preferred private JID when all resources leave a room MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For example if we are talking in private with nick Joe from room #foo, and then we leave that room, we start receiving Joe’s message from the server-wide JID e2e tests included!!! --- tests/end_to_end/__main__.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'tests/end_to_end') diff --git a/tests/end_to_end/__main__.py b/tests/end_to_end/__main__.py index 1408ea9..0e2ca64 100644 --- a/tests/end_to_end/__main__.py +++ b/tests/end_to_end/__main__.py @@ -303,6 +303,7 @@ common_replacements = { 'jid_admin': 'admin@example.com', 'nick_two': 'Bobby', 'lower_nick_one': 'nick', + 'lower_nick_two': 'bobby', } @@ -673,6 +674,17 @@ if __name__ == '__main__': partial(send_stanza, "re"), # The response is received from the in-room JID partial(expect_stanza, "/message[@from='%{irc_server_one}/{nick_two}'][@to='{jid_one}/{resource_one}'][@type='chat']/body[text()='re']"), + + # Now we leave the room, to check if the subsequent private messages are still received properly + partial(send_stanza, + ""), + partial(expect_stanza, + "/presence[@type='unavailable']/muc_user:x/muc_user:status[@code='110']"), + + # The private messages from this nick should now come (again) from the server-wide JID + partial(send_stanza, "hihihoho"), + partial(expect_stanza, + "/message[@from='{lower_nick_two}!{irc_server_one}'][@to='{jid_one}/{resource_one}']"), ] ) ) -- cgit v1.2.3 From 4e959a3869c4a69b9d59de69694644c37380ff11 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Thu, 23 Jun 2016 22:50:05 +0200 Subject: Add a simple e2e test that joins a channel with xep0106-encoded name --- tests/end_to_end/__main__.py | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) (limited to 'tests/end_to_end') diff --git a/tests/end_to_end/__main__.py b/tests/end_to_end/__main__.py index 0e2ca64..7859202 100644 --- a/tests/end_to_end/__main__.py +++ b/tests/end_to_end/__main__.py @@ -686,7 +686,22 @@ if __name__ == '__main__': partial(expect_stanza, "/message[@from='{lower_nick_two}!{irc_server_one}'][@to='{jid_one}/{resource_one}']"), ] - ) + ), + Scenario("encoded_channel_join", + [ + handshake_sequence(), + partial(send_stanza, + ""), + connection_sequence("irc.localhost", '{jid_one}/{resource_one}'), + partial(expect_stanza, + "/message/body[text()='Mode #biboumi@louiz.org:80 [+nt] by {irc_host_one}']"), + partial(expect_stanza, + ("/presence[@to='{jid_one}/{resource_one}'][@from='#biboumi\\40louiz.org\\3a80%{irc_server_one}/{nick_one}']/muc_user:x/muc_user:item[@affiliation='admin'][@role='moderator']", + "/presence/muc_user:x/muc_user:status[@code='110']") + ), + partial(expect_stanza, "/message[@from='#biboumi\\40louiz.org\\3a80%{irc_server_one}'][@type='groupchat']/subject[not(text())]"), + ]), + ) failures = 0 -- cgit v1.2.3 From 7ae29890c3fd498d8d4fecd64cfc0da703b77a1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Fri, 24 Jun 2016 11:25:04 +0200 Subject: Add a e2e for self-ping, with a single channel resource --- tests/end_to_end/__main__.py | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) (limited to 'tests/end_to_end') diff --git a/tests/end_to_end/__main__.py b/tests/end_to_end/__main__.py index 7859202..ebd2a73 100644 --- a/tests/end_to_end/__main__.py +++ b/tests/end_to_end/__main__.py @@ -701,7 +701,32 @@ if __name__ == '__main__': ), partial(expect_stanza, "/message[@from='#biboumi\\40louiz.org\\3a80%{irc_server_one}'][@type='groupchat']/subject[not(text())]"), ]), + Scenario("self_ping_on_real_channel", + [ + handshake_sequence(), + partial(send_stanza, + ""), + connection_sequence("irc.localhost", '{jid_one}/{resource_one}'), + partial(expect_stanza, + "/message/body[text()='Mode #foo [+nt] by {irc_host_one}']"), + partial(expect_stanza, + ("/presence[@to='{jid_one}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_one}']/muc_user:x/muc_user:item[@affiliation='admin'][@role='moderator']", + "/presence/muc_user:x/muc_user:status[@code='110']") + ), + partial(expect_stanza, "/message[@from='#foo%{irc_server_one}'][@type='groupchat']/subject[not(text())]"), + # Send a ping to ourself + partial(send_stanza, + ""), + # We receive our own ping request, + partial(expect_stanza, + "/iq[@from='{lower_nick_one}!{irc_server_one}'][@type='get'][@to='{jid_one}/{resource_one}'][@id='gnip_tsrif']"), + # Respond to the request + partial(send_stanza, + ""), + partial(expect_stanza, + "/iq[@from='#foo%{irc_server_one}/{nick_one}'][@type='result'][@to='{jid_one}/{resource_one}'][@id='first_ping']"), + ]), ) failures = 0 -- cgit v1.2.3 From 2fe8878169b094a12d74d69ea97314d172647ea8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Fri, 24 Jun 2016 11:43:13 +0200 Subject: Add a e2e test that checks self-ping with multiple resources behind one nick --- tests/end_to_end/__main__.py | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) (limited to 'tests/end_to_end') diff --git a/tests/end_to_end/__main__.py b/tests/end_to_end/__main__.py index ebd2a73..06aa1ce 100644 --- a/tests/end_to_end/__main__.py +++ b/tests/end_to_end/__main__.py @@ -726,6 +726,40 @@ if __name__ == '__main__': ""), partial(expect_stanza, "/iq[@from='#foo%{irc_server_one}/{nick_one}'][@type='result'][@to='{jid_one}/{resource_one}'][@id='first_ping']"), + + # Now join the same room, from the same bare JID, behind the same nick + partial(send_stanza, + ""), + partial(expect_stanza, + ("/presence[@to='{jid_one}/{resource_two}'][@from='#foo%{irc_server_one}/{nick_one}']/muc_user:x/muc_user:item[@affiliation='admin'][@role='moderator']", + "/presence/muc_user:x/muc_user:status[@code='110']") + ), + partial(expect_stanza, "/message[@from='#foo%{irc_server_one}'][@type='groupchat'][@to='{jid_one}/{resource_two}']/subject[not(text())]"), + + # And re-send a self ping + partial(send_stanza, + ""), + # We receive our own ping request. Note that we don't know the to value, it could be one of our two resources. + partial(expect_stanza, + "/iq[@from='{lower_nick_one}!{irc_server_one}'][@type='get'][@to][@id='gnip_dnoces']", + after = partial(save_value, "to", partial(extract_attribute, "/iq", "to"))), + # Respond to the request, using the extracted 'to' value as our 'from' + partial(send_stanza, + ""), + partial(expect_stanza, + "/iq[@from='#foo%{irc_server_one}/{nick_one}'][@type='result'][@to='{jid_one}/{resource_one}'][@id='second_ping']"), + + ## And re-do exactly the same thing, just change the resource initiating the self ping + partial(send_stanza, + ""), + partial(expect_stanza, + "/iq[@from='{lower_nick_one}!{irc_server_one}'][@type='get'][@to][@id='gnip_driht']", + after = partial(save_value, "to", partial(extract_attribute, "/iq", "to"))), + partial(send_stanza, + ""), + partial(expect_stanza, + "/iq[@from='#foo%{irc_server_one}/{nick_one}'][@type='result'][@to='{jid_one}/{resource_two}'][@id='third_ping']"), + ]), ) -- cgit v1.2.3 From 79002aa5c9ada3a09d5f09cde0cac4c456adaa47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Wed, 29 Jun 2016 19:52:43 +0200 Subject: Run e2e through with valgrind if BIBOUMI_E2E_VALGRIND is set in the env --- tests/end_to_end/__main__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'tests/end_to_end') diff --git a/tests/end_to_end/__main__.py b/tests/end_to_end/__main__.py index 06aa1ce..e6ed911 100644 --- a/tests/end_to_end/__main__.py +++ b/tests/end_to_end/__main__.py @@ -782,7 +782,7 @@ if __name__ == '__main__': for scenario in scenarios: test = BiboumiTest(scenario) - if not test.run(False): + if not test.run(os.getenv("E2E_BIBOUMI_VALGRIND") is not None): print("You can check the files slixmpp_%s_output.txt and biboumi_%s_output.txt to help you debug." % (scenario.name, scenario.name)) failures += 1 -- cgit v1.2.3 From 8cef7303187297bc98d8a9ddceef4674a9297e9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Sat, 2 Jul 2016 15:08:40 +0200 Subject: Add a valgrind suppression file --- tests/end_to_end/__main__.py | 2 +- tests/end_to_end/biboumi.supp | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) create mode 100644 tests/end_to_end/biboumi.supp (limited to 'tests/end_to_end') diff --git a/tests/end_to_end/__main__.py b/tests/end_to_end/__main__.py index e6ed911..0223e47 100644 --- a/tests/end_to_end/__main__.py +++ b/tests/end_to_end/__main__.py @@ -187,7 +187,7 @@ class BiboumiRunner(ProcessRunner): self.name = name self.fd = open("biboumi_%s_output.txt" % (name,), "w") if with_valgrind: - self.create = asyncio.create_subprocess_exec("valgrind", "--leak-check=full", "--show-leak-kinds=all", + self.create = asyncio.create_subprocess_exec("valgrind", "--suppressions=" + (os.environ.get("E2E_BIBOUMI_SUPP_DIR") or "") + "biboumi.supp", "--leak-check=full", "--show-leak-kinds=all", "--errors-for-leak-kinds=all", "--error-exitcode=16", "./biboumi", "test.conf", stdin=None, stdout=self.fd, stderr=self.fd, loop=None, limit=None) diff --git a/tests/end_to_end/biboumi.supp b/tests/end_to_end/biboumi.supp new file mode 100644 index 0000000..d153665 --- /dev/null +++ b/tests/end_to_end/biboumi.supp @@ -0,0 +1,10 @@ +{ + stdlibc++ thingy + Memcheck:Leak + match-leak-kinds: reachable + fun:malloc + ... + fun:call_init.part.0 + fun:_dl_init + ... +} -- cgit v1.2.3 From 0ce75ab52111ba27ca99961057b36b68f0a135a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Sun, 3 Jul 2016 15:43:11 +0200 Subject: Properly remove the resource from the server when we leave the last channel --- tests/end_to_end/__main__.py | 46 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 44 insertions(+), 2 deletions(-) (limited to 'tests/end_to_end') diff --git a/tests/end_to_end/__main__.py b/tests/end_to_end/__main__.py index 0223e47..d1d653d 100644 --- a/tests/end_to_end/__main__.py +++ b/tests/end_to_end/__main__.py @@ -229,7 +229,7 @@ class BiboumiTest: def __init__(self, scenario, expected_code=0): self.scenario = scenario - self.expected_code = 0 + self.expected_code = expected_code def run(self, with_valgrind=True): print("Running scenario: %s%s" % (self.scenario.name, " (with valgrind)" if with_valgrind else '')) @@ -355,6 +355,8 @@ def connection_end_sequence(irc_host, jid): xpath_re % (r'^%s: This server was created .*$' % irc_host)), partial(expect_stanza, xpath_re % (r'^%s: There are \d+ users and \d+ invisible on \d+ servers$' % irc_host)), + partial(expect_stanza, + xpath_re % (r'^%s: \d+ unknown connection\(s\)$' % irc_host), optional=True), partial(expect_stanza, xpath_re % (r'^%s: \d+ channels formed$' % irc_host), optional=True), partial(expect_stanza, @@ -582,7 +584,7 @@ if __name__ == '__main__': partial(send_stanza, "COUCOU"), partial(expect_stanza, "/iq[@type='result']/commands:command[@node='hello'][@status='completed']/commands:note[@type='info'][text()='Hello COUCOU!']") ]), - Scenario("simple_multisessionnick", + Scenario("multisessionnick", [ handshake_sequence(), partial(send_stanza, @@ -605,6 +607,46 @@ if __name__ == '__main__': "/presence/muc_user:x/muc_user:status[@code='110']") ), partial(expect_stanza, "/message[@from='#foo%{irc_server_one}'][@type='groupchat'][@to='{jid_one}/{resource_two}']/subject[not(text())]"), + + # A different user joins the same room + partial(send_stanza, + ""), + connection_sequence("irc.localhost", '{jid_two}/{resource_one}'), + + partial(expect_stanza, + ("/presence[@to='{jid_one}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_two}']",)), + partial(expect_stanza, + ("/presence[@to='{jid_one}/{resource_two}'][@from='#foo%{irc_server_one}/{nick_two}']",)), + + partial(expect_stanza, + "/presence[@to='{jid_second}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_one}']"), + partial(expect_stanza, + ("/presence[@to='{jid_two}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_two}']", + "/presence/muc_user:x/muc_user:status[@code='110']") + ), + partial(expect_stanza, "/message[@from='#foo%{irc_server_one}'][@type='groupchat']/subject[not(text())]"), + + # That second user sends a private message to the first one + partial(send_stanza, "RELLO"), + # Message is received with a server-wide JID, by the two resources behind nick_one + partial(expect_stanza, "/message[@from='{lower_nick_two}!{irc_server_one}'][@to='{jid_one}/{resource_one}'][@type='chat']/body[text()='RELLO']"), + partial(expect_stanza, "/message[@from='{lower_nick_two}!{irc_server_one}'][@to='{jid_one}/{resource_two}'][@type='chat']/body[text()='RELLO']"), + + # One resource leaves the server entirely. + partial(send_stanza, ""), + # The leave is forwarded only to us + partial(expect_stanza, + ("/presence[@type='unavailable']/muc_user:x/muc_user:status[@code='110']", + "/presence/status[text()='Biboumi note: 1 resources are still in this channel.']") + ), + # The second user sends two new private messages to the first user + partial(send_stanza, "first"), + partial(send_stanza, "second"), + # The first user receives the two messages, on the connected resource, once each + partial(expect_stanza, "/message[@from='{lower_nick_two}!{irc_server_one}'][@to='{jid_one}/{resource_one}'][@type='chat']/body[text()='first']"), + partial(expect_stanza, "/message[@from='{lower_nick_two}!{irc_server_one}'][@to='{jid_one}/{resource_one}'][@type='chat']/body[text()='second']"), + + ]), Scenario("channel_messages", [ -- cgit v1.2.3 From 47902953131a6fc6fc3ab24d15f5fd163c2f7add Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Sun, 31 Jul 2016 17:55:47 +0200 Subject: test kick --- tests/end_to_end/__main__.py | 61 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 59 insertions(+), 2 deletions(-) (limited to 'tests/end_to_end') diff --git a/tests/end_to_end/__main__.py b/tests/end_to_end/__main__.py index d1d653d..2442d03 100644 --- a/tests/end_to_end/__main__.py +++ b/tests/end_to_end/__main__.py @@ -220,6 +220,11 @@ def expect_stanza(xpaths, xmpp, biboumi, optional=False, after=None): print("Warning, from argument type passed to expect_stanza: %s" % (type(xpaths))) +def log_message(message, xmpp, biboumi): + print("%s" % (message,)) + asyncio.get_event_loop().call_soon(xmpp.run_scenario) + + class BiboumiTest: """ Spawns a biboumi process and a fake XMPP Component that will run a @@ -252,7 +257,6 @@ class BiboumiTest: asyncio.get_event_loop().call_soon(xmpp.run_scenario) xmpp.process() - code = asyncio.get_event_loop().run_until_complete(biboumi.wait()) xmpp.biboumi = None scenario.steps.clear() @@ -436,6 +440,8 @@ if __name__ == '__main__': [ handshake_sequence(), # First user joins + partial(log_message, + "First user joins"), partial(send_stanza, ""), connection_sequence("irc.localhost", '{jid_one}/{resource_one}'), @@ -448,16 +454,24 @@ if __name__ == '__main__': partial(expect_stanza, "/message[@from='#foo%{irc_server_one}'][@type='groupchat']/subject[not(text())]"), # Second user joins + partial(log_message, + "Second user joins"), partial(send_stanza, ""), connection_sequence("irc.localhost", '{jid_two}/{resource_one}'), # Our presence, sent to the other user + partial(log_message, + "Our presence sent to the other user"), partial(expect_stanza, ("/presence[@to='{jid_one}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_two}']/muc_user:x/muc_user:item[@affiliation='none'][@jid='~bobby@localhost'][@role='participant']",)), # The other user presence + partial(log_message, + "The other user presence"), partial(expect_stanza, "/presence[@to='{jid_second}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_one}']/muc_user:x/muc_user:item[@affiliation='none'][@jid='~nick@localhost'][@role='participant']"), # Our own presence + partial(log_message, + "Our own presence"), partial(expect_stanza, ("/presence[@to='{jid_two}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_two}']/muc_user:x/muc_user:item[@affiliation='none'][@jid='~bobby@localhost'][@role='participant']", "/presence/muc_user:x/muc_user:status[@code='110']") @@ -790,7 +804,6 @@ if __name__ == '__main__': ""), partial(expect_stanza, "/iq[@from='#foo%{irc_server_one}/{nick_one}'][@type='result'][@to='{jid_one}/{resource_one}'][@id='second_ping']"), - ## And re-do exactly the same thing, just change the resource initiating the self ping partial(send_stanza, ""), @@ -803,6 +816,50 @@ if __name__ == '__main__': "/iq[@from='#foo%{irc_server_one}/{nick_one}'][@type='result'][@to='{jid_one}/{resource_two}'][@id='third_ping']"), ]), + Scenario("simple_kick", + [ + handshake_sequence(), + # First user joins + partial(send_stanza, + ""), + connection_sequence("irc.localhost", '{jid_one}/{resource_one}'), + partial(expect_stanza, "/message"), + partial(expect_stanza, "/presence/muc_user:x/muc_user:status[@code='110']"), + partial(expect_stanza, "/message[@type='groupchat']/subject"), + + # Second user joins + partial(send_stanza, + ""), + connection_sequence("irc.localhost", '{jid_two}/{resource_one}'), + partial(expect_stanza, + "/presence/muc_user:x/muc_user:item[@affiliation='none'][@role='participant']",), + + partial(expect_stanza, + "/presence/muc_user:x/muc_user:item[@affiliation='admin'][@role='moderator']"), + partial(expect_stanza, + "/presence/muc_user:x/muc_user:status[@code='110']"), + partial(expect_stanza, "/message/subject"), + + # Moderator kicks participant + partial(log_message, "Moderator kicks participant"), + partial(send_stanza, + "reported"), + partial(log_message, "Presence is sent to everyone"), + partial(expect_stanza, + ("/presence[@type='unavailable'][@to='{jid_second}/{resource_one}']/muc_user:x/muc_user:item[@role='none']/muc_user:actor[@nick='{nick_one}']", + "/presence/muc_user:x/muc_user:item/muc_user:reason[text()='reported']", + "/presence/muc_user:x/muc_user:status[@code='307']", + "/presence/muc_user:x/muc_user:status[@code='110']" + )), + partial(expect_stanza, + ("/presence[@type='unavailable']/muc_user:x/muc_user:item[@role='none']/muc_user:actor[@nick='{nick_one}']", + "/presence/muc_user:x/muc_user:item/muc_user:reason[text()='reported']", + "/presence/muc_user:x/muc_user:status[@code='307']", + ), + ), + partial(expect_stanza, + "/iq[@id='kick1'][@type='result']"), + ]), ) failures = 0 -- cgit v1.2.3 From 3fbe01bb30a38111ce058ff78b517234280c0a09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Tue, 2 Aug 2016 11:11:27 +0200 Subject: Test the kick on a multisession nick --- tests/end_to_end/__main__.py | 60 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) (limited to 'tests/end_to_end') diff --git a/tests/end_to_end/__main__.py b/tests/end_to_end/__main__.py index 2442d03..d7e68b5 100644 --- a/tests/end_to_end/__main__.py +++ b/tests/end_to_end/__main__.py @@ -860,6 +860,66 @@ if __name__ == '__main__': partial(expect_stanza, "/iq[@id='kick1'][@type='result']"), ]), + Scenario("multisession_kick", + [ + handshake_sequence(), + # First user joins + partial(send_stanza, + ""), + connection_sequence("irc.localhost", '{jid_one}/{resource_one}'), + partial(expect_stanza, "/message"), + partial(expect_stanza, "/presence/muc_user:x/muc_user:status[@code='110']"), + partial(expect_stanza, "/message[@type='groupchat']/subject"), + + # Second user joins, from two resources + partial(send_stanza, + ""), + connection_sequence("irc.localhost", '{jid_two}/{resource_one}'), + partial(expect_stanza, + "/presence/muc_user:x/muc_user:item[@affiliation='none'][@role='participant']",), + + partial(expect_stanza, + "/presence/muc_user:x/muc_user:item[@affiliation='admin'][@role='moderator']"), + partial(expect_stanza, + "/presence/muc_user:x/muc_user:status[@code='110']"), + partial(expect_stanza, "/message/subject"), + + partial(send_stanza, + ""), + partial(expect_stanza, + "/presence[@to='{jid_two}/{resource_two}'][@from='#foo%{irc_server_one}/{nick_one}']"), + partial(expect_stanza, + ("/presence[@to='{jid_two}/{resource_two}'][@from='#foo%{irc_server_one}/{nick_two}']", + "/presence/muc_user:x/muc_user:status[@code='110']") + ), + partial(expect_stanza, "/message[@from='#foo%{irc_server_one}'][@type='groupchat'][@to='{jid_two}/{resource_two}']/subject[not(text())]"), + + # Moderator kicks participant + partial(log_message, "Moderator kicks participant"), + partial(send_stanza, + "reported"), + partial(log_message, "Unavailable presence is sent to the two resources"), + partial(expect_stanza, + ("/presence[@type='unavailable'][@to='{jid_second}/{resource_one}']/muc_user:x/muc_user:item[@role='none']/muc_user:actor[@nick='{nick_one}']", + "/presence/muc_user:x/muc_user:item/muc_user:reason[text()='reported']", + "/presence/muc_user:x/muc_user:status[@code='307']", + "/presence/muc_user:x/muc_user:status[@code='110']" + )), + partial(expect_stanza, + ("/presence[@type='unavailable'][@to='{jid_second}/{resource_two}']/muc_user:x/muc_user:item[@role='none']/muc_user:actor[@nick='{nick_one}']", + "/presence/muc_user:x/muc_user:item/muc_user:reason[text()='reported']", + "/presence/muc_user:x/muc_user:status[@code='307']", + "/presence/muc_user:x/muc_user:status[@code='110']" + )), + partial(expect_stanza, + ("/presence[@type='unavailable']/muc_user:x/muc_user:item[@role='none']/muc_user:actor[@nick='{nick_one}']", + "/presence/muc_user:x/muc_user:item/muc_user:reason[text()='reported']", + "/presence/muc_user:x/muc_user:status[@code='307']", + ), + ), + partial(expect_stanza, + "/iq[@id='kick1'][@type='result']"), + ]), ) failures = 0 -- cgit v1.2.3 From ec0e87a965fbdbd4682f1f3bcdbea2ff7440891e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Wed, 3 Aug 2016 14:28:29 +0200 Subject: Test the version request, in many ways --- tests/end_to_end/__main__.py | 73 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 71 insertions(+), 2 deletions(-) (limited to 'tests/end_to_end') diff --git a/tests/end_to_end/__main__.py b/tests/end_to_end/__main__.py index d7e68b5..4348197 100644 --- a/tests/end_to_end/__main__.py +++ b/tests/end_to_end/__main__.py @@ -112,7 +112,8 @@ def match(stanza, xpath): 'muc_user': 'http://jabber.org/protocol/muc#user', 'disco_items': 'http://jabber.org/protocol/disco#items', 'commands': 'http://jabber.org/protocol/commands', - 'dataform': 'jabber:x:data'}) + 'dataform': 'jabber:x:data', + 'version': 'jabber:iq:version'}) return matched @@ -122,7 +123,11 @@ def check_xpath(xpaths, xmpp, after, stanza): if not matched: raise StanzaError("Received stanza “%s” did not match expected xpath “%s”" % (stanza, xpath)) if after: - after(stanza, xmpp) + if isinstance(after, collections.Iterable): + for af in after: + af(stanza, xmpp) + else: + after(stanza, xmpp) def check_xpath_optional(xpaths, xmpp, after, stanza): @@ -920,6 +925,70 @@ if __name__ == '__main__': partial(expect_stanza, "/iq[@id='kick1'][@type='result']"), ]), + Scenario("self_version", + [ + handshake_sequence(), + partial(send_stanza, + ""), + connection_sequence("irc.localhost", '{jid_one}/{resource_one}'), + partial(expect_stanza, + "/message/body[text()='Mode #foo [+nt] by {irc_host_one}']"), + partial(expect_stanza, + ("/presence[@to='{jid_one}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_one}']/muc_user:x/muc_user:item[@affiliation='admin'][@role='moderator']", + "/presence/muc_user:x/muc_user:status[@code='110']") + ), + partial(expect_stanza, "/message[@from='#foo%{irc_server_one}'][@type='groupchat']/subject[not(text())]"), + + # Send a version request to ourself + partial(send_stanza, + ""), + # We receive our own request, + partial(expect_stanza, + "/iq[@from='{lower_nick_one}!{irc_server_one}'][@type='get'][@to='{jid_one}/{resource_one}']", + after = partial(save_value, "id", partial(extract_attribute, "/iq", 'id'))), + # Respond to the request + partial(send_stanza, + "e2e test1.0Fedora"), + partial(expect_stanza, + "/iq[@from='#foo%{irc_server_one}/{nick_one}'][@type='result'][@to='{jid_one}/{resource_one}'][@id='first_version']/version:query/version:name[text()='e2e test (through the biboumi gateway) 1.0 Fedora']"), + + # Now join the same room, from the same bare JID, behind the same nick + partial(send_stanza, + ""), + partial(expect_stanza, + ("/presence[@to='{jid_one}/{resource_two}'][@from='#foo%{irc_server_one}/{nick_one}']/muc_user:x/muc_user:item[@affiliation='admin'][@role='moderator']", + "/presence/muc_user:x/muc_user:status[@code='110']") + ), + partial(expect_stanza, "/message[@from='#foo%{irc_server_one}'][@type='groupchat'][@to='{jid_one}/{resource_two}']/subject[not(text())]"), + + # And re-send a self ping + partial(send_stanza, + ""), + # We receive our own request. Note that we don't know the to value, it could be one of our two resources. + partial(expect_stanza, + "/iq[@from='{lower_nick_one}!{irc_server_one}'][@type='get'][@to]", + after = (partial(save_value, "to", partial(extract_attribute, "/iq", "to")), + partial(save_value, "id", partial(extract_attribute, "/iq", "id")))), + # Respond to the request, using the extracted 'to' value as our 'from' + partial(send_stanza, + "e2e test1.0Fedora"), + partial(expect_stanza, + "/iq[@from='#foo%{irc_server_one}/{nick_one}'][@type='result'][@to='{jid_one}/{resource_two}'][@id='second_version']"), + + # And do exactly the same thing, but initiated by the other resource + partial(send_stanza, + ""), + # We receive our own request. Note that we don't know the to value, it could be one of our two resources. + partial(expect_stanza, + "/iq[@from='{lower_nick_one}!{irc_server_one}'][@type='get'][@to]", + after = (partial(save_value, "to", partial(extract_attribute, "/iq", "to")), + partial(save_value, "id", partial(extract_attribute, "/iq", "id")))), + # Respond to the request, using the extracted 'to' value as our 'from' + partial(send_stanza, + "e2e test1.0Fedora"), + partial(expect_stanza, + "/iq[@from='#foo%{irc_server_one}/{nick_one}'][@type='result'][@to='{jid_one}/{resource_one}'][@id='second_version']"), + ]), ) failures = 0 -- cgit v1.2.3