diff options
Diffstat (limited to 'tests/end_to_end')
88 files changed, 2710 insertions, 3002 deletions
diff --git a/tests/end_to_end/__main__.py b/tests/end_to_end/__main__.py index a029bea..cef554e 100644 --- a/tests/end_to_end/__main__.py +++ b/tests/end_to_end/__main__.py @@ -1,18 +1,19 @@ #!/usr/bin/env python3 +from functions import StanzaError, SkipStepError + import collections -import lxml.etree +import importlib +import sequences import datetime import slixmpp import asyncio import logging import signal import atexit -import time import sys -import io import os -from functools import partial + from slixmpp.xmlstream.matcher.base import MatcherBase if not hasattr(asyncio, "ensure_future"): @@ -25,20 +26,35 @@ class MatchAll(MatcherBase): return True -class StanzaError(Exception): - """ - Raised when a step fails. +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 """ - pass + def __init__(self, name, steps, conf): + """ + Steps is a list of 2-tuple: + [(action, answer), (action, answer)] + """ + self.name = name + self.steps = [] + self.conf = conf -class SkipStepError(Exception): - """ - Raised by a step when it needs to be skiped, by running - the next available step immediately. - """ - pass + def unwrap_tuples(elements): + """Yields all the value contained in the tuples, of tuples, of tuples… + For example unwrap_tuples((1, 2, 3, (4, 5, (6,)))) will yield 1, 2, 3, 4, 5, 6 + This works with any depth""" + if isinstance(elements, collections.abc.Iterable): + for elem in elements: + yield from unwrap_tuples(elem) + else: + yield elements + for step in unwrap_tuples(steps): + self.steps.append(step) class XMPPComponent(slixmpp.BaseXMPP): """ @@ -54,7 +70,7 @@ class XMPPComponent(slixmpp.BaseXMPP): self.stream_header = '<stream:stream %s %s from="%s" id="%s">' % ( 'xmlns="jabber:component:accept"', 'xmlns:stream="%s"' % self.stream_ns, - self.boundjid, self.get_id()) + self.boundjid, self.new_id()) self.stream_footer = "</stream:stream>" self.register_handler(slixmpp.Callback('Match All', @@ -67,6 +83,7 @@ class XMPPComponent(slixmpp.BaseXMPP): self.scenario = scenario self.biboumi = biboumi + self.timeout_handler = None # A callable, taking a stanza as argument and raising a StanzaError # exception if the test should fail. self.stanza_checker = None @@ -80,6 +97,13 @@ class XMPPComponent(slixmpp.BaseXMPP): self.scenario.steps = [] self.failed = True + def on_timeout(self, xpaths): + error_msg = "Timeout while waiting for a stanza that would match the expected xpath(s):" + for xpath in xpaths: + error_msg += "\n" + xpath + self.error(error_msg) + self.run_scenario() + def on_end_session(self, _): self.loop.stop() @@ -97,10 +121,13 @@ class XMPPComponent(slixmpp.BaseXMPP): self.run_scenario() def run_scenario(self): + if self.timeout_handler is not None: + self.timeout_handler.cancel() + self.timeout_handler = None if self.scenario.steps: step = self.scenario.steps.pop(0) try: - step(self, self.biboumi) + step(xmpp=self, biboumi=self.biboumi) except Exception as e: self.error(e) self.run_scenario() @@ -113,106 +140,6 @@ 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 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', - 'muc_owner': 'http://jabber.org/protocol/muc#owner', - 'muc': 'http://jabber.org/protocol/muc', - 'disco_info': 'http://jabber.org/protocol/disco#info', - 'muc_traffic': 'http://jabber.org/protocol/muc#traffic', - 'disco_items': 'http://jabber.org/protocol/disco#items', - 'commands': 'http://jabber.org/protocol/commands', - 'dataform': 'jabber:x:data', - 'version': 'jabber:iq:version', - 'mam': 'urn:xmpp:mam:2', - 'rms': 'http://jabber.org/protocol/rsm', - 'delay': 'urn:xmpp:delay', - 'forward': 'urn:xmpp:forward:0', - 'client': 'jabber:client', - 'rsm': 'http://jabber.org/protocol/rsm', - 'carbon': 'urn:xmpp:carbons:2', - 'hints': 'urn:xmpp:hints', - 'stanza': 'urn:ietf:params:xml:ns:xmpp-stanzas', - 'stable_id': 'urn:xmpp:sid:0'}) - return matched - - -def check_xpath(xpaths, xmpp, after, stanza): - for xpath in xpaths: - expected = True - real_xpath = xpath - # We can check that a stanza DOESN’T match, by adding a ! before it. - if xpath.startswith('!'): - expected = False - xpath = xpath[1:] - matched = match(stanza, xpath) - if (expected and not matched) or (not expected and matched): - raise StanzaError("Received stanza\n%s\ndid not match expected xpath\n%s" % (stanza, real_xpath)) - if after: - if isinstance(after, collections.Iterable): - for af in after: - af(stanza, xmpp) - else: - after(stanza, xmpp) - -def all_xpaths_match(stanza, xpaths): - for xpath in xpaths: - matched = match(stanza, xpath) - if not matched: - return False - return True - -def check_list_of_xpath(list_of_xpaths, xmpp, stanza): - found = None - for i, xpaths in enumerate(list_of_xpaths): - if all_xpaths_match(stanza, xpaths): - found = True - list_of_xpaths.pop(i) - break - - if not found: - raise StanzaError("Received stanza “%s” did not match any of the expected xpaths:\n%s" % (stanza, list_of_xpaths)) - - if list_of_xpaths: - step = partial(expect_unordered_already_formatted, list_of_xpaths) - xmpp.scenario.steps.insert(0, step) - - -def check_xpath_optional(xpaths, xmpp, after, stanza): - try: - check_xpath(xpaths, xmpp, after, 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 - 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, 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: - self.steps.append(step) - else: - self.steps.append(elem) - class ProcessRunner: def __init__(self): @@ -261,48 +188,6 @@ class IrcServerRunner(ProcessRunner): self.create = asyncio.create_subprocess_exec("charybdis", "-foreground", "-configfile", os.getcwd() + "/../tests/end_to_end/ircd.conf", stderr=asyncio.subprocess.PIPE) - -def send_stanza(stanza, xmpp, biboumi): - 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, after=None): - replacements = common_replacements - replacements.update(xmpp.saved_values) - check_func = check_xpath if not optional else check_xpath_optional - if isinstance(xpaths, str): - xmpp.stanza_checker = partial(check_func, [xpaths.format_map(replacements)], xmpp, after) - elif isinstance(xpaths, tuple): - xmpp.stanza_checker = partial(check_func, [xpath.format_map(replacements) for xpath in xpaths], xmpp, after) - else: - print("Warning, from argument type passed to expect_stanza: %s" % (type(xpaths))) - -def save_current_timestamp_plus_delta(key, delta, message, xmpp): - now_plus_delta = datetime.datetime.utcnow() + delta - xmpp.saved_values[key] = now_plus_delta.strftime("%FT%T.967Z") - -def sleep_for(duration, xmpp, biboumi): - time.sleep(duration) - asyncio.get_event_loop().call_soon(xmpp.run_scenario) - -# list_of_xpaths: [(xpath, xpath), (xpath, xpath), (xpath)] -def expect_unordered(list_of_xpaths, xmpp, biboumi): - formatted_list_of_xpaths = [] - for xpaths in list_of_xpaths: - formatted_xpaths = [] - for xpath in xpaths: - formatted_xpath = xpath.format_map(common_replacements) - formatted_xpaths.append(formatted_xpath) - formatted_list_of_xpaths.append(tuple(formatted_xpaths)) - expect_unordered_already_formatted(formatted_list_of_xpaths, xmpp, biboumi) - -def expect_unordered_already_formatted(formatted_list_of_xpaths, xmpp, biboumi): - xmpp.stanza_checker = partial(check_list_of_xpath, formatted_list_of_xpaths, xmpp) - - class BiboumiTest: """ Spawns a biboumi process and a fake XMPP Component that will run a @@ -333,6 +218,8 @@ class BiboumiTest: except FileNotFoundError: pass + start_datetime = datetime.datetime.now() + # Start the XMPP component and biboumi biboumi = BiboumiRunner(self.scenario.name) xmpp = XMPPComponent(self.scenario, biboumi) @@ -344,13 +231,16 @@ class BiboumiTest: code = asyncio.get_event_loop().run_until_complete(biboumi.wait()) xmpp.biboumi = None self.scenario.steps.clear() + + delta = datetime.datetime.now() - start_datetime + 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("[32;1mSuccess![0m") + print("[32;1mSuccess![0m ({}s)".format(round(delta.total_seconds(), 2))) else: failed = True @@ -390,2852 +280,42 @@ port=8811 persistent_by_default=true """,} -common_replacements = { - 'irc_server_one': 'irc.localhost@biboumi.localhost', - 'irc_server_two': 'localhost@biboumi.localhost', - 'irc_host_one': 'irc.localhost', - 'irc_host_two': 'localhost', - 'biboumi_host': 'biboumi.localhost', - 'resource_one': 'resource1', - 'resource_two': 'resource2', - 'nick_one': 'Nick', - 'jid_one': 'first@example.com', - 'jid_two': 'second@example.com', - 'jid_admin': 'admin@example.com', - 'nick_two': 'Bobby', - 'nick_three': 'Bernard', - 'lower_nick_one': 'nick', - 'lower_nick_two': 'bobby', -} - - -def handshake_sequence(): - return (partial(expect_stanza, "//handshake"), - partial(send_stanza, "<handshake xmlns='jabber:component:accept'/>")) - - -def connection_begin_sequence(irc_host, jid, expected_irc_presence=False, fixed_irc_server=False): - jid = jid.format_map(common_replacements) - if fixed_irc_server: - xpath = "/message[@to='" + jid + "'][@from='biboumi.localhost']/body[text()='%s']" - xpath_re = "/message[@to='" + jid + "'][@from='biboumi.localhost']/body[re:test(text(), '%s')]" - else: - xpath = "/message[@to='" + jid + "'][@from='" + irc_host + "@biboumi.localhost']/body[text()='%s']" - xpath_re = "/message[@to='" + jid + "'][@from='" + irc_host + "@biboumi.localhost']/body[re:test(text(), '%s')]" - result = ( - partial(expect_stanza, - (xpath % ('Connecting to %s:6697 (encrypted)' % irc_host), - "/message/hints:no-copy", - "/message/carbon:private" - ) - ), - 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.')) - - if expected_irc_presence: - result += (partial(expect_stanza, "/presence[@from='" + irc_host + "@biboumi.localhost']"),) - - # These five messages can be receive in any order - result += ( - partial(expect_stanza, - xpath_re % (r'^%s: (\*\*\* Checking Ident|\*\*\* Looking up your hostname\.\.\.|\*\*\* Found your hostname: .*|ACK multi-prefix|\*\*\* Got Ident response)$' % 'irc.localhost')), - partial(expect_stanza, - xpath_re % (r'^%s: (\*\*\* Checking Ident|\*\*\* Looking up your hostname\.\.\.|\*\*\* Found your hostname: .*|ACK multi-prefix|\*\*\* Got Ident response)$' % 'irc.localhost')), - partial(expect_stanza, - xpath_re % (r'^%s: (\*\*\* Checking Ident|\*\*\* Looking up your hostname\.\.\.|\*\*\* Found your hostname: .*|ACK multi-prefix|\*\*\* Got Ident response)$' % 'irc.localhost')), - partial(expect_stanza, - xpath_re % (r'^%s: (\*\*\* Checking Ident|\*\*\* Looking up your hostname\.\.\.|\*\*\* Found your hostname: .*|ACK multi-prefix|\*\*\* Got Ident response)$' % 'irc.localhost')), - partial(expect_stanza, - xpath_re % (r'^%s: (\*\*\* Checking Ident|\*\*\* Looking up your hostname\.\.\.|\*\*\* Found your hostname: .*|ACK multi-prefix|\*\*\* Got Ident response)$' % 'irc.localhost')), - ) - - return result - -def connection_tls_begin_sequence(irc_host, jid, fixed_irc_server): - jid = jid.format_map(common_replacements) - if fixed_irc_server: - xpath = "/message[@to='" + jid + "'][@from='biboumi.localhost']/body[text()='%s']" - xpath_re = "/message[@to='" + jid + "'][@from='biboumi.localhost']/body[re:test(text(), '%s')]" - else: - xpath = "/message[@to='" + jid + "'][@from='" + irc_host + "@biboumi.localhost']/body[text()='%s']" - xpath_re = "/message[@to='" + jid + "'][@from='" + irc_host + "@biboumi.localhost']/body[re:test(text(), '%s')]" - irc_host = 'irc.localhost' - return ( - partial(expect_stanza, - (xpath % ('Connecting to %s:7778 (encrypted)' % irc_host), - "/message/hints:no-copy", - "/message/carbon:private", - ) - ), - partial(expect_stanza, - xpath % 'Connected to IRC server (encrypted).'), - # These five messages can be receive in any order - partial(expect_stanza, - xpath_re % (r'^%s: (\*\*\* Checking Ident|\*\*\* Looking up your hostname\.\.\.|\*\*\* Found your hostname: .*|ACK multi-prefix|\*\*\* Got Ident response)$' % irc_host)), - partial(expect_stanza, - xpath_re % (r'^%s: (\*\*\* Checking Ident|\*\*\* Looking up your hostname\.\.\.|\*\*\* Found your hostname: .*|ACK multi-prefix|\*\*\* Got Ident response)$' % irc_host)), - partial(expect_stanza, - xpath_re % (r'^%s: (\*\*\* Checking Ident|\*\*\* Looking up your hostname\.\.\.|\*\*\* Found your hostname: .*|ACK multi-prefix|\*\*\* Got Ident response)$' % irc_host)), - partial(expect_stanza, - xpath_re % (r'^%s: (\*\*\* Checking Ident|\*\*\* Looking up your hostname\.\.\.|\*\*\* Found your hostname: .*|ACK multi-prefix|\*\*\* Got Ident response)$' % irc_host)), - partial(expect_stanza, - xpath_re % (r'^%s: (\*\*\* Checking Ident|\*\*\* Looking up your hostname\.\.\.|\*\*\* Found your hostname: .*|ACK multi-prefix|\*\*\* Got Ident response)$' % irc_host)), - ) - -def connection_end_sequence(irc_host, jid, fixed_irc_server=False): - jid = jid.format_map(common_replacements) - if fixed_irc_server: - xpath = "/message[@to='" + jid + "'][@from='biboumi.localhost']/body[text()='%s']" - xpath_re = "/message[@to='" + jid + "'][@from='biboumi.localhost']/body[re:test(text(), '%s')]" - else: - xpath = "/message[@to='" + jid + "'][@from='" + irc_host + "@biboumi.localhost']/body[text()='%s']" - xpath_re = "/message[@to='" + jid + "'][@from='" + irc_host + "@biboumi.localhost']/body[re:test(text(), '%s')]" - irc_host = 'irc.localhost' - return ( - partial(expect_stanza, - xpath_re % (r'^%s: Your host is .*$' % irc_host)), - partial(expect_stanza, - 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, - 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_re % r'^User mode for \w+ is \[\+Z?i\]$'), - ) - -def connection_middle_sequence(irc_host, jid, fixed_irc_server=False): - if fixed_irc_server: - xpath_re = "/message[@to='" + jid + "'][@from='biboumi.localhost']/body[re:test(text(), '%s')]" - else: - xpath_re = "/message[@to='" + jid + "'][@from='" + irc_host + "@biboumi.localhost']/body[re:test(text(), '%s')]" - irc_host = 'irc.localhost' - return ( - partial(expect_stanza, xpath_re % (r'^%s: \*\*\* You are exempt from flood limits$' % irc_host)), - ) - - -def connection_sequence(irc_host, jid, expected_irc_presence=False, fixed_irc_server=False): - return connection_begin_sequence(irc_host, jid, expected_irc_presence, fixed_irc_server=fixed_irc_server) +\ - connection_middle_sequence(irc_host, jid, fixed_irc_server=fixed_irc_server) +\ - connection_end_sequence(irc_host, jid, fixed_irc_server=fixed_irc_server) - -def connection_tls_sequence(irc_host, jid, fixed_irc_server=False): - return connection_tls_begin_sequence(irc_host, jid, fixed_irc_server) + \ - connection_middle_sequence(irc_host, jid, fixed_irc_server) +\ - connection_end_sequence(irc_host, jid, fixed_irc_server) - - -def extract_attribute(xpath, name, stanza): - matched = match(stanza, xpath) - return matched[0].get(name) - -def chan_name_from_jid(jid): - return jid[1:jid.find('%')] - -def extract_text(xpath, stanza): - matched = match(stanza, xpath) - return matched[0].text - -def save_value(name, func, stanza, xmpp): - xmpp.saved_values[name] = func(stanza) +def get_scenarios(test_path, provided_scenar_names): + """ + :param test_path: The path containing all the tests + :param provided_scenar_names: a list of scenario names provided on the + command line by the user. May be empty + :return: The list of scenarios to be run. If provided_scenar_names is + empty, we return all the existing scenarios, otherwise we just return + the one from that list + """ + scenarios = [] + for entry in os.scandir(os.path.join(test_path, "scenarios")): + if entry.is_file() and not entry.name.startswith('.') and entry.name.endswith('.py'): + module_name = entry.name[:-3] + if provided_scenar_names and module_name not in provided_scenar_names: + continue + if module_name == "__init__" or (provided_scenar_names and module_name not in provided_scenar_names): + continue + module_full_path = "scenarios.{}".format(module_name) + mod = importlib.import_module(module_full_path) + conf = "basic" + if hasattr(mod, "conf"): + conf = mod.conf + # Every scenario needs to start with the handshake sequence. + # Instead of repeating it everytime, we add it implicitely. This + # is done here. + scenarios.append(Scenario(module_name, (sequences.handshake(),) + mod.scenario, conf)) + return scenarios 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", - [ - handshake_sequence() - ]), - Scenario("irc_server_connection", - [ - handshake_sequence(), - partial(send_stanza, - "<presence from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}/{nick_one}' />"), - connection_sequence("irc.localhost", '{jid_one}/{resource_one}'), - ]), - Scenario("irc_server_connection_failure", - [ - handshake_sequence(), - partial(send_stanza, - "<presence from='{jid_one}/{resource_one}' to='#foo%doesnotexist@{biboumi_host}/{nick_one}' />"), - partial(expect_stanza, - "/message/body[text()='Connecting to doesnotexist:6697 (encrypted)']"), - partial(expect_stanza, - "/message/body[re:test(text(), 'Connection failed: (Domain name not found|Name or service not known)')]"), - partial(expect_stanza, - ("/presence[@from='#foo%doesnotexist@{biboumi_host}/{nick_one}']/muc:x", - "/presence/error[@type='cancel']/stanza:item-not-found", - "/presence/error[@type='cancel']/stanza:text[re:test(text(), '(Domain name not found|Name or service not known)')]")), - ]), - Scenario("simple_channel_join", - [ - handshake_sequence(), - partial(send_stanza, - "<presence from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}/{nick_one}' />"), - 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())]"), - ]), - Scenario("raw_names_command", - [ - handshake_sequence(), - partial(send_stanza, - "<presence from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}/{nick_one}' />"), - connection_sequence("irc.localhost", '{jid_one}/{resource_one}'), - partial(expect_stanza, - "/message/body"), - partial(expect_stanza, - "/presence/muc_user:x/muc_user:status[@code='110']" - ), - partial(expect_stanza, "/message/subject[not(text())]"), - partial(send_stanza, - "<message type='chat' from='{jid_one}/{resource_one}' to='{irc_server_one}'><body>NAMES</body></message>"), - partial(expect_stanza, "/message/body[text()='irc.localhost: = #foo @{nick_one} ']"), - partial(expect_stanza, "/message/body[text()='irc.localhost: * End of /NAMES list. ']"), - ]), - Scenario("quit", - [ - handshake_sequence(), - partial(send_stanza, - "<presence from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}/{nick_one}' />"), - 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 raw QUIT message - partial(send_stanza, "<message from='{jid_one}/{resource_one}' to='{irc_server_one}' type='chat'><body>QUIT bye bye</body></message>"), - partial(expect_stanza, ("/presence[@from='#foo%{irc_server_one}/{nick_one}'][@type='unavailable']/muc_user:x/muc_user:status[@code='110']", - "/presence[@from='#foo%{irc_server_one}/{nick_one}'][@type='unavailable']/muc_user:x/muc_user:status[@code='110']",)), - ]), - Scenario("multiple_channels_join", - [ - handshake_sequence(), - partial(send_stanza, - "<presence from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}/{nick_one}' />"), - partial(send_stanza, - "<presence from='{jid_one}/{resource_one}' to='#bar%{irc_server_one}/{nick_one}' />"), - partial(send_stanza, - "<presence from='{jid_one}/{resource_one}' to='#baz%{irc_server_one}/{nick_one}'> <x xmlns='http://jabber.org/protocol/muc'><password>SECRET</password></x></presence>"), - - 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())]"), - - partial(expect_stanza, - "/message/body[text()='Mode #bar [+nt] by {irc_host_one}']"), - partial(expect_stanza, - ("/presence[@to='{jid_one}/{resource_one}'][@from='#bar%{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='#bar%{irc_server_one}'][@type='groupchat']/subject[not(text())]"), - - partial(expect_stanza, - "/message/body[text()='Mode #baz [+nt] by {irc_host_one}']"), - partial(expect_stanza, - ("/presence[@to='{jid_one}/{resource_one}'][@from='#baz%{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='#baz%{irc_server_one}'][@type='groupchat']/subject[not(text())]"), - ]), - Scenario("not_connected_error", - [ - handshake_sequence(), - partial(send_stanza, - "<presence type='unavailable' from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}/{nick_one}' />"), - partial(send_stanza, - "<presence from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}/{nick_one}' />"), - 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())]"), - ]), - Scenario("channel_join_with_two_users", - [ - handshake_sequence(), - # First user joins - partial(send_stanza, - "<presence from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}/{nick_one}' />"), - 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, - "<presence from='{jid_two}/{resource_one}' to='#foo%{irc_server_one}/{nick_two}' />"), - connection_sequence("irc.localhost", '{jid_two}/{resource_one}'), - partial(expect_unordered, [ - ("/presence[@to='{jid_one}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_two}']/muc_user:x/muc_user:item[@affiliation='none'][@role='participant'][@jid='~bobby@localhost']",), - ("/presence[@to='{jid_two}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_one}']/muc_user:x/muc_user:item[@affiliation='admin'][@role='moderator']",), - ("/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']",), - ("/message[@from='#foo%{irc_server_one}'][@type='groupchat']/subject[not(text())]",), - ]), - ]), - Scenario("channel_force_join", - [ - handshake_sequence(), - # First user joins - partial(send_stanza, - "<presence from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}/{nick_one}'><x xmlns='http://jabber.org/protocol/muc'/></presence>"), - 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, - "<presence from='{jid_two}/{resource_one}' to='#foo%{irc_server_one}/{nick_two}'><x xmlns='http://jabber.org/protocol/muc'/></presence>"), - connection_sequence("irc.localhost", '{jid_two}/{resource_one}'), - partial(expect_unordered, [ - ("/presence[@to='{jid_one}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_two}']/muc_user:x/muc_user:item[@affiliation='none'][@role='participant'][@jid='~bobby@localhost']",), - ("/presence[@to='{jid_two}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_one}']/muc_user:x/muc_user:item[@affiliation='admin'][@role='moderator']",), - ("/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']",), - ("/message[@from='#foo%{irc_server_one}'][@type='groupchat']/subject[not(text())]",), - ]), - - # Here we simulate a desynchronization of a client: The client thinks it’s - # disconnected from the room, but biboumi still thinks it’s in the room. The - # client thus sends a join presence, and biboumi should send everything - # (user list, history, etc) in response. - partial(send_stanza, - "<presence from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}/{nick_three}'><x xmlns='http://jabber.org/protocol/muc'/></presence>"), - - partial(expect_unordered, [ - ("/presence[@to='{jid_one}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_two}']/muc_user:x/muc_user:item[@affiliation='none'][@role='participant'][@jid='~bobby@localhost']",), - ("/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']",), - ("/message[@from='#foo%{irc_server_one}'][@type='groupchat']/subject[not(text())]",), - ]), - # And also, that was not the same nickname - partial(expect_unordered, [ - ("/presence[@from='#foo%{irc_server_one}/{nick_one}'][@to='{jid_two}/{resource_one}'][@type='unavailable']/muc_user:x/muc_user:item[@nick='Bernard']", - "/presence/muc_user:x/muc_user:status[@code='303']"), - ("/presence[@from='#foo%{irc_server_one}/{nick_three}'][@to='{jid_two}/{resource_one}']",), - ("/presence[@from='#foo%{irc_server_one}/{nick_one}'][@to='{jid_one}/{resource_one}'][@type='unavailable']/muc_user:x/muc_user:item[@nick='Bernard']", - "/presence/muc_user:x/muc_user:status[@code='303']", - "/presence/muc_user:x/muc_user:status[@code='110']"), - ("/presence[@from='#foo%{irc_server_one}/{nick_three}'][@to='{jid_one}/{resource_one}']", - "/presence/muc_user:x/muc_user:status[@code='110']"), - ]), - ]), - Scenario("channel_join_with_password", - [ - handshake_sequence(), - # First user joins - partial(send_stanza, - "<presence from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}/{nick_one}' />"), - 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())]"), - - # Set a password in the room, by using /mode +k - partial(send_stanza, "<message from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}' type='groupchat'><body>/mode +k SECRET</body></message>"), - partial(expect_stanza, "/message[@from='#foo%{irc_server_one}'][@to='{jid_one}/{resource_one}'][@type='groupchat']/body[text()='Mode #foo [+k SECRET] by {nick_one}']"), - - # Second user tries to join, without a password - partial(send_stanza, - "<presence from='{jid_two}/{resource_one}' to='#foo%{irc_server_one}/{nick_two}'/>"), - connection_sequence("irc.localhost", '{jid_two}/{resource_one}'), - - partial(expect_stanza, "/message/body[text()='{irc_host_one}: #foo: Cannot join channel (+k) - bad key']"), - partial(expect_stanza, - "/presence[@type='error'][@from='#foo%{irc_server_one}/{nick_two}']/error[@type='auth']/stanza:not-authorized", - ), - - # Second user joins, with a password - partial(send_stanza, - "<presence from='{jid_two}/{resource_one}' to='#foo%{irc_server_one}/{nick_two}'> <x xmlns='http://jabber.org/protocol/muc'><password>SECRET</password></x></presence>"), - # connection_sequence("irc.localhost", '{jid_two}/{resource_one}'), - partial(expect_unordered, [ - ("/presence[@to='{jid_one}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_two}']/muc_user:x/muc_user:item[@affiliation='none'][@role='participant'][@jid='~bobby@localhost']",), - ("/presence[@to='{jid_two}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_one}']/muc_user:x/muc_user:item[@affiliation='admin'][@role='moderator']",), - ("/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']",), - ("/message[@from='#foo%{irc_server_one}'][@type='groupchat']/subject[not(text())]",), - ]), - - ]), - Scenario("channel_custom_topic", - [ - handshake_sequence(), - # First user joins - partial(send_stanza, - "<presence from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}/{nick_one}' />"), - 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())]"), - - # First user sets the topic - partial(send_stanza, - "<message from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}' type='groupchat'><subject>TOPIC TEST</subject></message>"), - 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, - "<presence from='{jid_two}/{resource_one}' to='#foo%{irc_server_one}/{nick_two}' />"), - 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_two}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_one}']/muc_user:x/muc_user:item[@affiliation='admin'][@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}/{nick_one}'][@type='groupchat']/subject[text()='TOPIC TEST']"), - ]), - Scenario("multiline_topic", - [ - handshake_sequence(), - # User joins - partial(send_stanza, - "<presence from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}/{nick_one}' />"), - connection_sequence("irc.localhost", '{jid_one}/{resource_one}'), - partial(expect_stanza, - "/message/body"), - partial(expect_stanza, "/presence"), - partial(expect_stanza, "/message[@from='#foo%{irc_server_one}'][@type='groupchat']/subject[not(text())]"), - - # User tries to set a multiline topic - partial(send_stanza, - "<message from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}' type='groupchat'><subject>FIRST LINE\nSECOND LINE.</subject></message>"), - partial(expect_stanza, "/message[@from='#foo%{irc_server_one}/{nick_one}'][@type='groupchat'][@to='{jid_one}/{resource_one}']/subject[text()='FIRST LINE SECOND LINE.']"), - ]), - Scenario("channel_basic_join_on_fixed_irc_server", - [ - handshake_sequence(), - partial(send_stanza, - "<presence from='{jid_one}/{resource_one}' to='#zgeg@{biboumi_host}/{nick_one}' />"), - connection_sequence("irc.localhost", '{jid_one}/{resource_one}', fixed_irc_server=True), - 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' - ), - Scenario("list_adhoc", - [ - handshake_sequence(), - partial(send_stanza, "<iq type='get' id='idwhatever' from='{jid_one}/{resource_one}' to='{biboumi_host}'><query xmlns='http://jabber.org/protocol/disco#items' node='http://jabber.org/protocol/commands' /></iq>"), - partial(expect_stanza, ("/iq[@type='result']/disco_items:query[@node='http://jabber.org/protocol/commands']", - "/iq/disco_items:query/disco_items:item[@node='configure']", - "/iq/disco_items:query/disco_items:item[4]", - "!/iq/disco_items:query/disco_items:item[5]")), - ]), - Scenario("list_admin_adhoc", - [ - handshake_sequence(), - partial(send_stanza, "<iq type='get' id='idwhatever' from='{jid_admin}/{resource_one}' to='{biboumi_host}'><query xmlns='http://jabber.org/protocol/disco#items' node='http://jabber.org/protocol/commands' /></iq>"), - partial(expect_stanza, ("/iq[@type='result']/disco_items:query[@node='http://jabber.org/protocol/commands']", - "/iq/disco_items:query/disco_items:item[6]", - "!/iq/disco_items:query/disco_items:item[7]")), - ]), - Scenario("list_adhoc_fixed_server", - [ - handshake_sequence(), - partial(send_stanza, "<iq type='get' id='idwhatever' from='{jid_one}/{resource_one}' to='{biboumi_host}'><query xmlns='http://jabber.org/protocol/disco#items' node='http://jabber.org/protocol/commands' /></iq>"), - partial(expect_stanza, ("/iq[@type='result']/disco_items:query[@node='http://jabber.org/protocol/commands']", - "/iq/disco_items:query/disco_items:item[@node='global-configure']", - "/iq/disco_items:query/disco_items:item[@node='server-configure']", - "/iq/disco_items:query/disco_items:item[6]", - "!/iq/disco_items:query/disco_items:item[7]")), - ], conf='fixed_server'), - Scenario("list_admin_adhoc_fixed_server", - [ - handshake_sequence(), - partial(send_stanza, "<iq type='get' id='idwhatever' from='{jid_admin}/{resource_one}' to='{biboumi_host}'><query xmlns='http://jabber.org/protocol/disco#items' node='http://jabber.org/protocol/commands' /></iq>"), - partial(expect_stanza, ("/iq[@type='result']/disco_items:query[@node='http://jabber.org/protocol/commands']", - "/iq/disco_items:query/disco_items:item[8]", - "!/iq/disco_items:query/disco_items:item[9]")), - ], conf='fixed_server'), - Scenario("list_adhoc_irc", - [ - handshake_sequence(), - partial(send_stanza, "<iq type='get' id='idwhatever' from='{jid_one}/{resource_one}' to='{irc_host_one}@{biboumi_host}'><query xmlns='http://jabber.org/protocol/disco#items' node='http://jabber.org/protocol/commands' /></iq>"), - partial(expect_stanza, ("/iq[@type='result']/disco_items:query[@node='http://jabber.org/protocol/commands']", - "/iq/disco_items:query/disco_items:item[2]")), - ]), - Scenario("list_muc_user_adhoc", - [ - handshake_sequence(), - partial(send_stanza, "<iq type='get' id='idwhatever' from='{jid_admin}/{resource_one}' to='#foo%{irc_server_one}/{nick_one}'><query xmlns='http://jabber.org/protocol/disco#items' node='http://jabber.org/protocol/commands' /></iq>"), - partial(expect_stanza, "/iq[@type='error']/error[@type='cancel']/stanza:feature-not-implemented"), - ] - ), - Scenario("execute_hello_adhoc_command", - [ - handshake_sequence(), - partial(send_stanza, "<iq type='set' id='hello-command1' from='{jid_one}/{resource_one}' to='{biboumi_host}'><command xmlns='http://jabber.org/protocol/commands' node='hello' action='execute' /></iq>"), - 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, "<iq type='set' id='hello-command2' from='{jid_one}/{resource_one}' to='{biboumi_host}'><command xmlns='http://jabber.org/protocol/commands' node='hello' sessionid='{sessionid}' action='next'><x xmlns='jabber:x:data' type='submit'><field var='name'><value>COUCOU</value></field></x></command></iq>"), - partial(expect_stanza, "/iq[@type='result']/commands:command[@node='hello'][@status='completed']/commands:note[@type='info'][text()='Hello COUCOU!']") - ]), - Scenario("execute_incomplete_hello_adhoc_command", - [ - handshake_sequence(), - partial(send_stanza, "<iq type='set' id='hello-command1' from='{jid_one}/{resource_one}' to='{biboumi_host}'><command xmlns='http://jabber.org/protocol/commands' node='hello' action='execute' /></iq>"), - partial(expect_stanza, ("/iq[@type='result']/commands:command[@node='hello'][@sessionid][@status='executing']", - "/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, "<iq type='set' id='hello-command2' from='{jid_one}/{resource_one}' to='{biboumi_host}'><command xmlns='http://jabber.org/protocol/commands' node='hello' sessionid='{sessionid}' action='next'><x xmlns='jabber:x:data' type='submit'></x></command></iq>"), - partial(expect_stanza, "/iq[@type='error']") - ]), - Scenario("execute_ping_adhoc_command", - [ - handshake_sequence(), - partial(send_stanza, "<iq type='set' id='ping-command1' from='{jid_one}/{resource_one}' to='{biboumi_host}'><command xmlns='http://jabber.org/protocol/commands' node='ping' action='execute' /></iq>"), - partial(expect_stanza, "/iq[@type='result']/commands:command[@node='ping'][@status='completed']/commands:note[@type='info'][text()='Pong']") - ]), - Scenario("execute_reload_adhoc_command", - [ - handshake_sequence(), - partial(send_stanza, "<iq type='set' id='ping-command1' from='{jid_admin}/{resource_one}' to='{biboumi_host}'><command xmlns='http://jabber.org/protocol/commands' node='reload' action='execute' /></iq>"), - partial(expect_stanza, "/iq[@type='result']/commands:command[@node='reload'][@status='completed']/commands:note[@type='info'][text()='Configuration reloaded.']") - ]), - Scenario("execute_forbidden_adhoc_command", - [ - handshake_sequence(), - partial(send_stanza, "<iq type='set' id='command1' from='{jid_one}/{resource_one}' to='{biboumi_host}'><command xmlns='http://jabber.org/protocol/commands' node='disconnect-user' action='execute' /></iq>"), - partial(expect_stanza, ("/iq[@type='error'][@id='command1']/commands:command[@node='disconnect-user']", - "/iq/commands:command/commands:error[@type='cancel']/stanza:forbidden")), - ]), - Scenario("execute_disconnect_user_adhoc_command", - [ - handshake_sequence(), - - partial(send_stanza, "<presence from='{jid_admin}/{resource_one}' to='#foo%{irc_server_one}/{nick_one}' />"), - connection_sequence("irc.localhost", '{jid_admin}/{resource_one}'), - partial(expect_stanza, "/message/body[text()='Mode #foo [+nt] by {irc_host_one}']"), - partial(expect_stanza, "/presence"), - partial(expect_stanza, "/message"), - - partial(send_stanza, "<iq type='set' id='command1' from='{jid_admin}/{resource_one}' to='{biboumi_host}'><command xmlns='http://jabber.org/protocol/commands' node='disconnect-user' action='execute' /></iq>"), - partial(expect_stanza, ("/iq[@type='result']/commands:command[@node='disconnect-user'][@sessionid][@status='executing']", - "/iq/commands:command/commands:actions/commands:next", - ), - after = partial(save_value, "sessionid", partial(extract_attribute, "/iq/commands:command[@node='disconnect-user']", "sessionid")) - ), - partial(send_stanza, "<iq type='set' id='command2' from='{jid_admin}/{resource_one}' to='{biboumi_host}'><command xmlns='http://jabber.org/protocol/commands' node='disconnect-user' sessionid='{sessionid}' action='next'><x xmlns='jabber:x:data' type='submit'><field var='jids'><value>{jid_admin}</value></field><field var='quit-message'><value>Disconnected by e2e</value></field></x></command></iq>"), - partial(expect_stanza, "/iq[@type='result']/commands:command[@node='disconnect-user'][@status='completed']/commands:note[@type='info'][text()='1 user has been disconnected.']"), - # Note, charybdis ignores our QUIT message, so we can't test it - partial(expect_stanza, "/presence[@type='unavailable'][@to='{jid_admin}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_one}']"), - ]), - Scenario("execute_admin_disconnect_from_server_adhoc_command", - [ - handshake_sequence(), - - # Admin connects to first server - partial(send_stanza, "<presence from='{jid_admin}/{resource_one}' to='#bar%{irc_server_one}/{nick_one}' />"), - connection_sequence("irc.localhost", '{jid_admin}/{resource_one}'), - partial(expect_stanza, "/message/body[text()='Mode #bar [+nt] by {irc_host_one}']"), - partial(expect_stanza, "/presence"), - partial(expect_stanza, "/message"), - - # Non-Admin connects to first server - partial(send_stanza, "<presence from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}/{nick_two}' />"), - 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"), - partial(expect_stanza, "/message"), - - # Non-admin connects to second server - partial(send_stanza, "<presence from='{jid_one}/{resource_one}' to='#bon%{irc_server_two}/{nick_three}' />"), - connection_sequence("localhost", '{jid_one}/{resource_one}'), - partial(expect_stanza, "/message/body[text()='Mode #bon [+nt] by {irc_host_one}']"), - partial(expect_stanza, "/presence"), - partial(expect_stanza, "/message"), - - # Execute as admin - partial(send_stanza, "<iq type='set' id='command1' from='{jid_admin}/{resource_one}' to='{biboumi_host}'><command xmlns='http://jabber.org/protocol/commands' node='disconnect-from-irc-server' action='execute' /></iq>"), - partial(expect_stanza, ("/iq[@type='result']/commands:command[@node='disconnect-from-irc-server'][@sessionid][@status='executing']", - "/iq/commands:command/dataform:x[@type='form']/dataform:field[@var='jid'][@type='list-single']/dataform:option[@label='{jid_one}']/dataform:value[text()='{jid_one}']", - "/iq/commands:command/dataform:x[@type='form']/dataform:field[@var='jid'][@type='list-single']/dataform:option[@label='{jid_admin}']/dataform:value[text()='{jid_admin}']", - "/iq/commands:command/commands:actions/commands:next", - ), - after = partial(save_value, "sessionid", partial(extract_attribute, "/iq/commands:command[@node='disconnect-from-irc-server']", "sessionid")) - ), - partial(send_stanza, "<iq type='set' id='command2' from='{jid_admin}/{resource_one}' to='{biboumi_host}'><command xmlns='http://jabber.org/protocol/commands' node='disconnect-from-irc-server' sessionid='{sessionid}' action='next'><x xmlns='jabber:x:data' type='submit'><field var='jid'><value>{jid_one}</value></field><field var='quit-message'><value>e2e test one</value></field></x></command></iq>"), - - partial(expect_stanza, ("/iq[@type='result']/commands:command[@node='disconnect-from-irc-server'][@sessionid][@status='executing']", - "/iq/commands:command/dataform:x[@type='form']/dataform:field[@var='quit-message'][@type='text-single']", - "/iq/commands:command/dataform:x[@type='form']/dataform:field[@var='irc-servers'][@type='list-multi']", - "/iq/commands:command/dataform:x[@type='form']/dataform:field[@var='irc-servers']/dataform:option[@label='localhost']/dataform:value[text()='localhost']", - "/iq/commands:command/dataform:x[@type='form']/dataform:field[@var='irc-servers']/dataform:option[@label='irc.localhost']/dataform:value[text()='irc.localhost']", - "/iq/commands:command/commands:actions/commands:next", - ), - after = partial(save_value, "sessionid", partial(extract_attribute, "/iq/commands:command[@node='disconnect-from-irc-server']", "sessionid")) - ), - partial(send_stanza, "<iq type='set' id='command2' from='{jid_admin}/{resource_one}' to='{biboumi_host}'><command xmlns='http://jabber.org/protocol/commands' node='disconnect-from-irc-server' sessionid='{sessionid}' action='next'><x xmlns='jabber:x:data' type='submit'><field var='irc-servers'><value>localhost</value></field><field var='quit-message'><value>Disconnected by e2e</value></field></x></command></iq>"), - partial(expect_unordered, [("/presence[@type='unavailable'][@to='{jid_one}/{resource_one}'][@from='#bon%{irc_server_two}/{nick_three}']",), - ("/iq[@type='result']/commands:command[@node='disconnect-from-irc-server'][@status='completed']/commands:note[@type='info'][text()='{jid_one} was disconnected from 1 IRC server.']",), - ]), - - - # Execute as non-admin (this skips the first step) - partial(send_stanza, "<iq type='set' id='command1' from='{jid_one}/{resource_one}' to='{biboumi_host}'><command xmlns='http://jabber.org/protocol/commands' node='disconnect-from-irc-server' action='execute' /></iq>"), - - partial(expect_stanza, ("/iq[@type='result']/commands:command[@node='disconnect-from-irc-server'][@sessionid][@status='executing']", - "/iq/commands:command/dataform:x[@type='form']/dataform:field[@var='quit-message'][@type='text-single']", - "/iq/commands:command/dataform:x[@type='form']/dataform:field[@var='irc-servers'][@type='list-multi']", - "/iq/commands:command/dataform:x[@type='form']/dataform:field[@var='irc-servers']/dataform:option[@label='irc.localhost']/dataform:value[text()='irc.localhost']", - "/iq/commands:command/commands:actions/commands:next", - ), - after = partial(save_value, "sessionid", partial(extract_attribute, "/iq/commands:command[@node='disconnect-from-irc-server']", "sessionid")) - ), - partial(send_stanza, "<iq type='set' id='command2' from='{jid_one}/{resource_one}' to='{biboumi_host}'><command xmlns='http://jabber.org/protocol/commands' node='disconnect-from-irc-server' sessionid='{sessionid}' action='next'><x xmlns='jabber:x:data' type='submit'><field var='irc-servers'><value>irc.localhost</value></field><field var='quit-message'><value>Disconnected by e2e</value></field></x></command></iq>"), - partial(expect_unordered, [("/presence[@type='unavailable'][@to='{jid_one}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_two}']",), - ("/iq[@type='result']/commands:command[@node='disconnect-from-irc-server'][@status='completed']/commands:note[@type='info'][text()='{jid_one}/{resource_one} was disconnected from 1 IRC server.']",), - ]), - ]), - Scenario("multisessionnick", - [ - handshake_sequence(), - partial(send_stanza, - "<presence from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}/{nick_one}' />"), - 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'][@to='{jid_one}/{resource_one}']/subject[not(text())]"), - - # The other resources joins the same room, with the same nick - partial(send_stanza, - "<presence from='{jid_one}/{resource_two}' to='#foo%{irc_server_one}/{nick_one}' />"), - # We receive our own join - partial(expect_unordered, - [("/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']"), - ("/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, - "<presence from='{jid_two}/{resource_one}' to='#foo%{irc_server_one}/{nick_two}' />"), - connection_sequence("irc.localhost", '{jid_two}/{resource_one}'), - - partial(expect_unordered, [ - ("/presence[@to='{jid_one}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_two}']",), - ("/presence[@to='{jid_one}/{resource_two}'][@from='#foo%{irc_server_one}/{nick_two}']",), - ("/presence[@to='{jid_two}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_two}']", - "/presence/muc_user:x/muc_user:status[@code='110']",), - ("/presence[@to='{jid_two}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_one}']",), - ] - ), - - 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, "<message from='{jid_two}/{resource_one}' to='#foo%{irc_server_one}/{nick_one}' type='chat'><body>RELLO</body></message>"), - # 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']", - "/message/hints:no-copy", - "/message/carbon:private", - "!/message/muc_user:x")), - partial(expect_stanza, "/message[@from='{lower_nick_two}%{irc_server_one}'][@to='{jid_one}/{resource_two}'][@type='chat']/body[text()='RELLO']"), - - - # First occupant (with the two resources) changes her/his nick - partial(send_stanza, "<presence from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}/{nick_two}' />"), - partial(expect_unordered, [ - ("/message[@to='{jid_one}/{resource_one}'][@type='chat']/body[text()='irc.localhost: Bobby: Nickname is already in use.']",), - ("/message[@to='{jid_one}/{resource_two}'][@type='chat']/body[text()='irc.localhost: Bobby: Nickname is already in use.']",), - ("/presence[@to='{jid_one}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_two}'][@type='error']",), - ("/presence[@to='{jid_one}/{resource_two}'][@from='#foo%{irc_server_one}/{nick_two}'][@type='error']",), - ]), - - # First occupant (with the two resources) changes her/his nick - partial(send_stanza, "<presence from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}/{nick_three}' />"), - partial(expect_unordered, [ - ("/presence[@from='#foo%{irc_server_one}/{nick_one}'][@to='{jid_two}/{resource_one}'][@type='unavailable']/muc_user:x/muc_user:item[@nick='Bernard']", - "/presence/muc_user:x/muc_user:status[@code='303']"), - ("/presence[@from='#foo%{irc_server_one}/{nick_three}'][@to='{jid_two}/{resource_one}']",), - ("/presence[@from='#foo%{irc_server_one}/{nick_one}'][@to='{jid_one}/{resource_one}'][@type='unavailable']/muc_user:x/muc_user:item[@nick='Bernard']", - "/presence/muc_user:x/muc_user:status[@code='303']", - "/presence/muc_user:x/muc_user:status[@code='110']"), - ("/presence[@from='#foo%{irc_server_one}/{nick_three}'][@to='{jid_one}/{resource_one}']", - "/presence/muc_user:x/muc_user:status[@code='110']"), - - ("/presence[@from='#foo%{irc_server_one}/{nick_one}'][@to='{jid_one}/{resource_two}'][@type='unavailable']/muc_user:x/muc_user:item[@nick='Bernard']", - "/presence/muc_user:x/muc_user:status[@code='303']", - "/presence/muc_user:x/muc_user:status[@code='110']"), - ("/presence[@from='#foo%{irc_server_one}/{nick_three}'][@to='{jid_one}/{resource_two}']", - "/presence/muc_user:x/muc_user:status[@code='110']"), - ]), - - # One resource leaves the server entirely. - partial(send_stanza, "<presence type='unavailable' from='{jid_one}/{resource_two}' to='#foo%{irc_server_one}/{nick_one}' />"), - # 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, "<message from='{jid_two}/{resource_one}' to='#foo%{irc_server_one}/{nick_three}' type='chat'><body>first</body></message>"), - partial(send_stanza, "<message from='{jid_two}/{resource_one}' to='#foo%{irc_server_one}/{nick_three}' type='chat'><body>second</body></message>"), - # The first user receives the two messages, on the connected resource, once each - - partial(expect_unordered, [ - ("/message[@from='{lower_nick_two}%{irc_server_one}'][@to='{jid_one}/{resource_one}'][@type='chat']/body[text()='first']",), - ("/message[@from='{lower_nick_two}%{irc_server_one}'][@to='{jid_one}/{resource_one}'][@type='chat']/body[text()='second']",), - ]), - ]), - Scenario("persistent_channel", - [ - # Join the channel with user 1 - handshake_sequence(), - partial(send_stanza, - "<presence from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}/{nick_one}' />"), - 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'][@to='{jid_one}/{resource_one}']/subject[not(text())]"), - - # Make it persistent for user 1 - partial(send_stanza, "<iq from='{jid_one}/{resource_one}' id='conf1' to='#foo%{irc_server_one}' type='get'><query xmlns='http://jabber.org/protocol/muc#owner'/></iq>"), - partial(expect_stanza, "/iq[@type='result']/muc_owner:query/dataform:x/dataform:field[@var='persistent'][@type='boolean']/dataform:value[text()='false']"), - partial(send_stanza, "<iq from='{jid_one}/{resource_one}' id='conf2' to='#foo%{irc_server_one}' type='set'><query xmlns='http://jabber.org/protocol/muc#owner'><x type='submit' xmlns='jabber:x:data'><field var='persistent' xmlns='jabber:x:data'><value>true</value></field></x></query></iq>"), - partial(expect_stanza, "/iq[@type='result']"), - - # Check that the value is now effectively true - partial(send_stanza, "<iq from='{jid_one}/{resource_one}' id='conf1' to='#foo%{irc_server_one}' type='get'><query xmlns='http://jabber.org/protocol/muc#owner'/></iq>"), - partial(expect_stanza, "/iq[@type='result']/muc_owner:query/dataform:x/dataform:field[@var='persistent'][@type='boolean']/dataform:value[text()='true']"), - - # A second user joins the same channel - partial(send_stanza, - "<presence from='{jid_two}/{resource_one}' to='#foo%{irc_server_one}/{nick_two}' />"), - connection_sequence("irc.localhost", '{jid_two}/{resource_one}'), - - partial(expect_unordered, [ - ("/presence[@to='{jid_one}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_two}']",), - ("/presence[@to='{jid_two}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_two}']", - "/presence/muc_user:x/muc_user:status[@code='110']",), - ("/presence[@to='{jid_two}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_one}']",), - ] - ), - - partial(expect_stanza, "/message[@from='#foo%{irc_server_one}'][@type='groupchat']/subject[not(text())]"), - - # First user leaves the room (but biboumi will stay in the channel) - partial(send_stanza, - "<presence from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}/{nick_one}' type='unavailable' />"), - # Only user 1 receives the unavailable presence - partial(expect_stanza, - ("/presence[@from='#foo%{irc_server_one}/{nick_one}'][@to='{jid_one}/{resource_one}'][@type='unavailable']/muc_user:x/muc_user:status[@code='110']", - "/presence/muc_user:x/muc_user:item[@affiliation='admin'][@role='moderator']")), - - # Second user sends a channel message - partial(send_stanza, "<message type='groupchat' from='{jid_two}/{resource_one}' to='#foo%{irc_server_one}'><body>coucou</body></message>"), - - # Message should only be received by user 2, since user 1 has no resource in the room - partial(expect_stanza, "/message[@type='groupchat'][@to='{jid_two}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_two}']"), - - # Second user leaves the channel - partial(send_stanza, "<presence type='unavailable' from='{jid_two}/{resource_one}' to='#foo%{irc_server_one}/{nick_two}' />"), - partial(expect_stanza, "/presence[@type='unavailable'][@from='#foo%{irc_server_one}/{nick_two}']"), - ]), - Scenario("channel_join_with_different_nick", - [ - handshake_sequence(), - partial(send_stanza, - "<presence from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}/{nick_one}' />"), - 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'][@to='{jid_one}/{resource_one}']/subject[not(text())]"), - - # The same resource joins a different channel with a different nick - partial(send_stanza, - "<presence from='{jid_one}/{resource_one}' to='#bar%{irc_server_one}/{nick_two}' />"), - - # We must receive a join presence in response, without any nick change (nick_two) must be ignored - partial(expect_stanza, - "/message/body[text()='Mode #bar [+nt] by {irc_host_one}']"), - partial(expect_stanza, - ("/presence[@to='{jid_one}/{resource_one}'][@from='#bar%{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='#bar%{irc_server_one}'][@type='groupchat'][@to='{jid_one}/{resource_one}']/subject[not(text())]"), - ]), - Scenario("notices", - [ - handshake_sequence(), - partial(send_stanza, - "<presence from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}/{nick_one}' />"), - connection_sequence("irc.localhost", '{jid_one}/{resource_one}'), - partial(expect_stanza, "/message"), - partial(expect_stanza, "/presence"), - partial(expect_stanza, "/message"), - - partial(send_stanza, "<message from='{jid_one}/{resource_one}' to='{irc_server_one}' type='chat'><body>NOTICE {nick_one} :[#foo] Hello in a notice.</body></message>"), - partial(expect_stanza, "/message[@from='#foo%{irc_server_one}/{nick_one}'][@type='groupchat']/body[text()='[notice] [#foo] Hello in a notice.']"), - ]), - Scenario("multiline_message", - [ - handshake_sequence(), - # First user joins - partial(send_stanza, - "<presence from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}/{nick_one}' />"), - 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 multi-line channel message - partial(send_stanza, "<message id='the-message-id' from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}' type='groupchat'><body>un\ndeux\ntrois</body></message>"), - # Receive multiple messages, in order - partial(expect_stanza, - "/message[@from='#foo%{irc_server_one}/{nick_one}'][@id='the-message-id'][@to='{jid_one}/{resource_one}'][@type='groupchat']/body[text()='un']"), - partial(expect_stanza, - "/message[@from='#foo%{irc_server_one}/{nick_one}'][@id][@to='{jid_one}/{resource_one}'][@type='groupchat']/body[text()='deux']"), - partial(expect_stanza, - "/message[@from='#foo%{irc_server_one}/{nick_one}'][@id][@to='{jid_one}/{resource_one}'][@type='groupchat']/body[text()='trois']"), - - # Send a simple message, with no id - partial(send_stanza, "<message from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}' type='groupchat'><body>hello</body></message>"), - - # Expect a non-empty id as a result (should be a uuid) - partial(expect_stanza, - "!/message[@id='']/body[text()='hello']"), - - # Second user joins - partial(send_stanza, - "<presence from='{jid_two}/{resource_one}' to='#foo%{irc_server_one}/{nick_two}' />"), - connection_sequence("irc.localhost", '{jid_two}/{resource_one}'), - # Our presence, sent to the other user - partial(expect_unordered, [ - ("/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']",), - ("/presence[@to='{jid_two}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_one}']/muc_user:x/muc_user:item[@affiliation='admin'][@role='moderator']",), - ("/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']"), - ("/message[@from='#foo%{irc_server_one}'][@type='groupchat']/subject[not(text())]",) - ]), - - # Send a multi-line channel message - partial(send_stanza, "<message id='the-message-id' from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}' type='groupchat'><body>un\ndeux\ntrois</body></message>"), - # Receive multiple messages, for each user - partial(expect_unordered, [ - ("/message[@from='#foo%{irc_server_one}/{nick_one}'][@id='the-message-id'][@to='{jid_one}/{resource_one}'][@type='groupchat']/body[text()='un']",), - ("/message[@from='#foo%{irc_server_one}/{nick_one}'][@id][@to='{jid_one}/{resource_one}'][@type='groupchat']/body[text()='deux']",), - ("/message[@from='#foo%{irc_server_one}/{nick_one}'][@id][@to='{jid_one}/{resource_one}'][@type='groupchat']/body[text()='trois']",), - - ("/message[@from='#foo%{irc_server_one}/{nick_one}'][@id][@to='{jid_two}/{resource_one}'][@type='groupchat']/body[text()='un']",), - ("/message[@from='#foo%{irc_server_one}/{nick_one}'][@id][@to='{jid_two}/{resource_one}'][@type='groupchat']/body[text()='deux']",), - ("/message[@from='#foo%{irc_server_one}/{nick_one}'][@id][@to='{jid_two}/{resource_one}'][@type='groupchat']/body[text()='trois']",), - ]) - ]), - Scenario("channel_messages", - [ - handshake_sequence(), - # First user joins - partial(send_stanza, - "<presence from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}/{nick_one}' />"), - 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())]"), - - # Second user joins - partial(send_stanza, - "<presence from='{jid_two}/{resource_one}' to='#foo%{irc_server_one}/{nick_two}' />"), - connection_sequence("irc.localhost", '{jid_two}/{resource_one}'), - # Our presence, sent to the other user - partial(expect_unordered, [ - ("/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']",), - ("/presence[@to='{jid_two}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_one}']/muc_user:x/muc_user:item[@affiliation='admin'][@role='moderator']",), - ("/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']"), - ("/message[@from='#foo%{irc_server_one}'][@type='groupchat']/subject[not(text())]",) - ]), - - # Send a channel message - partial(send_stanza, "<message from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}' type='groupchat'><body>coucou</body></message>"), - # Receive the message, forwarded to the two users - partial(expect_unordered, [ - ("/message[@from='#foo%{irc_server_one}/{nick_one}'][@to='{jid_one}/{resource_one}'][@type='groupchat']/body[text()='coucou']", - "/message/stable_id:stanza-id[@by='#foo%{irc_server_one}'][@id]"), - ("/message[@from='#foo%{irc_server_one}/{nick_one}'][@to='{jid_two}/{resource_one}'][@type='groupchat']/body[text()='coucou']", - "/message/stable_id:stanza-id[@by='#foo%{irc_server_one}'][@id]") - ]), - - # Send a private message, to a in-room JID - partial(send_stanza, "<message from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}/{nick_two}' type='chat'><body>coucou in private</body></message>"), - # 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, "<message from='{jid_two}/{resource_one}' to='{lower_nick_one}%{irc_server_one}' type='chat'><body>yes</body></message>"), - # 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']", - "/message/muc_user:x")), - - ## Do the exact same thing, from a different chan, - # to check if the response comes from the right JID - - partial(send_stanza, - "<presence from='{jid_one}/{resource_one}' to='#dummy%{irc_server_one}/{nick_one}' />"), - partial(expect_stanza, "/message"), - partial(expect_stanza, - "/presence/muc_user:x/muc_user:status[@code='110']"), - partial(expect_stanza, "/message[@from='#dummy%{irc_server_one}'][@type='groupchat']/subject"), - - - # Send a private message, to a in-room JID - partial(send_stanza, "<message from='{jid_one}/{resource_one}' to='#dummy%{irc_server_one}/{nick_two}' type='chat'><body>re in private</body></message>"), - # 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, "<message from='{jid_two}/{resource_one}' to='{lower_nick_one}%{irc_server_one}' type='chat'><body>re</body></message>"), - # The response is received from the in-room JID - partial(expect_stanza, "/message[@from='#dummy%{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, - "<presence from='{jid_one}/{resource_one}' to='#dummy%{irc_server_one}/{nick_one}' type='unavailable' />"), - 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, "<message from='{jid_two}/{resource_one}' to='{lower_nick_one}%{irc_server_one}' type='chat'><body>hihihoho</body></message>"), - 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, - "<presence from='{jid_one}/{resource_one}' to='#biboumi\\40louiz.org\\3a80%{irc_server_one}/{nick_one}' />"), - 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())]"), - ]), - Scenario("self_ping_with_error", - [ - handshake_sequence(), - partial(send_stanza, - "<presence from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}/{nick_one}' />"), - 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, - "<iq type='get' from='{jid_one}/{resource_one}' id='first_ping' to='#foo%{irc_server_one}/{nick_one}'><ping xmlns='urn:xmpp:ping' /></iq>"), - # 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 with an error - partial(send_stanza, - "<iq from='{jid_one}/{resource_one}' id='gnip_tsrif' to='{lower_nick_one}%{irc_server_one}' type='error'><error type='cancel'><feature-not-implemented xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/></error></iq>"), - partial(expect_stanza, - "/iq[@from='#foo%{irc_server_one}/{nick_one}'][@type='result'][@to='{jid_one}/{resource_one}'][@id='first_ping']"), - - # Send a ping to ourself - partial(send_stanza, - "<iq type='get' from='{jid_one}/{resource_one}' id='first_ping' to='#foo%{irc_server_one}/{nick_one}'><ping xmlns='urn:xmpp:ping' /></iq>"), - # 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 with an error - partial(send_stanza, - "<iq from='{jid_one}/{resource_one}' id='gnip_tsrif' to='{lower_nick_one}%{irc_server_one}' type='error'><error type='cancel'><service-unavailable xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/></error></iq>"), - partial(expect_stanza, - "/iq[@from='#foo%{irc_server_one}/{nick_one}'][@type='result'][@to='{jid_one}/{resource_one}'][@id='first_ping']"), - ]), - Scenario("self_ping_not_in_muc", - [ - handshake_sequence(), - partial(send_stanza, - "<presence from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}/{nick_one}' />"), - 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())]"), + provided_scenar_names = sys.argv[1:] + scenarios = get_scenarios(os.path.abspath(os.path.dirname(__file__)), provided_scenar_names) - # Send a ping to ourself, in a muc where we’re not - partial(send_stanza, - "<iq type='get' from='{jid_one}/{resource_one}' id='first_ping' to='#nil%{irc_server_one}/{nick_one}'><ping xmlns='urn:xmpp:ping' /></iq>"), - # Immediately receive an error - partial(expect_stanza, - "/iq[@from='#nil%{irc_server_one}/{nick_one}'][@type='error'][@to='{jid_one}/{resource_one}'][@id='first_ping']/error/stanza:not-allowed"), - - # Send a ping to ourself, in a muc where we are, but not this resource - partial(send_stanza, - "<iq type='get' from='{jid_one}/{resource_two}' id='first_ping' to='#foo%{irc_server_one}/{nick_one}'><ping xmlns='urn:xmpp:ping' /></iq>"), - # Immediately receive an error - partial(expect_stanza, - "/iq[@from='#foo%{irc_server_one}/{nick_one}'][@type='error'][@to='{jid_one}/{resource_two}'][@id='first_ping']/error/stanza:not-allowed"), - ]), - Scenario("self_ping_on_real_channel", - [ - handshake_sequence(), - partial(send_stanza, - "<presence from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}/{nick_one}' />"), - 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, - "<iq type='get' from='{jid_one}/{resource_one}' id='first_ping' to='#foo%{irc_server_one}/{nick_one}'><ping xmlns='urn:xmpp:ping' /></iq>"), - # 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, - "<iq type='result' to='{lower_nick_one}%{irc_server_one}' id='gnip_tsrif' from='{jid_one}/{resource_one}'/>"), - 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, - "<presence from='{jid_one}/{resource_two}' to='#foo%{irc_server_one}/{nick_one}' />"), - 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, - "<iq type='get' from='{jid_one}/{resource_one}' id='second_ping' to='#foo%{irc_server_one}/{nick_one}'><ping xmlns='urn:xmpp:ping' /></iq>"), - # 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, - "<iq type='result' to='{lower_nick_one}%{irc_server_one}' id='gnip_dnoces' from='{to}'/>"), - 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, - "<iq type='get' from='{jid_one}/{resource_two}' id='third_ping' to='#foo%{irc_server_one}/{nick_one}'><ping xmlns='urn:xmpp:ping' /></iq>"), - 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, - "<iq type='result' to='{lower_nick_one}%{irc_server_one}' id='gnip_driht' from='{to}'/>"), - partial(expect_stanza, - "/iq[@from='#foo%{irc_server_one}/{nick_one}'][@type='result'][@to='{jid_one}/{resource_two}'][@id='third_ping']"), - - ]), - Scenario("self_ping_fixed_server", [ - handshake_sequence(), - partial(send_stanza, - "<presence from='{jid_one}/{resource_one}' to='#foo@{biboumi_host}/{nick_one}' />"), - connection_sequence("irc.localhost", '{jid_one}/{resource_one}', fixed_irc_server=True), - partial(expect_stanza, - "/message/body[text()='Mode #foo [+nt] by {irc_host_one}']"), - partial(expect_stanza, - ("/presence[@to='{jid_one}/{resource_one}'][@from='#foo@{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='#foo@{biboumi_host}'][@type='groupchat']/subject[not(text())]"), - - # Send a ping to ourself - partial(send_stanza, - "<iq type='get' from='{jid_one}/{resource_one}' id='first_ping' to='#foo@{biboumi_host}/{nick_one}'><ping xmlns='urn:xmpp:ping' /></iq>"), - # We receive our own ping request, - partial(expect_stanza, - "/iq[@from='{lower_nick_one}@{biboumi_host}'][@type='get'][@to='{jid_one}/{resource_one}'][@id='gnip_tsrif']"), - # Respond to the request - partial(send_stanza, - "<iq type='result' to='{lower_nick_one}@{biboumi_host}' id='gnip_tsrif' from='{jid_one}/{resource_one}'/>"), - partial(expect_stanza, - "/iq[@from='#foo@{biboumi_host}/{nick_one}'][@type='result'][@to='{jid_one}/{resource_one}'][@id='first_ping']"), - ], conf="fixed_server"), - Scenario("simple_kick", - [ - handshake_sequence(), - # First user joins - partial(send_stanza, - "<presence from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}/{nick_one}' />"), - 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, - "<presence from='{jid_two}/{resource_one}' to='#foo%{irc_server_one}/{nick_two}' />"), - connection_sequence("irc.localhost", '{jid_two}/{resource_one}'), - partial(expect_unordered, [ - ("/presence/muc_user:x/muc_user:item[@affiliation='none'][@role='participant']",), - ("/presence/muc_user:x/muc_user:item[@affiliation='admin'][@role='moderator']",), - ("/presence/muc_user:x/muc_user:status[@code='110']",), - ("/message/subject",), - ]), - - # demonstrate bug https://lab.louiz.org/louiz/biboumi/issues/3291 - # First user joins an other channel - partial(send_stanza, - "<presence from='{jid_one}/{resource_one}' to='#bar%{irc_server_one}/{nick_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, - "<presence from='{jid_two}/{resource_one}' to='#bar%{irc_server_one}/{nick_two}' />"), - partial(expect_unordered, [ - ("/presence/muc_user:x/muc_user:item[@affiliation='none'][@role='participant']",), - ("/presence/muc_user:x/muc_user:item[@affiliation='admin'][@role='moderator']",), - ("/presence/muc_user:x/muc_user:status[@code='110']",), - ("/message/subject",), - ]), - - # Moderator kicks participant - partial(send_stanza, - "<iq id='kick1' to='#foo%{irc_server_one}' from='{jid_one}/{resource_one}' type='set'><query xmlns='http://jabber.org/protocol/muc#admin'><item nick='{nick_two}' role='none'><reason>reported</reason></item></query></iq>"), - partial(expect_unordered, [ - ("/presence[@type='unavailable'][@to='{jid_two}/{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']" - ), - ("/presence[@type='unavailable'][@to='{jid_one}/{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']", - ), - ("/iq[@id='kick1'][@type='result']",), - ]), - - # Bug 3291, suite. We must not receive any presence from #foo, here - partial(send_stanza, "<message from='{jid_two}/{resource_one}' to='{irc_server_one}' type='chat'><body>QUIT bye bye</body></message>"), - partial(expect_unordered, - [("/presence[@from='#bar%{irc_server_one}/{nick_two}'][@to='{jid_one}/{resource_one}']",), - ("/presence[@from='#bar%{irc_server_one}/{nick_two}'][@to='{jid_two}/{resource_one}']",), - ("/message",), - ("/message",)]) - ]), - Scenario("mode_change", - [ - handshake_sequence(), - # First user joins - partial(send_stanza, - "<presence from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}/{nick_one}' />"), - 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, - "<presence from='{jid_two}/{resource_one}' to='#foo%{irc_server_one}/{nick_two}' />"), - connection_sequence("irc.localhost", '{jid_two}/{resource_one}'), - partial(expect_unordered, [ - ("/presence/muc_user:x/muc_user:item[@affiliation='none'][@role='participant']",), - ("/presence/muc_user:x/muc_user:item[@affiliation='admin'][@role='moderator']",), - ("/presence/muc_user:x/muc_user:status[@code='110']",), - ("/message/subject",), - ]), - - # Change a user mode with a message starting with /mode - partial(send_stanza, - "<message from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}' type='groupchat'><body>/mode +v {nick_two}</body></message>"), - partial(expect_unordered, [ - ("/message[@to='{jid_one}/{resource_one}']/body[text()='Mode #foo [+v {nick_two}] by {nick_one}']",), - ("/message[@to='{jid_two}/{resource_one}']/body[text()='Mode #foo [+v {nick_two}] by {nick_one}']",), - ("/presence[@to='{jid_two}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_two}']/muc_user:x/muc_user:item[@affiliation='member'][@role='participant']",), - ("/presence[@to='{jid_one}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_two}']/muc_user:x/muc_user:item[@affiliation='member'][@role='participant']",) - ]), - - # using an iq - partial(send_stanza, - "<iq from='{jid_one}/{resource_one}' id='id1' to='#foo%{irc_server_one}' type='set'><query xmlns='http://jabber.org/protocol/muc#admin'><item affiliation='admin' nick='{nick_two}'/></query></iq>"), - partial(expect_unordered, [ - ("/message[@to='{jid_one}/{resource_one}']/body[text()='Mode #foo [+o {nick_two}] by {nick_one}']",), - ("/message[@to='{jid_two}/{resource_one}']/body[text()='Mode #foo [+o {nick_two}] by {nick_one}']",), - ("/presence[@to='{jid_two}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_two}']/muc_user:x/muc_user:item[@affiliation='admin'][@role='moderator']",), - ("/presence[@to='{jid_one}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_two}']/muc_user:x/muc_user:item[@affiliation='admin'][@role='moderator']",), - ("/iq[@id='id1'][@type='result'][@to='{jid_one}/{resource_one}'][@from='#foo%{irc_server_one}']",), - ]), - - # remove the mode - partial(send_stanza, - "<iq from='{jid_one}/{resource_one}' id='id1' to='#foo%{irc_server_one}' type='set'><query xmlns='http://jabber.org/protocol/muc#admin'><item affiliation='member' nick='{nick_two}' role='participant'/></query></iq>"), - partial(expect_unordered, [ - ("/message[@to='{jid_one}/{resource_one}']/body[text()='Mode #foo [+v-o {nick_two} {nick_two}] by {nick_one}']",), - ("/message[@to='{jid_two}/{resource_one}']/body[text()='Mode #foo [+v-o {nick_two} {nick_two}] by {nick_one}']",), - ("/presence[@to='{jid_two}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_two}']/muc_user:x/muc_user:item[@affiliation='member'][@role='participant']",), - ("/presence[@to='{jid_one}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_two}']/muc_user:x/muc_user:item[@affiliation='member'][@role='participant']",), - ("/iq[@id='id1'][@type='result'][@to='{jid_one}/{resource_one}'][@from='#foo%{irc_server_one}']",), - ]), - - # using an iq, an a non-existant nick - partial(send_stanza, - "<iq from='{jid_one}/{resource_one}' id='id1' to='#foo%{irc_server_one}' type='set'><query xmlns='http://jabber.org/protocol/muc#admin'><item affiliation='admin' nick='blectre'/></query></iq>"), - partial(expect_stanza, "/iq[@type='error']"), - - # using an iq, without the rights to do it - partial(send_stanza, - "<iq from='{jid_two}/{resource_one}' id='id1' to='#foo%{irc_server_one}' type='set'><query xmlns='http://jabber.org/protocol/muc#admin'><item affiliation='admin' nick='{nick_one}'/></query></iq>"), - partial(expect_unordered, [ - ("/iq[@type='error']",), - ("/message[@type='chat'][@to='{jid_two}/{resource_one}']",), - ]), - - # using an iq, with an unknown mode - partial(send_stanza, - "<iq from='{jid_two}/{resource_one}' id='id1' to='#foo%{irc_server_one}' type='set'><query xmlns='http://jabber.org/protocol/muc#admin'><item affiliation='owner' nick='{nick_one}'/></query></iq>"), - partial(expect_unordered, [ - ("/iq[@type='error']",), - ("/message[@type='chat'][@to='{jid_two}/{resource_one}']",), - ]), - - ]), - Scenario("multisession_kick", - [ - handshake_sequence(), - # First user joins - partial(send_stanza, - "<presence from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}/{nick_one}' />"), - 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, fprom two resources - partial(send_stanza, - "<presence from='{jid_two}/{resource_one}' to='#foo%{irc_server_one}/{nick_two}' />"), - connection_sequence("irc.localhost", '{jid_two}/{resource_one}'), - partial(expect_unordered, [ - ("/presence/muc_user:x/muc_user:item[@affiliation='none'][@role='participant']",), - ("/presence/muc_user:x/muc_user:item[@affiliation='admin'][@role='moderator']",), - ("/presence/muc_user:x/muc_user:status[@code='110']",), - ("/message/subject",), - ]), - - partial(send_stanza, - "<presence from='{jid_two}/{resource_two}' to='#foo%{irc_server_one}/{nick_two}' />"), - 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(send_stanza, - "<iq id='kick1' to='#foo%{irc_server_one}' from='{jid_one}/{resource_one}' type='set'><query xmlns='http://jabber.org/protocol/muc#admin'><item nick='{nick_two}' role='none'><reason>reported</reason></item></query></iq>"), - partial(expect_unordered, [ - ("/presence[@type='unavailable'][@to='{jid_two}/{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']" - ), - ("/presence[@type='unavailable'][@to='{jid_two}/{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']" - ), - ("/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']", - ), - ("/iq[@id='kick1'][@type='result']",), - ]), - ]), - Scenario("self_version", - [ - handshake_sequence(), - partial(send_stanza, - "<presence from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}/{nick_one}' />"), - 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, - "<iq type='get' from='{jid_one}/{resource_one}' id='first_version' to='#foo%{irc_server_one}/{nick_one}'><query xmlns='jabber:iq:version' /></iq>"), - # 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, - "<iq type='result' to='{lower_nick_one}%{irc_server_one}' id='{id}' from='{jid_one}/{resource_one}'><query xmlns='jabber:iq:version'><name>e2e test</name><version>1.0</version><os>Fedora</os></query></iq>"), - 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, - "<presence from='{jid_one}/{resource_two}' to='#foo%{irc_server_one}/{nick_one}' />"), - 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, - "<iq type='get' from='{jid_one}/{resource_two}' id='second_version' to='#foo%{irc_server_one}/{nick_one}'><query xmlns='jabber:iq:version' /></iq>"), - # 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, - "<iq type='result' to='{lower_nick_one}%{irc_server_one}' id='{id}' from='{to}'><query xmlns='jabber:iq:version'><name>e2e test</name><version>1.0</version><os>Fedora</os></query></iq>"), - 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, - "<iq type='get' from='{jid_one}/{resource_one}' id='second_version' to='#foo%{irc_server_one}/{nick_one}'><query xmlns='jabber:iq:version' /></iq>"), - # 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, - "<iq type='result' to='{lower_nick_one}%{irc_server_one}' id='{id}' from='{to}'><query xmlns='jabber:iq:version'><name>e2e test</name><version>1.0</version><os>Fedora</os></query></iq>"), - partial(expect_stanza, - "/iq[@from='#foo%{irc_server_one}/{nick_one}'][@type='result'][@to='{jid_one}/{resource_one}'][@id='second_version']"), - ]), - Scenario("version_on_global_nick", - [ - handshake_sequence(), - partial(send_stanza, - "<presence from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}/{nick_one}' />"), - 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())]"), - - partial(send_stanza, - "<iq type='get' from='{jid_one}/{resource_one}' id='first_version' to='{lower_nick_one}%{irc_server_one}'><query xmlns='jabber:iq:version' /></iq>"), - - 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'))), - partial(send_stanza, - "<iq type='result' to='{lower_nick_one}%{irc_server_one}' id='{id}' from='{jid_one}/{resource_one}'><query xmlns='jabber:iq:version'><name>e2e test</name><version>1.0</version><os>Fedora</os></query></iq>"), - partial(expect_stanza, - "/iq[@from='{lower_nick_one}%{irc_server_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']"), - - ]), - Scenario("self_invite", - [ - handshake_sequence(), - partial(send_stanza, - "<presence from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}/{nick_one}' />"), - 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())]"), - partial(send_stanza, - "<message from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}'><x xmlns='http://jabber.org/protocol/muc#user'><invite to='{nick_one}'/></x></message>"), - partial(expect_stanza, - "/message/body[text()='{nick_one} is already on channel #foo']") - ]), - Scenario("client_error", - [ - handshake_sequence(), - # First resource - partial(send_stanza, - "<presence from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}/{nick_one}' />"), - 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())]"), - - # Second resource, same channel - partial(send_stanza, - "<presence from='{jid_one}/{resource_two}' to='#foo%{irc_server_one}/{nick_one}' />"), - 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())]"), - - # Now the first resource has an error - partial(send_stanza, - "<message from='{jid_one}/{resource_one}' to='#foo%%{irc_server_one}/{nick_one}' type='error'><error type='cancel'><recipient-unavailable xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/></error></message>"), - # Receive a leave only to the leaving resource - partial(expect_stanza, - ("/presence[@type='unavailable'][@from='#foo%{irc_server_one}/{nick_one}'][@to='{jid_one}/{resource_one}']/muc_user:x/muc_user:status[@code='110']", - "/presence/status[text()='Biboumi note: 1 resources are still in this channel.']") - ), - ]), - Scenario("simple_mam", - [ - handshake_sequence(), - partial(send_stanza, - "<presence from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}/{nick_one}' />"), - 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 two channel messages - partial(send_stanza, "<message from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}' type='groupchat'><body>coucou</body></message>"), - partial(expect_stanza, - ("/message[@from='#foo%{irc_server_one}/{nick_one}'][@to='{jid_one}/{resource_one}'][@type='groupchat']/body[text()='coucou']", - "/message/stable_id:stanza-id[@by='#foo%{irc_server_one}'][@id]",) - ), - - partial(send_stanza, "<message from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}' type='groupchat'><body>coucou 2</body></message>"), - partial(expect_stanza, "/message[@from='#foo%{irc_server_one}/{nick_one}'][@to='{jid_one}/{resource_one}'][@type='groupchat']/body[text()='coucou 2']"), - - # Retrieve the complete archive - partial(send_stanza, "<iq to='#foo%{irc_server_one}' from='{jid_one}/{resource_one}' type='set' id='id1'><query xmlns='urn:xmpp:mam:2' queryid='qid1' /></iq>"), - - partial(expect_stanza, - ("/message/mam:result[@queryid='qid1']/forward:forwarded/delay:delay", - "/message/mam:result[@queryid='qid1']/forward:forwarded/client:message[@from='#foo%{irc_server_one}/{nick_one}'][@type='groupchat']/client:body[text()='coucou']") - ), - partial(expect_stanza, - ("/message/mam:result[@queryid='qid1']/forward:forwarded/delay:delay", - "/message/mam:result[@queryid='qid1']/forward:forwarded/client:message[@from='#foo%{irc_server_one}/{nick_one}'][@type='groupchat']/client:body[text()='coucou 2']") - ), - - partial(expect_stanza, - ("/iq[@type='result'][@id='id1'][@from='#foo%{irc_server_one}'][@to='{jid_one}/{resource_one}']", - "/iq/mam:fin/rms:set/rsm:last", - "/iq/mam:fin/rsm:set/rsm:first", - "/iq/mam:fin[@complete='true']")), - - # Retrieve an empty archive by specifying an early “end” date - partial(send_stanza, """<iq to='#foo%{irc_server_one}' from='{jid_one}/{resource_one}' type='set' id='id2'> - <query xmlns='urn:xmpp:mam:2' queryid='qid2'> - <x xmlns='jabber:x:data' type='submit'> - <field var='FORM_TYPE' type='hidden'> <value>urn:xmpp:mam:2</value></field> - <field var='end'><value>2000-06-07T00:00:00Z</value></field> - </x> - </query></iq>"""), - - partial(expect_stanza, - ("/iq[@type='result'][@id='id2'][@from='#foo%{irc_server_one}'][@to='{jid_one}/{resource_one}']", - "/iq/mam:fin[@complete='true']/rsm:set",)), - - # Retrieve an empty archive by specifying a late “start” date - # (note that this test will break in ~1000 years) - partial(send_stanza, """<iq to='#foo%{irc_server_one}' from='{jid_one}/{resource_one}' type='set' id='id3'> - <query xmlns='urn:xmpp:mam:2' queryid='qid3'> - <x xmlns='jabber:x:data' type='submit'> - <field var='FORM_TYPE' type='hidden'> <value>urn:xmpp:mam:2</value></field> - <field var='start'><value>3016-06-07T00:00:00Z</value></field> - </x> - </query></iq>"""), - - partial(expect_stanza, - ("/iq[@type='result'][@id='id3'][@from='#foo%{irc_server_one}'][@to='{jid_one}/{resource_one}']", - "/iq/mam:fin[@complete='true']/rsm:set")), - - # Retrieve the whole archive, but limit the response to one elemet - partial(send_stanza, "<iq to='#foo%{irc_server_one}' from='{jid_one}/{resource_one}' type='set' id='id4'><query xmlns='urn:xmpp:mam:2' queryid='qid4'><set xmlns='http://jabber.org/protocol/rsm'><max>1</max></set></query></iq>"), - - partial(expect_stanza, - ("/message/mam:result[@queryid='qid4']/forward:forwarded/delay:delay", - "/message/mam:result[@queryid='qid4']/forward:forwarded/client:message[@from='#foo%{irc_server_one}/{nick_one}'][@type='groupchat']/client:body[text()='coucou']") - ), - - partial(expect_stanza, - ("/iq[@type='result'][@id='id4'][@from='#foo%{irc_server_one}'][@to='{jid_one}/{resource_one}']", - "!/iq/mam:fin[@complete='true']/rsm:set")), - - ]), - Scenario("mam_with_timestamps", - [ - handshake_sequence(), - partial(send_stanza, - "<presence from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}/{nick_one}' />"), - 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 two channel messages - partial(send_stanza, "<message from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}' type='groupchat'><body>coucou</body></message>"), - partial(expect_stanza, - ("/message[@from='#foo%{irc_server_one}/{nick_one}'][@to='{jid_one}/{resource_one}'][@type='groupchat']/body[text()='coucou']", - "/message/stable_id:stanza-id[@by='#foo%{irc_server_one}'][@id]",) - ), - - partial(send_stanza, "<message from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}' type='groupchat'><body>coucou 2</body></message>"), - # Record the current time - partial(expect_stanza, "/message[@from='#foo%{irc_server_one}/{nick_one}'][@to='{jid_one}/{resource_one}'][@type='groupchat']/body[text()='coucou 2']", - after = partial(save_current_timestamp_plus_delta, "first_timestamp", datetime.timedelta(seconds=1))), - - # Wait two seconds before sending two new messages - partial(sleep_for, 2), - partial(send_stanza, "<message from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}' type='groupchat'><body>coucou 3</body></message>"), - partial(send_stanza, "<message from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}' type='groupchat'><body>coucou 4</body></message>"), - partial(expect_stanza, "/message[@type='groupchat']/body[text()='coucou 3']"), - partial(expect_stanza, "/message[@type='groupchat']/body[text()='coucou 4']", - after = partial(save_current_timestamp_plus_delta, "second_timestamp", datetime.timedelta(seconds=1))), - - # Retrieve the archive, after our saved datetime - partial(send_stanza, """<iq to='#foo%{irc_server_one}' from='{jid_one}/{resource_one}' type='set' id='id8'> - <query xmlns='urn:xmpp:mam:2' queryid='qid16'> - <x type='submit' xmlns='jabber:x:data'> - <field var='FORM_TYPE' xmlns='jabber:x:data'><value xmlns='jabber:x:data'>urn:xmpp:mam:2</value></field> - <field var='start' xmlns='jabber:x:data'><value xmlns='jabber:x:data'>{first_timestamp}</value></field> - <field var='end' xmlns='jabber:x:data'><value xmlns='jabber:x:data'>{second_timestamp}</value></field> - </x> - </query> - </iq>"""), - - - partial(expect_stanza, - ("/message/mam:result[@queryid='qid16']/forward:forwarded/delay:delay", - "/message/mam:result/forward:forwarded/client:message[@from='#foo%{irc_server_one}/{nick_one}'][@type='groupchat']/client:body[text()='coucou 3']") - ), - - partial(expect_stanza, - ("/message/mam:result[@queryid='qid16']/forward:forwarded/delay:delay", - "/message/mam:result/forward:forwarded/client:message[@from='#foo%{irc_server_one}/{nick_one}'][@type='groupchat']/client:body[text()='coucou 4']") - ), - - partial(expect_stanza, - ("/iq[@type='result'][@id='id8'][@from='#foo%{irc_server_one}'][@to='{jid_one}/{resource_one}']", - "/iq/mam:fin[@complete='true']/rsm:set")), - ]), - Scenario("join_history_limits", - [ - handshake_sequence(), - partial(send_stanza, - "<presence from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}/{nick_one}' />"), - 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 two channel messages - partial(send_stanza, "<message from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}' type='groupchat'><body>coucou</body></message>"), - partial(expect_stanza, - ("/message[@from='#foo%{irc_server_one}/{nick_one}'][@to='{jid_one}/{resource_one}'][@type='groupchat']/body[text()='coucou']", - "/message/stable_id:stanza-id[@by='#foo%{irc_server_one}'][@id]",) - ), - - partial(send_stanza, "<message from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}' type='groupchat'><body>coucou 2</body></message>"), - # Record the current time - partial(expect_stanza, "/message[@from='#foo%{irc_server_one}/{nick_one}'][@to='{jid_one}/{resource_one}'][@type='groupchat']/body[text()='coucou 2']", - after = partial(save_current_timestamp_plus_delta, "first_timestamp", datetime.timedelta(seconds=1))), - - # Wait two seconds before sending two new messages - partial(sleep_for, 2), - partial(send_stanza, "<message from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}' type='groupchat'><body>coucou 3</body></message>"), - partial(send_stanza, "<message from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}' type='groupchat'><body>coucou 4</body></message>"), - partial(expect_stanza, "/message[@type='groupchat']/body[text()='coucou 3']"), - partial(expect_stanza, "/message[@type='groupchat']/body[text()='coucou 4']", - after = partial(save_current_timestamp_plus_delta, "second_timestamp", datetime.timedelta(seconds=1))), - - # join some other channel, to stay connected to the server even after leaving #foo - partial(send_stanza, - "<presence from='{jid_one}/{resource_one}' to='#DUMMY%{irc_server_one}/{nick_one}' />"), - partial(expect_stanza, "/message"), - partial(expect_stanza, "/presence/muc_user:x/muc_user:status[@code='110']"), - partial(expect_stanza, "/message/subject"), - - # Leave #foo - partial(send_stanza, "<presence from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}' type='unavailable' />"), - partial(expect_stanza, "/presence[@type='unavailable']"), - - # Rejoin #foo, with some history limit - partial(send_stanza, - "<presence from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}/{nick_one}'><x xmlns='http://jabber.org/protocol/muc'><history maxchars='0'/></x></presence>"), - partial(expect_stanza, "/message"), - partial(expect_stanza, "/presence/muc_user:x/muc_user:status[@code='110']"), - partial(expect_stanza, "/message/subject"), - - partial(send_stanza, "<presence from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}' type='unavailable' />"), - partial(expect_stanza, "/presence[@type='unavailable']"), - - # Rejoin #foo, with some history limit - partial(send_stanza, - "<presence from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}/{nick_one}'><x xmlns='http://jabber.org/protocol/muc'><history maxstanzas='3'/></x></presence>"), - partial(expect_stanza, "/message"), - partial(expect_stanza, "/presence/muc_user:x/muc_user:status[@code='110']"), - partial(expect_stanza, - "/message[@from='#foo%{irc_server_one}/{nick_one}'][@type='groupchat']/body[text()='coucou 2']"), - partial(expect_stanza, - "/message[@from='#foo%{irc_server_one}/{nick_one}'][@type='groupchat']/body[text()='coucou 3']"), - partial(expect_stanza, - "/message[@from='#foo%{irc_server_one}/{nick_one}'][@type='groupchat']/body[text()='coucou 4']"), - partial(expect_stanza, "/message/subject"), - - partial(send_stanza, "<presence from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}' type='unavailable' />"), - partial(expect_stanza, "/presence[@type='unavailable']"), - - - # Rejoin #foo, with some history limit - partial(send_stanza, - "<presence from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}/{nick_one}'><x xmlns='http://jabber.org/protocol/muc'><history since='{first_timestamp}'/></x></presence>"), - partial(expect_stanza, "/message"), - partial(expect_stanza, "/presence/muc_user:x/muc_user:status[@code='110']"), - partial(expect_stanza, - "/message[@from='#foo%{irc_server_one}/{nick_one}'][@type='groupchat']/body[text()='coucou 3']"), - partial(expect_stanza, - "/message[@from='#foo%{irc_server_one}/{nick_one}'][@type='groupchat']/body[text()='coucou 4']"), - partial(expect_stanza, "/message/subject"), - partial(send_stanza, "<presence from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}' type='unavailable' />"), - partial(expect_stanza, "/presence[@type='unavailable']"), - - # Rejoin #foo, with some history limit - partial(send_stanza, - "<presence from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}/{nick_one}'><x xmlns='http://jabber.org/protocol/muc'><history seconds='1'/></x></presence>"), - partial(expect_stanza, "/message"), - partial(expect_stanza, "/presence/muc_user:x/muc_user:status[@code='110']"), - partial(expect_stanza, - "/message[@from='#foo%{irc_server_one}/{nick_one}'][@type='groupchat']/body[text()='coucou 3']"), - partial(expect_stanza, - "/message[@from='#foo%{irc_server_one}/{nick_one}'][@type='groupchat']/body[text()='coucou 4']"), - partial(expect_stanza, "/message/subject"), - partial(send_stanza, "<presence from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}' type='unavailable' />"), - partial(expect_stanza, "/presence[@type='unavailable']"), - - # Rejoin #foo, with some history limit - partial(send_stanza, - "<presence from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}/{nick_one}'><x xmlns='http://jabber.org/protocol/muc'><history seconds='5'/></x></presence>"), - partial(expect_stanza, "/message"), - partial(expect_stanza, "/presence/muc_user:x/muc_user:status[@code='110']"), - partial(expect_stanza, - "/message[@from='#foo%{irc_server_one}/{nick_one}'][@type='groupchat']/body[text()='coucou']"), partial(expect_stanza, - "/message[@from='#foo%{irc_server_one}/{nick_one}'][@type='groupchat']/body[text()='coucou 2']"), - partial(expect_stanza, - "/message[@from='#foo%{irc_server_one}/{nick_one}'][@type='groupchat']/body[text()='coucou 3']"), - partial(expect_stanza, - "/message[@from='#foo%{irc_server_one}/{nick_one}'][@type='groupchat']/body[text()='coucou 4']"), - partial(expect_stanza, "/message/subject"), - partial(send_stanza, "<presence from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}' type='unavailable' />"), - partial(expect_stanza, "/presence[@type='unavailable']"), - - ]), - Scenario("mam_on_fixed_server", - [ - handshake_sequence(), - - partial(send_stanza, - "<presence from='{jid_one}/{resource_one}' to='#foo@{biboumi_host}/{nick_one}' />"), - connection_sequence("irc.localhost", '{jid_one}/{resource_one}', fixed_irc_server=True), - partial(expect_stanza, - "/message/body[text()='Mode #foo [+nt] by {irc_host_one}']"), - partial(expect_stanza, - ("/presence[@to='{jid_one}/{resource_one}'][@from='#foo@{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='#foo@{biboumi_host}'][@type='groupchat']/subject[not(text())]"), - - partial(send_stanza, "<message from='{jid_one}/{resource_one}' to='#foo@{biboumi_host}' type='groupchat'><body>coucou</body></message>"), - partial(expect_stanza, "/message[@from='#foo@{biboumi_host}/{nick_one}'][@to='{jid_one}/{resource_one}'][@type='groupchat']/body[text()='coucou']"), - - partial(send_stanza, "<message from='{jid_one}/{resource_one}' to='#foo@{biboumi_host}' type='groupchat'><body>coucou 2</body></message>"), - partial(expect_stanza, "/message[@from='#foo@{biboumi_host}/{nick_one}'][@to='{jid_one}/{resource_one}'][@type='groupchat']/body[text()='coucou 2']"), - - # Retrieve the complete archive - partial(send_stanza, "<iq to='#foo@{biboumi_host}' from='{jid_one}/{resource_one}' type='set' id='id1'><query xmlns='urn:xmpp:mam:2' queryid='qid1' /></iq>"), - - partial(expect_stanza, - ("/message/mam:result[@queryid='qid1']/forward:forwarded/delay:delay", - "/message/mam:result[@queryid='qid1']/forward:forwarded/client:message[@from='#foo@{biboumi_host}/{nick_one}'][@type='groupchat']/client:body[text()='coucou']") - ), - partial(expect_stanza, - ("/message/mam:result[@queryid='qid1']/forward:forwarded/delay:delay", - "/message/mam:result[@queryid='qid1']/forward:forwarded/client:message[@from='#foo@{biboumi_host}/{nick_one}'][@type='groupchat']/client:body[text()='coucou 2']") - ), - ], conf="fixed_server"), - Scenario("default_mam_limit", - [ - handshake_sequence(), - partial(send_stanza, - "<presence from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}/{nick_one}' />"), - 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())]", - after = partial(save_value, "counter", lambda x: 0)), - ] + [ - partial(send_stanza, "<message from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}' type='groupchat'><body>{counter}</body></message>"), - partial(expect_stanza, - "/message[@from='#foo%{irc_server_one}/{nick_one}'][@to='{jid_one}/{resource_one}'][@type='groupchat']/body[text()='{counter}']", - after = partial(save_value, "counter", lambda stanza: str(1 + int(extract_text("/message/body", stanza)))) - ), - ] * 150 + [ - # Retrieve the archive, without any restriction - partial(send_stanza, "<iq to='#foo%{irc_server_one}' from='{jid_one}/{resource_one}' type='set' id='id1'><query xmlns='urn:xmpp:mam:2' queryid='qid1' /></iq>"), - partial(expect_stanza, - ("/message/mam:result[@queryid='qid1']/forward:forwarded/delay:delay", - "/message/mam:result[@queryid='qid1']/forward:forwarded/client:message[@from='#foo%{irc_server_one}/{nick_one}'][@type='groupchat']/client:body[text()='0']") - ), - ] + [ - # followed by 98 more messages - partial(expect_stanza, - ("/message/mam:result[@queryid='qid1']/forward:forwarded/delay:delay", - "/message/mam:result[@queryid='qid1']/forward:forwarded/client:message[@from='#foo%{irc_server_one}/{nick_one}'][@type='groupchat']/client:body") - ), - ] * 98 + [ - # and finally the message "99" - partial(expect_stanza, - ("/message/mam:result[@queryid='qid1']/forward:forwarded/delay:delay", - "/message/mam:result[@queryid='qid1']/forward:forwarded/client:message[@from='#foo%{irc_server_one}/{nick_one}'][@type='groupchat']/client:body[text()='99']"), - after = partial(save_value, "last_uuid", partial(extract_attribute, "/message/mam:result", "id")) - ), - # And it should not be marked as complete - partial(expect_stanza, - ("/iq[@type='result'][@id='id1'][@from='#foo%{irc_server_one}'][@to='{jid_one}/{resource_one}']", - "/iq/mam:fin/rsm:set/rsm:last[text()='{last_uuid}']", - "!/iq//mam:fin[@complete='true']", - "/iq//mam:fin")), - - # Retrieve the next page, using the “after” thingy - partial(send_stanza, "<iq to='#foo%{irc_server_one}' from='{jid_one}/{resource_one}' type='set' id='id2'><query xmlns='urn:xmpp:mam:2' queryid='qid2' ><set xmlns='http://jabber.org/protocol/rsm'><after>{last_uuid}</after></set></query></iq>"), - - partial(expect_stanza, - ("/message/mam:result[@queryid='qid2']/forward:forwarded/delay:delay", - "/message/mam:result[@queryid='qid2']/forward:forwarded/client:message[@from='#foo%{irc_server_one}/{nick_one}'][@type='groupchat']/client:body[text()='100']") - ), - ] + 48 * [ - partial(expect_stanza, - ("/message/mam:result[@queryid='qid2']/forward:forwarded/delay:delay", - "/message/mam:result[@queryid='qid2']/forward:forwarded/client:message[@from='#foo%{irc_server_one}/{nick_one}'][@type='groupchat']/client:body") - ), - ] + [ - partial(expect_stanza, - ("/message/mam:result[@queryid='qid2']/forward:forwarded/delay:delay", - "/message/mam:result[@queryid='qid2']/forward:forwarded/client:message[@from='#foo%{irc_server_one}/{nick_one}'][@type='groupchat']/client:body[text()='149']"), - after = partial(save_value, "last_uuid", partial(extract_attribute, "/message/mam:result", "id")) - ), - partial(expect_stanza, - ("/iq[@type='result'][@id='id2'][@from='#foo%{irc_server_one}'][@to='{jid_one}/{resource_one}']", - "/iq/mam:fin/rsm:set/rsm:last[text()='{last_uuid}']", - "/iq//mam:fin[@complete='true']", - "/iq//mam:fin")), - - # Send a request with a non-existing ID set as the “after” value. - partial(send_stanza, "<iq to='#foo%{irc_server_one}' from='{jid_one}/{resource_one}' type='set' id='id3'><query xmlns='urn:xmpp:mam:2' queryid='qid3' ><set xmlns='http://jabber.org/protocol/rsm'><after>DUMMY_ID</after></set></query></iq>"), - partial(expect_stanza, "/iq[@id='id3'][@type='error']/error[@type='cancel']/stanza:item-not-found"), - - # Request the last page just BEFORE the last message in the archive - partial(send_stanza, "<iq to='#foo%{irc_server_one}' from='{jid_one}/{resource_one}' type='set' id='id3'><query xmlns='urn:xmpp:mam:2' queryid='qid3' ><set xmlns='http://jabber.org/protocol/rsm'><before></before></set></query></iq>"), - - partial(expect_stanza, - ("/message/mam:result[@queryid='qid3']/forward:forwarded/delay:delay", - "/message/mam:result[@queryid='qid3']/forward:forwarded/client:message[@from='#foo%{irc_server_one}/{nick_one}'][@type='groupchat']/client:body[text()='50']") - ), - ] + 98 * [ - partial(expect_stanza, - ("/message/mam:result[@queryid='qid3']/forward:forwarded/delay:delay", - "/message/mam:result[@queryid='qid3']/forward:forwarded/client:message[@from='#foo%{irc_server_one}/{nick_one}'][@type='groupchat']/client:body") - ), - ] + [ - partial(expect_stanza, - ("/message/mam:result[@queryid='qid3']/forward:forwarded/delay:delay", - "/message/mam:result[@queryid='qid3']/forward:forwarded/client:message[@from='#foo%{irc_server_one}/{nick_one}'][@type='groupchat']/client:body[text()='149']"), - after = partial(save_value, "last_uuid", partial(extract_attribute, "/message/mam:result", "id")) - ), - partial(expect_stanza, - ("/iq[@type='result'][@id='id3'][@from='#foo%{irc_server_one}'][@to='{jid_one}/{resource_one}']", - "/iq/mam:fin/rsm:set/rsm:last[text()='{last_uuid}']", - "!/iq//mam:fin[@complete='true']", - "/iq//mam:fin")), - - # Do the same thing, but with a limit value. - partial(send_stanza, "<iq to='#foo%{irc_server_one}' from='{jid_one}/{resource_one}' type='set' id='id4'><query xmlns='urn:xmpp:mam:2' queryid='qid4' ><set xmlns='http://jabber.org/protocol/rsm'><before>{last_uuid}</before><max>2</max></set></query></iq>"), - partial(expect_stanza, - ("/message/mam:result[@queryid='qid4']/forward:forwarded/delay:delay", - "/message/mam:result[@queryid='qid4']/forward:forwarded/client:message[@from='#foo%{irc_server_one}/{nick_one}'][@type='groupchat']/client:body[text()='147']") - ), - partial(expect_stanza, - ("/message/mam:result[@queryid='qid4']/forward:forwarded/delay:delay", - "/message/mam:result[@queryid='qid4']/forward:forwarded/client:message[@from='#foo%{irc_server_one}/{nick_one}'][@type='groupchat']/client:body[text()='148']"), - after = partial(save_value, "last_uuid", partial(extract_attribute, "/message/mam:result", "id")) - ), - partial(expect_stanza, - ("/iq[@type='result'][@id='id4'][@from='#foo%{irc_server_one}'][@to='{jid_one}/{resource_one}']", - "/iq/mam:fin/rsm:set/rsm:last[text()='{last_uuid}']", - "!/iq/mam:fin[@complete='true']",)), - - # Test if everything is fine even with weird max value: 0 - partial(send_stanza, "<iq to='#foo%{irc_server_one}' from='{jid_one}/{resource_one}' type='set' id='id5'><query xmlns='urn:xmpp:mam:2' queryid='qid5' ><set xmlns='http://jabber.org/protocol/rsm'><before></before><max>0</max></set></query></iq>"), - - partial(expect_stanza, - ("/iq[@type='result'][@id='id5'][@from='#foo%{irc_server_one}'][@to='{jid_one}/{resource_one}']", - "!/iq/mam:fin[@complete='true']",)), - ]), - Scenario("channel_history_on_fixed_server", - [ - handshake_sequence(), - # First user join - partial(send_stanza, - "<presence from='{jid_one}/{resource_one}' to='#foo@{biboumi_host}/{nick_one}' />"), - connection_sequence("irc.localhost", '{jid_one}/{resource_one}', fixed_irc_server=True), - partial(expect_stanza, - "/message/body[text()='Mode #foo [+nt] by {irc_host_one}']"), - partial(expect_stanza, - ("/presence[@to='{jid_one}/{resource_one}'][@from='#foo@{biboumi_host}/{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@{biboumi_host}'][@type='groupchat']/subject[not(text())]"), - - # Send one channel message - partial(send_stanza, "<message from='{jid_one}/{resource_one}' to='#foo@{biboumi_host}' type='groupchat'><body>coucou</body></message>"), - partial(expect_stanza, "/message[@from='#foo@{biboumi_host}/{nick_one}'][@to='{jid_one}/{resource_one}'][@type='groupchat']/body[text()='coucou']"), - - # Second user joins - partial(send_stanza, - "<presence from='{jid_one}/{resource_two}' to='#foo@{biboumi_host}/{nick_one}' />"), - # connection_sequence("irc.localhost", '{jid_one}/{resource_two}'), - # partial(expect_stanza, - # "/message/body[text()='Mode #foo [+nt] by {irc_host_one}']"), - partial(expect_stanza, - ("/presence[@to='{jid_one}/{resource_two}'][@from='#foo@{biboumi_host}/{nick_one}']/muc_user:x/muc_user:item[@affiliation='admin'][@jid='~nick@localhost'][@role='moderator']", - "/presence/muc_user:x/muc_user:status[@code='110']") - ), - # Receive the history message - partial(expect_stanza, ("/message[@from='#foo@{biboumi_host}/{nick_one}']/body[text()='coucou']", - "/message/delay:delay[@from='#foo@{biboumi_host}']")), - - partial(expect_stanza, "/message[@from='#foo@{biboumi_host}'][@type='groupchat']/subject[not(text())]"), - ], conf="fixed_server"), - Scenario("channel_history", - [ - handshake_sequence(), - # First user join - partial(send_stanza, - "<presence from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}/{nick_one}' />"), - 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())]"), - - # Send one channel message - partial(send_stanza, "<message from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}' type='groupchat'><body>coucou</body></message>"), - partial(expect_stanza, "/message[@from='#foo%{irc_server_one}/{nick_one}'][@to='{jid_one}/{resource_one}'][@type='groupchat']/body[text()='coucou']"), - - # Second user joins - partial(send_stanza, - "<presence from='{jid_one}/{resource_two}' to='#foo%{irc_server_one}/{nick_one}' />"), - partial(expect_stanza, - ("/presence[@to='{jid_one}/{resource_two}'][@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']") - ), - # Receive the history message - partial(expect_stanza, ("/message[@from='#foo%{irc_server_one}/{nick_one}']/body[text()='coucou']", - "/message/delay:delay[@from='#foo%{irc_server_one}']")), - - partial(expect_stanza, "/message[@from='#foo%{irc_server_one}'][@type='groupchat']/subject[not(text())]"), - ]), - Scenario("simple_channel_list", - [ - handshake_sequence(), - - partial(send_stanza, - "<presence from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}/{nick_one}' />"), - 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())]"), - - partial(send_stanza, - "<presence from='{jid_one}/{resource_one}' to='#bar%{irc_server_one}/{nick_one}' />"), - partial(expect_stanza, - "/message/body[text()='Mode #bar [+nt] by {irc_host_one}']"), - partial(expect_stanza, "/presence"), - partial(expect_stanza, "/message[@from='#bar%{irc_server_one}'][@type='groupchat']/subject[not(text())]"), - - partial(send_stanza, "<iq from='{jid_one}/{resource_one}' id='id1' to='{irc_server_one}' type='get'><query xmlns='http://jabber.org/protocol/disco#items'/></iq>"), - partial(expect_stanza, ( - "/iq[@type='result']/disco_items:query", - "/iq/disco_items:query/disco_items:item[@jid='#foo%{irc_server_one}']", - "/iq/disco_items:query/disco_items:item[@jid='#bar%{irc_server_one}']" - )) - ]), - Scenario("channel_list_escaping", - [ - handshake_sequence(), - - partial(send_stanza, - "<presence from='{jid_one}/{resource_one}' to='#true\\2ffalse%{irc_server_one}/{nick_one}' />"), - connection_sequence("irc.localhost", '{jid_one}/{resource_one}'), - partial(expect_stanza, - "/message/body[text()='Mode #true/false [+nt] by {irc_host_one}']"), - partial(expect_stanza, - ("/presence[@to='{jid_one}/{resource_one}'][@from='#true\\2ffalse%{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='#true\\2ffalse%{irc_server_one}'][@type='groupchat']/subject[not(text())]"), - ]), - Scenario("channel_list_with_rsm", - [ - handshake_sequence(), - - partial(send_stanza, - "<presence from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}/{nick_one}' />"), - 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())]"), - - partial(send_stanza, - "<presence from='{jid_one}/{resource_one}' to='#bar%{irc_server_one}/{nick_one}' />"), - partial(expect_stanza, - "/message/body[text()='Mode #bar [+nt] by {irc_host_one}']"), - partial(expect_stanza, "/presence"), - partial(expect_stanza, "/message[@from='#bar%{irc_server_one}'][@type='groupchat']/subject[not(text())]"), - - partial(send_stanza, - "<presence from='{jid_one}/{resource_one}' to='#coucou%{irc_server_one}/{nick_one}' />"), - partial(expect_stanza, - "/message/body[text()='Mode #coucou [+nt] by {irc_host_one}']"), - partial(expect_stanza, "/presence"), - partial(expect_stanza, "/message[@from='#coucou%{irc_server_one}'][@type='groupchat']/subject[not(text())]"), - - # Ask for 0 item - partial(send_stanza, "<iq from='{jid_one}/{resource_one}' id='id1' to='{irc_server_one}' type='get'><query xmlns='http://jabber.org/protocol/disco#items'><set xmlns='http://jabber.org/protocol/rsm'><max>0</max></set></query></iq>"), - - # Get 0 item - partial(expect_stanza, ( - "/iq[@type='result']/disco_items:query", - )), - - # Ask for 2 (of 3) items We don’t have the count, - # because biboumi doesn’t have the complete list when - # it sends us the 2 items - partial(send_stanza, "<iq from='{jid_one}/{resource_one}' id='id1' to='{irc_server_one}' type='get'><query xmlns='http://jabber.org/protocol/disco#items'><set xmlns='http://jabber.org/protocol/rsm'><max>2</max></set></query></iq>"), - partial(expect_stanza, ( - "/iq[@type='result']/disco_items:query", - "/iq/disco_items:query/disco_items:item[@jid='#bar%{irc_server_one}']", - "/iq/disco_items:query/disco_items:item[@jid='#coucou%{irc_server_one}']", - "/iq/disco_items:query/rsm:set/rsm:first[text()='#bar%{irc_server_one}'][@index='0']", - "/iq/disco_items:query/rsm:set/rsm:last[text()='#coucou%{irc_server_one}']" - )), - - # Ask for 12 (of 3) items. We get the whole list, and thus we have the count included. - partial(send_stanza, "<iq from='{jid_one}/{resource_one}' id='id1' to='{irc_server_one}' type='get'><query xmlns='http://jabber.org/protocol/disco#items'><set xmlns='http://jabber.org/protocol/rsm'><max>12</max></set></query></iq>"), - partial(expect_stanza, ( - "/iq[@type='result']/disco_items:query", - "/iq/disco_items:query/disco_items:item[@jid='#bar%{irc_server_one}']", - "/iq/disco_items:query/disco_items:item[@jid='#coucou%{irc_server_one}']", - "/iq/disco_items:query/disco_items:item[@jid='#foo%{irc_server_one}']", - "/iq/disco_items:query/rsm:set/rsm:first[text()='#bar%{irc_server_one}'][@index='0']", - "/iq/disco_items:query/rsm:set/rsm:last[text()='#foo%{irc_server_one}']", - "/iq/disco_items:query/rsm:set/rsm:count[text()='3']" - )), - - # Ask for 1 item, AFTER the first item (so, - # the second). Since we don’t invalidate the cache - # with this request, we should have the count - # included. - partial(send_stanza, "<iq from='{jid_one}/{resource_one}' id='id1' to='{irc_server_one}' type='get'><query xmlns='http://jabber.org/protocol/disco#items'><set xmlns='http://jabber.org/protocol/rsm'><after>#bar%{irc_server_one}</after><max>1</max></set></query></iq>"), - partial(expect_stanza, ( - "/iq[@type='result']/disco_items:query", - "/iq/disco_items:query/disco_items:item[@jid='#coucou%{irc_server_one}']", - "/iq/disco_items:query/rsm:set/rsm:first[text()='#coucou%{irc_server_one}'][@index='1']", - "/iq/disco_items:query/rsm:set/rsm:last[text()='#coucou%{irc_server_one}']", - "/iq/disco_items:query/rsm:set/rsm:count[text()='3']" - )), - - # Ask for 1 item, AFTER the second item (so, - # the third). - partial(send_stanza, "<iq from='{jid_one}/{resource_one}' id='id1' to='{irc_server_one}' type='get'><query xmlns='http://jabber.org/protocol/disco#items'><set xmlns='http://jabber.org/protocol/rsm'><after>#coucou%{irc_server_one}</after><max>1</max></set></query></iq>"), - partial(expect_stanza, ( - "/iq[@type='result']/disco_items:query", - "/iq/disco_items:query/disco_items:item[@jid='#foo%{irc_server_one}']", - "/iq/disco_items:query/rsm:set/rsm:first[text()='#foo%{irc_server_one}'][@index='2']", - "/iq/disco_items:query/rsm:set/rsm:last[text()='#foo%{irc_server_one}']", - "/iq/disco_items:query/rsm:set/rsm:count[text()='3']" - )), - - # Ask for 1 item, AFTER the third item (so, - # the fourth). Since it doesn't exist, we get 0 item - partial(send_stanza, "<iq from='{jid_one}/{resource_one}' id='id1' to='{irc_server_one}' type='get'><query xmlns='http://jabber.org/protocol/disco#items'><set xmlns='http://jabber.org/protocol/rsm'><after>#foo%{irc_server_one}</after><max>1</max></set></query></iq>"), - partial(expect_stanza, ( - "/iq[@type='result']/disco_items:query", - "/iq/disco_items:query/rsm:set/rsm:count[text()='3']" - )), - ]), - Scenario("default_channel_list_limit", - [ - handshake_sequence(), - partial(send_stanza, - "<presence from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}/{nick_one}' />"), - connection_sequence("irc.localhost", '{jid_one}/{resource_one}'), - partial(expect_stanza, "/message"), - partial(expect_stanza, "/presence"), - partial(expect_stanza, "/message", - after = partial(save_value, "counter", lambda x: 0)), - ] + [ - partial(send_stanza, - "<presence from='{jid_one}/{resource_one}' to='#{counter}%{irc_server_one}/{nick_one}' />"), - partial(expect_stanza, "/message"), - partial(expect_stanza, "/presence", - after = partial(save_value, "counter", lambda stanza: str(1 + int(chan_name_from_jid(extract_attribute("/presence", "from", stanza)))))), - partial(expect_stanza, "/message") - ] * 110 + [ - partial(send_stanza, "<iq from='{jid_one}/{resource_one}' id='id1' to='{irc_server_one}' type='get'><query xmlns='http://jabber.org/protocol/disco#items'/></iq>"), - # charybdis sends the list in alphabetic order, so #foo is the last, and #99 is after #120 - partial(expect_stanza, ("/iq/disco_items:query/disco_items:item[@jid='#0%{irc_server_one}']", - "/iq/disco_items:query/disco_items:item[@jid='#1%{irc_server_one}']", - "/iq/disco_items:query/disco_items:item[@jid='#109%{irc_server_one}']", - "/iq/disco_items:query/disco_items:item[@jid='#9%{irc_server_one}']", - "!/iq/disco_items:query/disco_items:item[@jid='#foo%{irc_server_one}']", - "!/iq/disco_items:query/disco_items:item[@jid='#99%{irc_server_one}']", - "!/iq/disco_items:query/disco_items:item[@jid='#90%{irc_server_one}']")), - ]), - Scenario("complete_channel_list_with_pages_of_3", - [ - handshake_sequence(), - - partial(send_stanza, - "<presence from='{jid_one}/{resource_one}' to='#aaa%{irc_server_one}/{nick_one}' />"), - connection_sequence("irc.localhost", '{jid_one}/{resource_one}'), - partial(expect_stanza, "/message"), - partial(expect_stanza, "/presence"), - partial(expect_stanza, "/message"), - - partial(send_stanza, - "<presence from='{jid_one}/{resource_one}' to='#bbb%{irc_server_one}/{nick_one}' />"), - partial(expect_stanza, "/message"), - partial(expect_stanza, "/presence"), - partial(expect_stanza, "/message"), - - partial(send_stanza, - "<presence from='{jid_one}/{resource_one}' to='#ccc%{irc_server_one}/{nick_one}' />"), - partial(expect_stanza, "/message"), - partial(expect_stanza, "/presence"), - partial(expect_stanza, "/message"), - - partial(send_stanza, - "<presence from='{jid_one}/{resource_one}' to='#ddd%{irc_server_one}/{nick_one}' />"), - partial(expect_stanza, "/message"), - partial(expect_stanza, "/presence"), - partial(expect_stanza, "/message"), - - partial(send_stanza, - "<presence from='{jid_one}/{resource_one}' to='#eee%{irc_server_one}/{nick_one}' />"), - partial(expect_stanza, "/message"), - partial(expect_stanza, "/presence"), - partial(expect_stanza, "/message"), - - partial(send_stanza, - "<presence from='{jid_one}/{resource_one}' to='#fff%{irc_server_one}/{nick_one}' />"), - partial(expect_stanza, "/message"), - partial(expect_stanza, "/presence"), - partial(expect_stanza, "/message"), - - partial(send_stanza, - "<presence from='{jid_one}/{resource_one}' to='#ggg%{irc_server_one}/{nick_one}' />"), - partial(expect_stanza, "/message"), - partial(expect_stanza, "/presence"), - partial(expect_stanza, "/message"), - - partial(send_stanza, - "<presence from='{jid_one}/{resource_one}' to='#hhh%{irc_server_one}/{nick_one}' />"), - partial(expect_stanza, "/message"), - partial(expect_stanza, "/presence"), - partial(expect_stanza, "/message"), - - partial(send_stanza, - "<presence from='{jid_one}/{resource_one}' to='#iii%{irc_server_one}/{nick_one}' />"), - partial(expect_stanza, "/message"), - partial(expect_stanza, "/presence"), - partial(expect_stanza, "/message"), - - partial(send_stanza, - "<presence from='{jid_one}/{resource_one}' to='#jjj%{irc_server_one}/{nick_one}' />"), - partial(expect_stanza, "/message"), - partial(expect_stanza, "/presence"), - partial(expect_stanza, "/message"), - - partial(send_stanza, "<iq from='{jid_one}/{resource_one}' id='id' to='{irc_server_one}' type='get'><query xmlns='http://jabber.org/protocol/disco#items'><set xmlns='http://jabber.org/protocol/rsm'><max>3</max></set></query></iq>"), - partial(expect_stanza, ( - "/iq[@type='result']/disco_items:query", - "/iq/disco_items:query/disco_items:item[@jid='#aaa%{irc_server_one}']", - "/iq/disco_items:query/disco_items:item[@jid='#bbb%{irc_server_one}']", - "/iq/disco_items:query/disco_items:item[@jid='#ccc%{irc_server_one}']", - "/iq/disco_items:query/rsm:set/rsm:first[text()='#aaa%{irc_server_one}'][@index='0']", - "/iq/disco_items:query/rsm:set/rsm:last[text()='#ccc%{irc_server_one}']" - )), - - partial(send_stanza, "<iq from='{jid_one}/{resource_one}' id='id' to='{irc_server_one}' type='get'><query xmlns='http://jabber.org/protocol/disco#items'><set xmlns='http://jabber.org/protocol/rsm'><after>#ccc%{irc_server_one}</after><max>3</max></set></query></iq>"), - partial(expect_stanza, ( - "/iq[@type='result']/disco_items:query", - "/iq/disco_items:query/disco_items:item[@jid='#ddd%{irc_server_one}']", - "/iq/disco_items:query/disco_items:item[@jid='#eee%{irc_server_one}']", - "/iq/disco_items:query/disco_items:item[@jid='#fff%{irc_server_one}']", - "/iq/disco_items:query/rsm:set/rsm:first[text()='#ddd%{irc_server_one}'][@index='3']", - "/iq/disco_items:query/rsm:set/rsm:last[text()='#fff%{irc_server_one}']" - )), - - partial(send_stanza, "<iq from='{jid_one}/{resource_one}' id='id' to='{irc_server_one}' type='get'><query xmlns='http://jabber.org/protocol/disco#items'><set xmlns='http://jabber.org/protocol/rsm'><after>#fff%{irc_server_one}</after><max>3</max></set></query></iq>"), - partial(expect_stanza, ( - "/iq[@type='result']/disco_items:query", - "/iq/disco_items:query/disco_items:item[@jid='#ggg%{irc_server_one}']", - "/iq/disco_items:query/disco_items:item[@jid='#hhh%{irc_server_one}']", - "/iq/disco_items:query/disco_items:item[@jid='#iii%{irc_server_one}']", - "/iq/disco_items:query/rsm:set/rsm:first[text()='#ggg%{irc_server_one}'][@index='6']", - "/iq/disco_items:query/rsm:set/rsm:last[text()='#iii%{irc_server_one}']" - )), - - partial(send_stanza, "<iq from='{jid_one}/{resource_one}' id='id' to='{irc_server_one}' type='get'><query xmlns='http://jabber.org/protocol/disco#items'><set xmlns='http://jabber.org/protocol/rsm'><after>#iii%{irc_server_one}</after><max>3</max></set></query></iq>"), - partial(expect_stanza, ( - "/iq[@type='result']/disco_items:query", - "/iq/disco_items:query/disco_items:item[@jid='#jjj%{irc_server_one}']", - "/iq/disco_items:query/rsm:set/rsm:first[text()='#jjj%{irc_server_one}'][@index='9']", - "/iq/disco_items:query/rsm:set/rsm:last[text()='#jjj%{irc_server_one}']", - "/iq/disco_items:query/rsm:set/rsm:count[text()='10']" - )), - - partial(send_stanza, "<presence from='{jid_one}/{resource_one}' to='#aaa%{irc_server_one}/{nick_one}' type='unavailable' />"), - partial(send_stanza, "<presence from='{jid_one}/{resource_one}' to='#bbb%{irc_server_one}/{nick_one}' type='unavailable' />"), - partial(send_stanza, "<presence from='{jid_one}/{resource_one}' to='#ccc%{irc_server_one}/{nick_one}' type='unavailable' />"), - partial(send_stanza, "<presence from='{jid_one}/{resource_one}' to='#ddd%{irc_server_one}/{nick_one}' type='unavailable' />"), - partial(send_stanza, "<presence from='{jid_one}/{resource_one}' to='#eee%{irc_server_one}/{nick_one}' type='unavailable' />"), - partial(send_stanza, "<presence from='{jid_one}/{resource_one}' to='#fff%{irc_server_one}/{nick_one}' type='unavailable' />"), - partial(send_stanza, "<presence from='{jid_one}/{resource_one}' to='#ggg%{irc_server_one}/{nick_one}' type='unavailable' />"), - partial(send_stanza, "<presence from='{jid_one}/{resource_one}' to='#hhh%{irc_server_one}/{nick_one}' type='unavailable' />"), - partial(send_stanza, "<presence from='{jid_one}/{resource_one}' to='#iii%{irc_server_one}/{nick_one}' type='unavailable' />"), - partial(send_stanza, "<presence from='{jid_one}/{resource_one}' to='#jjj%{irc_server_one}/{nick_one}' type='unavailable' />"), - partial(expect_stanza, "/presence[@type='unavailable']"), - partial(expect_stanza, "/presence[@type='unavailable']"), - partial(expect_stanza, "/presence[@type='unavailable']"), - partial(expect_stanza, "/presence[@type='unavailable']"), - partial(expect_stanza, "/presence[@type='unavailable']"), - partial(expect_stanza, "/presence[@type='unavailable']"), - partial(expect_stanza, "/presence[@type='unavailable']"), - partial(expect_stanza, "/presence[@type='unavailable']"), - partial(expect_stanza, "/presence[@type='unavailable']"), - partial(expect_stanza, "/presence[@type='unavailable']") - ]), - Scenario("muc_traffic_info", - [ - handshake_sequence(), - - partial(send_stanza, - "<iq from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}' id='1' type='get'><query xmlns='http://jabber.org/protocol/disco#info' node='http://jabber.org/protocol/muc#traffic'/></iq>"), - partial(expect_stanza, "/iq[@from='#foo%{irc_server_one}'][@to='{jid_one}/{resource_one}'][@type='result']/disco_info:query[@node='http://jabber.org/protocol/muc#traffic']"), - ]), - Scenario("muc_disco_info", - [ - handshake_sequence(), - - partial(send_stanza, - "<iq from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}' id='1' type='get'><query xmlns='http://jabber.org/protocol/disco#info'/></iq>"), - partial(expect_stanza, - ("/iq[@from='#foo%{irc_server_one}'][@to='{jid_one}/{resource_one}'][@type='result']/disco_info:query", - "/iq[@type='result']/disco_info:query/disco_info:identity[@category='conference'][@type='irc'][@name='#foo on {irc_host_one}']", - "/iq/disco_info:query/disco_info:feature[@var='jabber:iq:version']", - "/iq/disco_info:query/disco_info:feature[@var='http://jabber.org/protocol/commands']", - "/iq/disco_info:query/disco_info:feature[@var='urn:xmpp:ping']", - "/iq/disco_info:query/disco_info:feature[@var='urn:xmpp:mam:2']", - "/iq/disco_info:query/disco_info:feature[@var='jabber:iq:version']", - )), - ]), - Scenario("fixed_muc_disco_info", - [ - handshake_sequence(), - - partial(send_stanza, - "<iq from='{jid_one}/{resource_one}' to='#foo@{biboumi_host}' id='1' type='get'><query xmlns='http://jabber.org/protocol/disco#info'/></iq>"), - partial(expect_stanza, - ("/iq[@from='#foo@{biboumi_host}'][@to='{jid_one}/{resource_one}'][@type='result']/disco_info:query", - "/iq[@type='result']/disco_info:query/disco_info:identity[@category='conference'][@type='irc'][@name='#foo on {irc_host_one}']", - "/iq/disco_info:query/disco_info:feature[@var='jabber:iq:version']", - "/iq/disco_info:query/disco_info:feature[@var='http://jabber.org/protocol/commands']", - "/iq/disco_info:query/disco_info:feature[@var='urn:xmpp:ping']", - "/iq/disco_info:query/disco_info:feature[@var='urn:xmpp:mam:2']", - "/iq/disco_info:query/disco_info:feature[@var='jabber:iq:version']", - )), - ], conf='fixed_server'), - Scenario("raw_message", - [ - handshake_sequence(), - partial(send_stanza, "<presence from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}/{nick_one}' />"), - connection_sequence("irc.localhost", '{jid_one}/{resource_one}'), - partial(expect_stanza, "/message"), - partial(expect_stanza, "/presence"), - partial(expect_stanza, "/message"), - - partial(send_stanza, "<message from='{jid_one}/{resource_one}' to='{irc_server_one}' type='chat'><body>WHOIS {nick_one}</body></message>"), - partial(expect_stanza, "/message[@from='{irc_server_one}'][@type='chat']/body[text()='irc.localhost: {nick_one} ~{nick_one} localhost * {nick_one}']"), - ]), - Scenario("raw_message_fixed_irc_server", - [ - handshake_sequence(), - partial(send_stanza, "<presence from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}/{nick_one}' />"), - connection_sequence("irc.localhost", '{jid_one}/{resource_one}', fixed_irc_server=True), - partial(expect_stanza, "/message"), - partial(expect_stanza, "/presence"), - partial(expect_stanza, "/message"), - - partial(send_stanza, "<message from='{jid_one}/{resource_one}' to='{biboumi_host}' type='chat'><body>WHOIS {nick_one}</body></message>"), - partial(expect_stanza, "/message[@from='{biboumi_host}'][@type='chat']/body[text()='irc.localhost: {nick_one} ~{nick_one} localhost * {nick_one}']"), - ], conf='fixed_server'), - Scenario("self_disco_info", - [ - handshake_sequence(), - partial(send_stanza, "<iq type='get' id='get1' from='{jid_one}/{resource_one}' to='{biboumi_host}'><query xmlns='http://jabber.org/protocol/disco#info'/></iq>"), - partial(expect_stanza, - ("/iq[@type='result']/disco_info:query/disco_info:identity[@category='conference'][@type='irc'][@name='Biboumi XMPP-IRC gateway']", - "/iq/disco_info:query/disco_info:feature[@var='jabber:iq:version']", - "/iq/disco_info:query/disco_info:feature[@var='http://jabber.org/protocol/commands']", - "/iq/disco_info:query/disco_info:feature[@var='urn:xmpp:ping']", - "/iq/disco_info:query/disco_info:feature[@var='urn:xmpp:mam:2']", - "/iq/disco_info:query/disco_info:feature[@var='jabber:iq:version']", - )), - ]), - Scenario("invite_other", - [ - handshake_sequence(), - partial(send_stanza, "<presence from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}/{nick_one}' />"), - connection_sequence("irc.localhost", '{jid_one}/{resource_one}'), - partial(expect_stanza, "/message"), - partial(expect_stanza, "/presence"), - partial(expect_stanza, "/message"), - - partial(send_stanza, "<presence from='{jid_two}/{resource_two}' to='#bar%{irc_server_one}@{biboumi_host}/{nick_two}' />"), - connection_sequence("irc.localhost", '{jid_two}/{resource_two}'), - partial(expect_stanza, "/message"), - partial(expect_stanza, "/presence"), - partial(expect_stanza, "/message"), - partial(send_stanza, "<message from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}'><x xmlns='http://jabber.org/protocol/muc#user'><invite to='{nick_two}'/></x></message>"), - partial(expect_stanza, "/message/body[text()='{nick_two} has been invited to #foo']"), - partial(expect_stanza, "/message[@to='{jid_two}/{resource_two}'][@from='#foo%{irc_server_one}']/muc_user:x/muc_user:invite[@from='#foo%{irc_server_one}/{nick_one}']"), - - partial(send_stanza, "<message from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}'><x xmlns='http://jabber.org/protocol/muc#user'><invite to='bertrand@example.com'/></x></message>"), - partial(expect_stanza, "/message[@to='bertrand@example.com'][@from='#foo%{irc_server_one}']/muc_user:x/muc_user:invite[@from='{jid_one}/{resource_one}']"), - ]), - Scenario("global_configure", - [ - handshake_sequence(), - partial(send_stanza, "<iq type='set' id='id1' from='{jid_one}/{resource_one}' to='{biboumi_host}'><command xmlns='http://jabber.org/protocol/commands' node='configure' action='execute' /></iq>"), - partial(expect_stanza, ("/iq[@type='result']/commands:command[@node='configure'][@sessionid][@status='executing']", - "/iq/commands:command/dataform:x[@type='form']/dataform:title[text()='Configure some global default settings.']", - "/iq/commands:command/dataform:x[@type='form']/dataform:instructions[text()='Edit the form, to configure your global settings for the component.']", - "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='text-single'][@var='max_history_length']/dataform:value[text()='20']", - "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='boolean'][@var='record_history']/dataform:value[text()='true']", - "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='boolean'][@var='persistent']/dataform:value[text()='false']", - "/iq/commands:command/commands:actions/commands:next", - ), - after = partial(save_value, "sessionid", partial(extract_attribute, "/iq[@type='result']/commands:command[@node='configure']", "sessionid")) - ), - partial(send_stanza, "<iq type='set' id='id2' from='{jid_one}/{resource_one}' to='{biboumi_host}'><command xmlns='http://jabber.org/protocol/commands' node='configure' sessionid='{sessionid}' action='next'><x xmlns='jabber:x:data' type='submit'><field var='record_history'><value>0</value></field><field var='max_history_length'><value>42</value></field></x></command></iq>"), - partial(expect_stanza, "/iq[@type='result']/commands:command[@node='configure'][@status='completed']/commands:note[@type='info'][text()='Configuration successfully applied.']"), - - partial(send_stanza, "<iq type='set' id='id3' from='{jid_one}/{resource_one}' to='{biboumi_host}'><command xmlns='http://jabber.org/protocol/commands' node='configure' action='execute' /></iq>"), - partial(expect_stanza, ("/iq[@type='result']/commands:command[@node='configure'][@sessionid][@status='executing']", - "/iq/commands:command/dataform:x[@type='form']/dataform:title[text()='Configure some global default settings.']", - "/iq/commands:command/dataform:x[@type='form']/dataform:instructions[text()='Edit the form, to configure your global settings for the component.']", - "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='text-single'][@var='max_history_length']/dataform:value[text()='42']", - "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='boolean'][@var='record_history']/dataform:value[text()='false']", - "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='boolean'][@var='persistent']/dataform:value[text()='false']", - "/iq/commands:command/commands:actions/commands:next", - ), - after = partial(save_value, "sessionid", partial(extract_attribute, "/iq[@type='result']/commands:command[@node='configure']", "sessionid")) - ), - partial(send_stanza, "<iq type='set' id='id4' from='{jid_one}/{resource_one}' to='{biboumi_host}'><command xmlns='http://jabber.org/protocol/commands' action='cancel' node='configure' sessionid='{sessionid}' /></iq>"), - partial(expect_stanza, "/iq[@type='result']/commands:command[@node='configure'][@status='canceled']"), - ]), - Scenario("global_configure_fixed", - [ - handshake_sequence(), - partial(send_stanza, "<iq type='set' id='id1' from='{jid_one}/{resource_one}' to='{biboumi_host}'><command xmlns='http://jabber.org/protocol/commands' node='global-configure' action='execute' /></iq>"), - partial(expect_stanza, ("/iq[@type='result']/commands:command[@node='global-configure'][@sessionid][@status='executing']", - "/iq/commands:command/dataform:x[@type='form']/dataform:title[text()='Configure some global default settings.']", - "/iq/commands:command/dataform:x[@type='form']/dataform:instructions[text()='Edit the form, to configure your global settings for the component.']", - "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='text-single'][@var='max_history_length']/dataform:value[text()='20']", - "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='boolean'][@var='record_history']/dataform:value[text()='true']", - "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='boolean'][@var='persistent']/dataform:value[text()='false']", - "/iq/commands:command/commands:actions/commands:next", - ), - after = partial(save_value, "sessionid", partial(extract_attribute, "/iq[@type='result']/commands:command[@node='global-configure']", "sessionid")) - ), - partial(send_stanza, "<iq type='set' id='id2' from='{jid_one}/{resource_one}' to='{biboumi_host}'><command xmlns='http://jabber.org/protocol/commands' node='global-configure' sessionid='{sessionid}' action='next'><x xmlns='jabber:x:data' type='submit'><field var='record_history'><value>0</value></field><field var='max_history_length'><value>42</value></field></x></command></iq>"), - partial(expect_stanza, "/iq[@type='result']/commands:command[@node='global-configure'][@status='completed']/commands:note[@type='info'][text()='Configuration successfully applied.']"), - - partial(send_stanza, "<iq type='set' id='id3' from='{jid_one}/{resource_one}' to='{biboumi_host}'><command xmlns='http://jabber.org/protocol/commands' node='global-configure' action='execute' /></iq>"), - partial(expect_stanza, ("/iq[@type='result']/commands:command[@node='global-configure'][@sessionid][@status='executing']", - "/iq/commands:command/dataform:x[@type='form']/dataform:title[text()='Configure some global default settings.']", - "/iq/commands:command/dataform:x[@type='form']/dataform:instructions[text()='Edit the form, to configure your global settings for the component.']", - "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='text-single'][@var='max_history_length']/dataform:value[text()='42']", - "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='boolean'][@var='record_history']/dataform:value[text()='false']", - "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='boolean'][@var='persistent']/dataform:value[text()='false']", - "/iq/commands:command/commands:actions/commands:next", - ), - after = partial(save_value, "sessionid", partial(extract_attribute, "/iq[@type='result']/commands:command[@node='global-configure']", "sessionid")) - ), - partial(send_stanza, "<iq type='set' id='id4' from='{jid_one}/{resource_one}' to='{biboumi_host}'><command xmlns='http://jabber.org/protocol/commands' action='cancel' node='global-configure' sessionid='{sessionid}' /></iq>"), - partial(expect_stanza, "/iq[@type='result']/commands:command[@node='global-configure'][@status='canceled']"), - - partial(send_stanza, "<iq type='set' id='id1' from='{jid_one}/{resource_one}' to='{biboumi_host}'><command xmlns='http://jabber.org/protocol/commands' node='server-configure' action='execute' /></iq>"), - partial(expect_stanza, ("/iq[@type='result']/commands:command[@node='server-configure'][@sessionid][@status='executing']",)) - ], conf='fixed_server'), - Scenario("global_configure_persistent_by_default", - [ - handshake_sequence(), - partial(send_stanza, "<iq type='set' id='id1' from='{jid_one}/{resource_one}' to='{biboumi_host}'><command xmlns='http://jabber.org/protocol/commands' node='configure' action='execute' /></iq>"), - partial(expect_stanza, ("/iq[@type='result']/commands:command[@node='configure'][@sessionid][@status='executing']", - "/iq/commands:command/dataform:x[@type='form']/dataform:title[text()='Configure some global default settings.']", - "/iq/commands:command/dataform:x[@type='form']/dataform:instructions[text()='Edit the form, to configure your global settings for the component.']", - "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='text-single'][@var='max_history_length']/dataform:value[text()='20']", - "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='boolean'][@var='record_history']/dataform:value[text()='true']", - "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='boolean'][@var='persistent']/dataform:value[text()='true']", - "/iq/commands:command/commands:actions/commands:next", - ), - ), - ],conf='persistent_by_default'), - Scenario("irc_server_configure", - [ - handshake_sequence(), - partial(send_stanza, "<iq type='set' id='id1' from='{jid_one}/{resource_one}' to='{irc_server_one}'><command xmlns='http://jabber.org/protocol/commands' node='configure' action='execute' /></iq>"), - partial(expect_stanza, ("/iq[@type='result']/commands:command[@node='configure'][@sessionid][@status='executing']", - "/iq/commands:command/dataform:x[@type='form']/dataform:title[text()='Configure the IRC server irc.localhost']", - "/iq/commands:command/dataform:x[@type='form']/dataform:instructions[text()='Edit the form, to configure the settings of the IRC server irc.localhost']", - "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='text-multi'][@var='ports']/dataform:value[text()='6667']", - "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='text-multi'][@var='tls_ports']/dataform:value[text()='6670']", - "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='text-multi'][@var='tls_ports']/dataform:value[text()='6697']", - "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='boolean'][@var='verify_cert']/dataform:value[text()='true']", - "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='text-single'][@var='fingerprint']", - "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='text-private'][@var='pass']", - "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='text-multi'][@var='after_connect_commands']", - "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='text-single'][@var='nick']", - "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='text-single'][@var='username']", - "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='text-single'][@var='realname']", - "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='text-single'][@var='encoding_in']", - "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='text-single'][@var='encoding_out']", - "/iq/commands:command/commands:actions/commands:next", - ), - after = partial(save_value, "sessionid", partial(extract_attribute, "/iq[@type='result']/commands:command[@node='configure']", "sessionid")) - ), - partial(send_stanza, "<iq type='set' id='id2' from='{jid_one}/{resource_one}' to='{irc_server_one}'>" - "<command xmlns='http://jabber.org/protocol/commands' node='configure' sessionid='{sessionid}' action='next'>" - "<x xmlns='jabber:x:data' type='submit'>" - "<field var='ports' />" - "<field var='tls_ports'><value>6697</value><value>6698</value></field>" - "<field var='verify_cert'><value>1</value></field>" - "<field var='fingerprint'><value>12:12:12</value></field>" - "<field var='pass'><value>coucou</value></field>" - "<field var='after_connect_commands'><value>first command</value><value>second command</value></field>" - "<field var='nick'><value>my_nickname</value></field>" - "<field var='username'><value>username</value></field>" - "<field var='realname'><value>realname</value></field>" - "<field var='encoding_out'><value>UTF-8</value></field>" - "<field var='encoding_in'><value>latin-1</value></field>" - "</x></command></iq>"), - partial(expect_stanza, "/iq[@type='result']/commands:command[@node='configure'][@status='completed']/commands:note[@type='info'][text()='Configuration successfully applied.']"), - - partial(send_stanza, "<iq type='set' id='id3' from='{jid_one}/{resource_one}' to='{irc_server_one}'><command xmlns='http://jabber.org/protocol/commands' node='configure' action='execute' /></iq>"), - partial(expect_stanza, ("/iq[@type='result']/commands:command[@node='configure'][@sessionid][@status='executing']", - "/iq/commands:command/dataform:x[@type='form']/dataform:title[text()='Configure the IRC server irc.localhost']", - "/iq/commands:command/dataform:x[@type='form']/dataform:instructions[text()='Edit the form, to configure the settings of the IRC server irc.localhost']", - "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='text-multi']", - "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='text-multi'][@var='tls_ports']/dataform:value[text()='6697']", - "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='text-multi'][@var='tls_ports']/dataform:value[text()='6698']", - "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='boolean'][@var='verify_cert']/dataform:value[text()='true']", - "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='text-single'][@var='fingerprint']/dataform:value[text()='12:12:12']", - "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='text-private'][@var='pass']/dataform:value[text()='coucou']", - "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='text-single'][@var='nick']/dataform:value[text()='my_nickname']", - "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='text-multi'][@var='after_connect_commands']/dataform:value[text()='first command']", - "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='text-multi'][@var='after_connect_commands']/dataform:value[text()='second command']", - "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='text-single'][@var='username']/dataform:value[text()='username']", - "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='text-single'][@var='realname']/dataform:value[text()='realname']", - "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='text-single'][@var='encoding_in']/dataform:value[text()='latin-1']", - "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='text-single'][@var='encoding_out']/dataform:value[text()='UTF-8']", - "/iq/commands:command/commands:actions/commands:next", - ), - after = partial(save_value, "sessionid", partial(extract_attribute, "/iq[@type='result']/commands:command[@node='configure']", "sessionid")) - ), - partial(send_stanza, "<iq type='set' id='id4' from='{jid_one}/{resource_one}' to='{irc_server_one}'><command xmlns='http://jabber.org/protocol/commands' action='cancel' node='configure' sessionid='{sessionid}' /></iq>"), - partial(expect_stanza, "/iq[@type='result']/commands:command[@node='configure'][@status='canceled']"), - - # Same thing, but try to empty some values - partial(send_stanza, "<iq type='set' id='id1' from='{jid_one}/{resource_one}' to='{irc_server_one}'><command xmlns='http://jabber.org/protocol/commands' node='configure' action='execute' /></iq>"), - partial(expect_stanza, "/iq[@type='result']", - after = partial(save_value, "sessionid", partial(extract_attribute, "/iq[@type='result']/commands:command[@node='configure']", "sessionid")) - ), - partial(send_stanza, "<iq type='set' id='id2' from='{jid_one}/{resource_one}' to='{irc_server_one}'>" - "<command xmlns='http://jabber.org/protocol/commands' node='configure' sessionid='{sessionid}' action='next'>" - "<x xmlns='jabber:x:data' type='submit'>" - "<field var='pass'><value></value></field>" - "<field var='after_connect_commands'></field>" - "<field var='username'><value></value></field>" - "<field var='realname'><value></value></field>" - "<field var='encoding_out'><value></value></field>" - "<field var='encoding_in'><value></value></field>" - "</x></command></iq>"), - partial(expect_stanza, "/iq[@type='result']/commands:command[@node='configure'][@status='completed']/commands:note[@type='info'][text()='Configuration successfully applied.']"), - - partial(send_stanza, "<iq type='set' id='id3' from='{jid_one}/{resource_one}' to='{irc_server_one}'><command xmlns='http://jabber.org/protocol/commands' node='configure' action='execute' /></iq>"), - partial(expect_stanza, ("/iq[@type='result']/commands:command[@node='configure'][@sessionid][@status='executing']", - "/iq/commands:command/dataform:x[@type='form']/dataform:title[text()='Configure the IRC server irc.localhost']", - "/iq/commands:command/dataform:x[@type='form']/dataform:instructions[text()='Edit the form, to configure the settings of the IRC server irc.localhost']", - "!/iq/commands:command/dataform:x/dataform:field[@var='tls_ports']/dataform:value", - "!/iq/commands:command/dataform:x[@type='form']/dataform:field[@var='pass']/dataform:value", - "!/iq/commands:command/dataform:x[@type='form']/dataform:field[@var='after_connect_commands']/dataform:value", - "!/iq/commands:command/dataform:x[@type='form']/dataform:field[@var='username']/dataform:value", - "!/iq/commands:command/dataform:x[@type='form']/dataform:field[@var='realname']/dataform:value", - "!/iq/commands:command/dataform:x[@type='form']/dataform:field[@var='encoding_in']/dataform:value", - "!/iq/commands:command/dataform:x[@type='form']/dataform:field[@var='encoding_out']/dataform:value", - "/iq/commands:command/commands:actions/commands:next", - ), - after = partial(save_value, "sessionid", partial(extract_attribute, "/iq[@type='result']/commands:command[@node='configure']", "sessionid")) - ), - partial(send_stanza, "<iq type='set' id='id4' from='{jid_one}/{resource_one}' to='{irc_server_one}'><command xmlns='http://jabber.org/protocol/commands' action='cancel' node='configure' sessionid='{sessionid}' /></iq>"), - partial(expect_stanza, "/iq[@type='result']/commands:command[@node='configure'][@status='canceled']"), - - ]), - Scenario("irc_channel_configure", - [ - handshake_sequence(), - partial(send_stanza, "<iq type='set' id='id1' from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}'><command xmlns='http://jabber.org/protocol/commands' node='configure' action='execute' /></iq>"), - partial(expect_stanza, ("/iq[@type='result']/commands:command[@node='configure'][@sessionid][@status='executing']", - "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='text-single'][@var='encoding_in']", - "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='text-single'][@var='encoding_out']", - "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='list-single'][@var='record_history']/dataform:value[text()='unset']", - ), - after = partial(save_value, "sessionid", partial(extract_attribute, "/iq[@type='result']/commands:command[@node='configure']", "sessionid")) - ), - partial(send_stanza, "<iq type='set' id='id2' from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}'>" - "<command xmlns='http://jabber.org/protocol/commands' node='configure' sessionid='{sessionid}' action='next'>" - "<x xmlns='jabber:x:data' type='submit'>" - "<field var='ports' />" - "<field var='encoding_out'><value>UTF-8</value></field>" - "<field var='encoding_in'><value>latin-1</value></field>" - "<field var='record_history'><value>true</value></field>" - "</x></command></iq>"), - partial(expect_stanza, "/iq[@type='result']/commands:command[@node='configure'][@status='completed']/commands:note[@type='info'][text()='Configuration successfully applied.']"), - - partial(send_stanza, "<iq type='set' id='id3' from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}'><command xmlns='http://jabber.org/protocol/commands' node='configure' action='execute' /></iq>"), - partial(expect_stanza, ("/iq[@type='result']/commands:command[@node='configure'][@sessionid][@status='executing']", - "/iq/commands:command/dataform:x[@type='form']/dataform:title[text()='Configure the IRC channel #foo on server irc.localhost']", - "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='text-single'][@var='encoding_in']/dataform:value[text()='latin-1']", - "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='text-single'][@var='encoding_out']/dataform:value[text()='UTF-8']", - "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='list-single'][@var='record_history']/dataform:value[text()='true']", - "/iq/commands:command/commands:actions/commands:next", - ), - after = partial(save_value, "sessionid", partial(extract_attribute, "/iq[@type='result']/commands:command[@node='configure']", "sessionid")) - ), - partial(send_stanza, "<iq type='set' id='id4' from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}'><command xmlns='http://jabber.org/protocol/commands' action='cancel' node='configure' sessionid='{sessionid}' /></iq>"), - partial(expect_stanza, "/iq[@type='result']/commands:command[@node='configure'][@status='canceled']"), - ]), - Scenario("irc_channel_configure_xep0045", - [ - handshake_sequence(), - partial(send_stanza, "<iq type='get' id='id1' from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}'><query xmlns='http://jabber.org/protocol/muc#owner'/></iq>"), - partial(expect_stanza, ("/iq[@type='result']/muc_owner:query", - "/iq/muc_owner:query/dataform:x[@type='form']/dataform:field[@type='text-single'][@var='encoding_in']", - "/iq/muc_owner:query/dataform:x[@type='form']/dataform:field[@type='text-single'][@var='encoding_out']", - ), - ), - partial(send_stanza, "<iq type='set' id='id2' from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}'>" - "<query xmlns='http://jabber.org/protocol/muc#owner'>" - "<x xmlns='jabber:x:data' type='submit'>" - "<field var='ports' />" - "<field var='encoding_out'><value>UTF-8</value></field>" - "<field var='encoding_in'><value>latin-1</value></field>" - "</x></query></iq>"), - partial(expect_stanza, "/iq[@type='result']"), - partial(send_stanza, "<iq type='set' id='id3' from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}'><query xmlns='http://jabber.org/protocol/muc#owner'> <x xmlns='jabber:x:data' type='cancel'/></query></iq>"), - partial(expect_stanza, "/iq[@type='result']"), - ]), - Scenario("irc_channel_configure_fixed", - [ - handshake_sequence(), - partial(send_stanza, "<iq type='set' id='id1' from='{jid_one}/{resource_one}' to='#foo@{biboumi_host}'><command xmlns='http://jabber.org/protocol/commands' node='configure' action='execute' /></iq>"), - partial(expect_stanza, ("/iq[@type='result']/commands:command[@node='configure'][@sessionid][@status='executing']", - "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='text-single'][@var='encoding_in']", - "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='text-single'][@var='encoding_out']", - ), - after = partial(save_value, "sessionid", partial(extract_attribute, "/iq[@type='result']/commands:command[@node='configure']", "sessionid")) - ), - partial(send_stanza, "<iq type='set' id='id2' from='{jid_one}/{resource_one}' to='#foo@{biboumi_host}'>" - "<command xmlns='http://jabber.org/protocol/commands' node='configure' sessionid='{sessionid}' action='next'>" - "<x xmlns='jabber:x:data' type='submit'>" - "<field var='ports' />" - "<field var='encoding_out'><value>UTF-8</value></field>" - "<field var='encoding_in'><value>latin-1</value></field>" - "</x></command></iq>"), - partial(expect_stanza, "/iq[@type='result']/commands:command[@node='configure'][@status='completed']/commands:note[@type='info'][text()='Configuration successfully applied.']"), - - partial(send_stanza, "<iq type='set' id='id3' from='{jid_one}/{resource_one}' to='#foo@{biboumi_host}'><command xmlns='http://jabber.org/protocol/commands' node='configure' action='execute' /></iq>"), - partial(expect_stanza, ("/iq[@type='result']/commands:command[@node='configure'][@sessionid][@status='executing']", - "/iq/commands:command/dataform:x[@type='form']/dataform:title[text()='Configure the IRC channel #foo on server irc.localhost']", - "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='text-single'][@var='encoding_in']/dataform:value[text()='latin-1']", - "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='text-single'][@var='encoding_out']/dataform:value[text()='UTF-8']", - "/iq/commands:command/commands:actions/commands:next", - ), - after = partial(save_value, "sessionid", partial(extract_attribute, "/iq[@type='result']/commands:command[@node='configure']", "sessionid")) - ), - partial(send_stanza, "<iq type='set' id='id4' from='{jid_one}/{resource_one}' to='#foo@{biboumi_host}'><command xmlns='http://jabber.org/protocol/commands' action='cancel' node='configure' sessionid='{sessionid}' /></iq>"), - partial(expect_stanza, "/iq[@type='result']/commands:command[@node='configure'][@status='canceled']"), - ], conf='fixed_server'), - Scenario("irc_tls_connection", - [ - handshake_sequence(), - # First, use an adhoc command to configure how we connect to the irc server, configure - # only one TLS port, and disable the cert verification. - partial(send_stanza, "<iq type='set' id='id1' from='{jid_one}/{resource_one}' to='{irc_server_one}'><command xmlns='http://jabber.org/protocol/commands' node='configure' action='execute' /></iq>"), - partial(expect_stanza, "/iq[@type='result']", - after = partial(save_value, "sessionid", partial(extract_attribute, "/iq[@type='result']/commands:command[@node='configure']", "sessionid"))), - partial(send_stanza, "<iq type='set' id='id2' from='{jid_one}/{resource_one}' to='{irc_server_one}'>" - "<command xmlns='http://jabber.org/protocol/commands' node='configure' sessionid='{sessionid}' action='next'>" - "<x xmlns='jabber:x:data' type='submit'>" - "<field var='ports' />" - "<field var='tls_ports'><value>7778</value></field>" - "<field var='verify_cert'><value>0</value></field>" - "<field var='nick'><value>my_special_nickname</value></field>" - "</x></command></iq>"), - partial(expect_stanza, "/iq[@type='result']/commands:command[@node='configure'][@status='completed']/commands:note[@type='info'][text()='Configuration successfully applied.']"), - - partial(send_stanza, - "<presence from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}/{nick_one}' />"), - connection_tls_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}/my_special_nickname']/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())]"), - ]), - Scenario("get_irc_connection_info", - [ - handshake_sequence(), - - partial(send_stanza, "<iq type='set' id='command1' from='{jid_one}/{resource_one}' to='{irc_server_one}'><command xmlns='http://jabber.org/protocol/commands' node='get-irc-connection-info' action='execute' /></iq>"), - partial(expect_stanza, "/iq/commands:command/commands:note[text()='You are not connected to the IRC server irc.localhost']"), - - partial(send_stanza, - "<presence from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}/{nick_one}' />"), - connection_sequence("irc.localhost", '{jid_one}/{resource_one}'), - partial(expect_stanza, "/message"), - partial(expect_stanza, "/presence"), - partial(expect_stanza, "/message"), - - partial(send_stanza, "<iq type='set' id='command2' from='{jid_one}/{resource_one}' to='{irc_server_one}'><command xmlns='http://jabber.org/protocol/commands' node='get-irc-connection-info' action='execute' /></iq>"), - partial(expect_stanza, r"/iq/commands:command/commands:note[re:test(text(), 'Connected to IRC server irc.localhost on port 6667 since \d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d \(\d+ seconds ago\)\.\n#foo from 1 resource: {resource_one}.*')]"), - ]), - Scenario("get_irc_connection_info_fixed", - [ - handshake_sequence(), - - partial(send_stanza, "<iq type='set' id='command1' from='{jid_one}/{resource_one}' to='{biboumi_host}'><command xmlns='http://jabber.org/protocol/commands' node='get-irc-connection-info' action='execute' /></iq>"), - partial(expect_stanza, "/iq/commands:command/commands:note[text()='You are not connected to the IRC server irc.localhost']"), - - partial(send_stanza, - "<presence from='{jid_one}/{resource_one}' to='#foo@{biboumi_host}/{nick_one}' />"), - connection_sequence("irc.localhost", '{jid_one}/{resource_one}', fixed_irc_server=True), - partial(expect_stanza, "/message"), - partial(expect_stanza, "/presence"), - partial(expect_stanza, "/message"), - - partial(send_stanza, "<iq type='set' id='command2' from='{jid_one}/{resource_one}' to='{biboumi_host}'><command xmlns='http://jabber.org/protocol/commands' node='get-irc-connection-info' action='execute' /></iq>"), - partial(expect_stanza, r"/iq/commands:command/commands:note[re:test(text(), 'Connected to IRC server irc.localhost on port 6667 since \d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d \(\d+ seconds ago\)\.\n#foo from 1 resource: {resource_one}.*')]"), - ], conf='fixed_server'), - Scenario("irc_server_presence_subscription", - [ - handshake_sequence(), - partial(send_stanza, "<presence type='subscribe' from='{jid_one}/{resource_one}' to='{irc_server_one}' id='sub1' />"), - partial(expect_stanza, "/presence[@to='{jid_one}'][@from='{irc_server_one}'][@type='subscribed']") - ]), - Scenario("fixed_irc_server_presence_subscription", - [ - handshake_sequence(), - partial(send_stanza, "<presence type='subscribe' from='{jid_one}/{resource_one}' to='{biboumi_host}' id='sub1' />"), - partial(expect_stanza, "/presence[@to='{jid_one}'][@from='{biboumi_host}'][@type='subscribed']") - ], conf='fixed_server'), - Scenario("leave_unjoined_chan", - [ - handshake_sequence(), - partial(send_stanza, "<presence from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}/{nick_one}' />"), - connection_sequence("irc.localhost", '{jid_one}/{resource_one}'), - partial(expect_stanza, "/message"), - partial(expect_stanza, "/presence"), - partial(expect_stanza, "/message"), - - partial(send_stanza, "<presence from='{jid_two}/{resource_two}' to='#foo%{irc_server_one}/{nick_one}' />"), - connection_begin_sequence("irc.localhost", '{jid_two}/{resource_two}'), - - partial(expect_stanza, "/message[@to='{jid_two}/{resource_two}'][@type='chat']/body[text()='irc.localhost: {nick_one}: Nickname is already in use.']"), - partial(expect_stanza, "/presence[@type='error']/error[@type='cancel'][@code='409']/stanza:conflict"), - partial(send_stanza, "<presence from='{jid_two}/{resource_two}' to='#foo%{irc_server_one}/{nick_one}' type='unavailable' />") - ]), - Scenario("basic_subscribe_unsubscribe", - [ - handshake_sequence(), - - # Mutual subscription exchange - partial(send_stanza, "<presence from='{jid_one}' to='{biboumi_host}' type='subscribe' id='subid1' />"), - partial(expect_stanza, "/presence[@type='subscribed'][@id='subid1']"), - - # Get the current presence of the biboumi gateway - partial(expect_stanza, "/presence"), - - partial(expect_stanza, "/presence[@type='subscribe']"), - partial(send_stanza, "<presence from='{jid_one}' to='{biboumi_host}' type='subscribed' />"), - - - # Unsubscribe - partial(send_stanza, "<presence from='{jid_one}' to='{biboumi_host}' type='unsubscribe' id='unsubid1' />"), - partial(expect_stanza, "/presence[@type='unavailable']"), - partial(expect_stanza, "/presence[@type='unsubscribed']"), - partial(expect_stanza, "/presence[@type='unsubscribe']"), - partial(send_stanza, "<presence from='{jid_one}' to='{biboumi_host}' type='unavailable' />"), - partial(send_stanza, "<presence from='{jid_one}' to='{biboumi_host}' type='unsubscribed' />"), - ]), - Scenario("resource_is_removed_from_server_when_last_chan_is_left", - [ - # Join the channel - handshake_sequence(), - partial(send_stanza, - "<presence from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}/{nick_one}' />"), - 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'][@to='{jid_one}/{resource_one}']/subject[not(text())]"), - - # Make it persistent - partial(send_stanza, "<iq from='{jid_one}/{resource_one}' id='conf1' to='#foo%{irc_server_one}' type='get'><query xmlns='http://jabber.org/protocol/muc#owner'/></iq>"), - partial(expect_stanza, "/iq[@type='result']/muc_owner:query/dataform:x/dataform:field[@var='persistent'][@type='boolean']/dataform:value[text()='false']"), - partial(send_stanza, "<iq from='{jid_one}/{resource_one}' id='conf2' to='#foo%{irc_server_one}' type='set'><query xmlns='http://jabber.org/protocol/muc#owner'><x type='submit' xmlns='jabber:x:data'><field var='persistent' xmlns='jabber:x:data'><value>true</value></field></x></query></iq>"), - partial(expect_stanza, "/iq[@type='result']"), - - partial(send_stanza, "<presence from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}/{nick_one}' type='unavailable' />"), - partial(expect_stanza, "/presence[@type='unavailable'][@from='#foo%{irc_server_one}/{nick_one}']"), - - # Join the same channel, with the same JID, but a different resource - partial(send_stanza, - "<presence from='{jid_one}/{resource_two}' to='#foo%{irc_server_one}/{nick_one}' />"), - 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())]"), - - # Join some other channel with someone else - partial(send_stanza, - "<presence from='{jid_two}/{resource_one}' to='#bar%{irc_server_one}/{nick_two}' />"), - connection_sequence("irc.localhost", '{jid_two}/{resource_one}'), - partial(expect_stanza, - "/message/body[text()='Mode #bar [+nt] by {irc_host_one}']"), - partial(expect_stanza, - ("/presence[@to='{jid_two}/{resource_one}'][@from='#bar%{irc_server_one}/{nick_two}']/muc_user:x/muc_user:item[@affiliation='admin'][@role='moderator']", - "/presence/muc_user:x/muc_user:status[@code='110']") - ), - partial(expect_stanza, "/message[@from='#bar%{irc_server_one}'][@type='groupchat'][@to='{jid_two}/{resource_one}']/subject[not(text())]"), - - # Send two messages from the second user to the first one - partial(send_stanza, "<message from='{jid_two}/{resource_one}' to='{lower_nick_one}%{irc_server_one}' type='chat'><body>kikoo</body></message>"), - partial(send_stanza, "<message from='{jid_two}/{resource_one}' to='{lower_nick_one}%{irc_server_one}' type='chat'><body>second kikoo</body></message>"), - - # We must receive each message only once, no duplicate - partial(expect_stanza, "/message/body[text()='kikoo']"), - partial(expect_stanza, "/message/body[text()='second kikoo']"), - ] - ), - Scenario("irc_server_presence_in_roster", - [ - handshake_sequence(), - - # Mutual subscription exchange - partial(send_stanza, "<presence from='{jid_one}' to='{irc_server_one}' type='subscribe' id='subid1' />"), - partial(expect_stanza, "/presence[@type='subscribed'][@id='subid1']"), - - partial(expect_stanza, "/presence[@type='subscribe']"), - partial(send_stanza, "<presence from='{jid_one}' to='{irc_server_one}' type='subscribed' />"), - - # Join a channel on that server - partial(send_stanza, - "<presence from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}/{nick_one}' />"), - - # We must receive the IRC server presence, in the connection sequence - connection_sequence("irc.localhost", '{jid_one}/{resource_one}', expected_irc_presence=True), - 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())]"), - - # Leave the channel, and thus the IRC server - partial(send_stanza, "<presence type='unavailable' from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}/{nick_one}' />"), - partial(expect_stanza, "/presence[@type='unavailable'][@from='#foo%{irc_server_one}/{nick_one}']"), - partial(expect_stanza, "/presence[@from='{irc_server_one}'][@to='{jid_one}'][@type='unavailable']"), - ]) - ) - - failures = 0 - - scenar_list = sys.argv[1:] irc_output = open("irc_output.txt", "w") irc = IrcServerRunner() print("Starting irc server…") @@ -3249,17 +329,18 @@ if __name__ == '__main__': if b"now running in foreground mode" in res: break print("irc server started.") - checks = len([s for s in scenarios if s.name in scenar_list]) if scenar_list else len(scenarios) - print("Running %s checks for biboumi." % checks) + number_of_checks = len([s for s in scenarios if s.name in provided_scenar_names]) if provided_scenar_names else len(scenarios) + print("Running %s checks for biboumi." % number_of_checks) + + failures = 0 for s in scenarios: - if scenar_list and s.name not in scenar_list: - continue test = BiboumiTest(s) if not test.run(): print("You can check the files slixmpp_%s_output.txt and biboumi_%s_output.txt to help you debug." % (s.name, s.name)) failures += 1 + sys.stdout.flush() print("Waiting for irc server to exit…") irc.stop() @@ -3271,3 +352,4 @@ if __name__ == '__main__': sys.exit(1) else: print("All tests passed successfully") + diff --git a/tests/end_to_end/functions.py b/tests/end_to_end/functions.py new file mode 100644 index 0000000..97cdfb0 --- /dev/null +++ b/tests/end_to_end/functions.py @@ -0,0 +1,169 @@ +from functools import partial +import collections +import datetime +import asyncio +import time +import lxml.etree +import io + +common_replacements = { + 'irc_server_one': 'irc.localhost@biboumi.localhost', + 'irc_server_two': 'localhost@biboumi.localhost', + 'irc_host_one': 'irc.localhost', + 'irc_host_two': 'localhost', + 'biboumi_host': 'biboumi.localhost', + 'resource_one': 'resource1', + 'resource_two': 'resource2', + 'nick_one': 'Nick', + 'jid_one': 'first@example.com', + 'jid_two': 'second@example.com', + 'jid_admin': 'admin@example.com', + 'nick_two': 'Bobby', + 'nick_three': 'Bernard', + 'lower_nick_one': 'nick', + 'lower_nick_two': 'bobby', +} + +class SkipStepError(Exception): + """ + Raised by a step when it needs to be skiped, by running + the next available step immediately. + """ + pass + +class StanzaError(Exception): + """ + Raised when a step fails. + """ + pass + +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', + 'muc_owner': 'http://jabber.org/protocol/muc#owner', + 'muc': 'http://jabber.org/protocol/muc', + 'disco_info': 'http://jabber.org/protocol/disco#info', + 'muc_traffic': 'http://jabber.org/protocol/muc#traffic', + 'disco_items': 'http://jabber.org/protocol/disco#items', + 'commands': 'http://jabber.org/protocol/commands', + 'dataform': 'jabber:x:data', + 'version': 'jabber:iq:version', + 'mam': 'urn:xmpp:mam:2', + 'rms': 'http://jabber.org/protocol/rsm', + 'delay': 'urn:xmpp:delay', + 'forward': 'urn:xmpp:forward:0', + 'client': 'jabber:client', + 'rsm': 'http://jabber.org/protocol/rsm', + 'carbon': 'urn:xmpp:carbons:2', + 'hints': 'urn:xmpp:hints', + 'stanza': 'urn:ietf:params:xml:ns:xmpp-stanzas', + 'stable_id': 'urn:xmpp:sid:0'}) + return matched + +def check_xpath(xpaths, xmpp, after, stanza): + for xpath in xpaths: + expected = True + real_xpath = xpath + # We can check that a stanza DOESN’T match, by adding a ! before it. + if xpath.startswith('!'): + expected = False + xpath = xpath[1:] + matched = match(stanza, xpath) + if (expected and not matched) or (not expected and matched): + raise StanzaError("Received stanza\n%s\ndid not match expected xpath\n%s" % (stanza, real_xpath)) + if after: + if isinstance(after, collections.Iterable): + for af in after: + af(stanza, xmpp) + else: + after(stanza, xmpp) + +def check_xpath_optional(xpaths, xmpp, after, stanza): + try: + check_xpath(xpaths, xmpp, after, stanza) + except StanzaError: + raise SkipStepError() + +def all_xpaths_match(stanza, xpaths): + try: + check_xpath(xpaths, None, None, stanza) + except StanzaError: + return False + return True + +def check_list_of_xpath(list_of_xpaths, xmpp, stanza): + found = False + for i, xpaths in enumerate(list_of_xpaths): + if all_xpaths_match(stanza, xpaths): + found = True + list_of_xpaths.pop(i) + break + + if not found: + raise StanzaError("Received stanza “%s” did not match any of the expected xpaths:\n%s" % (stanza, list_of_xpaths)) + + if list_of_xpaths: + step = partial(expect_unordered_already_formatted, list_of_xpaths) + xmpp.scenario.steps.insert(0, step) + +def extract_attribute(xpath, name): + def f(xpath, name, stanza): + matched = match(stanza, xpath) + return matched[0].get(name) + return partial(f, xpath, name) + +def extract_text(xpath, stanza): + matched = match(stanza, xpath) + return matched[0].text + +def save_value(name, func): + def f(name, func, stanza, xmpp): + xmpp.saved_values[name] = func(stanza) + return partial(f, name, func) + +def expect_stanza(*args, optional=False, after=None): + def f(*xpaths, xmpp, biboumi, optional, after): + replacements = common_replacements + replacements.update(xmpp.saved_values) + check_func = check_xpath if not optional else check_xpath_optional + formatted_xpaths = [xpath.format_map(replacements) for xpath in xpaths] + xmpp.stanza_checker = partial(check_func, formatted_xpaths, xmpp, after) + xmpp.timeout_handler = asyncio.get_event_loop().call_later(10, partial(xmpp.on_timeout, formatted_xpaths)) + return partial(f, *args, optional=optional, after=after) + +def send_stanza(stanza): + def internal(stanza, xmpp, biboumi): + replacements = common_replacements + replacements.update(xmpp.saved_values) + xmpp.send_raw(stanza.format_map(replacements)) + asyncio.get_event_loop().call_soon(xmpp.run_scenario) + return partial(internal, stanza) + +def expect_unordered(*args): + def f(*lists_of_xpaths, xmpp, biboumi): + formatted_list_of_xpaths = [] + for list_of_xpaths in lists_of_xpaths: + formatted_xpaths = [] + for xpath in list_of_xpaths: + formatted_xpath = xpath.format_map(common_replacements) + formatted_xpaths.append(formatted_xpath) + formatted_list_of_xpaths.append(tuple(formatted_xpaths)) + expect_unordered_already_formatted(formatted_list_of_xpaths, xmpp, biboumi) + xmpp.timeout_handler = asyncio.get_event_loop().call_later(10, partial(xmpp.on_timeout, formatted_list_of_xpaths)) + return partial(f, *args) + +def expect_unordered_already_formatted(formatted_list_of_xpaths, xmpp, biboumi): + xmpp.stanza_checker = partial(check_list_of_xpath, formatted_list_of_xpaths, xmpp) + +def sleep_for(duration): + def f(duration, xmpp, biboumi): + time.sleep(duration) + asyncio.get_event_loop().call_soon(xmpp.run_scenario) + return partial(f, duration) + +def save_current_timestamp_plus_delta(key, delta): + def f(key, delta, message, xmpp): + now_plus_delta = datetime.datetime.utcnow() + delta + xmpp.saved_values[key] = now_plus_delta.strftime("%FT%T.967Z") + return partial(f, key, delta) diff --git a/tests/end_to_end/scenarios/__init__.py b/tests/end_to_end/scenarios/__init__.py new file mode 100644 index 0000000..1fef72e --- /dev/null +++ b/tests/end_to_end/scenarios/__init__.py @@ -0,0 +1,10 @@ +# Do "from scenarios import *" instead of repeating these imports everytime in every scenario + +from functions import expect_stanza, send_stanza, expect_unordered, save_value, extract_attribute, extract_text, sleep_for, save_current_timestamp_plus_delta +import datetime +import sequences +import scenarios.simple_channel_join +import scenarios.channel_join_with_two_users +import scenarios.simple_channel_join_fixed +import scenarios.channel_join_on_fixed_irc_server +import scenarios.multiple_channels_join diff --git a/tests/end_to_end/scenarios/basic_handshake_success.py b/tests/end_to_end/scenarios/basic_handshake_success.py new file mode 100644 index 0000000..9e1ffb3 --- /dev/null +++ b/tests/end_to_end/scenarios/basic_handshake_success.py @@ -0,0 +1,8 @@ +from scenarios import * + +# At the start of every scenario, we automatically insert a +# sequences.handshake() call. So, this scenario is just here to test that +# this basic thing works fine. + +scenario = ( +) diff --git a/tests/end_to_end/scenarios/basic_subscribe_unsubscribe.py b/tests/end_to_end/scenarios/basic_subscribe_unsubscribe.py new file mode 100644 index 0000000..6082fa6 --- /dev/null +++ b/tests/end_to_end/scenarios/basic_subscribe_unsubscribe.py @@ -0,0 +1,23 @@ +from scenarios import * + +scenario = ( + + + # Mutual subscription exchange + send_stanza("<presence from='{jid_one}' to='{biboumi_host}' type='subscribe' id='subid1' />"), + expect_stanza("/presence[@type='subscribed'][@id='subid1']"), + + # Get the current presence of the biboumi gateway + expect_stanza("/presence"), + + expect_stanza("/presence[@type='subscribe']"), + send_stanza("<presence from='{jid_one}' to='{biboumi_host}' type='subscribed' />"), + + # Unsubscribe + send_stanza("<presence from='{jid_one}' to='{biboumi_host}' type='unsubscribe' id='unsubid1' />"), + expect_stanza("/presence[@type='unavailable']"), + expect_stanza("/presence[@type='unsubscribed']"), + expect_stanza("/presence[@type='unsubscribe']"), + send_stanza("<presence from='{jid_one}' to='{biboumi_host}' type='unavailable' />"), + send_stanza("<presence from='{jid_one}' to='{biboumi_host}' type='unsubscribed' />"), +) diff --git a/tests/end_to_end/scenarios/channel_custom_topic.py b/tests/end_to_end/scenarios/channel_custom_topic.py new file mode 100644 index 0000000..1fbfb5c --- /dev/null +++ b/tests/end_to_end/scenarios/channel_custom_topic.py @@ -0,0 +1,30 @@ +from scenarios import * + +import scenarios.simple_channel_join + +scenario = ( + scenarios.simple_channel_join.scenario, + + # First user sets the topic + send_stanza("<message from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}' type='groupchat'><subject>TOPIC TEST</subject></message>"), + expect_stanza("/message[@from='#foo%{irc_server_one}/{nick_one}'][@type='groupchat'][@to='{jid_one}/{resource_one}']/subject[text()='TOPIC TEST']"), + + # Second user joins + send_stanza("<presence from='{jid_two}/{resource_one}' to='#foo%{irc_server_one}/{nick_two}' />"), + sequences.connection("irc.localhost", '{jid_two}/{resource_one}'), + expect_unordered( + [ + "/presence[@to='{jid_one}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_two}']/muc_user:x/muc_user:item[@affiliation='none'][@jid='{lower_nick_two}%{irc_server_one}/~{nick_two}@localhost'][@role='participant']" + ], + [ + "/presence[@to='{jid_two}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_one}']/muc_user:x/muc_user:item[@affiliation='admin'][@role='moderator']" + ], + [ + "/presence[@to='{jid_two}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_two}']/muc_user:x/muc_user:item[@affiliation='none'][@jid='{lower_nick_two}%{irc_server_one}/~{nick_two}@localhost'][@role='participant']", + "/presence/muc_user:x/muc_user:status[@code='110']" + ], + [ + "/message[@from='#foo%{irc_server_one}/{nick_one}'][@type='groupchat']/subject[text()='TOPIC TEST']" + ] + ) +) diff --git a/tests/end_to_end/scenarios/channel_force_join.py b/tests/end_to_end/scenarios/channel_force_join.py new file mode 100644 index 0000000..089da51 --- /dev/null +++ b/tests/end_to_end/scenarios/channel_force_join.py @@ -0,0 +1,45 @@ +from scenarios import * + +import scenarios.channel_join_with_two_users + +scenario = ( + scenarios.channel_join_with_two_users.scenario, + # Here we simulate a desynchronization of a client: The client thinks it’s + # disconnected from the room, but biboumi still thinks it’s in the room. The + # client thus sends a join presence, and biboumi should send everything + # (user list, history, etc) in response. + send_stanza("<presence from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}/{nick_three}'><x xmlns='http://jabber.org/protocol/muc'/></presence>"), + expect_unordered( + [ + "/presence[@to='{jid_one}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_two}']/muc_user:x/muc_user:item[@affiliation='none'][@role='participant'][@jid='{lower_nick_two}%{irc_server_one}/~{nick_two}@localhost']" + ], + [ + "/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']" + ], + [ + "/message[@from='#foo%{irc_server_one}'][@type='groupchat']/subject[not(text())]" + ] + ), + + # And also, that was not the same nickname, so everyone receives a nick change + expect_unordered( + [ + "/presence[@from='#foo%{irc_server_one}/{nick_one}'][@to='{jid_two}/{resource_one}'][@type='unavailable']/muc_user:x/muc_user:item[@nick='Bernard']", + "/presence/muc_user:x/muc_user:status[@code='303']", + ], + [ + "/presence[@from='#foo%{irc_server_one}/{nick_three}'][@to='{jid_two}/{resource_one}']", + ], + [ + "/presence[@from='#foo%{irc_server_one}/{nick_one}'][@to='{jid_one}/{resource_one}'][@type='unavailable']/muc_user:x/muc_user:item[@nick='Bernard']", + "/presence/muc_user:x/muc_user:status[@code='303']", + "/presence/muc_user:x/muc_user:status[@code='110']", + ], + [ + "/presence[@from='#foo%{irc_server_one}/{nick_three}'][@to='{jid_one}/{resource_one}']", + "/presence/muc_user:x/muc_user:status[@code='110']", + ], + ), +) + diff --git a/tests/end_to_end/scenarios/channel_history.py b/tests/end_to_end/scenarios/channel_history.py new file mode 100644 index 0000000..ade978b --- /dev/null +++ b/tests/end_to_end/scenarios/channel_history.py @@ -0,0 +1,18 @@ +from scenarios import * + +scenario = ( + scenarios.simple_channel_join.scenario, + + # Send one channel message + send_stanza("<message from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}' type='groupchat'><body>coucou</body></message>"), + expect_stanza("/message[@from='#foo%{irc_server_one}/{nick_one}'][@to='{jid_one}/{resource_one}'][@type='groupchat']/body[text()='coucou']"), + + # Second user joins + send_stanza("<presence from='{jid_one}/{resource_two}' to='#foo%{irc_server_one}/{nick_one}' />"), + expect_stanza("/presence[@to='{jid_one}/{resource_two}'][@from='#foo%{irc_server_one}/{nick_one}']/muc_user:x/muc_user:item[@affiliation='admin'][@jid='{lower_nick_one}%{irc_server_one}/~{nick_one}@localhost'][@role='moderator']", + "/presence/muc_user:x/muc_user:status[@code='110']"), + # Receive the history message + expect_stanza("/message[@from='#foo%{irc_server_one}/{nick_one}']/body[text()='coucou']", + "/message/delay:delay[@from='#foo%{irc_server_one}']"), + expect_stanza("/message[@from='#foo%{irc_server_one}'][@type='groupchat']/subject[not(text())]"), +) diff --git a/tests/end_to_end/scenarios/channel_history_on_fixed_server.py b/tests/end_to_end/scenarios/channel_history_on_fixed_server.py new file mode 100644 index 0000000..40a665b --- /dev/null +++ b/tests/end_to_end/scenarios/channel_history_on_fixed_server.py @@ -0,0 +1,20 @@ +from scenarios import * + +conf = 'fixed_server' + +scenario = ( + scenarios.channel_join_on_fixed_irc_server.scenario, + + # Send one channel message + send_stanza("<message from='{jid_one}/{resource_one}' to='#foo@{biboumi_host}' type='groupchat'><body>coucou</body></message>"), + expect_stanza("/message[@from='#foo@{biboumi_host}/{nick_one}'][@to='{jid_one}/{resource_one}'][@type='groupchat']/body[text()='coucou']"), + + # Second user joins + send_stanza("<presence from='{jid_one}/{resource_two}' to='#foo@{biboumi_host}/{nick_one}' />"), + expect_stanza("/presence[@to='{jid_one}/{resource_two}'][@from='#foo@{biboumi_host}/{nick_one}']/muc_user:x/muc_user:item[@affiliation='admin'][@jid='{lower_nick_one}@{biboumi_host}/~{nick_one}@localhost'][@role='moderator']", + "/presence/muc_user:x/muc_user:status[@code='110']"), + # Receive the history message + expect_stanza("/message[@from='#foo@{biboumi_host}/{nick_one}']/body[text()='coucou']", + "/message/delay:delay[@from='#foo@{biboumi_host}']"), + expect_stanza("/message[@from='#foo@{biboumi_host}'][@type='groupchat']/subject[not(text())]"), +) diff --git a/tests/end_to_end/scenarios/channel_join_on_fixed_irc_server.py b/tests/end_to_end/scenarios/channel_join_on_fixed_irc_server.py new file mode 100644 index 0000000..1fe9908 --- /dev/null +++ b/tests/end_to_end/scenarios/channel_join_on_fixed_irc_server.py @@ -0,0 +1,13 @@ +from scenarios import * + +conf = "fixed_server" + +scenario = ( + + send_stanza("<presence from='{jid_one}/{resource_one}' to='#foo@{biboumi_host}/{nick_one}' />"), + sequences.connection("irc.localhost", '{jid_one}/{resource_one}', fixed_irc_server=True), + expect_stanza("/message/body[text()='Mode #foo [+nt] by {irc_host_one}']"), + expect_stanza("/presence[@to='{jid_one}/{resource_one}'][@from='#foo@{biboumi_host}/{nick_one}']/muc_user:x/muc_user:item[@affiliation='admin'][@role='moderator']", + "/presence/muc_user:x/muc_user:status[@code='110']"), + expect_stanza("/message[@from='#foo@{biboumi_host}'][@type='groupchat']/subject[not(text())]"), +) diff --git a/tests/end_to_end/scenarios/channel_join_with_different_nick.py b/tests/end_to_end/scenarios/channel_join_with_different_nick.py new file mode 100644 index 0000000..388b098 --- /dev/null +++ b/tests/end_to_end/scenarios/channel_join_with_different_nick.py @@ -0,0 +1,14 @@ +from scenarios import * + +from scenarios.simple_channel_join import expect_self_join_presence + +scenario = ( + send_stanza("<presence from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}/{nick_one}' />"), + sequences.connection("irc.localhost", '{jid_one}/{resource_one}'), + expect_self_join_presence(jid = '{jid_one}/{resource_one}', chan = "#foo", nick = "{nick_one}"), + + # The same resource joins a different channel with a different nick + send_stanza("<presence from='{jid_one}/{resource_one}' to='#bar%{irc_server_one}/{nick_two}' />"), + # We must receive a join presence in response, without any nick change (nick_two) must be ignored + expect_self_join_presence(jid = '{jid_one}/{resource_one}', chan = "#bar", nick = "{nick_one}"), +) diff --git a/tests/end_to_end/scenarios/channel_join_with_password.py b/tests/end_to_end/scenarios/channel_join_with_password.py new file mode 100644 index 0000000..4c5e508 --- /dev/null +++ b/tests/end_to_end/scenarios/channel_join_with_password.py @@ -0,0 +1,35 @@ +from scenarios import * + +import scenarios.simple_channel_join + +scenario = ( + scenarios.simple_channel_join.scenario, + + # Set a password in the room, by using /mode +k + send_stanza("<message from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}' type='groupchat'><body>/mode +k SECRET</body></message>"), + expect_stanza("/message[@from='#foo%{irc_server_one}'][@to='{jid_one}/{resource_one}'][@type='groupchat']/body[text()='Mode #foo [+k SECRET] by {nick_one}']"), + + # Second user tries to join, without a password (error ensues) + send_stanza("<presence from='{jid_two}/{resource_one}' to='#foo%{irc_server_one}/{nick_two}'/>"), + sequences.connection("irc.localhost", '{jid_two}/{resource_one}'), + expect_stanza("/message/body[text()='{irc_host_one}: #foo: Cannot join channel (+k) - bad key']"), + expect_stanza("/presence[@type='error'][@from='#foo%{irc_server_one}/{nick_two}']/error[@type='auth']/stanza:not-authorized"), + + # Second user joins, with the correct password (success) + send_stanza("<presence from='{jid_two}/{resource_one}' to='#foo%{irc_server_one}/{nick_two}'> <x xmlns='http://jabber.org/protocol/muc'><password>SECRET</password></x></presence>"), + expect_unordered( + [ + "/presence[@to='{jid_one}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_two}']/muc_user:x/muc_user:item[@affiliation='none'][@role='participant'][@jid='{lower_nick_two}%{irc_server_one}/~{nick_two}@localhost']" + ], + [ + "/presence[@to='{jid_two}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_one}']/muc_user:x/muc_user:item[@affiliation='admin'][@role='moderator']" + ], + [ + "/presence[@to='{jid_two}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_two}']/muc_user:x/muc_user:item[@affiliation='none'][@jid='{lower_nick_two}%{irc_server_one}/~{nick_two}@localhost'][@role='participant']", + "/presence/muc_user:x/muc_user:status[@code='110']" + ], + [ + "/message[@from='#foo%{irc_server_one}'][@type='groupchat']/subject[not(text())]" + ] + ) +) diff --git a/tests/end_to_end/scenarios/channel_join_with_two_users.py b/tests/end_to_end/scenarios/channel_join_with_two_users.py new file mode 100644 index 0000000..4e22c50 --- /dev/null +++ b/tests/end_to_end/scenarios/channel_join_with_two_users.py @@ -0,0 +1,25 @@ +from scenarios import * + +scenario = ( + scenarios.simple_channel_join.scenario, + + # Second user joins + send_stanza("<presence from='{jid_two}/{resource_one}' to='#foo%{irc_server_one}/{nick_two}' />"), + sequences.connection("irc.localhost", '{jid_two}/{resource_one}'), + expect_unordered( + [ + "/presence[@to='{jid_one}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_two}']/muc_user:x/muc_user:item[@affiliation='none'][@role='participant'][@jid='{lower_nick_two}%{irc_server_one}/~{nick_two}@localhost']" + ], + [ + "/presence[@to='{jid_two}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_one}']/muc_user:x/muc_user:item[@affiliation='admin'][@role='moderator']" + ], + [ + "/presence[@to='{jid_two}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_two}']/muc_user:x/muc_user:item[@affiliation='none'][@jid='{lower_nick_two}%{irc_server_one}/~{nick_two}@localhost'][@role='participant']", + "/presence/muc_user:x/muc_user:status[@code='110']" + ], + [ + "/message[@from='#foo%{irc_server_one}'][@type='groupchat']/subject[not(text())]" + ] + ) +) + diff --git a/tests/end_to_end/scenarios/channel_list_escaping.py b/tests/end_to_end/scenarios/channel_list_escaping.py new file mode 100644 index 0000000..12c3ff9 --- /dev/null +++ b/tests/end_to_end/scenarios/channel_list_escaping.py @@ -0,0 +1,10 @@ +from scenarios import * + +scenario = ( + send_stanza("<presence from='{jid_one}/{resource_one}' to='#true\\2ffalse%{irc_server_one}/{nick_one}' />"), + sequences.connection("irc.localhost", '{jid_one}/{resource_one}'), + expect_stanza("/message/body[text()='Mode #true/false [+nt] by {irc_host_one}']"), + expect_stanza("/presence[@to='{jid_one}/{resource_one}'][@from='#true\\2ffalse%{irc_server_one}/{nick_one}']/muc_user:x/muc_user:item[@affiliation='admin'][@role='moderator']", + "/presence/muc_user:x/muc_user:status[@code='110']"), + expect_stanza("/message[@from='#true\\2ffalse%{irc_server_one}'][@type='groupchat']/subject[not(text())]"), +) diff --git a/tests/end_to_end/scenarios/channel_list_with_rsm.py b/tests/end_to_end/scenarios/channel_list_with_rsm.py new file mode 100644 index 0000000..79e76f4 --- /dev/null +++ b/tests/end_to_end/scenarios/channel_list_with_rsm.py @@ -0,0 +1,67 @@ +from scenarios import * + +scenario = ( + scenarios.simple_channel_join.scenario, + + send_stanza("<presence from='{jid_one}/{resource_one}' to='#bar%{irc_server_one}/{nick_one}' />"), + expect_stanza("/message/body[text()='Mode #bar [+nt] by {irc_host_one}']"), + expect_stanza("/presence"), + expect_stanza("/message[@from='#bar%{irc_server_one}'][@type='groupchat']/subject[not(text())]"), + + send_stanza("<presence from='{jid_one}/{resource_one}' to='#coucou%{irc_server_one}/{nick_one}' />"), + expect_stanza("/message/body[text()='Mode #coucou [+nt] by {irc_host_one}']"), + expect_stanza("/presence"), + expect_stanza("/message[@from='#coucou%{irc_server_one}'][@type='groupchat']/subject[not(text())]"), + + # Ask for 0 item + send_stanza("<iq from='{jid_one}/{resource_one}' id='id1' to='{irc_server_one}' type='get'><query xmlns='http://jabber.org/protocol/disco#items'><set xmlns='http://jabber.org/protocol/rsm'><max>0</max></set></query></iq>"), + + # Get 0 item + expect_stanza("/iq[@type='result']/disco_items:query"), + + # Ask for 2 (of 3) items We don’t have the count, + # because biboumi doesn’t have the complete list when + # it sends us the 2 items + send_stanza("<iq from='{jid_one}/{resource_one}' id='id1' to='{irc_server_one}' type='get'><query xmlns='http://jabber.org/protocol/disco#items'><set xmlns='http://jabber.org/protocol/rsm'><max>2</max></set></query></iq>"), + expect_stanza("/iq[@type='result']/disco_items:query", + "/iq/disco_items:query/disco_items:item[@jid='#bar%{irc_server_one}']", + "/iq/disco_items:query/disco_items:item[@jid='#coucou%{irc_server_one}']", + "/iq/disco_items:query/rsm:set/rsm:first[text()='#bar%{irc_server_one}'][@index='0']", + "/iq/disco_items:query/rsm:set/rsm:last[text()='#coucou%{irc_server_one}']"), + + # Ask for 12 (of 3) items. We get the whole list, and thus we have the count included. + send_stanza("<iq from='{jid_one}/{resource_one}' id='id1' to='{irc_server_one}' type='get'><query xmlns='http://jabber.org/protocol/disco#items'><set xmlns='http://jabber.org/protocol/rsm'><max>12</max></set></query></iq>"), + expect_stanza("/iq[@type='result']/disco_items:query", + "/iq/disco_items:query/disco_items:item[@jid='#bar%{irc_server_one}']", + "/iq/disco_items:query/disco_items:item[@jid='#coucou%{irc_server_one}']", + "/iq/disco_items:query/disco_items:item[@jid='#foo%{irc_server_one}']", + "/iq/disco_items:query/rsm:set/rsm:first[text()='#bar%{irc_server_one}'][@index='0']", + "/iq/disco_items:query/rsm:set/rsm:last[text()='#foo%{irc_server_one}']", + "/iq/disco_items:query/rsm:set/rsm:count[text()='3']"), + + # Ask for 1 item, AFTER the first item (so, + # the second). Since we don’t invalidate the cache + # with this request, we should have the count + # included. + send_stanza("<iq from='{jid_one}/{resource_one}' id='id1' to='{irc_server_one}' type='get'><query xmlns='http://jabber.org/protocol/disco#items'><set xmlns='http://jabber.org/protocol/rsm'><after>#bar%{irc_server_one}</after><max>1</max></set></query></iq>"), + expect_stanza("/iq[@type='result']/disco_items:query", + "/iq/disco_items:query/disco_items:item[@jid='#coucou%{irc_server_one}']", + "/iq/disco_items:query/rsm:set/rsm:first[text()='#coucou%{irc_server_one}'][@index='1']", + "/iq/disco_items:query/rsm:set/rsm:last[text()='#coucou%{irc_server_one}']", + "/iq/disco_items:query/rsm:set/rsm:count[text()='3']"), + + # Ask for 1 item, AFTER the second item (so, + # the third). + send_stanza("<iq from='{jid_one}/{resource_one}' id='id1' to='{irc_server_one}' type='get'><query xmlns='http://jabber.org/protocol/disco#items'><set xmlns='http://jabber.org/protocol/rsm'><after>#coucou%{irc_server_one}</after><max>1</max></set></query></iq>"), + expect_stanza("/iq[@type='result']/disco_items:query", + "/iq/disco_items:query/disco_items:item[@jid='#foo%{irc_server_one}']", + "/iq/disco_items:query/rsm:set/rsm:first[text()='#foo%{irc_server_one}'][@index='2']", + "/iq/disco_items:query/rsm:set/rsm:last[text()='#foo%{irc_server_one}']", + "/iq/disco_items:query/rsm:set/rsm:count[text()='3']"), + + # Ask for 1 item, AFTER the third item (so, + # the fourth). Since it doesn't exist, we get 0 item + send_stanza("<iq from='{jid_one}/{resource_one}' id='id1' to='{irc_server_one}' type='get'><query xmlns='http://jabber.org/protocol/disco#items'><set xmlns='http://jabber.org/protocol/rsm'><after>#foo%{irc_server_one}</after><max>1</max></set></query></iq>"), + expect_stanza("/iq[@type='result']/disco_items:query", + "/iq/disco_items:query/rsm:set/rsm:count[text()='3']"), +) diff --git a/tests/end_to_end/scenarios/channel_messages.py b/tests/end_to_end/scenarios/channel_messages.py new file mode 100644 index 0000000..8ea979c --- /dev/null +++ b/tests/end_to_end/scenarios/channel_messages.py @@ -0,0 +1,70 @@ +from scenarios import * + +import scenarios.simple_channel_join + +scenario = ( + scenarios.simple_channel_join.scenario, + + # Second user joins + send_stanza("<presence from='{jid_two}/{resource_one}' to='#foo%{irc_server_one}/{nick_two}' />"), + sequences.connection("irc.localhost", '{jid_two}/{resource_one}'), + + # Our presence, sent to the other user, and ourself + expect_unordered( + ["/presence[@to='{jid_one}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_two}']/muc_user:x/muc_user:item[@affiliation='none'][@jid='{lower_nick_two}%{irc_server_one}/~{nick_two}@localhost'][@role='participant']"], + ["/presence[@to='{jid_two}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_one}']/muc_user:x/muc_user:item[@affiliation='admin'][@role='moderator']"], + [ + "/presence[@to='{jid_two}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_two}']/muc_user:x/muc_user:item[@affiliation='none'][@jid='{lower_nick_two}%{irc_server_one}/~{nick_two}@localhost'][@role='participant']", + "/presence/muc_user:x/muc_user:status[@code='110']" + ], + ["/message[@from='#foo%{irc_server_one}'][@type='groupchat']/subject[not(text())]"], + ), + + # Send a channel message + send_stanza("<message from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}' type='groupchat'><body>coucou</body></message>"), + # Receive the message, forwarded to the two users + expect_unordered( + [ + "/message[@from='#foo%{irc_server_one}/{nick_one}'][@to='{jid_one}/{resource_one}'][@type='groupchat']/body[text()='coucou']", + "/message/stable_id:stanza-id[@by='#foo%{irc_server_one}'][@id]" + ], + [ + "/message[@from='#foo%{irc_server_one}/{nick_one}'][@to='{jid_two}/{resource_one}'][@type='groupchat']/body[text()='coucou']", + "/message/stable_id:stanza-id[@by='#foo%{irc_server_one}'][@id]" + ] + ), + + # Send a private message, to a in-room JID + send_stanza("<message from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}/{nick_two}' type='chat'><body>coucou in private</body></message>"), + # Message is received with a server-wide JID + 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 + send_stanza("<message from='{jid_two}/{resource_one}' to='{lower_nick_one}%{irc_server_one}' type='chat'><body>yes</body></message>"), + # The response is received from the in-room JID + expect_stanza("/message[@from='#foo%{irc_server_one}/{nick_two}'][@to='{jid_one}/{resource_one}'][@type='chat']/body[text()='yes']", + "/message/muc_user:x"), + # Do the exact same thing, from a different chan, + # to check if the response comes from the right JID + send_stanza("<presence from='{jid_one}/{resource_one}' to='#dummy%{irc_server_one}/{nick_one}' />"), + expect_stanza("/message"), + expect_stanza("/presence/muc_user:x/muc_user:status[@code='110']"), + expect_stanza("/message[@from='#dummy%{irc_server_one}'][@type='groupchat']/subject"), + # Send a private message, to a in-room JID + send_stanza("<message from='{jid_one}/{resource_one}' to='#dummy%{irc_server_one}/{nick_two}' type='chat'><body>re in private</body></message>"), + + # Message is received with a server-wide JID + 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 + send_stanza("<message from='{jid_two}/{resource_one}' to='{lower_nick_one}%{irc_server_one}' type='chat'><body>re</body></message>"), + # The response is received from the in-room JID + expect_stanza("/message[@from='#dummy%{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 + send_stanza("<presence from='{jid_one}/{resource_one}' to='#dummy%{irc_server_one}/{nick_one}' type='unavailable' />"), + 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 + send_stanza("<message from='{jid_two}/{resource_one}' to='{lower_nick_one}%{irc_server_one}' type='chat'><body>hihihoho</body></message>"), + expect_stanza("/message[@from='{lower_nick_two}%{irc_server_one}'][@to='{jid_one}/{resource_one}']"), +) diff --git a/tests/end_to_end/scenarios/client_error.py b/tests/end_to_end/scenarios/client_error.py new file mode 100644 index 0000000..ca5eaee --- /dev/null +++ b/tests/end_to_end/scenarios/client_error.py @@ -0,0 +1,16 @@ +from scenarios import * + +scenario = ( + scenarios.simple_channel_join.scenario, + # Second resource, same channel + send_stanza("<presence from='{jid_one}/{resource_two}' to='#foo%{irc_server_one}/{nick_one}' />"), + 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']"), + expect_stanza("/message[@from='#foo%{irc_server_one}'][@type='groupchat'][@to='{jid_one}/{resource_two}']/subject[not(text())]"), + + # Now the first resource has an error + send_stanza("<message from='{jid_one}/{resource_one}' to='#foo%%{irc_server_one}/{nick_one}' type='error'><error type='cancel'><recipient-unavailable xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/></error></message>"), + # Receive a leave only to the leaving resource + expect_stanza("/presence[@type='unavailable'][@from='#foo%{irc_server_one}/{nick_one}'][@to='{jid_one}/{resource_one}']/muc_user:x/muc_user:status[@code='110']", + "/presence/status[text()='Biboumi note: 1 resources are still in this channel.']"), +) diff --git a/tests/end_to_end/scenarios/complete_channel_list_with_pages_of_3.py b/tests/end_to_end/scenarios/complete_channel_list_with_pages_of_3.py new file mode 100644 index 0000000..8cef926 --- /dev/null +++ b/tests/end_to_end/scenarios/complete_channel_list_with_pages_of_3.py @@ -0,0 +1,106 @@ +from scenarios import * + +scenario = ( + send_stanza("<presence from='{jid_one}/{resource_one}' to='#aaa%{irc_server_one}/{nick_one}' />"), + sequences.connection("irc.localhost", '{jid_one}/{resource_one}'), + expect_stanza("/message"), + expect_stanza("/presence"), + expect_stanza("/message"), + + send_stanza("<presence from='{jid_one}/{resource_one}' to='#bbb%{irc_server_one}/{nick_one}' />"), + expect_stanza("/message"), + expect_stanza("/presence"), + expect_stanza("/message"), + + send_stanza("<presence from='{jid_one}/{resource_one}' to='#ccc%{irc_server_one}/{nick_one}' />"), + expect_stanza("/message"), + expect_stanza("/presence"), + expect_stanza("/message"), + + send_stanza("<presence from='{jid_one}/{resource_one}' to='#ddd%{irc_server_one}/{nick_one}' />"), + expect_stanza("/message"), + expect_stanza("/presence"), + expect_stanza("/message"), + + send_stanza("<presence from='{jid_one}/{resource_one}' to='#eee%{irc_server_one}/{nick_one}' />"), + expect_stanza("/message"), + expect_stanza("/presence"), + expect_stanza("/message"), + + send_stanza("<presence from='{jid_one}/{resource_one}' to='#fff%{irc_server_one}/{nick_one}' />"), + expect_stanza("/message"), + expect_stanza("/presence"), + expect_stanza("/message"), + + send_stanza("<presence from='{jid_one}/{resource_one}' to='#ggg%{irc_server_one}/{nick_one}' />"), + expect_stanza("/message"), + expect_stanza("/presence"), + expect_stanza("/message"), + + send_stanza("<presence from='{jid_one}/{resource_one}' to='#hhh%{irc_server_one}/{nick_one}' />"), + expect_stanza("/message"), + expect_stanza("/presence"), + expect_stanza("/message"), + + send_stanza("<presence from='{jid_one}/{resource_one}' to='#iii%{irc_server_one}/{nick_one}' />"), + expect_stanza("/message"), + expect_stanza("/presence"), + expect_stanza("/message"), + + send_stanza("<presence from='{jid_one}/{resource_one}' to='#jjj%{irc_server_one}/{nick_one}' />"), + expect_stanza("/message"), + expect_stanza("/presence"), + expect_stanza("/message"), + + send_stanza("<iq from='{jid_one}/{resource_one}' id='id' to='{irc_server_one}' type='get'><query xmlns='http://jabber.org/protocol/disco#items'><set xmlns='http://jabber.org/protocol/rsm'><max>3</max></set></query></iq>"), + expect_stanza("/iq[@type='result']/disco_items:query", + "/iq/disco_items:query/disco_items:item[@jid='#aaa%{irc_server_one}']", + "/iq/disco_items:query/disco_items:item[@jid='#bbb%{irc_server_one}']", + "/iq/disco_items:query/disco_items:item[@jid='#ccc%{irc_server_one}']", + "/iq/disco_items:query/rsm:set/rsm:first[text()='#aaa%{irc_server_one}'][@index='0']", + "/iq/disco_items:query/rsm:set/rsm:last[text()='#ccc%{irc_server_one}']"), + + send_stanza("<iq from='{jid_one}/{resource_one}' id='id' to='{irc_server_one}' type='get'><query xmlns='http://jabber.org/protocol/disco#items'><set xmlns='http://jabber.org/protocol/rsm'><after>#ccc%{irc_server_one}</after><max>3</max></set></query></iq>"), + expect_stanza("/iq[@type='result']/disco_items:query", + "/iq/disco_items:query/disco_items:item[@jid='#ddd%{irc_server_one}']", + "/iq/disco_items:query/disco_items:item[@jid='#eee%{irc_server_one}']", + "/iq/disco_items:query/disco_items:item[@jid='#fff%{irc_server_one}']", + "/iq/disco_items:query/rsm:set/rsm:first[text()='#ddd%{irc_server_one}'][@index='3']", + "/iq/disco_items:query/rsm:set/rsm:last[text()='#fff%{irc_server_one}']"), + + send_stanza("<iq from='{jid_one}/{resource_one}' id='id' to='{irc_server_one}' type='get'><query xmlns='http://jabber.org/protocol/disco#items'><set xmlns='http://jabber.org/protocol/rsm'><after>#fff%{irc_server_one}</after><max>3</max></set></query></iq>"), + expect_stanza("/iq[@type='result']/disco_items:query", + "/iq/disco_items:query/disco_items:item[@jid='#ggg%{irc_server_one}']", + "/iq/disco_items:query/disco_items:item[@jid='#hhh%{irc_server_one}']", + "/iq/disco_items:query/disco_items:item[@jid='#iii%{irc_server_one}']", + "/iq/disco_items:query/rsm:set/rsm:first[text()='#ggg%{irc_server_one}'][@index='6']", + "/iq/disco_items:query/rsm:set/rsm:last[text()='#iii%{irc_server_one}']"), + + send_stanza("<iq from='{jid_one}/{resource_one}' id='id' to='{irc_server_one}' type='get'><query xmlns='http://jabber.org/protocol/disco#items'><set xmlns='http://jabber.org/protocol/rsm'><after>#iii%{irc_server_one}</after><max>3</max></set></query></iq>"), + expect_stanza("/iq[@type='result']/disco_items:query", + "/iq/disco_items:query/disco_items:item[@jid='#jjj%{irc_server_one}']", + "/iq/disco_items:query/rsm:set/rsm:first[text()='#jjj%{irc_server_one}'][@index='9']", + "/iq/disco_items:query/rsm:set/rsm:last[text()='#jjj%{irc_server_one}']", + "/iq/disco_items:query/rsm:set/rsm:count[text()='10']"), + + send_stanza("<presence from='{jid_one}/{resource_one}' to='#aaa%{irc_server_one}/{nick_one}' type='unavailable' />"), + send_stanza("<presence from='{jid_one}/{resource_one}' to='#bbb%{irc_server_one}/{nick_one}' type='unavailable' />"), + send_stanza("<presence from='{jid_one}/{resource_one}' to='#ccc%{irc_server_one}/{nick_one}' type='unavailable' />"), + send_stanza("<presence from='{jid_one}/{resource_one}' to='#ddd%{irc_server_one}/{nick_one}' type='unavailable' />"), + send_stanza("<presence from='{jid_one}/{resource_one}' to='#eee%{irc_server_one}/{nick_one}' type='unavailable' />"), + send_stanza("<presence from='{jid_one}/{resource_one}' to='#fff%{irc_server_one}/{nick_one}' type='unavailable' />"), + send_stanza("<presence from='{jid_one}/{resource_one}' to='#ggg%{irc_server_one}/{nick_one}' type='unavailable' />"), + send_stanza("<presence from='{jid_one}/{resource_one}' to='#hhh%{irc_server_one}/{nick_one}' type='unavailable' />"), + send_stanza("<presence from='{jid_one}/{resource_one}' to='#iii%{irc_server_one}/{nick_one}' type='unavailable' />"), + send_stanza("<presence from='{jid_one}/{resource_one}' to='#jjj%{irc_server_one}/{nick_one}' type='unavailable' />"), + expect_stanza("/presence[@type='unavailable']"), + expect_stanza("/presence[@type='unavailable']"), + expect_stanza("/presence[@type='unavailable']"), + expect_stanza("/presence[@type='unavailable']"), + expect_stanza("/presence[@type='unavailable']"), + expect_stanza("/presence[@type='unavailable']"), + expect_stanza("/presence[@type='unavailable']"), + expect_stanza("/presence[@type='unavailable']"), + expect_stanza("/presence[@type='unavailable']"), + expect_stanza("/presence[@type='unavailable']"), +) diff --git a/tests/end_to_end/scenarios/configure_bad_value.py b/tests/end_to_end/scenarios/configure_bad_value.py new file mode 100644 index 0000000..4d2575c --- /dev/null +++ b/tests/end_to_end/scenarios/configure_bad_value.py @@ -0,0 +1,21 @@ +from scenarios import * + +scenario = ( + # Configure the throttle option with an incorrect value + send_stanza("<iq type='set' id='id1' from='{jid_one}/{resource_one}' to='{irc_server_one}'><command xmlns='http://jabber.org/protocol/commands' node='configure' action='execute' /></iq>"), + expect_stanza("/iq[@type='result']", + after = save_value("sessionid", extract_attribute("/iq[@type='result']/commands:command[@node='configure']", "sessionid"))), + send_stanza("<iq type='set' id='id2' from='{jid_one}/{resource_one}' to='{irc_server_one}'>" + "<command xmlns='http://jabber.org/protocol/commands' node='configure' sessionid='{sessionid}' action='next'>" + "<x xmlns='jabber:x:data' type='submit'>" + "<field var='throttle_limit'><value>bleh</value></field>" + "<field var='max_history_length'><value>bleh</value></field>" + "</x></command></iq>"), + expect_stanza("/iq[@type='result']/commands:command[@node='configure'][@status='completed']"), + + # These options should have their default value + send_stanza("<iq type='set' id='id2' from='{jid_one}/{resource_one}' to='{irc_server_one}'><command xmlns='http://jabber.org/protocol/commands' node='configure' action='execute' /></iq>"), + expect_stanza("/iq[@type='result']", + "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='text-single'][@var='throttle_limit']/dataform:value[text()='10']", + "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='text-single'][@var='max_history_length']/dataform:value[text()='20']"), +) diff --git a/tests/end_to_end/scenarios/default_channel_list_limit.py b/tests/end_to_end/scenarios/default_channel_list_limit.py new file mode 100644 index 0000000..84f96b3 --- /dev/null +++ b/tests/end_to_end/scenarios/default_channel_list_limit.py @@ -0,0 +1,52 @@ +from scenarios import * + +def incr_counter(): + counter = -1 + def f(stanza): + nonlocal counter + counter += 1 + return counter + return f + +counter = incr_counter() + +scenario = ( + # Disable the throttling, otherwise it’s way too long + send_stanza("<iq type='set' id='id1' from='{jid_one}/{resource_one}' to='{irc_server_one}'><command xmlns='http://jabber.org/protocol/commands' node='configure' action='execute' /></iq>"), + expect_stanza("/iq[@type='result']", + after = save_value("sessionid", extract_attribute("/iq[@type='result']/commands:command[@node='configure']", "sessionid"))), + send_stanza("<iq type='set' id='id2' from='{jid_one}/{resource_one}' to='{irc_server_one}'>" + "<command xmlns='http://jabber.org/protocol/commands' node='configure' sessionid='{sessionid}' action='next'>" + "<x xmlns='jabber:x:data' type='submit'>" + "<field var='ports'><value>6667</value></field>" + "<field var='tls_ports'><value>6697</value><value>6670</value></field>" + "<field var='throttle_limit'><value>9999</value></field>" + "</x></command></iq>"), + expect_stanza("/iq[@type='result']/commands:command[@node='configure'][@status='completed']/commands:note[@type='info'][text()='Configuration successfully applied.']", + after = save_value("counter", counter)), + + + send_stanza("<presence from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}/{nick_one}' />"), + sequences.connection(), + + scenarios.simple_channel_join.expect_self_join_presence(jid = '{jid_one}/{resource_one}', chan = "#foo", nick = "{nick_one}"), + + + ( + send_stanza("<presence from='{jid_one}/{resource_one}' to='#{counter}%{irc_server_one}/{nick_one}' />"), + expect_stanza("/message"), + expect_stanza("/presence", + after = save_value("counter", counter)), + expect_stanza("/message"), + ) * 110, + + send_stanza("<iq from='{jid_one}/{resource_one}' id='id1' to='{irc_server_one}' type='get'><query xmlns='http://jabber.org/protocol/disco#items'/></iq>"), + # charybdis sends the list in alphabetic order, so #foo is the last, and #99 is after #120 + expect_stanza("/iq/disco_items:query/disco_items:item[@jid='#0%{irc_server_one}']", + "/iq/disco_items:query/disco_items:item[@jid='#1%{irc_server_one}']", + "/iq/disco_items:query/disco_items:item[@jid='#109%{irc_server_one}']", + "/iq/disco_items:query/disco_items:item[@jid='#9%{irc_server_one}']", + "!/iq/disco_items:query/disco_items:item[@jid='#foo%{irc_server_one}']", + "!/iq/disco_items:query/disco_items:item[@jid='#99%{irc_server_one}']", + "!/iq/disco_items:query/disco_items:item[@jid='#90%{irc_server_one}']"), +) diff --git a/tests/end_to_end/scenarios/default_mam_limit.py b/tests/end_to_end/scenarios/default_mam_limit.py new file mode 100644 index 0000000..02fcaa7 --- /dev/null +++ b/tests/end_to_end/scenarios/default_mam_limit.py @@ -0,0 +1,105 @@ +from scenarios import * + +scenario = ( + # Disable the throttling, otherwise it’s way too long + send_stanza("<iq type='set' id='id1' from='{jid_one}/{resource_one}' to='{irc_server_one}'><command xmlns='http://jabber.org/protocol/commands' node='configure' action='execute' /></iq>"), + expect_stanza("/iq[@type='result']", + after = save_value("sessionid", extract_attribute("/iq[@type='result']/commands:command[@node='configure']", "sessionid"))), + send_stanza("<iq type='set' id='id2' from='{jid_one}/{resource_one}' to='{irc_server_one}'>" + "<command xmlns='http://jabber.org/protocol/commands' node='configure' sessionid='{sessionid}' action='next'>" + "<x xmlns='jabber:x:data' type='submit'>" + "<field var='ports'><value>6667</value></field>" + "<field var='tls_ports'><value>6697</value><value>6670</value></field>" + "<field var='throttle_limit'><value>9999</value></field>" + "</x></command></iq>"), + expect_stanza("/iq[@type='result']/commands:command[@node='configure'][@status='completed']/commands:note[@type='info'][text()='Configuration successfully applied.']"), + + send_stanza("<presence from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}/{nick_one}' />"), + sequences.connection("irc.localhost", '{jid_one}/{resource_one}'), + expect_stanza("/message/body[text()='Mode #foo [+nt] by {irc_host_one}']"), + 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']"), + expect_stanza("/message[@from='#foo%{irc_server_one}'][@type='groupchat']/subject[not(text())]", + after = save_value("counter", lambda x: 0)), + ( + send_stanza("<message from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}' type='groupchat'><body>{counter}</body></message>"), + expect_stanza("/message[@from='#foo%{irc_server_one}/{nick_one}'][@to='{jid_one}/{resource_one}'][@type='groupchat']/body[text()='{counter}']", + after = save_value("counter", lambda stanza: str(1 + int(extract_text("/message/body", stanza))))), + ) * 150, + + # Retrieve the archive, without any restriction + send_stanza("<iq to='#foo%{irc_server_one}' from='{jid_one}/{resource_one}' type='set' id='id1'><query xmlns='urn:xmpp:mam:2' queryid='qid1' /></iq>"), + expect_stanza("/message/mam:result[@queryid='qid1']/forward:forwarded/delay:delay", + "/message/mam:result[@queryid='qid1']/forward:forwarded/client:message[@from='#foo%{irc_server_one}/{nick_one}'][@type='groupchat']/client:body[text()='0']"), + # followed by 98 more messages + ( + expect_stanza("/message/mam:result[@queryid='qid1']/forward:forwarded/delay:delay", + "/message/mam:result[@queryid='qid1']/forward:forwarded/client:message[@from='#foo%{irc_server_one}/{nick_one}'][@type='groupchat']/client:body"), + ) * 98, + + # and finally the message "99" + expect_stanza("/message/mam:result[@queryid='qid1']/forward:forwarded/delay:delay", + "/message/mam:result[@queryid='qid1']/forward:forwarded/client:message[@from='#foo%{irc_server_one}/{nick_one}'][@type='groupchat']/client:body[text()='99']", + after = save_value("last_uuid", extract_attribute("/message/mam:result", "id"))), + + # And it should not be marked as complete + expect_stanza("/iq[@type='result'][@id='id1'][@from='#foo%{irc_server_one}'][@to='{jid_one}/{resource_one}']", + "/iq/mam:fin/rsm:set/rsm:last[text()='{last_uuid}']", + "!/iq//mam:fin[@complete='true']", + "/iq//mam:fin"), + + # Retrieve the next page, using the “after” thingy + send_stanza("<iq to='#foo%{irc_server_one}' from='{jid_one}/{resource_one}' type='set' id='id2'><query xmlns='urn:xmpp:mam:2' queryid='qid2' ><set xmlns='http://jabber.org/protocol/rsm'><after>{last_uuid}</after></set></query></iq>"), + + expect_stanza("/message/mam:result[@queryid='qid2']/forward:forwarded/delay:delay", + "/message/mam:result[@queryid='qid2']/forward:forwarded/client:message[@from='#foo%{irc_server_one}/{nick_one}'][@type='groupchat']/client:body[text()='100']"), + ( + expect_stanza("/message/mam:result[@queryid='qid2']/forward:forwarded/delay:delay", + "/message/mam:result[@queryid='qid2']/forward:forwarded/client:message[@from='#foo%{irc_server_one}/{nick_one}'][@type='groupchat']/client:body"), + ) * 48, + expect_stanza("/message/mam:result[@queryid='qid2']/forward:forwarded/delay:delay", + "/message/mam:result[@queryid='qid2']/forward:forwarded/client:message[@from='#foo%{irc_server_one}/{nick_one}'][@type='groupchat']/client:body[text()='149']", + after = save_value("last_uuid", extract_attribute("/message/mam:result", "id"))), + expect_stanza("/iq[@type='result'][@id='id2'][@from='#foo%{irc_server_one}'][@to='{jid_one}/{resource_one}']", + "/iq/mam:fin/rsm:set/rsm:last[text()='{last_uuid}']", + "/iq//mam:fin[@complete='true']", + "/iq//mam:fin"), + + # Send a request with a non-existing ID set as the “after” value. + send_stanza("<iq to='#foo%{irc_server_one}' from='{jid_one}/{resource_one}' type='set' id='id3'><query xmlns='urn:xmpp:mam:2' queryid='qid3' ><set xmlns='http://jabber.org/protocol/rsm'><after>DUMMY_ID</after></set></query></iq>"), + expect_stanza("/iq[@id='id3'][@type='error']/error[@type='cancel']/stanza:item-not-found"), + + # Request the last page just BEFORE the last message in the archive + send_stanza("<iq to='#foo%{irc_server_one}' from='{jid_one}/{resource_one}' type='set' id='id3'><query xmlns='urn:xmpp:mam:2' queryid='qid3' ><set xmlns='http://jabber.org/protocol/rsm'><before></before></set></query></iq>"), + + expect_stanza("/message/mam:result[@queryid='qid3']/forward:forwarded/delay:delay", + "/message/mam:result[@queryid='qid3']/forward:forwarded/client:message[@from='#foo%{irc_server_one}/{nick_one}'][@type='groupchat']/client:body[text()='50']"), + ( + expect_stanza("/message/mam:result[@queryid='qid3']/forward:forwarded/delay:delay", + "/message/mam:result[@queryid='qid3']/forward:forwarded/client:message[@from='#foo%{irc_server_one}/{nick_one}'][@type='groupchat']/client:body"), + ) * 98, + expect_stanza("/message/mam:result[@queryid='qid3']/forward:forwarded/delay:delay", + "/message/mam:result[@queryid='qid3']/forward:forwarded/client:message[@from='#foo%{irc_server_one}/{nick_one}'][@type='groupchat']/client:body[text()='149']", + after = save_value("last_uuid", extract_attribute("/message/mam:result", "id"))), + expect_stanza("/iq[@type='result'][@id='id3'][@from='#foo%{irc_server_one}'][@to='{jid_one}/{resource_one}']", + "/iq/mam:fin/rsm:set/rsm:last[text()='{last_uuid}']", + "!/iq//mam:fin[@complete='true']", + "/iq//mam:fin"), + + # Do the same thing, but with a limit value. + send_stanza("<iq to='#foo%{irc_server_one}' from='{jid_one}/{resource_one}' type='set' id='id4'><query xmlns='urn:xmpp:mam:2' queryid='qid4' ><set xmlns='http://jabber.org/protocol/rsm'><before>{last_uuid}</before><max>2</max></set></query></iq>"), + expect_stanza("/message/mam:result[@queryid='qid4']/forward:forwarded/delay:delay", + "/message/mam:result[@queryid='qid4']/forward:forwarded/client:message[@from='#foo%{irc_server_one}/{nick_one}'][@type='groupchat']/client:body[text()='147']"), + expect_stanza("/message/mam:result[@queryid='qid4']/forward:forwarded/delay:delay", + "/message/mam:result[@queryid='qid4']/forward:forwarded/client:message[@from='#foo%{irc_server_one}/{nick_one}'][@type='groupchat']/client:body[text()='148']", + after = save_value("last_uuid", extract_attribute("/message/mam:result", "id"))), + expect_stanza("/iq[@type='result'][@id='id4'][@from='#foo%{irc_server_one}'][@to='{jid_one}/{resource_one}']", + "/iq/mam:fin/rsm:set/rsm:last[text()='{last_uuid}']", + "!/iq/mam:fin[@complete='true']"), + + # Test if everything is fine even with weird max value: 0 + send_stanza("<iq to='#foo%{irc_server_one}' from='{jid_one}/{resource_one}' type='set' id='id5'><query xmlns='urn:xmpp:mam:2' queryid='qid5' ><set xmlns='http://jabber.org/protocol/rsm'><before></before><max>0</max></set></query></iq>"), + + expect_stanza("/iq[@type='result'][@id='id5'][@from='#foo%{irc_server_one}'][@to='{jid_one}/{resource_one}']", + "!/iq/mam:fin[@complete='true']"), +) diff --git a/tests/end_to_end/scenarios/encoded_channel_join.py b/tests/end_to_end/scenarios/encoded_channel_join.py new file mode 100644 index 0000000..fd4144a --- /dev/null +++ b/tests/end_to_end/scenarios/encoded_channel_join.py @@ -0,0 +1,10 @@ +from scenarios import * + +scenario = ( + send_stanza("<presence from='{jid_one}/{resource_one}' to='#biboumi\\40louiz.org\\3a80%{irc_server_one}/{nick_one}' />"), + sequences.connection("irc.localhost", '{jid_one}/{resource_one}'), + expect_stanza("/message/body[text()='Mode #biboumi@louiz.org:80 [+nt] by {irc_host_one}']"), + 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']"), + expect_stanza("/message[@from='#biboumi\\40louiz.org\\3a80%{irc_server_one}'][@type='groupchat']/subject[not(text())]"), +) diff --git a/tests/end_to_end/scenarios/execute_admin_disconnect_from_server_adhoc_command.py b/tests/end_to_end/scenarios/execute_admin_disconnect_from_server_adhoc_command.py new file mode 100644 index 0000000..18dfe94 --- /dev/null +++ b/tests/end_to_end/scenarios/execute_admin_disconnect_from_server_adhoc_command.py @@ -0,0 +1,68 @@ +from scenarios import * + +from scenarios.simple_channel_join import expect_self_join_presence + +scenario = ( + # Admin connects to first server + send_stanza("<presence from='{jid_admin}/{resource_one}' to='#bar%{irc_server_one}/{nick_one}' />"), + sequences.connection("irc.localhost", '{jid_admin}/{resource_one}'), + expect_self_join_presence(jid = '{jid_admin}/{resource_one}', chan = "#bar", nick = "{nick_one}"), + + # Non-Admin connects to first server + send_stanza("<presence from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}/{nick_two}' />"), + sequences.connection("irc.localhost", '{jid_one}/{resource_one}'), + expect_self_join_presence(jid = '{jid_one}/{resource_one}', chan = "#foo", nick = "{nick_two}"), + + # Non-admin connects to second server + send_stanza("<presence from='{jid_one}/{resource_two}' to='#bon%{irc_server_two}/{nick_three}' />"), + sequences.connection("{irc_host_two}", '{jid_one}/{resource_two}'), + expect_self_join_presence(jid = '{jid_one}/{resource_two}', chan = "#bon", nick = "{nick_three}", irc_server = "{irc_server_two}"), + + # Execute as admin + send_stanza("<iq type='set' id='command1' from='{jid_admin}/{resource_one}' to='{biboumi_host}'><command xmlns='http://jabber.org/protocol/commands' node='disconnect-from-irc-server' action='execute' /></iq>"), + expect_stanza("/iq[@type='result']/commands:command[@node='disconnect-from-irc-server'][@sessionid][@status='executing']", + "/iq/commands:command/dataform:x[@type='form']/dataform:field[@var='jid'][@type='list-single']/dataform:option[@label='{jid_one}']/dataform:value[text()='{jid_one}']", + "/iq/commands:command/dataform:x[@type='form']/dataform:field[@var='jid'][@type='list-single']/dataform:option[@label='{jid_admin}']/dataform:value[text()='{jid_admin}']", + "/iq/commands:command/commands:actions/commands:next", + after = save_value("sessionid", extract_attribute("/iq/commands:command[@node='disconnect-from-irc-server']", "sessionid")) + ), + send_stanza("<iq type='set' id='command2' from='{jid_admin}/{resource_one}' to='{biboumi_host}'><command xmlns='http://jabber.org/protocol/commands' node='disconnect-from-irc-server' sessionid='{sessionid}' action='next'><x xmlns='jabber:x:data' type='submit'><field var='jid'><value>{jid_one}</value></field><field var='quit-message'><value>e2e test one</value></field></x></command></iq>"), + expect_stanza("/iq[@type='result']/commands:command[@node='disconnect-from-irc-server'][@sessionid][@status='executing']", + "/iq/commands:command/dataform:x[@type='form']/dataform:field[@var='quit-message'][@type='text-single']", + "/iq/commands:command/dataform:x[@type='form']/dataform:field[@var='irc-servers'][@type='list-multi']", + "/iq/commands:command/dataform:x[@type='form']/dataform:field[@var='irc-servers']/dataform:option[@label='localhost']/dataform:value[text()='localhost']", + "/iq/commands:command/dataform:x[@type='form']/dataform:field[@var='irc-servers']/dataform:option[@label='irc.localhost']/dataform:value[text()='irc.localhost']", + "/iq/commands:command/commands:actions/commands:complete", + after = save_value("sessionid", extract_attribute("/iq/commands:command[@node='disconnect-from-irc-server']", "sessionid")) + ), + # Command is successfull + send_stanza("<iq type='set' id='command3' from='{jid_admin}/{resource_one}' to='{biboumi_host}'><command xmlns='http://jabber.org/protocol/commands' node='disconnect-from-irc-server' sessionid='{sessionid}' action='complete'><x xmlns='jabber:x:data' type='submit'><field var='irc-servers'><value>localhost</value></field><field var='quit-message'><value>Disconnected by e2e</value></field></x></command></iq>"), + # User is being disconnected + expect_unordered( + [ + "/presence[@type='unavailable'][@to='{jid_one}/{resource_two}'][@from='#bon%{irc_server_two}/{nick_three}']", + "/presence/status[text()='Disconnected by e2e']" + ], + [ + "/iq[@type='result']/commands:command[@node='disconnect-from-irc-server'][@status='completed']/commands:note[@type='info'][text()='{jid_one} was disconnected from 1 IRC server.']", + ]), + + # Execute as non-admin (this skips the first step) + send_stanza("<iq type='set' id='command4' from='{jid_one}/{resource_one}' to='{biboumi_host}'><command xmlns='http://jabber.org/protocol/commands' node='disconnect-from-irc-server' action='execute' /></iq>"), + expect_stanza("/iq[@type='result']/commands:command[@node='disconnect-from-irc-server'][@sessionid][@status='executing']", + "/iq/commands:command/dataform:x[@type='form']/dataform:field[@var='quit-message'][@type='text-single']", + "/iq/commands:command/dataform:x[@type='form']/dataform:field[@var='irc-servers'][@type='list-multi']", + "/iq/commands:command/dataform:x[@type='form']/dataform:field[@var='irc-servers']/dataform:option[@label='irc.localhost']/dataform:value[text()='irc.localhost']", + "/iq/commands:command/commands:actions/commands:complete", + after = save_value("sessionid", extract_attribute("/iq/commands:command[@node='disconnect-from-irc-server']", "sessionid")) + ), + send_stanza("<iq type='set' id='command5' from='{jid_one}/{resource_one}' to='{biboumi_host}'><command xmlns='http://jabber.org/protocol/commands' node='disconnect-from-irc-server' sessionid='{sessionid}' action='complete'><x xmlns='jabber:x:data' type='submit'><field var='irc-servers'><value>irc.localhost</value></field><field var='quit-message'><value>Disconnected by e2e</value></field></x></command></iq>"), + expect_unordered( + [ + "/presence[@type='unavailable'][@to='{jid_one}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_two}']", + "/presence/status[text()='Disconnected by e2e']" + ], + [ + "/iq[@type='result']/commands:command[@node='disconnect-from-irc-server'][@status='completed']/commands:note[@type='info'][text()='{jid_one}/{resource_one} was disconnected from 1 IRC server.']", + ]), +) diff --git a/tests/end_to_end/scenarios/execute_disconnect_user_adhoc_command.py b/tests/end_to_end/scenarios/execute_disconnect_user_adhoc_command.py new file mode 100644 index 0000000..a680017 --- /dev/null +++ b/tests/end_to_end/scenarios/execute_disconnect_user_adhoc_command.py @@ -0,0 +1,19 @@ +from scenarios import * + +from scenarios.simple_channel_join import expect_self_join_presence + +scenario = ( + send_stanza("<presence from='{jid_admin}/{resource_one}' to='#foo%{irc_server_one}/{nick_one}' />"), + sequences.connection("irc.localhost", '{jid_admin}/{resource_one}'), + expect_self_join_presence(jid = '{jid_admin}/{resource_one}', chan = "#foo", nick = "{nick_one}"), + + send_stanza("<iq type='set' id='command1' from='{jid_admin}/{resource_one}' to='{biboumi_host}'><command xmlns='http://jabber.org/protocol/commands' node='disconnect-user' action='execute' /></iq>"), + expect_stanza("/iq[@type='result']/commands:command[@node='disconnect-user'][@sessionid][@status='executing']", + "/iq/commands:command/commands:actions/commands:complete", + after = save_value("sessionid", extract_attribute("/iq/commands:command[@node='disconnect-user']", "sessionid")) + ), + send_stanza("<iq type='set' id='command2' from='{jid_admin}/{resource_one}' to='{biboumi_host}'><command xmlns='http://jabber.org/protocol/commands' node='disconnect-user' sessionid='{sessionid}' action='complete'><x xmlns='jabber:x:data' type='submit'><field var='jids'><value>{jid_admin}</value></field><field var='quit-message'><value>Disconnected by e2e</value></field></x></command></iq>"), + expect_stanza("/iq[@type='result']/commands:command[@node='disconnect-user'][@status='completed']/commands:note[@type='info'][text()='1 user has been disconnected.']"), + # Note, charybdis ignores our QUIT message, so we can't test it + expect_stanza("/presence[@type='unavailable'][@to='{jid_admin}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_one}']"), +) diff --git a/tests/end_to_end/scenarios/execute_forbidden_adhoc_command.py b/tests/end_to_end/scenarios/execute_forbidden_adhoc_command.py new file mode 100644 index 0000000..10c98ab --- /dev/null +++ b/tests/end_to_end/scenarios/execute_forbidden_adhoc_command.py @@ -0,0 +1,7 @@ +from scenarios import * + +scenario = ( + send_stanza("<iq type='set' id='command1' from='{jid_one}/{resource_one}' to='{biboumi_host}'><command xmlns='http://jabber.org/protocol/commands' node='disconnect-user' action='execute' /></iq>"), + expect_stanza("/iq[@type='error'][@id='command1']/commands:command[@node='disconnect-user']", + "/iq/commands:command/commands:error[@type='cancel']/stanza:forbidden"), +) diff --git a/tests/end_to_end/scenarios/execute_hello_adhoc_command.py b/tests/end_to_end/scenarios/execute_hello_adhoc_command.py new file mode 100644 index 0000000..916d95a --- /dev/null +++ b/tests/end_to_end/scenarios/execute_hello_adhoc_command.py @@ -0,0 +1,14 @@ +from scenarios import * + +scenario = ( + send_stanza("<iq type='set' id='hello-command1' from='{jid_one}/{resource_one}' to='{biboumi_host}'><command xmlns='http://jabber.org/protocol/commands' node='hello' action='execute' /></iq>"), + 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:complete", + after = save_value("sessionid", extract_attribute("/iq[@type='result']/commands:command[@node='hello']", "sessionid")) + ), + send_stanza("<iq type='set' id='hello-command2' from='{jid_one}/{resource_one}' to='{biboumi_host}'><command xmlns='http://jabber.org/protocol/commands' node='hello' sessionid='{sessionid}' action='complete'><x xmlns='jabber:x:data' type='submit'><field var='name'><value>COUCOU</value></field></x></command></iq>"), + expect_stanza("/iq[@type='result']/commands:command[@node='hello'][@status='completed']/commands:note[@type='info'][text()='Hello COUCOU!']") +) diff --git a/tests/end_to_end/scenarios/execute_incomplete_hello_adhoc_command.py b/tests/end_to_end/scenarios/execute_incomplete_hello_adhoc_command.py new file mode 100644 index 0000000..83b2a55 --- /dev/null +++ b/tests/end_to_end/scenarios/execute_incomplete_hello_adhoc_command.py @@ -0,0 +1,11 @@ +from scenarios import * + +scenario = ( + send_stanza("<iq type='set' id='hello-command1' from='{jid_one}/{resource_one}' to='{biboumi_host}'><command xmlns='http://jabber.org/protocol/commands' node='hello' action='execute' /></iq>"), + expect_stanza("/iq[@type='result']/commands:command[@node='hello'][@sessionid][@status='executing']", + "/iq/commands:command/commands:actions/commands:complete", + after = save_value("sessionid", extract_attribute("/iq[@type='result']/commands:command[@node='hello']", "sessionid")) + ), + send_stanza("<iq type='set' id='hello-command2' from='{jid_one}/{resource_one}' to='{biboumi_host}'><command xmlns='http://jabber.org/protocol/commands' node='hello' sessionid='{sessionid}' action='complete'><x xmlns='jabber:x:data' type='submit'></x></command></iq>"), + expect_stanza("/iq[@type='error']") +) diff --git a/tests/end_to_end/scenarios/execute_ping_adhoc_command.py b/tests/end_to_end/scenarios/execute_ping_adhoc_command.py new file mode 100644 index 0000000..bcdefe1 --- /dev/null +++ b/tests/end_to_end/scenarios/execute_ping_adhoc_command.py @@ -0,0 +1,6 @@ +from scenarios import * + +scenario = ( + send_stanza("<iq type='set' id='ping-command1' from='{jid_one}/{resource_one}' to='{biboumi_host}'><command xmlns='http://jabber.org/protocol/commands' node='ping' action='execute' /></iq>"), + expect_stanza("/iq[@type='result']/commands:command[@node='ping'][@status='completed']/commands:note[@type='info'][text()='Pong']") +) diff --git a/tests/end_to_end/scenarios/execute_reload_adhoc_command.py b/tests/end_to_end/scenarios/execute_reload_adhoc_command.py new file mode 100644 index 0000000..5c4e1f7 --- /dev/null +++ b/tests/end_to_end/scenarios/execute_reload_adhoc_command.py @@ -0,0 +1,6 @@ +from scenarios import * + +scenario = ( + send_stanza("<iq type='set' id='ping-command1' from='{jid_admin}/{resource_one}' to='{biboumi_host}'><command xmlns='http://jabber.org/protocol/commands' node='reload' action='execute' /></iq>"), + expect_stanza("/iq[@type='result']/commands:command[@node='reload'][@status='completed']/commands:note[@type='info'][text()='Configuration reloaded.']"), +) diff --git a/tests/end_to_end/scenarios/fixed_irc_server_subscription.py b/tests/end_to_end/scenarios/fixed_irc_server_subscription.py new file mode 100644 index 0000000..091cf2a --- /dev/null +++ b/tests/end_to_end/scenarios/fixed_irc_server_subscription.py @@ -0,0 +1,8 @@ +from scenarios import * + +conf = 'fixed_server' + +scenario = ( + send_stanza("<presence type='subscribe' from='{jid_one}/{resource_one}' to='{biboumi_host}' id='sub1' />"), + expect_stanza("/presence[@to='{jid_one}'][@from='{biboumi_host}'][@type='subscribed']") +) diff --git a/tests/end_to_end/scenarios/fixed_muc_disco_info.py b/tests/end_to_end/scenarios/fixed_muc_disco_info.py new file mode 100644 index 0000000..6cabb49 --- /dev/null +++ b/tests/end_to_end/scenarios/fixed_muc_disco_info.py @@ -0,0 +1,14 @@ +from scenarios import * + +conf = 'fixed_server' + +scenario = ( + send_stanza("<iq from='{jid_one}/{resource_one}' to='#foo@{biboumi_host}' id='1' type='get'><query xmlns='http://jabber.org/protocol/disco#info'/></iq>"), + expect_stanza("/iq[@from='#foo@{biboumi_host}'][@to='{jid_one}/{resource_one}'][@type='result']/disco_info:query", + "/iq[@type='result']/disco_info:query/disco_info:identity[@category='conference'][@type='irc'][@name='#foo on {irc_host_one}']", + "/iq/disco_info:query/disco_info:feature[@var='jabber:iq:version']", + "/iq/disco_info:query/disco_info:feature[@var='http://jabber.org/protocol/commands']", + "/iq/disco_info:query/disco_info:feature[@var='urn:xmpp:ping']", + "/iq/disco_info:query/disco_info:feature[@var='urn:xmpp:mam:2']", + "/iq/disco_info:query/disco_info:feature[@var='jabber:iq:version']"), +) diff --git a/tests/end_to_end/scenarios/get_irc_connection_info.py b/tests/end_to_end/scenarios/get_irc_connection_info.py new file mode 100644 index 0000000..25f2a87 --- /dev/null +++ b/tests/end_to_end/scenarios/get_irc_connection_info.py @@ -0,0 +1,15 @@ +from scenarios import * + +scenario = ( + send_stanza("<iq type='set' id='command1' from='{jid_one}/{resource_one}' to='{irc_server_one}'><command xmlns='http://jabber.org/protocol/commands' node='get-irc-connection-info' action='execute' /></iq>"), + expect_stanza("/iq/commands:command/commands:note[text()='You are not connected to the IRC server irc.localhost']"), + + send_stanza("<presence from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}/{nick_one}' />"), + sequences.connection("irc.localhost", '{jid_one}/{resource_one}'), + expect_stanza("/message"), + expect_stanza("/presence"), + expect_stanza("/message"), + + send_stanza("<iq type='set' id='command2' from='{jid_one}/{resource_one}' to='{irc_server_one}'><command xmlns='http://jabber.org/protocol/commands' node='get-irc-connection-info' action='execute' /></iq>"), + expect_stanza(r"/iq/commands:command/commands:note[re:test(text(), 'Connected to IRC server irc.localhost on port 6667 since \d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d \(\d+ seconds ago\)\.\n#foo from 1 resource: {resource_one}.*')]"), +) diff --git a/tests/end_to_end/scenarios/get_irc_connection_info_fixed.py b/tests/end_to_end/scenarios/get_irc_connection_info_fixed.py new file mode 100644 index 0000000..d9be151 --- /dev/null +++ b/tests/end_to_end/scenarios/get_irc_connection_info_fixed.py @@ -0,0 +1,17 @@ +from scenarios import * + +conf = 'fixed_server' + +scenario = ( + send_stanza("<iq type='set' id='command1' from='{jid_one}/{resource_one}' to='{biboumi_host}'><command xmlns='http://jabber.org/protocol/commands' node='get-irc-connection-info' action='execute' /></iq>"), + expect_stanza("/iq/commands:command/commands:note[text()='You are not connected to the IRC server irc.localhost']"), + + send_stanza("<presence from='{jid_one}/{resource_one}' to='#foo@{biboumi_host}/{nick_one}' />"), + sequences.connection("irc.localhost", '{jid_one}/{resource_one}', fixed_irc_server=True), + expect_stanza("/message"), + expect_stanza("/presence"), + expect_stanza("/message"), + + send_stanza("<iq type='set' id='command2' from='{jid_one}/{resource_one}' to='{biboumi_host}'><command xmlns='http://jabber.org/protocol/commands' node='get-irc-connection-info' action='execute' /></iq>"), + expect_stanza(r"/iq/commands:command/commands:note[re:test(text(), 'Connected to IRC server irc.localhost on port 6667 since \d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d \(\d+ seconds ago\)\.\n#foo from 1 resource: {resource_one}.*')]"), +) diff --git a/tests/end_to_end/scenarios/global_configure.py b/tests/end_to_end/scenarios/global_configure.py new file mode 100644 index 0000000..d7771c4 --- /dev/null +++ b/tests/end_to_end/scenarios/global_configure.py @@ -0,0 +1,27 @@ +from scenarios import * + +scenario = ( + send_stanza("<iq type='set' id='id1' from='{jid_one}/{resource_one}' to='{biboumi_host}'><command xmlns='http://jabber.org/protocol/commands' node='configure' action='execute' /></iq>"), + expect_stanza("/iq[@type='result']/commands:command[@node='configure'][@sessionid][@status='executing']", + "/iq/commands:command/dataform:x[@type='form']/dataform:title[text()='Configure some global default settings.']", + "/iq/commands:command/dataform:x[@type='form']/dataform:instructions[text()='Edit the form, to configure your global settings for the component.']", + "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='text-single'][@var='max_history_length']/dataform:value[text()='20']", + "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='boolean'][@var='record_history']/dataform:value[text()='true']", + "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='boolean'][@var='persistent']/dataform:value[text()='false']", + "/iq/commands:command/commands:actions/commands:complete", + after = save_value("sessionid", extract_attribute("/iq[@type='result']/commands:command[@node='configure']", "sessionid"))), + send_stanza("<iq type='set' id='id2' from='{jid_one}/{resource_one}' to='{biboumi_host}'><command xmlns='http://jabber.org/protocol/commands' node='configure' sessionid='{sessionid}' action='complete'><x xmlns='jabber:x:data' type='submit'><field var='record_history'><value>0</value></field><field var='max_history_length'><value>42</value></field></x></command></iq>"), + expect_stanza("/iq[@type='result']/commands:command[@node='configure'][@status='completed']/commands:note[@type='info'][text()='Configuration successfully applied.']"), + + send_stanza("<iq type='set' id='id3' from='{jid_one}/{resource_one}' to='{biboumi_host}'><command xmlns='http://jabber.org/protocol/commands' node='configure' action='execute' /></iq>"), + expect_stanza("/iq[@type='result']/commands:command[@node='configure'][@sessionid][@status='executing']", + "/iq/commands:command/dataform:x[@type='form']/dataform:title[text()='Configure some global default settings.']", + "/iq/commands:command/dataform:x[@type='form']/dataform:instructions[text()='Edit the form, to configure your global settings for the component.']", + "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='text-single'][@var='max_history_length']/dataform:value[text()='42']", + "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='boolean'][@var='record_history']/dataform:value[text()='false']", + "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='boolean'][@var='persistent']/dataform:value[text()='false']", + "/iq/commands:command/commands:actions/commands:complete", + after = save_value("sessionid", extract_attribute("/iq[@type='result']/commands:command[@node='configure']", "sessionid"))), + send_stanza("<iq type='set' id='id4' from='{jid_one}/{resource_one}' to='{biboumi_host}'><command xmlns='http://jabber.org/protocol/commands' action='cancel' node='configure' sessionid='{sessionid}' /></iq>"), + expect_stanza("/iq[@type='result']/commands:command[@node='configure'][@status='canceled']"), +) diff --git a/tests/end_to_end/scenarios/global_configure_fixed.py b/tests/end_to_end/scenarios/global_configure_fixed.py new file mode 100644 index 0000000..8df70ad --- /dev/null +++ b/tests/end_to_end/scenarios/global_configure_fixed.py @@ -0,0 +1,32 @@ +from scenarios import * + +conf = 'fixed_server' + +scenario = ( + send_stanza("<iq type='set' id='id1' from='{jid_one}/{resource_one}' to='{biboumi_host}'><command xmlns='http://jabber.org/protocol/commands' node='global-configure' action='execute' /></iq>"), + expect_stanza("/iq[@type='result']/commands:command[@node='global-configure'][@sessionid][@status='executing']", + "/iq/commands:command/dataform:x[@type='form']/dataform:title[text()='Configure some global default settings.']", + "/iq/commands:command/dataform:x[@type='form']/dataform:instructions[text()='Edit the form, to configure your global settings for the component.']", + "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='text-single'][@var='max_history_length']/dataform:value[text()='20']", + "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='boolean'][@var='record_history']/dataform:value[text()='true']", + "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='boolean'][@var='persistent']/dataform:value[text()='false']", + "/iq/commands:command/commands:actions/commands:complete", + after = save_value("sessionid", extract_attribute("/iq[@type='result']/commands:command[@node='global-configure']", "sessionid"))), + send_stanza("<iq type='set' id='id2' from='{jid_one}/{resource_one}' to='{biboumi_host}'><command xmlns='http://jabber.org/protocol/commands' node='global-configure' sessionid='{sessionid}' action='complete'><x xmlns='jabber:x:data' type='submit'><field var='record_history'><value>0</value></field><field var='max_history_length'><value>42</value></field></x></command></iq>"), + expect_stanza("/iq[@type='result']/commands:command[@node='global-configure'][@status='completed']/commands:note[@type='info'][text()='Configuration successfully applied.']"), + + send_stanza("<iq type='set' id='id3' from='{jid_one}/{resource_one}' to='{biboumi_host}'><command xmlns='http://jabber.org/protocol/commands' node='global-configure' action='execute' /></iq>"), + expect_stanza("/iq[@type='result']/commands:command[@node='global-configure'][@sessionid][@status='executing']", + "/iq/commands:command/dataform:x[@type='form']/dataform:title[text()='Configure some global default settings.']", + "/iq/commands:command/dataform:x[@type='form']/dataform:instructions[text()='Edit the form, to configure your global settings for the component.']", + "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='text-single'][@var='max_history_length']/dataform:value[text()='42']", + "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='boolean'][@var='record_history']/dataform:value[text()='false']", + "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='boolean'][@var='persistent']/dataform:value[text()='false']", + "/iq/commands:command/commands:actions/commands:complete", + after = save_value("sessionid", extract_attribute("/iq[@type='result']/commands:command[@node='global-configure']", "sessionid"))), + send_stanza("<iq type='set' id='id4' from='{jid_one}/{resource_one}' to='{biboumi_host}'><command xmlns='http://jabber.org/protocol/commands' action='cancel' node='global-configure' sessionid='{sessionid}' /></iq>"), + expect_stanza("/iq[@type='result']/commands:command[@node='global-configure'][@status='canceled']"), + + send_stanza("<iq type='set' id='id1' from='{jid_one}/{resource_one}' to='{biboumi_host}'><command xmlns='http://jabber.org/protocol/commands' node='server-configure' action='execute' /></iq>"), + expect_stanza("/iq[@type='result']/commands:command[@node='server-configure'][@sessionid][@status='executing']"), +) diff --git a/tests/end_to_end/scenarios/global_configure_persistent_by_default.py b/tests/end_to_end/scenarios/global_configure_persistent_by_default.py new file mode 100644 index 0000000..db47e88 --- /dev/null +++ b/tests/end_to_end/scenarios/global_configure_persistent_by_default.py @@ -0,0 +1,15 @@ +from scenarios import * + +conf='persistent_by_default' + +scenario = ( + send_stanza("<iq type='set' id='id1' from='{jid_one}/{resource_one}' to='{biboumi_host}'><command xmlns='http://jabber.org/protocol/commands' node='configure' action='execute' /></iq>"), + expect_stanza("/iq[@type='result']/commands:command[@node='configure'][@sessionid][@status='executing']", + "/iq/commands:command/dataform:x[@type='form']/dataform:title[text()='Configure some global default settings.']", + "/iq/commands:command/dataform:x[@type='form']/dataform:instructions[text()='Edit the form, to configure your global settings for the component.']", + "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='text-single'][@var='max_history_length']/dataform:value[text()='20']", + "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='boolean'][@var='record_history']/dataform:value[text()='true']", + "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='boolean'][@var='persistent']/dataform:value[text()='true']", + "/iq/commands:command/commands:actions/commands:complete", + ), +) diff --git a/tests/end_to_end/scenarios/invite_other.py b/tests/end_to_end/scenarios/invite_other.py new file mode 100644 index 0000000..2badf77 --- /dev/null +++ b/tests/end_to_end/scenarios/invite_other.py @@ -0,0 +1,21 @@ +from scenarios import * + +scenario = ( + send_stanza("<presence from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}/{nick_one}' />"), + sequences.connection("irc.localhost", '{jid_one}/{resource_one}'), + expect_stanza("/message"), + expect_stanza("/presence"), + expect_stanza("/message"), + + send_stanza("<presence from='{jid_two}/{resource_two}' to='#bar%{irc_server_one}/{nick_two}' />"), + sequences.connection("irc.localhost", '{jid_two}/{resource_two}'), + expect_stanza("/message"), + expect_stanza("/presence"), + expect_stanza("/message"), + send_stanza("<message from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}'><x xmlns='http://jabber.org/protocol/muc#user'><invite to='{nick_two}'/></x></message>"), + expect_stanza("/message/body[text()='{nick_two} has been invited to #foo']"), + expect_stanza("/message[@to='{jid_two}/{resource_two}'][@from='#foo%{irc_server_one}']/muc_user:x/muc_user:invite[@from='#foo%{irc_server_one}/{nick_one}']"), + + send_stanza("<message from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}'><x xmlns='http://jabber.org/protocol/muc#user'><invite to='bertrand@example.com'/></x></message>"), + expect_stanza("/message[@to='bertrand@example.com'][@from='#foo%{irc_server_one}']/muc_user:x/muc_user:invite[@from='{jid_one}/{resource_one}']"), +) diff --git a/tests/end_to_end/scenarios/irc_channel_configure.py b/tests/end_to_end/scenarios/irc_channel_configure.py new file mode 100644 index 0000000..dcc78db --- /dev/null +++ b/tests/end_to_end/scenarios/irc_channel_configure.py @@ -0,0 +1,35 @@ +from scenarios import * + +scenario = ( + send_stanza("<iq type='set' id='id1' from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}'><command xmlns='http://jabber.org/protocol/commands' node='configure' action='execute'><dummy/></command></iq>"), + expect_stanza("/iq[@type='result']/commands:command[@node='configure'][@sessionid][@status='executing']", + "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='text-single'][@var='encoding_in']", + "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='text-single'][@var='encoding_out']", + "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='list-single'][@var='record_history']/dataform:value[text()='unset']", + "!/iq/commands:command/commands:dummy", + + after = save_value("sessionid", extract_attribute("/iq[@type='result']/commands:command[@node='configure']", "sessionid")) + ), + send_stanza("<iq type='set' id='id2' from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}'>" + "<command xmlns='http://jabber.org/protocol/commands' node='configure' sessionid='{sessionid}' action='complete'>" + "<x xmlns='jabber:x:data' type='submit'>" + "<field var='ports' />" + "<field var='encoding_out'><value>UTF-8</value></field>" + "<field var='encoding_in'><value>latin-1</value></field>" + "<field var='record_history'><value>true</value></field>" + "</x></command></iq>"), + expect_stanza("/iq[@type='result']/commands:command[@node='configure'][@status='completed']/commands:note[@type='info'][text()='Configuration successfully applied.']"), + + send_stanza("<iq type='set' id='id3' from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}'><command xmlns='http://jabber.org/protocol/commands' node='configure' action='execute' /></iq>"), + expect_stanza("/iq[@type='result']/commands:command[@node='configure'][@sessionid][@status='executing']", + "/iq/commands:command/dataform:x[@type='form']/dataform:title[text()='Configure the IRC channel #foo on server irc.localhost']", + "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='text-single'][@var='encoding_in']/dataform:value[text()='latin-1']", + "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='text-single'][@var='encoding_out']/dataform:value[text()='UTF-8']", + "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='list-single'][@var='record_history']/dataform:value[text()='true']", + "/iq/commands:command/commands:actions/commands:complete", + + after = save_value("sessionid", extract_attribute("/iq[@type='result']/commands:command[@node='configure']", "sessionid")) + ), + send_stanza("<iq type='set' id='id4' from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}'><command xmlns='http://jabber.org/protocol/commands' action='cancel' node='configure' sessionid='{sessionid}' /></iq>"), + expect_stanza("/iq[@type='result']/commands:command[@node='configure'][@status='canceled']"), +) diff --git a/tests/end_to_end/scenarios/irc_channel_configure_fixed.py b/tests/end_to_end/scenarios/irc_channel_configure_fixed.py new file mode 100644 index 0000000..4f18c83 --- /dev/null +++ b/tests/end_to_end/scenarios/irc_channel_configure_fixed.py @@ -0,0 +1,33 @@ +from scenarios import * + +conf = 'fixed_server' + +scenario = ( + send_stanza("<iq type='set' id='id1' from='{jid_one}/{resource_one}' to='#foo@{biboumi_host}'><command xmlns='http://jabber.org/protocol/commands' node='configure' action='execute' /></iq>"), + expect_stanza("/iq[@type='result']/commands:command[@node='configure'][@sessionid][@status='executing']", + "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='text-single'][@var='encoding_in']", + "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='text-single'][@var='encoding_out']", + + after = save_value("sessionid", extract_attribute("/iq[@type='result']/commands:command[@node='configure']", "sessionid")) + ), + send_stanza("<iq type='set' id='id2' from='{jid_one}/{resource_one}' to='#foo@{biboumi_host}'>" + "<command xmlns='http://jabber.org/protocol/commands' node='configure' sessionid='{sessionid}' action='complete'>" + "<x xmlns='jabber:x:data' type='submit'>" + "<field var='ports' />" + "<field var='encoding_out'><value>UTF-8</value></field>" + "<field var='encoding_in'><value>latin-1</value></field>" + "</x></command></iq>"), + expect_stanza("/iq[@type='result']/commands:command[@node='configure'][@status='completed']/commands:note[@type='info'][text()='Configuration successfully applied.']"), + + send_stanza("<iq type='set' id='id3' from='{jid_one}/{resource_one}' to='#foo@{biboumi_host}'><command xmlns='http://jabber.org/protocol/commands' node='configure' action='execute' /></iq>"), + expect_stanza("/iq[@type='result']/commands:command[@node='configure'][@sessionid][@status='executing']", + "/iq/commands:command/dataform:x[@type='form']/dataform:title[text()='Configure the IRC channel #foo on server irc.localhost']", + "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='text-single'][@var='encoding_in']/dataform:value[text()='latin-1']", + "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='text-single'][@var='encoding_out']/dataform:value[text()='UTF-8']", + "/iq/commands:command/commands:actions/commands:complete", + + after = save_value("sessionid", extract_attribute("/iq[@type='result']/commands:command[@node='configure']", "sessionid")) + ), + send_stanza("<iq type='set' id='id4' from='{jid_one}/{resource_one}' to='#foo@{biboumi_host}'><command xmlns='http://jabber.org/protocol/commands' action='cancel' node='configure' sessionid='{sessionid}' /></iq>"), + expect_stanza("/iq[@type='result']/commands:command[@node='configure'][@status='canceled']"), +) diff --git a/tests/end_to_end/scenarios/irc_channel_configure_xep0045.py b/tests/end_to_end/scenarios/irc_channel_configure_xep0045.py new file mode 100644 index 0000000..c19990d --- /dev/null +++ b/tests/end_to_end/scenarios/irc_channel_configure_xep0045.py @@ -0,0 +1,20 @@ +from scenarios import * + +scenario = ( + send_stanza("<iq type='get' id='id1' from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}'><query xmlns='http://jabber.org/protocol/muc#owner'/></iq>"), + expect_stanza("/iq[@type='result']/muc_owner:query", + "/iq/muc_owner:query/dataform:x[@type='form']/dataform:field[@type='text-single'][@var='encoding_in']", + "/iq/muc_owner:query/dataform:x[@type='form']/dataform:field[@type='text-single'][@var='encoding_out']", + + ), + send_stanza("<iq type='set' id='id2' from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}'>" + "<query xmlns='http://jabber.org/protocol/muc#owner'>" + "<x xmlns='jabber:x:data' type='submit'>" + "<field var='ports' />" + "<field var='encoding_out'><value>UTF-8</value></field>" + "<field var='encoding_in'><value>latin-1</value></field>" + "</x></query></iq>"), + expect_stanza("/iq[@type='result']"), + send_stanza("<iq type='set' id='id3' from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}'><query xmlns='http://jabber.org/protocol/muc#owner'> <x xmlns='jabber:x:data' type='cancel'/></query></iq>"), + expect_stanza("/iq[@type='result']"), +) diff --git a/tests/end_to_end/scenarios/irc_server_configure.py b/tests/end_to_end/scenarios/irc_server_configure.py new file mode 100644 index 0000000..1470e6e --- /dev/null +++ b/tests/end_to_end/scenarios/irc_server_configure.py @@ -0,0 +1,105 @@ +from scenarios import * + +scenario = ( + send_stanza("<iq type='set' id='id1' from='{jid_one}/{resource_one}' to='{irc_server_one}'><command xmlns='http://jabber.org/protocol/commands' node='configure' action='execute' /></iq>"), + expect_stanza("/iq[@type='result']/commands:command[@node='configure'][@sessionid][@status='executing']", + "/iq/commands:command/dataform:x[@type='form']/dataform:title[text()='Configure the IRC server irc.localhost']", + "/iq/commands:command/dataform:x[@type='form']/dataform:instructions[text()='Edit the form, to configure the settings of the IRC server irc.localhost']", + "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='text-multi'][@var='ports']/dataform:value[text()='6667']", + "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='text-multi'][@var='tls_ports']/dataform:value[text()='6670']", + "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='text-multi'][@var='tls_ports']/dataform:value[text()='6697']", + "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='boolean'][@var='verify_cert']/dataform:value[text()='true']", + "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='text-single'][@var='fingerprint']", + "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='text-single'][@var='throttle_limit']", + "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='text-private'][@var='pass']", + "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='text-multi'][@var='after_connect_commands']", + "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='text-single'][@var='max_history_length']", + "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='text-single'][@var='nick']", + "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='text-single'][@var='username']", + "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='text-single'][@var='realname']", + "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='text-single'][@var='encoding_in']", + "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='text-single'][@var='encoding_out']", + "/iq/commands:command/commands:actions/commands:complete", + after = save_value("sessionid", extract_attribute("/iq[@type='result']/commands:command[@node='configure']", "sessionid")) + ), + send_stanza("<iq type='set' id='id2' from='{jid_one}/{resource_one}' to='{irc_server_one}'>" + "<command xmlns='http://jabber.org/protocol/commands' node='configure' sessionid='{sessionid}' action='complete'>" + "<x xmlns='jabber:x:data' type='submit'>" + "<field var='ports' />" + "<field var='tls_ports'><value>6697</value><value>6698</value></field>" + "<field var='verify_cert'><value>1</value></field>" + "<field var='fingerprint'><value>12:12:12</value></field>" + "<field var='pass'><value>coucou</value></field>" + "<field var='after_connect_commands'><value>first command</value><value>second command</value></field>" + "<field var='nick'><value>my_nickname</value></field>" + "<field var='username'><value>username</value></field>" + "<field var='throttle_limit'><value>42</value></field>" + "<field var='max_history_length'><value>69</value></field>" + "<field var='realname'><value>realname</value></field>" + "<field var='encoding_out'><value>UTF-8</value></field>" + "<field var='encoding_in'><value>latin-1</value></field>" + "</x></command></iq>"), + expect_stanza("/iq[@type='result']/commands:command[@node='configure'][@status='completed']/commands:note[@type='info'][text()='Configuration successfully applied.']"), + + send_stanza("<iq type='set' id='id3' from='{jid_one}/{resource_one}' to='{irc_server_one}'><command xmlns='http://jabber.org/protocol/commands' node='configure' action='execute' /></iq>"), + expect_stanza("/iq[@type='result']/commands:command[@node='configure'][@sessionid][@status='executing']", + "/iq/commands:command/dataform:x[@type='form']/dataform:title[text()='Configure the IRC server irc.localhost']", + "/iq/commands:command/dataform:x[@type='form']/dataform:instructions[text()='Edit the form, to configure the settings of the IRC server irc.localhost']", + "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='text-multi']", + "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='text-multi'][@var='tls_ports']/dataform:value[text()='6697']", + "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='text-multi'][@var='tls_ports']/dataform:value[text()='6698']", + "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='boolean'][@var='verify_cert']/dataform:value[text()='true']", + "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='text-single'][@var='fingerprint']/dataform:value[text()='12:12:12']", + "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='text-private'][@var='pass']/dataform:value[text()='coucou']", + "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='text-single'][@var='nick']/dataform:value[text()='my_nickname']", + "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='text-multi'][@var='after_connect_commands']/dataform:value[text()='first command']", + "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='text-multi'][@var='after_connect_commands']/dataform:value[text()='second command']", + "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='text-single'][@var='username']/dataform:value[text()='username']", + "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='text-single'][@var='realname']/dataform:value[text()='realname']", + "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='text-single'][@var='throttle_limit']/dataform:value[text()='42']", + "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='text-single'][@var='max_history_length']/dataform:value[text()='69']", + "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='text-single'][@var='encoding_in']/dataform:value[text()='latin-1']", + "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='text-single'][@var='encoding_out']/dataform:value[text()='UTF-8']", + "/iq/commands:command/commands:actions/commands:complete", + after = save_value("sessionid", extract_attribute("/iq[@type='result']/commands:command[@node='configure']", "sessionid")) + ), + send_stanza("<iq type='set' id='id4' from='{jid_one}/{resource_one}' to='{irc_server_one}'><command xmlns='http://jabber.org/protocol/commands' action='cancel' node='configure' sessionid='{sessionid}' /></iq>"), + expect_stanza("/iq[@type='result']/commands:command[@node='configure'][@status='canceled']"), + + # Same thing, but try to empty some values + send_stanza("<iq type='set' id='id1' from='{jid_one}/{resource_one}' to='{irc_server_one}'><command xmlns='http://jabber.org/protocol/commands' node='configure' action='execute' /></iq>"), + expect_stanza("/iq[@type='result']", + after = save_value("sessionid", extract_attribute("/iq[@type='result']/commands:command[@node='configure']", "sessionid")) + ), + send_stanza("<iq type='set' id='id2' from='{jid_one}/{resource_one}' to='{irc_server_one}'>" + "<command xmlns='http://jabber.org/protocol/commands' node='configure' sessionid='{sessionid}' action='complete'>" + "<x xmlns='jabber:x:data' type='submit'>" + "<field var='pass'><value></value></field>" + "<field var='after_connect_commands'></field>" + "<field var='username'><value></value></field>" + "<field var='realname'><value></value></field>" + "<field var='throttle_limit'><value></value></field>" + "<field var='encoding_out'><value></value></field>" + "<field var='encoding_in'><value></value></field>" + "</x></command></iq>"), + expect_stanza("/iq[@type='result']/commands:command[@node='configure'][@status='completed']/commands:note[@type='info'][text()='Configuration successfully applied.']"), + + send_stanza("<iq type='set' id='id3' from='{jid_one}/{resource_one}' to='{irc_server_one}'><command xmlns='http://jabber.org/protocol/commands' node='configure' action='execute' /></iq>"), + expect_stanza("/iq[@type='result']/commands:command[@node='configure'][@sessionid][@status='executing']", + "/iq/commands:command/dataform:x[@type='form']/dataform:title[text()='Configure the IRC server irc.localhost']", + "/iq/commands:command/dataform:x[@type='form']/dataform:instructions[text()='Edit the form, to configure the settings of the IRC server irc.localhost']", + "!/iq/commands:command/dataform:x/dataform:field[@var='tls_ports']/dataform:value", + "!/iq/commands:command/dataform:x[@type='form']/dataform:field[@var='pass']/dataform:value", + "!/iq/commands:command/dataform:x[@type='form']/dataform:field[@var='after_connect_commands']/dataform:value", + "!/iq/commands:command/dataform:x[@type='form']/dataform:field[@var='username']/dataform:value", + "!/iq/commands:command/dataform:x[@type='form']/dataform:field[@var='realname']/dataform:value", + "!/iq/commands:command/dataform:x[@type='form']/dataform:field[@var='encoding_in']/dataform:value", + "!/iq/commands:command/dataform:x[@type='form']/dataform:field[@var='encoding_out']/dataform:value", + "/iq/commands:command/commands:actions/commands:complete", + "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='text-single'][@var='throttle_limit']/dataform:value[text()='10']", # An invalid value sets this field to its default value + after = save_value("sessionid", extract_attribute("/iq[@type='result']/commands:command[@node='configure']", "sessionid")) + ), + send_stanza("<iq type='set' id='id4' from='{jid_one}/{resource_one}' to='{irc_server_one}'><command xmlns='http://jabber.org/protocol/commands' action='cancel' node='configure' sessionid='{sessionid}' /></iq>"), + expect_stanza("/iq[@type='result']/commands:command[@node='configure'][@status='canceled']"), + + ) diff --git a/tests/end_to_end/scenarios/irc_server_connection.py b/tests/end_to_end/scenarios/irc_server_connection.py new file mode 100644 index 0000000..25ea749 --- /dev/null +++ b/tests/end_to_end/scenarios/irc_server_connection.py @@ -0,0 +1,7 @@ +from scenarios import * + +scenario = ( + send_stanza("<presence from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}/{nick_one}' />"), + sequences.connection(), + ) + diff --git a/tests/end_to_end/scenarios/irc_server_connection_failure.py b/tests/end_to_end/scenarios/irc_server_connection_failure.py new file mode 100644 index 0000000..2abe39f --- /dev/null +++ b/tests/end_to_end/scenarios/irc_server_connection_failure.py @@ -0,0 +1,11 @@ +from scenarios import * + +scenario = ( + send_stanza("<presence from='{jid_one}/{resource_one}' to='#foo%doesnotexist@{biboumi_host}/{nick_one}' />"), + expect_stanza("/message/body[text()='Connecting to doesnotexist:6697 (encrypted)']"), + expect_stanza("/message/body[re:test(text(), 'Connection failed: (Domain name not found|Name or service not known)')]"), + expect_stanza("/presence[@from='#foo%doesnotexist@{biboumi_host}/{nick_one}']/muc:x", + "/presence/error[@type='cancel']/stanza:item-not-found", + "/presence/error[@type='cancel']/stanza:text[re:test(text(), '(Domain name not found|Name or service not known)')]", + ), +) diff --git a/tests/end_to_end/scenarios/irc_server_presence_in_roster.py b/tests/end_to_end/scenarios/irc_server_presence_in_roster.py new file mode 100644 index 0000000..bc2016d --- /dev/null +++ b/tests/end_to_end/scenarios/irc_server_presence_in_roster.py @@ -0,0 +1,25 @@ +from scenarios import * + +scenario = ( + # Mutual subscription exchange + send_stanza("<presence from='{jid_one}' to='{irc_server_one}' type='subscribe' id='subid1' />"), + expect_stanza("/presence[@type='subscribed'][@id='subid1']"), + + expect_stanza("/presence[@type='subscribe']"), + send_stanza("<presence from='{jid_one}' to='{irc_server_one}' type='subscribed' />"), + + # Join a channel on that server + send_stanza("<presence from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}/{nick_one}' />"), + + # We must receive the IRC server presence, in the connection sequence + sequences.connection("irc.localhost", '{jid_one}/{resource_one}', expected_irc_presence=True), + expect_stanza("/message/body[text()='Mode #foo [+nt] by {irc_host_one}']"), + 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']"), + expect_stanza("/message[@from='#foo%{irc_server_one}'][@type='groupchat']/subject[not(text())]"), + + # Leave the channel, and thus the IRC server + send_stanza("<presence type='unavailable' from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}/{nick_one}' />"), + expect_stanza("/presence[@type='unavailable'][@from='#foo%{irc_server_one}/{nick_one}']"), + expect_stanza("/presence[@from='{irc_server_one}'][@to='{jid_one}'][@type='unavailable']"), +) diff --git a/tests/end_to_end/scenarios/irc_server_presence_subscription.py b/tests/end_to_end/scenarios/irc_server_presence_subscription.py new file mode 100644 index 0000000..e9ad1a5 --- /dev/null +++ b/tests/end_to_end/scenarios/irc_server_presence_subscription.py @@ -0,0 +1,6 @@ +from scenarios import * + +scenario = ( + send_stanza("<presence type='subscribe' from='{jid_one}/{resource_one}' to='{irc_server_one}' id='sub1' />"), + expect_stanza("/presence[@to='{jid_one}'][@from='{irc_server_one}'][@type='subscribed']"), +) diff --git a/tests/end_to_end/scenarios/irc_tls_connection.py b/tests/end_to_end/scenarios/irc_tls_connection.py new file mode 100644 index 0000000..8b30893 --- /dev/null +++ b/tests/end_to_end/scenarios/irc_tls_connection.py @@ -0,0 +1,25 @@ +from scenarios import * + +scenario = ( + # First, use an adhoc command to configure how we connect to the irc server, configure + # only one TLS port, and disable the cert verification. + send_stanza("<iq type='set' id='id1' from='{jid_one}/{resource_one}' to='{irc_server_one}'><command xmlns='http://jabber.org/protocol/commands' node='configure' action='execute' /></iq>"), + expect_stanza("/iq[@type='result']", + after = save_value("sessionid", extract_attribute("/iq[@type='result']/commands:command[@node='configure']", "sessionid"))), + send_stanza("<iq type='set' id='id2' from='{jid_one}/{resource_one}' to='{irc_server_one}'>" + "<command xmlns='http://jabber.org/protocol/commands' node='configure' sessionid='{sessionid}' action='next'>" + "<x xmlns='jabber:x:data' type='submit'>" + "<field var='ports' />" + "<field var='tls_ports'><value>7778</value></field>" + "<field var='verify_cert'><value>0</value></field>" + "<field var='nick'><value>my_special_nickname</value></field>" + "</x></command></iq>"), + expect_stanza("/iq[@type='result']/commands:command[@node='configure'][@status='completed']/commands:note[@type='info'][text()='Configuration successfully applied.']"), + + send_stanza("<presence from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}/{nick_one}' />"), + sequences.connection_tls("irc.localhost", '{jid_one}/{resource_one}'), + expect_stanza("/message/body[text()='Mode #foo [+nt] by {irc_host_one}']"), + expect_stanza("/presence[@to='{jid_one}/{resource_one}'][@from='#foo%{irc_server_one}/my_special_nickname']/muc_user:x/muc_user:item[@affiliation='admin'][@role='moderator']", + "/presence/muc_user:x/muc_user:status[@code='110']"), + expect_stanza("/message[@from='#foo%{irc_server_one}'][@type='groupchat']/subject[not(text())]"), +) diff --git a/tests/end_to_end/scenarios/join_history_limit.py b/tests/end_to_end/scenarios/join_history_limit.py new file mode 100644 index 0000000..2432f14 --- /dev/null +++ b/tests/end_to_end/scenarios/join_history_limit.py @@ -0,0 +1,108 @@ +from scenarios import * + +scenario = ( + # Disable the throttling because the test is based on timings + send_stanza("<iq type='set' id='id1' from='{jid_one}/{resource_one}' to='{irc_server_one}'><command xmlns='http://jabber.org/protocol/commands' node='configure' action='execute' /></iq>"), + expect_stanza("/iq[@type='result']", + after = save_value("sessionid", extract_attribute("/iq[@type='result']/commands:command[@node='configure']", "sessionid"))), + send_stanza("<iq type='set' id='id2' from='{jid_one}/{resource_one}' to='{irc_server_one}'>" + "<command xmlns='http://jabber.org/protocol/commands' node='configure' sessionid='{sessionid}' action='next'>" + "<x xmlns='jabber:x:data' type='submit'>" + "<field var='ports'><value>6667</value></field>" + "<field var='tls_ports'><value>6697</value><value>6670</value></field>" + "<field var='throttle_limit'><value>9999</value></field>" + "</x></command></iq>"), + expect_stanza("/iq[@type='result']/commands:command[@node='configure'][@status='completed']/commands:note[@type='info'][text()='Configuration successfully applied.']"), + + + send_stanza("<presence from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}/{nick_one}' />"), + sequences.connection("irc.localhost", '{jid_one}/{resource_one}'), + expect_stanza("/message/body[text()='Mode #foo [+nt] by {irc_host_one}']"), + 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']"), + expect_stanza("/message[@from='#foo%{irc_server_one}'][@type='groupchat']/subject[not(text())]"), + + # Send two channel messages + send_stanza("<message from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}' type='groupchat'><body>coucou</body></message>"), + expect_stanza("/message[@from='#foo%{irc_server_one}/{nick_one}'][@to='{jid_one}/{resource_one}'][@type='groupchat']/body[text()='coucou']", + "/message/stable_id:stanza-id[@by='#foo%{irc_server_one}'][@id]"), + + send_stanza("<message from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}' type='groupchat'><body>coucou 2</body></message>"), + # Record the current time + expect_stanza("/message[@from='#foo%{irc_server_one}/{nick_one}'][@to='{jid_one}/{resource_one}'][@type='groupchat']/body[text()='coucou 2']", + after = save_current_timestamp_plus_delta("first_timestamp", datetime.timedelta(seconds=1))), + + # Wait two seconds before sending two new messages + sleep_for(2), + send_stanza("<message from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}' type='groupchat'><body>coucou 3</body></message>"), + send_stanza("<message from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}' type='groupchat'><body>coucou 4</body></message>"), + expect_stanza("/message[@type='groupchat']/body[text()='coucou 3']"), + expect_stanza("/message[@type='groupchat']/body[text()='coucou 4']", + after = save_current_timestamp_plus_delta("second_timestamp", datetime.timedelta(seconds=1))), + + # join some other channel, to stay connected to the server even after leaving #foo + send_stanza("<presence from='{jid_one}/{resource_one}' to='#DUMMY%{irc_server_one}/{nick_one}' />"), + expect_stanza("/message"), + expect_stanza("/presence/muc_user:x/muc_user:status[@code='110']"), + expect_stanza("/message/subject"), + + # Leave #foo + send_stanza("<presence from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}' type='unavailable' />"), + expect_stanza("/presence[@type='unavailable']"), + + sleep_for(0.2), + + # Rejoin #foo, with some history limit + send_stanza("<presence from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}/{nick_one}'><x xmlns='http://jabber.org/protocol/muc'><history maxchars='0'/></x></presence>"), + expect_stanza("/message"), + expect_stanza("/presence/muc_user:x/muc_user:status[@code='110']"), + expect_stanza("/message/subject"), + + send_stanza("<presence from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}' type='unavailable' />"), + expect_stanza("/presence[@type='unavailable']"), + + sleep_for(0.2), + + # Rejoin #foo, with some history limit + send_stanza("<presence from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}/{nick_one}'><x xmlns='http://jabber.org/protocol/muc'><history maxstanzas='3'/></x></presence>"), + expect_stanza("/message"), + expect_stanza("/presence/muc_user:x/muc_user:status[@code='110']"), + expect_stanza("/message[@from='#foo%{irc_server_one}/{nick_one}'][@type='groupchat']/body[text()='coucou 2']"), + expect_stanza("/message[@from='#foo%{irc_server_one}/{nick_one}'][@type='groupchat']/body[text()='coucou 3']"), + expect_stanza("/message[@from='#foo%{irc_server_one}/{nick_one}'][@type='groupchat']/body[text()='coucou 4']"), + expect_stanza("/message/subject"), + + send_stanza("<presence from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}' type='unavailable' />"), + expect_stanza("/presence[@type='unavailable']"), + + # Rejoin #foo, with some history limit + send_stanza("<presence from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}/{nick_one}'><x xmlns='http://jabber.org/protocol/muc'><history since='{first_timestamp}'/></x></presence>"), + expect_stanza("/message"), + expect_stanza("/presence/muc_user:x/muc_user:status[@code='110']"), + expect_stanza("/message[@from='#foo%{irc_server_one}/{nick_one}'][@type='groupchat']/body[text()='coucou 3']"), + expect_stanza("/message[@from='#foo%{irc_server_one}/{nick_one}'][@type='groupchat']/body[text()='coucou 4']"), + expect_stanza("/message/subject"), + send_stanza("<presence from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}' type='unavailable' />"), + expect_stanza("/presence[@type='unavailable']"), + + # Rejoin #foo, with some history limit + send_stanza("<presence from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}/{nick_one}'><x xmlns='http://jabber.org/protocol/muc'><history seconds='1'/></x></presence>"), + expect_stanza("/message"), + expect_stanza("/presence/muc_user:x/muc_user:status[@code='110']"), + expect_stanza("/message[@from='#foo%{irc_server_one}/{nick_one}'][@type='groupchat']/body[text()='coucou 3']"), + expect_stanza("/message[@from='#foo%{irc_server_one}/{nick_one}'][@type='groupchat']/body[text()='coucou 4']"), + expect_stanza("/message/subject"), + send_stanza("<presence from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}' type='unavailable' />"), + expect_stanza("/presence[@type='unavailable']"), + + # Rejoin #foo, with some history limit + send_stanza("<presence from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}/{nick_one}'><x xmlns='http://jabber.org/protocol/muc'><history seconds='5'/></x></presence>"), + expect_stanza("/message"), + expect_stanza("/presence/muc_user:x/muc_user:status[@code='110']"), + expect_stanza("/message[@from='#foo%{irc_server_one}/{nick_one}'][@type='groupchat']/body[text()='coucou']"), expect_stanza("/message[@from='#foo%{irc_server_one}/{nick_one}'][@type='groupchat']/body[text()='coucou 2']"), + expect_stanza("/message[@from='#foo%{irc_server_one}/{nick_one}'][@type='groupchat']/body[text()='coucou 3']"), + expect_stanza("/message[@from='#foo%{irc_server_one}/{nick_one}'][@type='groupchat']/body[text()='coucou 4']"), + expect_stanza("/message/subject"), + send_stanza("<presence from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}' type='unavailable' />"), + expect_stanza("/presence[@type='unavailable']"), +) diff --git a/tests/end_to_end/scenarios/leave_unjoined_chan.py b/tests/end_to_end/scenarios/leave_unjoined_chan.py new file mode 100644 index 0000000..a9751d7 --- /dev/null +++ b/tests/end_to_end/scenarios/leave_unjoined_chan.py @@ -0,0 +1,16 @@ +from scenarios import * + +scenario = ( + send_stanza("<presence from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}/{nick_one}' />"), + sequences.connection("irc.localhost", '{jid_one}/{resource_one}'), + expect_stanza("/message"), + expect_stanza("/presence"), + expect_stanza("/message"), + + send_stanza("<presence from='{jid_two}/{resource_two}' to='#foo%{irc_server_one}/{nick_one}' />"), + sequences.connection_begin("irc.localhost", '{jid_two}/{resource_two}'), + + expect_stanza("/message[@to='{jid_two}/{resource_two}'][@type='chat']/body[text()='irc.localhost: {nick_one}: Nickname is already in use.']"), + expect_stanza("/presence[@type='error']/error[@type='cancel'][@code='409']/stanza:conflict"), + send_stanza("<presence from='{jid_two}/{resource_two}' to='#foo%{irc_server_one}/{nick_one}' type='unavailable' />") +) diff --git a/tests/end_to_end/scenarios/list_adhoc.py b/tests/end_to_end/scenarios/list_adhoc.py new file mode 100644 index 0000000..7b46312 --- /dev/null +++ b/tests/end_to_end/scenarios/list_adhoc.py @@ -0,0 +1,10 @@ +from scenarios import * + +scenario = ( + send_stanza("<iq type='get' id='idwhatever' from='{jid_one}/{resource_one}' to='{biboumi_host}'><query xmlns='http://jabber.org/protocol/disco#items' node='http://jabber.org/protocol/commands' /></iq>"), + expect_stanza("/iq[@type='result']/disco_items:query[@node='http://jabber.org/protocol/commands']", + "/iq/disco_items:query/disco_items:item[@node='configure']", + "/iq/disco_items:query/disco_items:item[4]", + "!/iq/disco_items:query/disco_items:item[5]"), + +) diff --git a/tests/end_to_end/scenarios/list_adhoc_fixed_server.py b/tests/end_to_end/scenarios/list_adhoc_fixed_server.py new file mode 100644 index 0000000..fef378b --- /dev/null +++ b/tests/end_to_end/scenarios/list_adhoc_fixed_server.py @@ -0,0 +1,12 @@ +from scenarios import * + +conf = "fixed_server" + +scenario = ( + send_stanza("<iq type='get' id='idwhatever' from='{jid_one}/{resource_one}' to='{biboumi_host}'><query xmlns='http://jabber.org/protocol/disco#items' node='http://jabber.org/protocol/commands' /></iq>"), + expect_stanza("/iq[@type='result']/disco_items:query[@node='http://jabber.org/protocol/commands']", + "/iq/disco_items:query/disco_items:item[@node='global-configure']", + "/iq/disco_items:query/disco_items:item[@node='server-configure']", + "/iq/disco_items:query/disco_items:item[6]", + "!/iq/disco_items:query/disco_items:item[7]"), +) diff --git a/tests/end_to_end/scenarios/list_adhoc_irc.py b/tests/end_to_end/scenarios/list_adhoc_irc.py new file mode 100644 index 0000000..ff94a1b --- /dev/null +++ b/tests/end_to_end/scenarios/list_adhoc_irc.py @@ -0,0 +1,8 @@ +from scenarios import * + +scenario = ( + send_stanza("<iq type='get' id='idwhatever' from='{jid_one}/{resource_one}' to='{irc_host_one}@{biboumi_host}'><query xmlns='http://jabber.org/protocol/disco#items' node='http://jabber.org/protocol/commands' /></iq>"), + expect_stanza("/iq[@type='result']/disco_items:query[@node='http://jabber.org/protocol/commands']", + "/iq/disco_items:query/disco_items:item[2]", + "!/iq/disco_items:query/disco_items:item[3]"), +) diff --git a/tests/end_to_end/scenarios/list_admin_adhoc.py b/tests/end_to_end/scenarios/list_admin_adhoc.py new file mode 100644 index 0000000..0b71662 --- /dev/null +++ b/tests/end_to_end/scenarios/list_admin_adhoc.py @@ -0,0 +1,9 @@ +from scenarios import * + +scenario = ( + send_stanza("<iq type='get' id='idwhatever' from='{jid_admin}/{resource_one}' to='{biboumi_host}'><query xmlns='http://jabber.org/protocol/disco#items' node='http://jabber.org/protocol/commands' /></iq>"), + expect_stanza("/iq[@type='result']/disco_items:query[@node='http://jabber.org/protocol/commands']", + "/iq/disco_items:query/disco_items:item[@node='configure']", + "/iq/disco_items:query/disco_items:item[6]", + "!/iq/disco_items:query/disco_items:item[7]"), +) diff --git a/tests/end_to_end/scenarios/list_admin_adhoc_fixed_server.py b/tests/end_to_end/scenarios/list_admin_adhoc_fixed_server.py new file mode 100644 index 0000000..8e2775e --- /dev/null +++ b/tests/end_to_end/scenarios/list_admin_adhoc_fixed_server.py @@ -0,0 +1,12 @@ +from scenarios import * + +conf = "fixed_server" + +scenario = ( + send_stanza("<iq type='get' id='idwhatever' from='{jid_admin}/{resource_one}' to='{biboumi_host}'><query xmlns='http://jabber.org/protocol/disco#items' node='http://jabber.org/protocol/commands' /></iq>"), + expect_stanza("/iq[@type='result']/disco_items:query[@node='http://jabber.org/protocol/commands']", + "/iq/disco_items:query/disco_items:item[@node='global-configure']", + "/iq/disco_items:query/disco_items:item[@node='server-configure']", + "/iq/disco_items:query/disco_items:item[8]", + "!/iq/disco_items:query/disco_items:item[9]"), +) diff --git a/tests/end_to_end/scenarios/list_muc_user_adhoc.py b/tests/end_to_end/scenarios/list_muc_user_adhoc.py new file mode 100644 index 0000000..6827a8d --- /dev/null +++ b/tests/end_to_end/scenarios/list_muc_user_adhoc.py @@ -0,0 +1,6 @@ +from scenarios import * + +scenario = ( + send_stanza("<iq type='get' id='idwhatever' from='{jid_admin}/{resource_one}' to='#foo%{irc_server_one}/{nick_one}'><query xmlns='http://jabber.org/protocol/disco#items' node='http://jabber.org/protocol/commands' /></iq>"), + expect_stanza("/iq[@type='error']/error[@type='cancel']/stanza:feature-not-implemented"), +) diff --git a/tests/end_to_end/scenarios/mam_on_fixed_server.py b/tests/end_to_end/scenarios/mam_on_fixed_server.py new file mode 100644 index 0000000..200f04e --- /dev/null +++ b/tests/end_to_end/scenarios/mam_on_fixed_server.py @@ -0,0 +1,21 @@ +from scenarios import * + +conf = 'fixed_server' + +scenario = ( + scenarios.channel_join_on_fixed_irc_server.scenario, + + send_stanza("<message from='{jid_one}/{resource_one}' to='#foo@{biboumi_host}' type='groupchat'><body>coucou</body></message>"), + expect_stanza("/message[@from='#foo@{biboumi_host}/{nick_one}'][@to='{jid_one}/{resource_one}'][@type='groupchat']/body[text()='coucou']"), + + send_stanza("<message from='{jid_one}/{resource_one}' to='#foo@{biboumi_host}' type='groupchat'><body>coucou 2</body></message>"), + expect_stanza("/message[@from='#foo@{biboumi_host}/{nick_one}'][@to='{jid_one}/{resource_one}'][@type='groupchat']/body[text()='coucou 2']"), + + # Retrieve the complete archive + send_stanza("<iq to='#foo@{biboumi_host}' from='{jid_one}/{resource_one}' type='set' id='id1'><query xmlns='urn:xmpp:mam:2' queryid='qid1' /></iq>"), + + expect_stanza("/message/mam:result[@queryid='qid1']/forward:forwarded/delay:delay", + "/message/mam:result[@queryid='qid1']/forward:forwarded/client:message[@from='#foo@{biboumi_host}/{nick_one}'][@type='groupchat']/client:body[text()='coucou']"), + expect_stanza("/message/mam:result[@queryid='qid1']/forward:forwarded/delay:delay", + "/message/mam:result[@queryid='qid1']/forward:forwarded/client:message[@from='#foo@{biboumi_host}/{nick_one}'][@type='groupchat']/client:body[text()='coucou 2']"), +) diff --git a/tests/end_to_end/scenarios/mam_with_timestamps.py b/tests/end_to_end/scenarios/mam_with_timestamps.py new file mode 100644 index 0000000..0ed0333 --- /dev/null +++ b/tests/end_to_end/scenarios/mam_with_timestamps.py @@ -0,0 +1,63 @@ +from scenarios import * + +scenario = ( + scenarios.simple_channel_join.scenario, + + # Send two channel messages + send_stanza("<message from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}' type='groupchat'><body>coucou</body></message>"), + expect_stanza("/message[@from='#foo%{irc_server_one}/{nick_one}'][@to='{jid_one}/{resource_one}'][@type='groupchat']/body[text()='coucou']", + "/message/stable_id:stanza-id[@by='#foo%{irc_server_one}'][@id]"), + + send_stanza("<message from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}' type='groupchat'><body>coucou 2</body></message>"), + # Record the current time + expect_stanza("/message[@from='#foo%{irc_server_one}/{nick_one}'][@to='{jid_one}/{resource_one}'][@type='groupchat']/body[text()='coucou 2']", + after = save_current_timestamp_plus_delta("first_timestamp", datetime.timedelta(seconds=1))), + + # Wait two seconds before sending two new messages + sleep_for(2), + send_stanza("<message from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}' type='groupchat'><body>coucou 3</body></message>"), + send_stanza("<message from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}' type='groupchat'><body>coucou 4</body></message>"), + expect_stanza("/message[@type='groupchat']/body[text()='coucou 3']"), + expect_stanza("/message[@type='groupchat']/body[text()='coucou 4']", + after = save_current_timestamp_plus_delta("second_timestamp", datetime.timedelta(seconds=1))), + + # Retrieve the archive, after our saved datetime + send_stanza("""<iq to='#foo%{irc_server_one}' from='{jid_one}/{resource_one}' type='set' id='id8'> + <query xmlns='urn:xmpp:mam:2' queryid='qid16'> + <x type='submit' xmlns='jabber:x:data'> + <field var='FORM_TYPE' xmlns='jabber:x:data'><value xmlns='jabber:x:data'>urn:xmpp:mam:2</value></field> + <field var='start' xmlns='jabber:x:data'><value xmlns='jabber:x:data'>{first_timestamp}</value></field> + <field var='end' xmlns='jabber:x:data'><value xmlns='jabber:x:data'>{second_timestamp}</value></field> + </x> + </query> + </iq>"""), + + expect_stanza("/message/mam:result[@queryid='qid16']/forward:forwarded/delay:delay", + "/message/mam:result/forward:forwarded/client:message[@from='#foo%{irc_server_one}/{nick_one}'][@type='groupchat']/client:body[text()='coucou 3']"), + + expect_stanza("/message/mam:result[@queryid='qid16']/forward:forwarded/delay:delay", + "/message/mam:result/forward:forwarded/client:message[@from='#foo%{irc_server_one}/{nick_one}'][@type='groupchat']/client:body[text()='coucou 4']"), + + expect_stanza("/iq[@type='result'][@id='id8'][@from='#foo%{irc_server_one}'][@to='{jid_one}/{resource_one}']", + "/iq/mam:fin[@complete='true']/rsm:set"), + + # Try the same thing, but only with the 'start' value, omitting the end + send_stanza("""<iq from='{jid_one}/{resource_one}' id='id888' to='#foo%{irc_server_one}' type='set'> + <query queryid='qid17' xmlns='urn:xmpp:mam:2'> + <x type='submit' xmlns='jabber:x:data'> + <field type='hidden' var='FORM_TYPE' xmlns='jabber:x:data'><value xmlns='jabber:x:data'>urn:xmpp:mam:2</value></field> + <field var='start' xmlns='jabber:x:data'><value xmlns='jabber:x:data'>{first_timestamp}</value></field> + </x> + </query> + </iq>"""), + + expect_stanza("/message/mam:result[@queryid='qid17']/forward:forwarded/delay:delay", + "/message/mam:result/forward:forwarded/client:message[@from='#foo%{irc_server_one}/{nick_one}'][@type='groupchat']/client:body[text()='coucou 3']"), + + expect_stanza("/message/mam:result[@queryid='qid17']/forward:forwarded/delay:delay", + "/message/mam:result/forward:forwarded/client:message[@from='#foo%{irc_server_one}/{nick_one}'][@type='groupchat']/client:body[text()='coucou 4']"), + + expect_stanza("/iq[@type='result'][@id='id888'][@from='#foo%{irc_server_one}'][@to='{jid_one}/{resource_one}']", + "/iq/mam:fin[@complete='true']/rsm:set"), + +) diff --git a/tests/end_to_end/scenarios/mode_change.py b/tests/end_to_end/scenarios/mode_change.py new file mode 100644 index 0000000..4cbf036 --- /dev/null +++ b/tests/end_to_end/scenarios/mode_change.py @@ -0,0 +1,52 @@ +from scenarios import * + +scenario = ( + scenarios.channel_join_with_two_users.scenario, + + # Change a user mode with a message starting with /mode + send_stanza("<message from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}' type='groupchat'><body>/mode +v {nick_two}</body></message>"), + expect_unordered( + ["/message[@to='{jid_one}/{resource_one}']/body[text()='Mode #foo [+v {nick_two}] by {nick_one}']"], + ["/message[@to='{jid_two}/{resource_one}']/body[text()='Mode #foo [+v {nick_two}] by {nick_one}']"], + ["/presence[@to='{jid_two}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_two}']/muc_user:x/muc_user:item[@affiliation='member'][@role='participant']"], + ["/presence[@to='{jid_one}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_two}']/muc_user:x/muc_user:item[@affiliation='member'][@role='participant']"], + ), + + # using an iq + send_stanza("<iq from='{jid_one}/{resource_one}' id='id1' to='#foo%{irc_server_one}' type='set'><query xmlns='http://jabber.org/protocol/muc#admin'><item affiliation='admin' nick='{nick_two}'/></query></iq>"), + expect_unordered( + ["/message[@to='{jid_one}/{resource_one}']/body[text()='Mode #foo [+o {nick_two}] by {nick_one}']"], + ["/message[@to='{jid_two}/{resource_one}']/body[text()='Mode #foo [+o {nick_two}] by {nick_one}']"], + ["/presence[@to='{jid_two}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_two}']/muc_user:x/muc_user:item[@affiliation='admin'][@role='moderator']"], + ["/presence[@to='{jid_one}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_two}']/muc_user:x/muc_user:item[@affiliation='admin'][@role='moderator']"], + ["/iq[@id='id1'][@type='result'][@to='{jid_one}/{resource_one}'][@from='#foo%{irc_server_one}']"], + ), + + # remove the mode + send_stanza("<iq from='{jid_one}/{resource_one}' id='id1' to='#foo%{irc_server_one}' type='set'><query xmlns='http://jabber.org/protocol/muc#admin'><item affiliation='member' nick='{nick_two}' role='participant'/></query></iq>"), + expect_unordered( + ["/message[@to='{jid_one}/{resource_one}']/body[text()='Mode #foo [+v-o {nick_two} {nick_two}] by {nick_one}']"], + ["/message[@to='{jid_two}/{resource_one}']/body[text()='Mode #foo [+v-o {nick_two} {nick_two}] by {nick_one}']"], + ["/presence[@to='{jid_two}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_two}']/muc_user:x/muc_user:item[@affiliation='member'][@role='participant']"], + ["/presence[@to='{jid_one}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_two}']/muc_user:x/muc_user:item[@affiliation='member'][@role='participant']"], + ["/iq[@id='id1'][@type='result'][@to='{jid_one}/{resource_one}'][@from='#foo%{irc_server_one}']"], + ), + + # using an iq, an a non-existant nick + send_stanza("<iq from='{jid_one}/{resource_one}' id='id1' to='#foo%{irc_server_one}' type='set'><query xmlns='http://jabber.org/protocol/muc#admin'><item affiliation='admin' nick='blectre'/></query></iq>"), + expect_stanza("/iq[@type='error']"), + + # using an iq, without the rights to do it + send_stanza("<iq from='{jid_two}/{resource_one}' id='id1' to='#foo%{irc_server_one}' type='set'><query xmlns='http://jabber.org/protocol/muc#admin'><item affiliation='admin' nick='{nick_one}'/></query></iq>"), + expect_unordered( + ["/iq[@type='error']"], + ["/message[@type='chat'][@to='{jid_two}/{resource_one}']"] + ), + + # using an iq, with an unknown mode + send_stanza("<iq from='{jid_two}/{resource_one}' id='id1' to='#foo%{irc_server_one}' type='set'><query xmlns='http://jabber.org/protocol/muc#admin'><item affiliation='owner' nick='{nick_one}'/></query></iq>"), + expect_unordered( + ["/iq[@type='error']"], + ["/message[@type='chat'][@to='{jid_two}/{resource_one}']"], + ), +) diff --git a/tests/end_to_end/scenarios/muc_disco_info.py b/tests/end_to_end/scenarios/muc_disco_info.py new file mode 100644 index 0000000..19e90f2 --- /dev/null +++ b/tests/end_to_end/scenarios/muc_disco_info.py @@ -0,0 +1,28 @@ +from scenarios import * + +scenario = ( + send_stanza("<iq from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}' id='1' type='get'><query xmlns='http://jabber.org/protocol/disco#info'/></iq>"), + expect_stanza("/iq[@from='#foo%{irc_server_one}'][@to='{jid_one}/{resource_one}'][@type='result']/disco_info:query", + "/iq[@type='result']/disco_info:query/disco_info:identity[@category='conference'][@type='irc'][@name='#foo on {irc_host_one}']", + "/iq/disco_info:query/disco_info:feature[@var='jabber:iq:version']", + "/iq/disco_info:query/disco_info:feature[@var='http://jabber.org/protocol/commands']", + "/iq/disco_info:query/disco_info:feature[@var='urn:xmpp:ping']", + "/iq/disco_info:query/disco_info:feature[@var='urn:xmpp:mam:2']", + "/iq/disco_info:query/disco_info:feature[@var='jabber:iq:version']", + "/iq/disco_info:query/disco_info:feature[@var='muc_nonanonymous']", + "!/iq/disco_info:query/dataform:x/dataform:field[@var='muc#roominfo_occupants']"), + + # Join the channel, and re-do the same query + send_stanza("<presence from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}/{nick_one}' />"), + sequences.connection("irc.localhost", '{jid_one}/{resource_one}'), + expect_stanza("/message/body[text()='Mode #foo [+nt] by {irc_host_one}']"), + 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']"), + + expect_stanza("/message[@from='#foo%{irc_server_one}'][@type='groupchat']/subject[not(text())]"), + + send_stanza("<iq from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}' id='2' type='get'><query xmlns='http://jabber.org/protocol/disco#info'/></iq>"), + expect_stanza("/iq[@from='#foo%{irc_server_one}'][@to='{jid_one}/{resource_one}'][@type='result']/disco_info:query", + "/iq/disco_info:query/dataform:x/dataform:field[@var='muc#roominfo_occupants']/dataform:value[text()='1']", + "/iq/disco_info:query/dataform:x/dataform:field[@var='FORM_TYPE'][@type='hidden']/dataform:value[text()='http://jabber.org/protocol/muc#roominfo']"), +) diff --git a/tests/end_to_end/scenarios/muc_message_from_unjoined_resource.py b/tests/end_to_end/scenarios/muc_message_from_unjoined_resource.py new file mode 100644 index 0000000..bffe3aa --- /dev/null +++ b/tests/end_to_end/scenarios/muc_message_from_unjoined_resource.py @@ -0,0 +1,16 @@ +from scenarios import * + +scenario = ( + scenarios.simple_channel_join.scenario, + + # Send a channel message + send_stanza("<message from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}' type='groupchat'><body>coucou</body></message>"), + # Receive the message + expect_stanza("/message[@from='#foo%{irc_server_one}/{nick_one}'][@to='{jid_one}/{resource_one}'][@type='groupchat']/body[text()='coucou']", + "/message/stable_id:stanza-id[@by='#foo%{irc_server_one}'][@id]"), + # Send a message from a resource that is not joined + send_stanza("<message from='{jid_one}/{resource_two}' to='#foo%{irc_server_one}' type='groupchat'><body>coucou</body></message>"), + expect_stanza("/message[@type='error']/error[@type='modify']/stanza:text[text()='You are not a participant in this room.']", + "/message/error/stanza:not-acceptable" + ), +) diff --git a/tests/end_to_end/scenarios/muc_traffic_info.py b/tests/end_to_end/scenarios/muc_traffic_info.py new file mode 100644 index 0000000..0ef0d37 --- /dev/null +++ b/tests/end_to_end/scenarios/muc_traffic_info.py @@ -0,0 +1,6 @@ +from scenarios import * + +scenario = ( + send_stanza("<iq from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}' id='1' type='get'><query xmlns='http://jabber.org/protocol/disco#info' node='http://jabber.org/protocol/muc#traffic'/></iq>"), + expect_stanza("/iq[@from='#foo%{irc_server_one}'][@to='{jid_one}/{resource_one}'][@type='result']/disco_info:query[@node='http://jabber.org/protocol/muc#traffic']"), +) diff --git a/tests/end_to_end/scenarios/multiline_message.py b/tests/end_to_end/scenarios/multiline_message.py new file mode 100644 index 0000000..fc88e66 --- /dev/null +++ b/tests/end_to_end/scenarios/multiline_message.py @@ -0,0 +1,62 @@ +from scenarios import * + +import scenarios.simple_channel_join + +scenario = ( + scenarios.simple_channel_join.scenario, + + # Send a multi-line channel message + send_stanza("<message id='the-message-id' from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}' type='groupchat'><body>un\ndeux\ntrois</body></message>"), + # Receive multiple messages, in order + expect_stanza("/message[@from='#foo%{irc_server_one}/{nick_one}'][@id='the-message-id'][@to='{jid_one}/{resource_one}'][@type='groupchat']/body[text()='un']"), + expect_stanza("/message[@from='#foo%{irc_server_one}/{nick_one}'][@id][@to='{jid_one}/{resource_one}'][@type='groupchat']/body[text()='deux']"), + expect_stanza("/message[@from='#foo%{irc_server_one}/{nick_one}'][@id][@to='{jid_one}/{resource_one}'][@type='groupchat']/body[text()='trois']"), + + # Send a simple message, with no id + send_stanza("<message from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}' type='groupchat'><body>hello</body></message>"), + + # Expect a non-empty id as a result (should be a uuid) + expect_stanza("!/message[@id='']", + "/message[@id]/body[text()='hello']"), + + # even though we reflect the message to XMPP only + # when we send it to IRC, there’s still a race + # condition if the XMPP client receives the + # reflection (and the IRC server didn’t yet receive + # it), then the new user joins the room, and then + # finally the IRC server sends the message to “all + # participants of the channel”, including the new + # one, that was not supposed to be there when the + # message was sent in the first place by the first + # XMPP user. There’s nothing we can do about it until + # all servers support the echo-message IRCv3 + # extension… So, we just sleep a little bit before + # joining the room with the new user. + sleep_for(0.2), + # Second user joins + send_stanza("<presence from='{jid_two}/{resource_one}' to='#foo%{irc_server_one}/{nick_two}' />"), + sequences.connection("irc.localhost", '{jid_two}/{resource_one}'), + # Our presence, sent to the other user + expect_unordered( + ["/presence[@to='{jid_one}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_two}']/muc_user:x/muc_user:item[@affiliation='none'][@jid='{lower_nick_two}%{irc_server_one}/~{nick_two}@localhost'][@role='participant']"], + ["/presence[@to='{jid_two}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_one}']/muc_user:x/muc_user:item[@affiliation='admin'][@role='moderator']"], + [ + "/presence[@to='{jid_two}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_two}']/muc_user:x/muc_user:item[@affiliation='none'][@jid='{lower_nick_two}%{irc_server_one}/~{nick_two}@localhost'][@role='participant']", + "/presence/muc_user:x/muc_user:status[@code='110']" + ], + ["/message[@from='#foo%{irc_server_one}'][@type='groupchat']/subject[not(text())]"] + ), + + # Send a multi-line channel message + send_stanza("<message id='the-message-id' from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}' type='groupchat'><body>a\nb\nc</body></message>"), + # Receive multiple messages, for each user + expect_unordered( + ["/message[@from='#foo%{irc_server_one}/{nick_one}'][@id='the-message-id'][@to='{jid_one}/{resource_one}'][@type='groupchat']/body[text()='a']"], + ["/message[@from='#foo%{irc_server_one}/{nick_one}'][@id][@to='{jid_one}/{resource_one}'][@type='groupchat']/body[text()='b']"], + ["/message[@from='#foo%{irc_server_one}/{nick_one}'][@id][@to='{jid_one}/{resource_one}'][@type='groupchat']/body[text()='c']"], + + ["/message[@from='#foo%{irc_server_one}/{nick_one}'][@id][@to='{jid_two}/{resource_one}'][@type='groupchat']/body[text()='a']"], + ["/message[@from='#foo%{irc_server_one}/{nick_one}'][@id][@to='{jid_two}/{resource_one}'][@type='groupchat']/body[text()='b']"], + ["/message[@from='#foo%{irc_server_one}/{nick_one}'][@id][@to='{jid_two}/{resource_one}'][@type='groupchat']/body[text()='c']"], + ) +) diff --git a/tests/end_to_end/scenarios/multiline_topic.py b/tests/end_to_end/scenarios/multiline_topic.py new file mode 100644 index 0000000..ca163a0 --- /dev/null +++ b/tests/end_to_end/scenarios/multiline_topic.py @@ -0,0 +1,11 @@ +from scenarios import * + +import scenarios.simple_channel_join + +scenario = ( + scenarios.simple_channel_join.scenario, + # User tries to set a multiline topic + send_stanza("<message from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}' type='groupchat'><subject>FIRST LINE\nSECOND LINE.</subject></message>"), + # Server converts the newline into spaces, because IRC can’t have them in the topic + expect_stanza("/message[@from='#foo%{irc_server_one}/{nick_one}'][@type='groupchat'][@to='{jid_one}/{resource_one}']/subject[text()='FIRST LINE SECOND LINE.']") +) diff --git a/tests/end_to_end/scenarios/multiple_channels_join.py b/tests/end_to_end/scenarios/multiple_channels_join.py new file mode 100644 index 0000000..84e4360 --- /dev/null +++ b/tests/end_to_end/scenarios/multiple_channels_join.py @@ -0,0 +1,18 @@ +from scenarios import * + +from scenarios.simple_channel_join import expect_self_join_presence + +scenario = ( + # Join 3 rooms, on the same server, with three different nicks + send_stanza("<presence from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}/{nick_one}' />"), + send_stanza("<presence from='{jid_one}/{resource_one}' to='#bar%{irc_server_one}/{nick_two}' />"), + send_stanza("<presence from='{jid_one}/{resource_one}' to='#baz%{irc_server_one}/{nick_three}'> <x xmlns='http://jabber.org/protocol/muc'><password>SECRET</password></x></presence>"), + + sequences.connection(), + + # The first nick we specified should be the only one we receive, the rest was ignored + expect_self_join_presence(jid='{jid_one}/{resource_one}', chan="#foo", nick="{nick_one}"), + expect_self_join_presence(jid='{jid_one}/{resource_one}', chan="#bar", nick="{nick_one}"), + expect_self_join_presence(jid='{jid_one}/{resource_one}', chan="#baz", nick="{nick_one}"), +) + diff --git a/tests/end_to_end/scenarios/multisession_kick.py b/tests/end_to_end/scenarios/multisession_kick.py new file mode 100644 index 0000000..7d8679f --- /dev/null +++ b/tests/end_to_end/scenarios/multisession_kick.py @@ -0,0 +1,46 @@ +from scenarios import * + +scenario = ( + scenarios.simple_channel_join.scenario, + + # Second user joins, from two resources + send_stanza("<presence from='{jid_two}/{resource_one}' to='#foo%{irc_server_one}/{nick_two}' />"), + sequences.connection("irc.localhost", '{jid_two}/{resource_one}'), + expect_unordered( + ["/presence/muc_user:x/muc_user:item[@affiliation='none'][@role='participant']"], + ["/presence/muc_user:x/muc_user:item[@affiliation='admin'][@role='moderator']"], + ["/presence/muc_user:x/muc_user:status[@code='110']"], + ["/message/subject"] + ), + # Second resource + send_stanza("<presence from='{jid_two}/{resource_two}' to='#foo%{irc_server_one}/{nick_two}' />"), + expect_stanza("/presence[@to='{jid_two}/{resource_two}'][@from='#foo%{irc_server_one}/{nick_one}']"), + expect_stanza("/presence[@to='{jid_two}/{resource_two}'][@from='#foo%{irc_server_one}/{nick_two}']", + "/presence/muc_user:x/muc_user:status[@code='110']" + ), + expect_stanza("/message[@from='#foo%{irc_server_one}'][@type='groupchat'][@to='{jid_two}/{resource_two}']/subject[not(text())]"), + + # Moderator kicks participant + send_stanza("<iq id='kick1' to='#foo%{irc_server_one}' from='{jid_one}/{resource_one}' type='set'><query xmlns='http://jabber.org/protocol/muc#admin'><item nick='{nick_two}' role='none'><reason>reported</reason></item></query></iq>"), + expect_unordered( + [ + "/presence[@type='unavailable'][@to='{jid_two}/{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']" + ], + [ + "/presence[@type='unavailable'][@to='{jid_two}/{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']" + ], + ["/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']", + ], + [ + "/iq[@id='kick1'][@type='result']" + ] + ), +) diff --git a/tests/end_to_end/scenarios/multisessionnick.py b/tests/end_to_end/scenarios/multisessionnick.py new file mode 100644 index 0000000..4e72ce7 --- /dev/null +++ b/tests/end_to_end/scenarios/multisessionnick.py @@ -0,0 +1,125 @@ +from scenarios import * + +from scenarios.simple_channel_join import expect_self_join_presence + +scenario = ( + # Resource one joins a channel + send_stanza("<presence from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}/{nick_one}' />"), + sequences.connection(), + expect_self_join_presence(jid = '{jid_one}/{resource_one}', chan = "#foo", nick = "{nick_one}"), + + # The other resources joins the same room, with the same nick + send_stanza("<presence from='{jid_one}/{resource_two}' to='#foo%{irc_server_one}/{nick_one}' />"), + + # We receive our own join + expect_unordered( + [ + "/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']" + ], + [ + "/message[@from='#foo%{irc_server_one}'][@type='groupchat'][@to='{jid_one}/{resource_two}']/subject[not(text())]" + + ] + ), + + # A different user joins the same room + send_stanza("<presence from='{jid_two}/{resource_one}' to='#foo%{irc_server_one}/{nick_two}' />"), + sequences.connection("irc.localhost", '{jid_two}/{resource_one}'), + expect_unordered( + # The new user’s presence is sent to the the existing occupant (two resources) + [ + "/presence[@to='{jid_one}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_two}']" + ], + [ + "/presence[@to='{jid_one}/{resource_two}'][@from='#foo%{irc_server_one}/{nick_two}']" + ], + # the new user receives her own presence + [ + "/presence[@to='{jid_two}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_two}']", + "/presence/muc_user:x/muc_user:status[@code='110']" + ], + # the new user receives the presence of the existing occupant + [ + "/presence[@to='{jid_two}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_one}']", + ], + [ + "/message[@from='#foo%{irc_server_one}'][@type='groupchat']/subject[not(text())]", + ], + ), + + # That second user sends a private message to the first one + send_stanza("<message from='{jid_two}/{resource_one}' to='#foo%{irc_server_one}/{nick_one}' type='chat'><body>RELLO</body></message>"), + + # Message is received with a server-wide JID, by the two resources behind nick_one + expect_unordered( + [ + "/message[@from='{lower_nick_two}%{irc_server_one}'][@to='{jid_one}/{resource_one}'][@type='chat']/body[text()='RELLO']", + "/message/hints:no-copy", + "/message/carbon:private", + "!/message/muc_user:x", + ], + [ + "/message[@from='{lower_nick_two}%{irc_server_one}'][@to='{jid_one}/{resource_two}'][@type='chat']/body[text()='RELLO']", + "/message/hints:no-copy", + "/message/carbon:private", + "!/message/muc_user:x", + ] + ), + + # First occupant (with the two resources) changes her/his nick to a conflicting one + send_stanza("<presence from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}/{nick_two}' />"), + expect_unordered( + ["/message[@to='{jid_one}/{resource_one}'][@type='chat']/body[text()='irc.localhost: Bobby: Nickname is already in use.']"], + ["/message[@to='{jid_one}/{resource_two}'][@type='chat']/body[text()='irc.localhost: Bobby: Nickname is already in use.']"], + ["/presence[@to='{jid_one}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_two}'][@type='error']"], + ["/presence[@to='{jid_one}/{resource_two}'][@from='#foo%{irc_server_one}/{nick_two}'][@type='error']"] + ), + + # First occupant (with the two resources) changes her/his nick + send_stanza("<presence from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}/{nick_three}' />"), + expect_unordered( + [ + "/presence[@from='#foo%{irc_server_one}/{nick_one}'][@to='{jid_two}/{resource_one}'][@type='unavailable']/muc_user:x/muc_user:item[@nick='Bernard']", + "/presence/muc_user:x/muc_user:status[@code='303']" + ], + [ + "/presence[@from='#foo%{irc_server_one}/{nick_three}'][@to='{jid_two}/{resource_one}']" + ], + [ + "/presence[@from='#foo%{irc_server_one}/{nick_one}'][@to='{jid_one}/{resource_one}'][@type='unavailable']/muc_user:x/muc_user:item[@nick='Bernard']", + "/presence/muc_user:x/muc_user:status[@code='303']", + "/presence/muc_user:x/muc_user:status[@code='110']" + ], + [ + "/presence[@from='#foo%{irc_server_one}/{nick_three}'][@to='{jid_one}/{resource_one}']", + "/presence/muc_user:x/muc_user:status[@code='110']" + ], + [ + "/presence[@from='#foo%{irc_server_one}/{nick_one}'][@to='{jid_one}/{resource_two}'][@type='unavailable']/muc_user:x/muc_user:item[@nick='Bernard']", + "/presence/muc_user:x/muc_user:status[@code='303']", + "/presence/muc_user:x/muc_user:status[@code='110']" + ], + [ + "/presence[@from='#foo%{irc_server_one}/{nick_three}'][@to='{jid_one}/{resource_two}']", + "/presence/muc_user:x/muc_user:status[@code='110']" + ] + ), + + # One resource leaves the server entirely. + send_stanza("<presence type='unavailable' from='{jid_one}/{resource_two}' to='#foo%{irc_server_one}/{nick_one}' />"), + # The leave is forwarded only to that resource + 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 + send_stanza("<message from='{jid_two}/{resource_one}' to='#foo%{irc_server_one}/{nick_three}' type='chat'><body>first</body></message>"), + send_stanza("<message from='{jid_two}/{resource_one}' to='#foo%{irc_server_one}/{nick_three}' type='chat'><body>second</body></message>"), + + # The first user receives the two messages, on the connected resource, once each + expect_unordered( + ["/message[@from='{lower_nick_two}%{irc_server_one}'][@to='{jid_one}/{resource_one}'][@type='chat']/body[text()='first']"], + ["/message[@from='{lower_nick_two}%{irc_server_one}'][@to='{jid_one}/{resource_one}'][@type='chat']/body[text()='second']"] + ), +) diff --git a/tests/end_to_end/scenarios/nick_change_in_join.py b/tests/end_to_end/scenarios/nick_change_in_join.py new file mode 100644 index 0000000..47aecc6 --- /dev/null +++ b/tests/end_to_end/scenarios/nick_change_in_join.py @@ -0,0 +1,18 @@ +from scenarios import * + +from scenarios.simple_channel_join import expect_self_join_presence + +scenario = ( + send_stanza("<presence from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}/{nick_one}' />"), + sequences.connection(), + expect_self_join_presence(jid = '{jid_one}/{resource_one}', chan = "#foo", nick = "{nick_one}"), + + send_stanza("<presence from='{jid_one}/{resource_one}' to='#bar%{irc_server_one}/{nick_two}' />"), + expect_stanza("/message/body[text()='Mode #bar [+nt] by {irc_host_one}']"), + expect_stanza("/presence[@to='{jid_one}/{resource_one}'][@from='#bar%{irc_server_one}/{nick_one}']/muc_user:x/muc_user:item[@affiliation='admin'][@role='moderator']", + "/presence/muc_user:x/muc_user:status[@code='110']", + "/presence/muc_user:x/muc_user:status[@code='210']", # This status signals that the server forced our nick to NOT be the one we asked + ), + expect_stanza("/message[@from='#bar%{irc_server_one}'][@type='groupchat']/subject[not(text())]"), +) + diff --git a/tests/end_to_end/scenarios/not_connected_error.py b/tests/end_to_end/scenarios/not_connected_error.py new file mode 100644 index 0000000..cc9fb35 --- /dev/null +++ b/tests/end_to_end/scenarios/not_connected_error.py @@ -0,0 +1,12 @@ +from scenarios import * + +from scenarios.simple_channel_join import expect_self_join_presence + +scenario = ( + send_stanza("<presence type='unavailable' from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}/{nick_one}' />"), + # Fixme: what is the purpose of this test? Check that we don’t receive anything here…? + + send_stanza("<presence from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}/{nick_one}' />"), + sequences.connection(), + expect_self_join_presence(jid='{jid_one}/{resource_one}', chan="#foo", nick="{nick_one}"), +) diff --git a/tests/end_to_end/scenarios/notices.py b/tests/end_to_end/scenarios/notices.py new file mode 100644 index 0000000..dddee5d --- /dev/null +++ b/tests/end_to_end/scenarios/notices.py @@ -0,0 +1,10 @@ +from scenarios import * + +import scenarios.simple_channel_join + +scenario = ( + scenarios.simple_channel_join.scenario, + + send_stanza("<message from='{jid_one}/{resource_one}' to='{irc_server_one}' type='chat'><body>NOTICE {nick_one} :[#foo] Hello in a notice.</body></message>"), + expect_stanza("/message[@from='#foo%{irc_server_one}/{nick_one}'][@type='groupchat']/body[text()='[notice] [#foo] Hello in a notice.']"), +) diff --git a/tests/end_to_end/scenarios/persistent_channel.py b/tests/end_to_end/scenarios/persistent_channel.py new file mode 100644 index 0000000..3521a84 --- /dev/null +++ b/tests/end_to_end/scenarios/persistent_channel.py @@ -0,0 +1,48 @@ +from scenarios import * + +import scenarios.simple_channel_join + +scenario = ( + # Join the channel with user 1 + scenarios.simple_channel_join.scenario, + + # Make it persistent for user 1 + send_stanza("<iq from='{jid_one}/{resource_one}' id='conf1' to='#foo%{irc_server_one}' type='get'><query xmlns='http://jabber.org/protocol/muc#owner'/></iq>"), + expect_stanza("/iq[@type='result']/muc_owner:query/dataform:x/dataform:field[@var='persistent'][@type='boolean']/dataform:value[text()='false']"), + send_stanza("<iq from='{jid_one}/{resource_one}' id='conf2' to='#foo%{irc_server_one}' type='set'><query xmlns='http://jabber.org/protocol/muc#owner'><x type='submit' xmlns='jabber:x:data'><field var='persistent' xmlns='jabber:x:data'><value>true</value></field></x></query></iq>"), + expect_stanza("/iq[@type='result']"), + + # Check that the value is now effectively true + send_stanza("<iq from='{jid_one}/{resource_one}' id='conf1' to='#foo%{irc_server_one}' type='get'><query xmlns='http://jabber.org/protocol/muc#owner'/></iq>"), + expect_stanza("/iq[@type='result']/muc_owner:query/dataform:x/dataform:field[@var='persistent'][@type='boolean']/dataform:value[text()='true']"), + + # A second user joins the same channel + send_stanza("<presence from='{jid_two}/{resource_one}' to='#foo%{irc_server_one}/{nick_two}' />"), + sequences.connection("irc.localhost", '{jid_two}/{resource_one}'), + expect_unordered( + ["/presence[@to='{jid_one}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_two}']"], + [ + "/presence[@to='{jid_two}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_two}']", + "/presence/muc_user:x/muc_user:status[@code='110']" + ], + ["/presence[@to='{jid_two}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_one}']"], + ), + expect_stanza("/message[@from='#foo%{irc_server_one}'][@type='groupchat']/subject[not(text())]"), + + # First user leaves the room (but biboumi will stay in the channel) + send_stanza("<presence from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}/{nick_one}' type='unavailable' />"), + + # Only user 1 receives the unavailable presence + expect_stanza("/presence[@from='#foo%{irc_server_one}/{nick_one}'][@to='{jid_one}/{resource_one}'][@type='unavailable']/muc_user:x/muc_user:status[@code='110']", + "/presence/muc_user:x/muc_user:item[@affiliation='admin'][@role='moderator']"), + + # Second user sends a channel message + send_stanza("<message type='groupchat' from='{jid_two}/{resource_one}' to='#foo%{irc_server_one}'><body>coucou</body></message>"), + + # Message should only be received by user 2, since user 1 has no resource in the room + expect_stanza("/message[@type='groupchat'][@to='{jid_two}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_two}']"), + + # Second user leaves the channel + send_stanza("<presence type='unavailable' from='{jid_two}/{resource_one}' to='#foo%{irc_server_one}/{nick_two}' />"), + expect_stanza("/presence[@type='unavailable'][@from='#foo%{irc_server_one}/{nick_two}']"), +) diff --git a/tests/end_to_end/scenarios/quit.py b/tests/end_to_end/scenarios/quit.py new file mode 100644 index 0000000..ced5a96 --- /dev/null +++ b/tests/end_to_end/scenarios/quit.py @@ -0,0 +1,12 @@ +from scenarios import * + +import scenarios.simple_channel_join + +scenario = ( + scenarios.simple_channel_join.scenario, + + # Send a raw QUIT message + send_stanza("<message from='{jid_one}/{resource_one}' to='{irc_server_one}' type='chat'><body>QUIT bye bye</body></message>"), + expect_stanza("/presence[@from='#foo%{irc_server_one}/{nick_one}'][@type='unavailable']/muc_user:x/muc_user:status[@code='110']"), +) + diff --git a/tests/end_to_end/scenarios/raw_message.py b/tests/end_to_end/scenarios/raw_message.py new file mode 100644 index 0000000..96a3f3d --- /dev/null +++ b/tests/end_to_end/scenarios/raw_message.py @@ -0,0 +1,12 @@ +from scenarios import * + +scenario = ( + send_stanza("<presence from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}/{nick_one}' />"), + sequences.connection("irc.localhost", '{jid_one}/{resource_one}'), + expect_stanza("/message"), + expect_stanza("/presence"), + expect_stanza("/message"), + + send_stanza("<message from='{jid_one}/{resource_one}' to='{irc_server_one}' type='chat'><body>WHOIS {nick_one}</body></message>"), + expect_stanza("/message[@from='{irc_server_one}'][@type='chat']/body[text()='irc.localhost: {nick_one} ~{nick_one} localhost * {nick_one}']"), +) diff --git a/tests/end_to_end/scenarios/raw_message_fixed_irc_server.py b/tests/end_to_end/scenarios/raw_message_fixed_irc_server.py new file mode 100644 index 0000000..8196d12 --- /dev/null +++ b/tests/end_to_end/scenarios/raw_message_fixed_irc_server.py @@ -0,0 +1,15 @@ +from scenarios import * + +conf = 'fixed_server' + +scenario = ( + send_stanza("<presence from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}/{nick_one}' />"), + sequences.connection("irc.localhost", '{jid_one}/{resource_one}', fixed_irc_server=True), + expect_stanza("/message"), + expect_stanza("/presence"), + expect_stanza("/message"), + + send_stanza("<message from='{jid_one}/{resource_one}' to='{biboumi_host}' type='chat'><body>WHOIS {nick_one}</body></message>"), + expect_stanza("/message[@from='{biboumi_host}'][@type='chat']/body[text()='irc.localhost: {nick_one} ~{nick_one} localhost * {nick_one}']"), + +) diff --git a/tests/end_to_end/scenarios/raw_names_command.py b/tests/end_to_end/scenarios/raw_names_command.py new file mode 100644 index 0000000..09b47be --- /dev/null +++ b/tests/end_to_end/scenarios/raw_names_command.py @@ -0,0 +1,13 @@ +from functions import send_stanza, expect_stanza + +import scenarios.simple_channel_join + +join_channel = scenarios.simple_channel_join.scenario + +scenario = ( + join_channel, + + send_stanza("<message type='chat' from='{jid_one}/{resource_one}' to='{irc_server_one}'><body>NAMES</body></message>"), + expect_stanza("/message/body[text()='irc.localhost: = #foo @{nick_one} ']"), + expect_stanza("/message/body[text()='irc.localhost: * End of /NAMES list. ']"), +) diff --git a/tests/end_to_end/scenarios/resource_is_removed_from_server_when_last_chan_is_left.py b/tests/end_to_end/scenarios/resource_is_removed_from_server_when_last_chan_is_left.py new file mode 100644 index 0000000..8d208f0 --- /dev/null +++ b/tests/end_to_end/scenarios/resource_is_removed_from_server_when_last_chan_is_left.py @@ -0,0 +1,42 @@ +from scenarios import * + +scenario = ( + # Join the channel + send_stanza("<presence from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}/{nick_one}' />"), + sequences.connection("irc.localhost", '{jid_one}/{resource_one}'), + expect_stanza("/message/body[text()='Mode #foo [+nt] by {irc_host_one}']"), + 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']"), + expect_stanza("/message[@from='#foo%{irc_server_one}'][@type='groupchat'][@to='{jid_one}/{resource_one}']/subject[not(text())]"), + + # Make it persistent + send_stanza("<iq from='{jid_one}/{resource_one}' id='conf1' to='#foo%{irc_server_one}' type='get'><query xmlns='http://jabber.org/protocol/muc#owner'/></iq>"), + expect_stanza("/iq[@type='result']/muc_owner:query/dataform:x/dataform:field[@var='persistent'][@type='boolean']/dataform:value[text()='false']"), + send_stanza("<iq from='{jid_one}/{resource_one}' id='conf2' to='#foo%{irc_server_one}' type='set'><query xmlns='http://jabber.org/protocol/muc#owner'><x type='submit' xmlns='jabber:x:data'><field var='persistent' xmlns='jabber:x:data'><value>true</value></field></x></query></iq>"), + expect_stanza("/iq[@type='result']"), + + send_stanza("<presence from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}/{nick_one}' type='unavailable' />"), + expect_stanza("/presence[@type='unavailable'][@from='#foo%{irc_server_one}/{nick_one}']"), + + # Join the same channel, with the same JID, but a different resource + send_stanza("<presence from='{jid_one}/{resource_two}' to='#foo%{irc_server_one}/{nick_one}' />"), + 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']"), + expect_stanza("/message[@from='#foo%{irc_server_one}'][@type='groupchat'][@to='{jid_one}/{resource_two}']/subject[not(text())]"), + + # Join some other channel with someone else + send_stanza("<presence from='{jid_two}/{resource_one}' to='#bar%{irc_server_one}/{nick_two}' />"), + sequences.connection("irc.localhost", '{jid_two}/{resource_one}'), + expect_stanza("/message/body[text()='Mode #bar [+nt] by {irc_host_one}']"), + expect_stanza("/presence[@to='{jid_two}/{resource_one}'][@from='#bar%{irc_server_one}/{nick_two}']/muc_user:x/muc_user:item[@affiliation='admin'][@role='moderator']", + "/presence/muc_user:x/muc_user:status[@code='110']"), + expect_stanza("/message[@from='#bar%{irc_server_one}'][@type='groupchat'][@to='{jid_two}/{resource_one}']/subject[not(text())]"), + + # Send two messages from the second user to the first one + send_stanza("<message from='{jid_two}/{resource_one}' to='{lower_nick_one}%{irc_server_one}' type='chat'><body>kikoo</body></message>"), + send_stanza("<message from='{jid_two}/{resource_one}' to='{lower_nick_one}%{irc_server_one}' type='chat'><body>second kikoo</body></message>"), + + # We must receive each message only once, no duplicate + expect_stanza("/message/body[text()='kikoo']"), + expect_stanza("/message/body[text()='second kikoo']"), +) diff --git a/tests/end_to_end/scenarios/self_disco_info.py b/tests/end_to_end/scenarios/self_disco_info.py new file mode 100644 index 0000000..6430dbd --- /dev/null +++ b/tests/end_to_end/scenarios/self_disco_info.py @@ -0,0 +1,11 @@ +from scenarios import * + +scenario = ( + send_stanza("<iq type='get' id='get1' from='{jid_one}/{resource_one}' to='{biboumi_host}'><query xmlns='http://jabber.org/protocol/disco#info'/></iq>"), + expect_stanza("/iq[@type='result']/disco_info:query/disco_info:identity[@category='conference'][@type='irc'][@name='Biboumi XMPP-IRC gateway']", + "/iq/disco_info:query/disco_info:feature[@var='jabber:iq:version']", + "/iq/disco_info:query/disco_info:feature[@var='http://jabber.org/protocol/commands']", + "/iq/disco_info:query/disco_info:feature[@var='urn:xmpp:ping']", + "/iq/disco_info:query/disco_info:feature[@var='urn:xmpp:mam:2']", + "/iq/disco_info:query/disco_info:feature[@var='jabber:iq:version']"), +) diff --git a/tests/end_to_end/scenarios/self_invite.py b/tests/end_to_end/scenarios/self_invite.py new file mode 100644 index 0000000..7959b3a --- /dev/null +++ b/tests/end_to_end/scenarios/self_invite.py @@ -0,0 +1,7 @@ +from scenarios import * + +scenario = ( + scenarios.simple_channel_join.scenario, + send_stanza("<message from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}'><x xmlns='http://jabber.org/protocol/muc#user'><invite to='{nick_one}'/></x></message>"), + expect_stanza("/message/body[text()='{nick_one} is already on channel #foo']") +) diff --git a/tests/end_to_end/scenarios/self_ping_fixed_server.py b/tests/end_to_end/scenarios/self_ping_fixed_server.py new file mode 100644 index 0000000..453387c --- /dev/null +++ b/tests/end_to_end/scenarios/self_ping_fixed_server.py @@ -0,0 +1,11 @@ +from scenarios import * + +conf = "fixed_server" + +scenario = ( + scenarios.simple_channel_join_fixed.scenario, + + # Send a ping to ourself + send_stanza("<iq type='get' from='{jid_one}/{resource_one}' id='first_ping' to='#foo@{biboumi_host}/{nick_one}'><ping xmlns='urn:xmpp:ping' /></iq>"), + expect_stanza("/iq[@from='#foo@{biboumi_host}/{nick_one}'][@type='result'][@to='{jid_one}/{resource_one}'][@id='first_ping']"), +) diff --git a/tests/end_to_end/scenarios/self_ping_not_in_muc.py b/tests/end_to_end/scenarios/self_ping_not_in_muc.py new file mode 100644 index 0000000..eb7d092 --- /dev/null +++ b/tests/end_to_end/scenarios/self_ping_not_in_muc.py @@ -0,0 +1,15 @@ +from scenarios import * + +scenario = ( + scenarios.simple_channel_join.scenario, + + # Send a ping to ourself, in a muc where we’re not + send_stanza("<iq type='get' from='{jid_one}/{resource_one}' id='first_ping' to='#nil%{irc_server_one}/{nick_one}'><ping xmlns='urn:xmpp:ping' /></iq>"), + # Immediately receive an error + expect_stanza("/iq[@from='#nil%{irc_server_one}/{nick_one}'][@type='error'][@to='{jid_one}/{resource_one}'][@id='first_ping']/error/stanza:not-acceptable"), + + # Send a ping to ourself, in a muc where we are, but not this resource + send_stanza("<iq type='get' from='{jid_one}/{resource_two}' id='first_ping' to='#foo%{irc_server_one}/{nick_one}'><ping xmlns='urn:xmpp:ping' /></iq>"), + # Immediately receive an error + expect_stanza("/iq[@from='#foo%{irc_server_one}/{nick_one}'][@type='error'][@to='{jid_one}/{resource_two}'][@id='first_ping']/error/stanza:not-acceptable"), +) diff --git a/tests/end_to_end/scenarios/self_ping_on_real_channel.py b/tests/end_to_end/scenarios/self_ping_on_real_channel.py new file mode 100644 index 0000000..6cbb210 --- /dev/null +++ b/tests/end_to_end/scenarios/self_ping_on_real_channel.py @@ -0,0 +1,23 @@ +from scenarios import * + +scenario = ( + scenarios.simple_channel_join.scenario, + + # Send a ping to ourself + send_stanza("<iq type='get' from='{jid_one}/{resource_one}' id='first_ping' to='#foo%{irc_server_one}/{nick_one}'><ping xmlns='urn:xmpp:ping' /></iq>"), + 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 + send_stanza("<presence from='{jid_one}/{resource_two}' to='#foo%{irc_server_one}/{nick_one}' />"), + 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']"), + + expect_stanza("/message[@from='#foo%{irc_server_one}'][@type='groupchat'][@to='{jid_one}/{resource_two}']/subject[not(text())]"), + + # And re-send a self ping + send_stanza("<iq type='get' from='{jid_one}/{resource_one}' id='second_ping' to='#foo%{irc_server_one}/{nick_one}'><ping xmlns='urn:xmpp:ping' /></iq>"), + 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 + send_stanza("<iq type='get' from='{jid_one}/{resource_two}' id='third_ping' to='#foo%{irc_server_one}/{nick_one}'><ping xmlns='urn:xmpp:ping' /></iq>"), + expect_stanza("/iq[@from='#foo%{irc_server_one}/{nick_one}'][@type='result'][@to='{jid_one}/{resource_two}'][@id='third_ping']"), +) diff --git a/tests/end_to_end/scenarios/self_ping_with_error.py b/tests/end_to_end/scenarios/self_ping_with_error.py new file mode 100644 index 0000000..0266d20 --- /dev/null +++ b/tests/end_to_end/scenarios/self_ping_with_error.py @@ -0,0 +1,13 @@ +from scenarios import * + +scenario = ( + scenarios.simple_channel_join.scenario, + + # Send a ping to ourself + send_stanza("<iq type='get' from='{jid_one}/{resource_one}' id='first_ping' to='#foo%{irc_server_one}/{nick_one}'><ping xmlns='urn:xmpp:ping' /></iq>"), + expect_stanza("/iq[@from='#foo%{irc_server_one}/{nick_one}'][@type='result'][@to='{jid_one}/{resource_one}'][@id='first_ping']"), + + # Send a ping to ourself + send_stanza("<iq type='get' from='{jid_one}/{resource_one}' id='first_ping' to='#foo%{irc_server_one}/{nick_one}'><ping xmlns='urn:xmpp:ping' /></iq>"), + expect_stanza("/iq[@from='#foo%{irc_server_one}/{nick_one}'][@type='result'][@to='{jid_one}/{resource_one}'][@id='first_ping']"), +) diff --git a/tests/end_to_end/scenarios/self_version.py b/tests/end_to_end/scenarios/self_version.py new file mode 100644 index 0000000..f567355 --- /dev/null +++ b/tests/end_to_end/scenarios/self_version.py @@ -0,0 +1,39 @@ +from scenarios import * + +scenario = ( + scenarios.simple_channel_join.scenario, + + # Send a version request to ourself + send_stanza("<iq type='get' from='{jid_one}/{resource_one}' id='first_version' to='#foo%{irc_server_one}/{nick_one}'><query xmlns='jabber:iq:version' /></iq>"), + # We receive our own request, + expect_stanza("/iq[@from='{lower_nick_one}%{irc_server_one}'][@type='get'][@to='{jid_one}/{resource_one}']", + after = save_value("id", extract_attribute("/iq", 'id'))), + # Respond to the request, and receive our own response + send_stanza("<iq type='result' to='{lower_nick_one}%{irc_server_one}' id='{id}' from='{jid_one}/{resource_one}'><query xmlns='jabber:iq:version'><name>e2e test</name><version>1.0</version><os>Fedora</os></query></iq>"), + 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 + send_stanza("<presence from='{jid_one}/{resource_two}' to='#foo%{irc_server_one}/{nick_one}' />"), + 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']"), + expect_stanza("/message[@from='#foo%{irc_server_one}'][@type='groupchat'][@to='{jid_one}/{resource_two}']/subject[not(text())]"), + + # And re-send a self ping + send_stanza("<iq type='get' from='{jid_one}/{resource_two}' id='second_version' to='#foo%{irc_server_one}/{nick_one}'><query xmlns='jabber:iq:version' /></iq>"), + # We receive our own request. Note that we don't know the `to` value, it could be one of our two resources. + expect_stanza("/iq[@from='{lower_nick_one}%{irc_server_one}'][@type='get'][@to]", + after = (save_value("to", extract_attribute("/iq", "to")), + save_value("id", extract_attribute("/iq", "id")))), + # Respond to the request, using the extracted 'to' value as our 'from' + send_stanza("<iq type='result' to='{lower_nick_one}%{irc_server_one}' id='{id}' from='{to}'><query xmlns='jabber:iq:version'><name>e2e test</name><version>1.0</version><os>Fedora</os></query></iq>"), + 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 + send_stanza("<iq type='get' from='{jid_one}/{resource_one}' id='second_version' to='#foo%{irc_server_one}/{nick_one}'><query xmlns='jabber:iq:version' /></iq>"), + expect_stanza("/iq[@from='{lower_nick_one}%{irc_server_one}'][@type='get'][@to]", + after = (save_value("to", extract_attribute("/iq", "to")), + save_value("id", extract_attribute("/iq", "id")))), + # Respond to the request, using the extracted 'to' value as our 'from' + send_stanza("<iq type='result' to='{lower_nick_one}%{irc_server_one}' id='{id}' from='{to}'><query xmlns='jabber:iq:version'><name>e2e test</name><version>1.0</version><os>Fedora</os></query></iq>"), + expect_stanza("/iq[@from='#foo%{irc_server_one}/{nick_one}'][@type='result'][@to='{jid_one}/{resource_one}'][@id='second_version']"), +) diff --git a/tests/end_to_end/scenarios/simple_channel_join.py b/tests/end_to_end/scenarios/simple_channel_join.py new file mode 100644 index 0000000..b09d6be --- /dev/null +++ b/tests/end_to_end/scenarios/simple_channel_join.py @@ -0,0 +1,21 @@ +from scenarios import * + +def expect_self_join_presence(jid, chan, nick, irc_server="{irc_server_one}"): + return ( + expect_stanza("/message/body[text()='Mode " + chan + " [+nt] by irc.localhost']"), + expect_stanza("/presence[@to='" + jid +"'][@from='" + chan + "%" + irc_server + "/" + nick + "']/muc_user:x/muc_user:item[@affiliation='admin'][@role='moderator']", + "/presence/muc_user:x/muc_user:status[@code='100']", # Rooms are all non-anonymous + "/presence/muc_user:x/muc_user:status[@code='110']", + ), + expect_stanza("/message[@from='" + chan + "%" + irc_server + "'][@type='groupchat']/subject[not(text())]"), + ) + + +scenario = ( + send_stanza("<presence from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}/{nick_one}' />"), + sequences.connection(), + + expect_self_join_presence(jid = '{jid_one}/{resource_one}', chan = "#foo", nick = "{nick_one}"), + +) + diff --git a/tests/end_to_end/scenarios/simple_channel_join_fixed.py b/tests/end_to_end/scenarios/simple_channel_join_fixed.py new file mode 100644 index 0000000..6efd20f --- /dev/null +++ b/tests/end_to_end/scenarios/simple_channel_join_fixed.py @@ -0,0 +1,12 @@ +from scenarios import * + +conf = "fixed_server" + +scenario = ( + send_stanza("<presence from='{jid_one}/{resource_one}' to='#foo@{biboumi_host}/{nick_one}' />"), + sequences.connection("irc.localhost", '{jid_one}/{resource_one}', fixed_irc_server=True), + expect_stanza("/message/body[text()='Mode #foo [+nt] by {irc_host_one}']"), + expect_stanza("/presence[@to='{jid_one}/{resource_one}'][@from='#foo@{biboumi_host}/{nick_one}']/muc_user:x/muc_user:item[@affiliation='admin'][@role='moderator']", + "/presence/muc_user:x/muc_user:status[@code='110']"), + expect_stanza("/message[@from='#foo@{biboumi_host}'][@type='groupchat']/subject[not(text())]"), +) diff --git a/tests/end_to_end/scenarios/simple_channel_list.py b/tests/end_to_end/scenarios/simple_channel_list.py new file mode 100644 index 0000000..7406e86 --- /dev/null +++ b/tests/end_to_end/scenarios/simple_channel_list.py @@ -0,0 +1,14 @@ +from scenarios import * + +scenario = ( + scenarios.multiple_channels_join.scenario, + + send_stanza("<iq from='{jid_one}/{resource_one}' id='id1' to='{irc_server_one}' type='get'><query xmlns='http://jabber.org/protocol/disco#items'/></iq>"), + expect_stanza("/iq[@type='result']/disco_items:query", + "/iq/disco_items:query/rsm:set/rsm:count[text()='3']", + "/iq/disco_items:query/rsm:set/rsm:first", + "/iq/disco_items:query/rsm:set/rsm:last", + "/iq/disco_items:query/disco_items:item[@jid='#foo%{irc_server_one}']", + "/iq/disco_items:query/disco_items:item[@jid='#bar%{irc_server_one}']", + "/iq/disco_items:query/disco_items:item[@jid='#baz%{irc_server_one}']"), +) diff --git a/tests/end_to_end/scenarios/simple_kick.py b/tests/end_to_end/scenarios/simple_kick.py new file mode 100644 index 0000000..0e06589 --- /dev/null +++ b/tests/end_to_end/scenarios/simple_kick.py @@ -0,0 +1,49 @@ +from scenarios import * + +scenario = ( + scenarios.channel_join_with_two_users.scenario, + # demonstrate bug https://lab.louiz.org/louiz/biboumi/issues/3291 + # First user joins an other channel + send_stanza("<presence from='{jid_one}/{resource_one}' to='#bar%{irc_server_one}/{nick_one}' />"), + expect_stanza("/message"), + expect_stanza("/presence/muc_user:x/muc_user:status[@code='110']"), + expect_stanza("/message[@type='groupchat']/subject"), + + # Second user joins + send_stanza("<presence from='{jid_two}/{resource_one}' to='#bar%{irc_server_one}/{nick_two}' />"), + expect_unordered( + ["/presence[@to='{jid_one}/{resource_one}']/muc_user:x/muc_user:item[@affiliation='none'][@role='participant']"], + [ + "/presence[@to='{jid_two}/{resource_one}']/muc_user:x/muc_user:item[@affiliation='none'][@role='participant']", + "/presence/muc_user:x/muc_user:status[@code='110']" + ], + ["/presence[@to='{jid_two}/{resource_one}']/muc_user:x/muc_user:item[@affiliation='admin'][@role='moderator']"], + ["/message/subject"] + ), + + # Moderator kicks participant + send_stanza("<iq id='kick1' to='#foo%{irc_server_one}' from='{jid_one}/{resource_one}' type='set'><query xmlns='http://jabber.org/protocol/muc#admin'><item nick='{nick_two}' role='none'><reason>reported</reason></item></query></iq>"), + expect_unordered( + [ + "/presence[@type='unavailable'][@to='{jid_two}/{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']" + ], + [ + "/presence[@type='unavailable'][@to='{jid_one}/{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']", + ], + ["/iq[@id='kick1'][@type='result']"] + ), + + # Bug 3291, suite. We must not receive any presence from #foo, here + send_stanza("<message from='{jid_two}/{resource_one}' to='{irc_server_one}' type='chat'><body>QUIT bye bye</body></message>"), + expect_unordered( + ["/presence[@from='#bar%{irc_server_one}/{nick_two}'][@to='{jid_one}/{resource_one}']"], + ["/presence[@from='#bar%{irc_server_one}/{nick_two}'][@to='{jid_two}/{resource_one}']"], + ["/message"], + ["/message"], + ), +) diff --git a/tests/end_to_end/scenarios/simple_mam.py b/tests/end_to_end/scenarios/simple_mam.py new file mode 100644 index 0000000..4509eeb --- /dev/null +++ b/tests/end_to_end/scenarios/simple_mam.py @@ -0,0 +1,60 @@ +from scenarios import * + +scenario = ( + scenarios.simple_channel_join.scenario, + + # Send two channel messages + send_stanza("<message from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}' type='groupchat'><body>coucou</body></message>"), + expect_stanza("/message[@from='#foo%{irc_server_one}/{nick_one}'][@to='{jid_one}/{resource_one}'][@type='groupchat']/body[text()='coucou']", + "/message/stable_id:stanza-id[@by='#foo%{irc_server_one}'][@id]"), + + send_stanza("<message from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}' type='groupchat'><body>coucou 2</body></message>"), + expect_stanza("/message[@from='#foo%{irc_server_one}/{nick_one}'][@to='{jid_one}/{resource_one}'][@type='groupchat']/body[text()='coucou 2']"), + + # Retrieve the complete archive + send_stanza("<iq to='#foo%{irc_server_one}' from='{jid_one}/{resource_one}' type='set' id='id1'><query xmlns='urn:xmpp:mam:2' queryid='qid1' /></iq>"), + + expect_stanza("/message/mam:result[@queryid='qid1']/forward:forwarded/delay:delay", + "/message/mam:result[@queryid='qid1']/forward:forwarded/client:message[@from='#foo%{irc_server_one}/{nick_one}'][@type='groupchat']/client:body[text()='coucou']"), + expect_stanza("/message/mam:result[@queryid='qid1']/forward:forwarded/delay:delay", + "/message/mam:result[@queryid='qid1']/forward:forwarded/client:message[@from='#foo%{irc_server_one}/{nick_one}'][@type='groupchat']/client:body[text()='coucou 2']"), + + expect_stanza("/iq[@type='result'][@id='id1'][@from='#foo%{irc_server_one}'][@to='{jid_one}/{resource_one}']", + "/iq/mam:fin/rms:set/rsm:last", + "/iq/mam:fin/rsm:set/rsm:first", + "/iq/mam:fin[@complete='true']"), + + # Retrieve an empty archive by specifying an early “end” date + send_stanza("""<iq to='#foo%{irc_server_one}' from='{jid_one}/{resource_one}' type='set' id='id2'> + <query xmlns='urn:xmpp:mam:2' queryid='qid2'> + <x xmlns='jabber:x:data' type='submit'> + <field var='FORM_TYPE' type='hidden'> <value>urn:xmpp:mam:2</value></field> + <field var='end'><value>2000-06-07T00:00:00Z</value></field> + </x> + </query></iq>"""), + + expect_stanza("/iq[@type='result'][@id='id2'][@from='#foo%{irc_server_one}'][@to='{jid_one}/{resource_one}']", + "/iq/mam:fin[@complete='true']/rsm:set"), + + # Retrieve an empty archive by specifying a late “start” date + # (note that this test will break in ~1000 years) + send_stanza("""<iq to='#foo%{irc_server_one}' from='{jid_one}/{resource_one}' type='set' id='id3'> + <query xmlns='urn:xmpp:mam:2' queryid='qid3'> + <x xmlns='jabber:x:data' type='submit'> + <field var='FORM_TYPE' type='hidden'> <value>urn:xmpp:mam:2</value></field> + <field var='start'><value>3016-06-07T00:00:00Z</value></field> + </x> + </query></iq>"""), + + expect_stanza("/iq[@type='result'][@id='id3'][@from='#foo%{irc_server_one}'][@to='{jid_one}/{resource_one}']", + "/iq/mam:fin[@complete='true']/rsm:set"), + + # Retrieve the whole archive, but limit the response to one elemet + send_stanza("<iq to='#foo%{irc_server_one}' from='{jid_one}/{resource_one}' type='set' id='id4'><query xmlns='urn:xmpp:mam:2' queryid='qid4'><set xmlns='http://jabber.org/protocol/rsm'><max>1</max></set></query></iq>"), + + expect_stanza("/message/mam:result[@queryid='qid4']/forward:forwarded/delay:delay", + "/message/mam:result[@queryid='qid4']/forward:forwarded/client:message[@from='#foo%{irc_server_one}/{nick_one}'][@type='groupchat']/client:body[text()='coucou']"), + + expect_stanza("/iq[@type='result'][@id='id4'][@from='#foo%{irc_server_one}'][@to='{jid_one}/{resource_one}']", + "!/iq/mam:fin[@complete='true']/rsm:set"), +) diff --git a/tests/end_to_end/scenarios/slash_me_channel_message.py b/tests/end_to_end/scenarios/slash_me_channel_message.py new file mode 100644 index 0000000..d30fba3 --- /dev/null +++ b/tests/end_to_end/scenarios/slash_me_channel_message.py @@ -0,0 +1,18 @@ +from scenarios import * + +scenario = ( + scenarios.channel_join_with_two_users.scenario, + # Send a channel message + send_stanza("<message from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}' type='groupchat'><body>/me rit en IRC</body></message>"), + # Receive the message, forwarded to the two users + expect_unordered( + [ + "/message[@from='#foo%{irc_server_one}/{nick_one}'][@to='{jid_one}/{resource_one}'][@type='groupchat']/body[text()='/me rit en IRC']", + "/message/stable_id:stanza-id[@by='#foo%{irc_server_one}'][@id]" + ], + [ + "/message[@from='#foo%{irc_server_one}/{nick_one}'][@to='{jid_two}/{resource_one}'][@type='groupchat']/body[text()='/me rit en IRC']", + "/message/stable_id:stanza-id[@by='#foo%{irc_server_one}'][@id]" + ], + ), +) diff --git a/tests/end_to_end/sequences.py b/tests/end_to_end/sequences.py new file mode 100644 index 0000000..8a40e52 --- /dev/null +++ b/tests/end_to_end/sequences.py @@ -0,0 +1,108 @@ +from functions import expect_stanza, send_stanza, common_replacements + +def handshake(): + return ( + expect_stanza("//handshake"), + send_stanza("<handshake xmlns='jabber:component:accept'/>") + ) + +def connection_begin(irc_host, jid, expected_irc_presence=False, fixed_irc_server=False): + jid = jid.format_map(common_replacements) + if fixed_irc_server: + xpath = "/message[@to='" + jid + "'][@from='biboumi.localhost']/body[text()='%s']" + xpath_re = "/message[@to='" + jid + "'][@from='biboumi.localhost']/body[re:test(text(), '%s')]" + else: + xpath = "/message[@to='" + jid + "'][@from='" + irc_host + "@biboumi.localhost']/body[text()='%s']" + xpath_re = "/message[@to='" + jid + "'][@from='" + irc_host + "@biboumi.localhost']/body[re:test(text(), '%s')]" + result = ( + expect_stanza(xpath % ('Connecting to %s:6697 (encrypted)' % irc_host), + "/message/hints:no-copy", + "/message/carbon:private" + ), + expect_stanza(xpath % 'Connection failed: Connection refused'), + expect_stanza(xpath % ('Connecting to %s:6670 (encrypted)' % irc_host)), + expect_stanza(xpath % 'Connection failed: Connection refused'), + expect_stanza(xpath % ('Connecting to %s:6667 (not encrypted)' % irc_host)), + expect_stanza(xpath % 'Connected to IRC server.')) + + if expected_irc_presence: + result += (expect_stanza("/presence[@from='" + irc_host + "@biboumi.localhost']"),) + + # These five messages can be receive in any order + result += ( + expect_stanza(xpath_re % (r'^%s: (\*\*\* Checking Ident|\*\*\* Looking up your hostname\.\.\.|\*\*\* Found your hostname: .*|ACK multi-prefix|\*\*\* Got Ident response)$' % 'irc.localhost')), + expect_stanza(xpath_re % (r'^%s: (\*\*\* Checking Ident|\*\*\* Looking up your hostname\.\.\.|\*\*\* Found your hostname: .*|ACK multi-prefix|\*\*\* Got Ident response)$' % 'irc.localhost')), + expect_stanza(xpath_re % (r'^%s: (\*\*\* Checking Ident|\*\*\* Looking up your hostname\.\.\.|\*\*\* Found your hostname: .*|ACK multi-prefix|\*\*\* Got Ident response)$' % 'irc.localhost')), + expect_stanza(xpath_re % (r'^%s: (\*\*\* Checking Ident|\*\*\* Looking up your hostname\.\.\.|\*\*\* Found your hostname: .*|ACK multi-prefix|\*\*\* Got Ident response)$' % 'irc.localhost')), + expect_stanza(xpath_re % (r'^%s: (\*\*\* Checking Ident|\*\*\* Looking up your hostname\.\.\.|\*\*\* Found your hostname: .*|ACK multi-prefix|\*\*\* Got Ident response)$' % 'irc.localhost')), + ) + + return result + +def connection_tls_begin(irc_host, jid, fixed_irc_server): + jid = jid.format_map(common_replacements) + if fixed_irc_server: + xpath = "/message[@to='" + jid + "'][@from='biboumi.localhost']/body[text()='%s']" + xpath_re = "/message[@to='" + jid + "'][@from='biboumi.localhost']/body[re:test(text(), '%s')]" + else: + xpath = "/message[@to='" + jid + "'][@from='" + irc_host + "@biboumi.localhost']/body[text()='%s']" + xpath_re = "/message[@to='" + jid + "'][@from='" + irc_host + "@biboumi.localhost']/body[re:test(text(), '%s')]" + irc_host = 'irc.localhost' + return ( + expect_stanza(xpath % ('Connecting to %s:7778 (encrypted)' % irc_host), + "/message/hints:no-copy", + "/message/carbon:private", + ), + expect_stanza(xpath % 'Connected to IRC server (encrypted).'), + # These five messages can be receive in any order + expect_stanza(xpath_re % (r'^%s: (\*\*\* Checking Ident|\*\*\* Looking up your hostname\.\.\.|\*\*\* Found your hostname: .*|ACK multi-prefix|\*\*\* Got Ident response)$' % irc_host)), + expect_stanza(xpath_re % (r'^%s: (\*\*\* Checking Ident|\*\*\* Looking up your hostname\.\.\.|\*\*\* Found your hostname: .*|ACK multi-prefix|\*\*\* Got Ident response)$' % irc_host)), + expect_stanza(xpath_re % (r'^%s: (\*\*\* Checking Ident|\*\*\* Looking up your hostname\.\.\.|\*\*\* Found your hostname: .*|ACK multi-prefix|\*\*\* Got Ident response)$' % irc_host)), + expect_stanza(xpath_re % (r'^%s: (\*\*\* Checking Ident|\*\*\* Looking up your hostname\.\.\.|\*\*\* Found your hostname: .*|ACK multi-prefix|\*\*\* Got Ident response)$' % irc_host)), + expect_stanza(xpath_re % (r'^%s: (\*\*\* Checking Ident|\*\*\* Looking up your hostname\.\.\.|\*\*\* Found your hostname: .*|ACK multi-prefix|\*\*\* Got Ident response)$' % irc_host)), + ) + +def connection_end(irc_host, jid, fixed_irc_server=False): + jid = jid.format_map(common_replacements) + if fixed_irc_server: + xpath = "/message[@to='" + jid + "'][@from='biboumi.localhost']/body[text()='%s']" + xpath_re = "/message[@to='" + jid + "'][@from='biboumi.localhost']/body[re:test(text(), '%s')]" + else: + xpath = "/message[@to='" + jid + "'][@from='" + irc_host + "@biboumi.localhost']/body[text()='%s']" + xpath_re = "/message[@to='" + jid + "'][@from='" + irc_host + "@biboumi.localhost']/body[re:test(text(), '%s')]" + irc_host = 'irc.localhost' + return ( + expect_stanza(xpath_re % (r'^%s: Your host is .*$' % irc_host)), + expect_stanza(xpath_re % (r'^%s: This server was created .*$' % irc_host)), + expect_stanza(xpath_re % (r'^%s: There are \d+ users and \d+ invisible on \d+ servers$' % irc_host)), + expect_stanza(xpath_re % (r'^%s: \d+ unknown connection\(s\)$' % irc_host), optional=True), + expect_stanza(xpath_re % (r'^%s: \d+ channels formed$' % irc_host), optional=True), + expect_stanza(xpath_re % (r'^%s: I have \d+ clients and \d+ servers$' % irc_host)), + expect_stanza(xpath_re % (r'^%s: \d+ \d+ Current local users \d+, max \d+$' % irc_host)), + expect_stanza(xpath_re % (r'^%s: \d+ \d+ Current global users \d+, max \d+$' % irc_host)), + expect_stanza(xpath_re % (r'^%s: Highest connection count: \d+ \(\d+ clients\) \(\d+ connections received\)$' % irc_host)), + expect_stanza(xpath % "- This is charybdis MOTD you might replace it, but if not your friends will\n- laugh at you.\n"), + expect_stanza(xpath_re % r'^User mode for \w+ is \[\+Z?i\]$'), + ) + +def connection_middle(irc_host, jid, fixed_irc_server=False): + if fixed_irc_server: + xpath_re = "/message[@to='" + jid + "'][@from='biboumi.localhost']/body[re:test(text(), '%s')]" + else: + xpath_re = "/message[@to='" + jid + "'][@from='" + irc_host + "@biboumi.localhost']/body[re:test(text(), '%s')]" + irc_host = 'irc.localhost' + return ( + expect_stanza(xpath_re % (r'^%s: \*\*\* You are exempt from flood limits$' % irc_host)), + ) + + +def connection(irc_host="irc.localhost", jid="{jid_one}/{resource_one}", expected_irc_presence=False, fixed_irc_server=False): + return connection_begin(irc_host, jid, expected_irc_presence, fixed_irc_server=fixed_irc_server) + \ + connection_middle(irc_host, jid, fixed_irc_server=fixed_irc_server) + \ + connection_end(irc_host, jid, fixed_irc_server=fixed_irc_server) + +def connection_tls(irc_host="irc.localhost", jid="{jid_one}/{resource_one}", fixed_irc_server=False): + return connection_tls_begin(irc_host, jid, fixed_irc_server) + \ + connection_middle(irc_host, jid, fixed_irc_server) +\ + connection_end(irc_host, jid, fixed_irc_server) + |