From bd41bc8b0270b83e0efd331eb010fa4347a2ef67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Thu, 10 Oct 2019 11:10:25 +0200 Subject: Split all the e2e tests in their own files MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some duplication is avoided, but most importantly: it’s much much cleaner, easier to find, functions have been cleaned up (functools.partial usages are now hidden), etc. --- tests/end_to_end/__main__.py | 3148 +------------------- tests/end_to_end/functions.py | 166 ++ tests/end_to_end/scenarios/__init__.py | 10 + .../scenarios/basic_handshake_success.py | 5 + .../scenarios/basic_subscribe_unsubscribe.py | 23 + tests/end_to_end/scenarios/channel_custom_topic.py | 30 + tests/end_to_end/scenarios/channel_force_join.py | 45 + tests/end_to_end/scenarios/channel_history.py | 18 + .../scenarios/channel_history_on_fixed_server.py | 20 + .../scenarios/channel_join_on_fixed_irc_server.py | 13 + .../scenarios/channel_join_with_different_nick.py | 15 + .../scenarios/channel_join_with_password.py | 35 + .../scenarios/channel_join_with_two_users.py | 25 + .../end_to_end/scenarios/channel_list_escaping.py | 12 + .../end_to_end/scenarios/channel_list_with_rsm.py | 67 + tests/end_to_end/scenarios/channel_messages.py | 70 + tests/end_to_end/scenarios/client_error.py | 16 + .../complete_channel_list_with_pages_of_3.py | 108 + .../scenarios/default_channel_list_limit.py | 54 + tests/end_to_end/scenarios/default_mam_limit.py | 107 + tests/end_to_end/scenarios/encoded_channel_join.py | 11 + ...e_admin_disconnect_from_server_adhoc_command.py | 70 + .../execute_disconnect_user_adhoc_command.py | 20 + .../scenarios/execute_forbidden_adhoc_command.py | 8 + .../scenarios/execute_hello_adhoc_command.py | 15 + .../execute_incomplete_hello_adhoc_command.py | 12 + .../scenarios/execute_ping_adhoc_command.py | 7 + .../scenarios/execute_reload_adhoc_command.py | 7 + .../scenarios/fixed_irc_server_subscription.py | 9 + tests/end_to_end/scenarios/fixed_muc_disco_info.py | 16 + .../scenarios/get_irc_connection_info.py | 17 + .../scenarios/get_irc_connection_info_fixed.py | 19 + tests/end_to_end/scenarios/global_configure.py | 28 + .../end_to_end/scenarios/global_configure_fixed.py | 33 + .../global_configure_persistent_by_default.py | 16 + tests/end_to_end/scenarios/invite_other.py | 22 + .../end_to_end/scenarios/irc_channel_configure.py | 36 + .../scenarios/irc_channel_configure_fixed.py | 34 + .../scenarios/irc_channel_configure_xep0045.py | 21 + tests/end_to_end/scenarios/irc_server_configure.py | 103 + .../end_to_end/scenarios/irc_server_connection.py | 8 + .../scenarios/irc_server_connection_failure.py | 12 + .../scenarios/irc_server_presence_in_roster.py | 27 + .../scenarios/irc_server_presence_subscription.py | 7 + tests/end_to_end/scenarios/irc_tls_connection.py | 26 + tests/end_to_end/scenarios/join_history_limit.py | 110 + tests/end_to_end/scenarios/leave_unjoined_chan.py | 17 + tests/end_to_end/scenarios/list_adhoc.py | 11 + .../scenarios/list_adhoc_fixed_server.py | 13 + tests/end_to_end/scenarios/list_adhoc_irc.py | 9 + tests/end_to_end/scenarios/list_admin_adhoc.py | 10 + .../scenarios/list_admin_adhoc_fixed_server.py | 13 + tests/end_to_end/scenarios/list_muc_user_adhoc.py | 7 + tests/end_to_end/scenarios/mam_on_fixed_server.py | 21 + tests/end_to_end/scenarios/mam_with_timestamps.py | 43 + tests/end_to_end/scenarios/mode_change.py | 52 + tests/end_to_end/scenarios/muc_disco_info.py | 30 + .../muc_message_from_unjoined_resource.py | 16 + tests/end_to_end/scenarios/muc_traffic_info.py | 8 + tests/end_to_end/scenarios/multiline_message.py | 62 + tests/end_to_end/scenarios/multiline_topic.py | 11 + .../end_to_end/scenarios/multiple_channels_join.py | 20 + tests/end_to_end/scenarios/multisession_kick.py | 46 + tests/end_to_end/scenarios/multisessionnick.py | 127 + tests/end_to_end/scenarios/nick_change_in_join.py | 19 + tests/end_to_end/scenarios/not_connected_error.py | 13 + tests/end_to_end/scenarios/notices.py | 10 + tests/end_to_end/scenarios/persistent_channel.py | 48 + tests/end_to_end/scenarios/quit.py | 12 + tests/end_to_end/scenarios/raw_message.py | 13 + .../scenarios/raw_message_fixed_irc_server.py | 17 + tests/end_to_end/scenarios/raw_names_command.py | 13 + ...s_removed_from_server_when_last_chan_is_left.py | 43 + tests/end_to_end/scenarios/self_disco_info.py | 12 + tests/end_to_end/scenarios/self_invite.py | 7 + .../end_to_end/scenarios/self_ping_fixed_server.py | 11 + tests/end_to_end/scenarios/self_ping_not_in_muc.py | 15 + .../scenarios/self_ping_on_real_channel.py | 23 + tests/end_to_end/scenarios/self_ping_with_error.py | 13 + tests/end_to_end/scenarios/self_version.py | 39 + tests/end_to_end/scenarios/simple_channel_join.py | 22 + .../scenarios/simple_channel_join_fixed.py | 13 + tests/end_to_end/scenarios/simple_channel_list.py | 14 + tests/end_to_end/scenarios/simple_kick.py | 49 + tests/end_to_end/scenarios/simple_mam.py | 60 + .../scenarios/slash_me_channel_message.py | 18 + tests/end_to_end/sequences.py | 108 + 87 files changed, 2693 insertions(+), 3096 deletions(-) create mode 100644 tests/end_to_end/functions.py create mode 100644 tests/end_to_end/scenarios/__init__.py create mode 100644 tests/end_to_end/scenarios/basic_handshake_success.py create mode 100644 tests/end_to_end/scenarios/basic_subscribe_unsubscribe.py create mode 100644 tests/end_to_end/scenarios/channel_custom_topic.py create mode 100644 tests/end_to_end/scenarios/channel_force_join.py create mode 100644 tests/end_to_end/scenarios/channel_history.py create mode 100644 tests/end_to_end/scenarios/channel_history_on_fixed_server.py create mode 100644 tests/end_to_end/scenarios/channel_join_on_fixed_irc_server.py create mode 100644 tests/end_to_end/scenarios/channel_join_with_different_nick.py create mode 100644 tests/end_to_end/scenarios/channel_join_with_password.py create mode 100644 tests/end_to_end/scenarios/channel_join_with_two_users.py create mode 100644 tests/end_to_end/scenarios/channel_list_escaping.py create mode 100644 tests/end_to_end/scenarios/channel_list_with_rsm.py create mode 100644 tests/end_to_end/scenarios/channel_messages.py create mode 100644 tests/end_to_end/scenarios/client_error.py create mode 100644 tests/end_to_end/scenarios/complete_channel_list_with_pages_of_3.py create mode 100644 tests/end_to_end/scenarios/default_channel_list_limit.py create mode 100644 tests/end_to_end/scenarios/default_mam_limit.py create mode 100644 tests/end_to_end/scenarios/encoded_channel_join.py create mode 100644 tests/end_to_end/scenarios/execute_admin_disconnect_from_server_adhoc_command.py create mode 100644 tests/end_to_end/scenarios/execute_disconnect_user_adhoc_command.py create mode 100644 tests/end_to_end/scenarios/execute_forbidden_adhoc_command.py create mode 100644 tests/end_to_end/scenarios/execute_hello_adhoc_command.py create mode 100644 tests/end_to_end/scenarios/execute_incomplete_hello_adhoc_command.py create mode 100644 tests/end_to_end/scenarios/execute_ping_adhoc_command.py create mode 100644 tests/end_to_end/scenarios/execute_reload_adhoc_command.py create mode 100644 tests/end_to_end/scenarios/fixed_irc_server_subscription.py create mode 100644 tests/end_to_end/scenarios/fixed_muc_disco_info.py create mode 100644 tests/end_to_end/scenarios/get_irc_connection_info.py create mode 100644 tests/end_to_end/scenarios/get_irc_connection_info_fixed.py create mode 100644 tests/end_to_end/scenarios/global_configure.py create mode 100644 tests/end_to_end/scenarios/global_configure_fixed.py create mode 100644 tests/end_to_end/scenarios/global_configure_persistent_by_default.py create mode 100644 tests/end_to_end/scenarios/invite_other.py create mode 100644 tests/end_to_end/scenarios/irc_channel_configure.py create mode 100644 tests/end_to_end/scenarios/irc_channel_configure_fixed.py create mode 100644 tests/end_to_end/scenarios/irc_channel_configure_xep0045.py create mode 100644 tests/end_to_end/scenarios/irc_server_configure.py create mode 100644 tests/end_to_end/scenarios/irc_server_connection.py create mode 100644 tests/end_to_end/scenarios/irc_server_connection_failure.py create mode 100644 tests/end_to_end/scenarios/irc_server_presence_in_roster.py create mode 100644 tests/end_to_end/scenarios/irc_server_presence_subscription.py create mode 100644 tests/end_to_end/scenarios/irc_tls_connection.py create mode 100644 tests/end_to_end/scenarios/join_history_limit.py create mode 100644 tests/end_to_end/scenarios/leave_unjoined_chan.py create mode 100644 tests/end_to_end/scenarios/list_adhoc.py create mode 100644 tests/end_to_end/scenarios/list_adhoc_fixed_server.py create mode 100644 tests/end_to_end/scenarios/list_adhoc_irc.py create mode 100644 tests/end_to_end/scenarios/list_admin_adhoc.py create mode 100644 tests/end_to_end/scenarios/list_admin_adhoc_fixed_server.py create mode 100644 tests/end_to_end/scenarios/list_muc_user_adhoc.py create mode 100644 tests/end_to_end/scenarios/mam_on_fixed_server.py create mode 100644 tests/end_to_end/scenarios/mam_with_timestamps.py create mode 100644 tests/end_to_end/scenarios/mode_change.py create mode 100644 tests/end_to_end/scenarios/muc_disco_info.py create mode 100644 tests/end_to_end/scenarios/muc_message_from_unjoined_resource.py create mode 100644 tests/end_to_end/scenarios/muc_traffic_info.py create mode 100644 tests/end_to_end/scenarios/multiline_message.py create mode 100644 tests/end_to_end/scenarios/multiline_topic.py create mode 100644 tests/end_to_end/scenarios/multiple_channels_join.py create mode 100644 tests/end_to_end/scenarios/multisession_kick.py create mode 100644 tests/end_to_end/scenarios/multisessionnick.py create mode 100644 tests/end_to_end/scenarios/nick_change_in_join.py create mode 100644 tests/end_to_end/scenarios/not_connected_error.py create mode 100644 tests/end_to_end/scenarios/notices.py create mode 100644 tests/end_to_end/scenarios/persistent_channel.py create mode 100644 tests/end_to_end/scenarios/quit.py create mode 100644 tests/end_to_end/scenarios/raw_message.py create mode 100644 tests/end_to_end/scenarios/raw_message_fixed_irc_server.py create mode 100644 tests/end_to_end/scenarios/raw_names_command.py create mode 100644 tests/end_to_end/scenarios/resource_is_removed_from_server_when_last_chan_is_left.py create mode 100644 tests/end_to_end/scenarios/self_disco_info.py create mode 100644 tests/end_to_end/scenarios/self_invite.py create mode 100644 tests/end_to_end/scenarios/self_ping_fixed_server.py create mode 100644 tests/end_to_end/scenarios/self_ping_not_in_muc.py create mode 100644 tests/end_to_end/scenarios/self_ping_on_real_channel.py create mode 100644 tests/end_to_end/scenarios/self_ping_with_error.py create mode 100644 tests/end_to_end/scenarios/self_version.py create mode 100644 tests/end_to_end/scenarios/simple_channel_join.py create mode 100644 tests/end_to_end/scenarios/simple_channel_join_fixed.py create mode 100644 tests/end_to_end/scenarios/simple_channel_list.py create mode 100644 tests/end_to_end/scenarios/simple_kick.py create mode 100644 tests/end_to_end/scenarios/simple_mam.py create mode 100644 tests/end_to_end/scenarios/slash_me_channel_message.py create mode 100644 tests/end_to_end/sequences.py diff --git a/tests/end_to_end/__main__.py b/tests/end_to_end/__main__.py index 49df45f..b94b3b1 100644 --- a/tests/end_to_end/__main__.py +++ b/tests/end_to_end/__main__.py @@ -1,7 +1,10 @@ #!/usr/bin/env python3 +from functions import StanzaError, SkipStepError + import collections import lxml.etree +import importlib import datetime import slixmpp import asyncio @@ -25,20 +28,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): """ @@ -100,7 +118,7 @@ class XMPPComponent(slixmpp.BaseXMPP): 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() @@ -117,49 +135,6 @@ class XMPPComponent(slixmpp.BaseXMPP): 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) @@ -183,37 +158,6 @@ def check_list_of_xpath(list_of_xpaths, xmpp, stanza): 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): self.process = None @@ -262,24 +206,6 @@ class IrcServerRunner(ProcessRunner): 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") @@ -395,3005 +321,34 @@ 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, "")) - - -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): + 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 + scenarios.append(Scenario(module_name, 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, - ""), - connection_sequence("irc.localhost", '{jid_one}/{resource_one}'), - ]), - Scenario("irc_server_connection_failure", - [ - handshake_sequence(), - partial(send_stanza, - ""), - 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, - ""), - 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='100']", # Rooms are all non-anonymous - "/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, - ""), - 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, - "NAMES"), - 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, - ""), - 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, "QUIT bye bye"), - partial(expect_stanza, ("/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, - ""), - partial(send_stanza, - ""), - partial(send_stanza, - " SECRET"), - - 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("nick_change_in_join", - [ - handshake_sequence(), - partial(send_stanza, - ""), - connection_sequence("irc.localhost", '{jid_one}/{resource_one}'), - partial(expect_stanza, - "/message/body[text()='Mode #foo [+nt] by {irc_host_one}']"), - partial(expect_stanza, - ("/presence[@to='{jid_one}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_one}']/muc_user:x/muc_user:item[@affiliation='admin'][@role='moderator']", - "/presence/muc_user:x/muc_user:status[@code='110']") - ), - partial(expect_stanza, "/message[@from='#foo%{irc_server_one}'][@type='groupchat']/subject[not(text())]"), - - partial(send_stanza, - ""), - 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']", - "/presence/muc_user:x/muc_user:status[@code='210']") - ), - partial(expect_stanza, "/message[@from='#bar%{irc_server_one}'][@type='groupchat']/subject[not(text())]"), - - partial(send_stanza, - ""), - partial(expect_stanza, - "/message/body[text()='Mode #boule [+nt] by {irc_host_one}']"), - partial(expect_stanza, - ("/presence[@to='{jid_one}/{resource_one}'][@from='#boule%{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']") - ), - partial(expect_stanza, "/message[@from='#boule%{irc_server_one}'][@type='groupchat']/subject[not(text())]"), - ]), - Scenario("not_connected_error", - [ - handshake_sequence(), - partial(send_stanza, - ""), - partial(send_stanza, - ""), - connection_sequence("irc.localhost", '{jid_one}/{resource_one}'), - partial(expect_stanza, - "/message/body[text()='Mode #foo [+nt] by {irc_host_one}']"), - partial(expect_stanza, - ("/presence[@to='{jid_one}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_one}']/muc_user:x/muc_user:item[@affiliation='admin'][@role='moderator']", - "/presence/muc_user:x/muc_user:status[@code='110']") - ), - partial(expect_stanza, "/message[@from='#foo%{irc_server_one}'][@type='groupchat']/subject[not(text())]"), - ]), - Scenario("channel_join_with_two_users", - [ - handshake_sequence(), - # First user joins - partial(send_stanza, - ""), - connection_sequence("irc.localhost", '{jid_one}/{resource_one}'), - partial(expect_stanza, - "/message/body[text()='Mode #foo [+nt] by {irc_host_one}']"), - partial(expect_stanza, - ("/presence[@to='{jid_one}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_one}']/muc_user:x/muc_user:item[@affiliation='admin'][@jid='{lower_nick_one}%{irc_server_one}/~{nick_one}@localhost'][@role='moderator']", - "/presence/muc_user:x/muc_user:status[@code='110']") - ), - partial(expect_stanza, "/message[@from='#foo%{irc_server_one}'][@type='groupchat']/subject[not(text())]"), - - # Second user joins - partial(send_stanza, - ""), - connection_sequence("irc.localhost", '{jid_two}/{resource_one}'), - 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='{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())]",), - ]), - ]), - Scenario("channel_force_join", - [ - handshake_sequence(), - # First user joins - partial(send_stanza, - ""), - connection_sequence("irc.localhost", '{jid_one}/{resource_one}'), - partial(expect_stanza, - "/message/body[text()='Mode #foo [+nt] by {irc_host_one}']"), - partial(expect_stanza, - ("/presence[@to='{jid_one}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_one}']/muc_user:x/muc_user:item[@affiliation='admin'][@jid='{lower_nick_one}%{irc_server_one}/~{nick_one}@localhost'][@role='moderator']", - "/presence/muc_user:x/muc_user:status[@code='110']") - ), - partial(expect_stanza, "/message[@from='#foo%{irc_server_one}'][@type='groupchat']/subject[not(text())]"), - - # Second user joins - partial(send_stanza, - ""), - connection_sequence("irc.localhost", '{jid_two}/{resource_one}'), - 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='{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())]",), - ]), - - # 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, - ""), - - 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='{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 - 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, - ""), - 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='{lower_nick_one}%{irc_server_one}/~{nick_one}@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, "/mode +k SECRET"), - 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, - ""), - 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, - " SECRET"), - # 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='{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())]",), - ]), - - ]), - Scenario("channel_custom_topic", - [ - handshake_sequence(), - # First user joins - partial(send_stanza, - ""), - connection_sequence("irc.localhost", '{jid_one}/{resource_one}'), - partial(expect_stanza, - "/message/body[text()='Mode #foo [+nt] by {irc_host_one}']"), - partial(expect_stanza, - ("/presence[@to='{jid_one}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_one}']/muc_user:x/muc_user:item[@affiliation='admin'][@jid='{lower_nick_one}%{irc_server_one}/~{nick_one}@localhost'][@role='moderator']", - "/presence/muc_user:x/muc_user:status[@code='110']") - ), - partial(expect_stanza, "/message[@from='#foo%{irc_server_one}'][@type='groupchat']/subject[not(text())]"), - - # First user sets the topic - partial(send_stanza, - "TOPIC TEST"), - partial(expect_stanza, "/message[@from='#foo%{irc_server_one}/{nick_one}'][@type='groupchat'][@to='{jid_one}/{resource_one}']/subject[text()='TOPIC TEST']"), - - # Second user joins - partial(send_stanza, - ""), - 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='{lower_nick_two}%{irc_server_one}/~{nick_two}@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='{lower_nick_two}%{irc_server_one}/~{nick_two}@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, - ""), - 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, - "FIRST LINE\nSECOND LINE."), - 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, - ""), - 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, ""), - 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, ""), - 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, ""), - 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, ""), - 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, ""), - 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, ""), - partial(expect_stanza, "/iq[@type='error']/error[@type='cancel']/stanza:feature-not-implemented"), - ] - ), - Scenario("execute_hello_adhoc_command", - [ - handshake_sequence(), - partial(send_stanza, ""), - partial(expect_stanza, ("/iq[@type='result']/commands:command[@node='hello'][@sessionid][@status='executing']", - "/iq/commands:command/dataform:x[@type='form']/dataform:title[text()='Configure your name.']", - "/iq/commands:command/dataform:x[@type='form']/dataform:instructions[text()='Please provide your name.']", - "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='text-single']/dataform:required", - "/iq/commands:command/commands:actions/commands:complete", - ), - after = partial(save_value, "sessionid", partial(extract_attribute, "/iq[@type='result']/commands:command[@node='hello']", "sessionid")) - ), - partial(send_stanza, "COUCOU"), - partial(expect_stanza, "/iq[@type='result']/commands:command[@node='hello'][@status='completed']/commands:note[@type='info'][text()='Hello COUCOU!']") - ]), - Scenario("execute_incomplete_hello_adhoc_command", - [ - handshake_sequence(), - partial(send_stanza, ""), - partial(expect_stanza, ("/iq[@type='result']/commands:command[@node='hello'][@sessionid][@status='executing']", - "/iq/commands:command/commands:actions/commands:complete", - ), - after = partial(save_value, "sessionid", partial(extract_attribute, "/iq[@type='result']/commands:command[@node='hello']", "sessionid")) - ), - partial(send_stanza, ""), - partial(expect_stanza, "/iq[@type='error']") - ]), - Scenario("execute_ping_adhoc_command", - [ - handshake_sequence(), - partial(send_stanza, ""), - 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, ""), - 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, ""), - 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, ""), - 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, ""), - partial(expect_stanza, ("/iq[@type='result']/commands:command[@node='disconnect-user'][@sessionid][@status='executing']", - "/iq/commands:command/commands:actions/commands:complete", - ), - after = partial(save_value, "sessionid", partial(extract_attribute, "/iq/commands:command[@node='disconnect-user']", "sessionid")) - ), - partial(send_stanza, "{jid_admin}Disconnected by e2e"), - 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, ""), - 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, ""), - 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, ""), - 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, ""), - 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, "{jid_one}e2e test one"), - - 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:complete", - ), - after = partial(save_value, "sessionid", partial(extract_attribute, "/iq/commands:command[@node='disconnect-from-irc-server']", "sessionid")) - ), - partial(send_stanza, "localhostDisconnected by e2e"), - 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, ""), - - 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:complete", - ), - after = partial(save_value, "sessionid", partial(extract_attribute, "/iq/commands:command[@node='disconnect-from-irc-server']", "sessionid")) - ), - partial(send_stanza, "irc.localhostDisconnected by e2e"), - 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, - ""), - 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, - ""), - # 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, - ""), - 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}']",), - ("/message[@from='#foo%{irc_server_one}'][@type='groupchat']/subject[not(text())]",), - ] - ), - - # That second user sends a private message to the first one - partial(send_stanza, "RELLO"), - # Message is received with a server-wide JID, by the two resources behind nick_one - partial(expect_stanza, ("/message[@from='{lower_nick_two}%{irc_server_one}'][@to='{jid_one}/{resource_one}'][@type='chat']/body[text()='RELLO']", - "/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, ""), - 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, ""), - 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, ""), - # The leave is forwarded only to us - partial(expect_stanza, - ("/presence[@type='unavailable']/muc_user:x/muc_user:status[@code='110']", - "/presence/status[text()='Biboumi note: 1 resources are still in this channel.']", - ) - ), - - # The second user sends two new private messages to the first user - partial(send_stanza, "first"), - partial(send_stanza, "second"), - # The first user receives the two messages, on the connected resource, once each - - partial(expect_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, - ""), - 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, ""), - partial(expect_stanza, "/iq[@type='result']/muc_owner:query/dataform:x/dataform:field[@var='persistent'][@type='boolean']/dataform:value[text()='false']"), - partial(send_stanza, "true"), - partial(expect_stanza, "/iq[@type='result']"), - - # Check that the value is now effectively true - partial(send_stanza, ""), - 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, - ""), - 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, - ""), - # 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, "coucou"), - - # 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, ""), - partial(expect_stanza, "/presence[@type='unavailable'][@from='#foo%{irc_server_one}/{nick_two}']"), - ]), - Scenario("channel_join_with_different_nick", - [ - handshake_sequence(), - partial(send_stanza, - ""), - connection_sequence("irc.localhost", '{jid_one}/{resource_one}'), - partial(expect_stanza, - "/message/body[text()='Mode #foo [+nt] by {irc_host_one}']"), - partial(expect_stanza, - ("/presence[@to='{jid_one}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_one}']/muc_user:x/muc_user:item[@affiliation='admin'][@role='moderator']", - "/presence/muc_user:x/muc_user:status[@code='110']") - ), - partial(expect_stanza, "/message[@from='#foo%{irc_server_one}'][@type='groupchat'][@to='{jid_one}/{resource_one}']/subject[not(text())]"), - - # The same resource joins a different channel with a different nick - partial(send_stanza, - ""), - - # 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, - ""), - connection_sequence("irc.localhost", '{jid_one}/{resource_one}'), - partial(expect_stanza, "/message"), - partial(expect_stanza, "/presence"), - partial(expect_stanza, "/message"), - - partial(send_stanza, "NOTICE {nick_one} :[#foo] Hello in a notice."), - 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, - ""), - 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, "un\ndeux\ntrois"), - # 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, "hello"), - - # Expect a non-empty id as a result (should be a uuid) - partial(expect_stanza, - ("!/message[@id='']/body[text()='hello']", - "/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. - partial(sleep_for, 1), - - # Second user joins - partial(send_stanza, - ""), - 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='{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 - partial(send_stanza, "a\nb\nc"), - # 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()='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']",), - ]) - ]), - Scenario("channel_messages", - [ - handshake_sequence(), - # First user joins - partial(send_stanza, - ""), - connection_sequence("irc.localhost", '{jid_one}/{resource_one}'), - partial(expect_stanza, - "/message/body[text()='Mode #foo [+nt] by {irc_host_one}']"), - partial(expect_stanza, - ("/presence[@to='{jid_one}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_one}']/muc_user:x/muc_user:item[@affiliation='admin'][@role='moderator']", - "/presence/muc_user:x/muc_user:status[@code='110']") - ), - partial(expect_stanza, "/message[@from='#foo%{irc_server_one}'][@type='groupchat']/subject[not(text())]"), - - # Second user joins - partial(send_stanza, - ""), - connection_sequence("irc.localhost", '{jid_two}/{resource_one}'), - # Our presence, sent to the other user - partial(expect_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 - partial(send_stanza, "coucou"), - # 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, "coucou in private"), - # Message is received with a server-wide JID - partial(expect_stanza, "/message[@from='{lower_nick_one}%{irc_server_one}'][@to='{jid_two}/{resource_one}'][@type='chat']/body[text()='coucou in private']"), - - # Respond to the message, to the server-wide JID - partial(send_stanza, "yes"), - # The response is received from the in-room JID - partial(expect_stanza, ("/message[@from='#foo%{irc_server_one}/{nick_two}'][@to='{jid_one}/{resource_one}'][@type='chat']/body[text()='yes']", - "/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, - ""), - 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, "re in private"), - # Message is received with a server-wide JID - partial(expect_stanza, "/message[@from='{lower_nick_one}%{irc_server_one}'][@to='{jid_two}/{resource_one}'][@type='chat']/body[text()='re in private']"), - - # Respond to the message, to the server-wide JID - partial(send_stanza, "re"), - # The response is received from the in-room JID - partial(expect_stanza, "/message[@from='#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, - ""), - partial(expect_stanza, - "/presence[@type='unavailable']/muc_user:x/muc_user:status[@code='110']"), - - # The private messages from this nick should now come (again) from the server-wide JID - partial(send_stanza, "hihihoho"), - partial(expect_stanza, - "/message[@from='{lower_nick_two}%{irc_server_one}'][@to='{jid_one}/{resource_one}']"), - ] - ), - Scenario("slash_me_channel_message", - [ - handshake_sequence(), - # First user joins - partial(send_stanza, - ""), - connection_sequence("irc.localhost", '{jid_one}/{resource_one}'), - partial(expect_stanza, - "/message/body[text()='Mode #foo [+nt] by {irc_host_one}']"), - partial(expect_stanza, - ("/presence[@to='{jid_one}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_one}']/muc_user:x/muc_user:item[@affiliation='admin'][@role='moderator']", - "/presence/muc_user:x/muc_user:status[@code='110']") - ), - partial(expect_stanza, "/message[@from='#foo%{irc_server_one}'][@type='groupchat']/subject[not(text())]"), - - # Second user joins - partial(send_stanza, - ""), - connection_sequence("irc.localhost", '{jid_two}/{resource_one}'), - # Our presence, sent to the other user - partial(expect_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 - partial(send_stanza, "/me rit en IRC"), - # 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()='/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]") - ]), - ]), - Scenario("muc_message_from_unjoined_resource", - [ - handshake_sequence(), - partial(send_stanza, - ""), - connection_sequence("irc.localhost", '{jid_one}/{resource_one}'), - partial(expect_stanza, - "/message/body[text()='Mode #foo [+nt] by {irc_host_one}']"), - partial(expect_stanza, "/presence"), - partial(expect_stanza, "/message/subject"), - - # Send a channel message - partial(send_stanza, "coucou"), - # Receive the 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]"), - ), - - # Send a message from a resource that is not joined - partial(send_stanza, "coucou"), - partial(expect_stanza, ("/message[@type='error']/error[@type='modify']/stanza:text[text()='You are not a participant in this room.']", - "/message/error/stanza:not-acceptable" - )) - - ]), - Scenario("encoded_channel_join", - [ - handshake_sequence(), - partial(send_stanza, - ""), - connection_sequence("irc.localhost", '{jid_one}/{resource_one}'), - partial(expect_stanza, - "/message/body[text()='Mode #biboumi@louiz.org:80 [+nt] by {irc_host_one}']"), - partial(expect_stanza, - ("/presence[@to='{jid_one}/{resource_one}'][@from='#biboumi\\40louiz.org\\3a80%{irc_server_one}/{nick_one}']/muc_user:x/muc_user:item[@affiliation='admin'][@role='moderator']", - "/presence/muc_user:x/muc_user:status[@code='110']") - ), - partial(expect_stanza, "/message[@from='#biboumi\\40louiz.org\\3a80%{irc_server_one}'][@type='groupchat']/subject[not(text())]"), - ]), - Scenario("self_ping_with_error", - [ - handshake_sequence(), - partial(send_stanza, - ""), - connection_sequence("irc.localhost", '{jid_one}/{resource_one}'), - partial(expect_stanza, - "/message/body[text()='Mode #foo [+nt] by {irc_host_one}']"), - partial(expect_stanza, - ("/presence[@to='{jid_one}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_one}']/muc_user:x/muc_user:item[@affiliation='admin'][@role='moderator']", - "/presence/muc_user:x/muc_user:status[@code='110']") - ), - partial(expect_stanza, "/message[@from='#foo%{irc_server_one}'][@type='groupchat']/subject[not(text())]"), - - # Send a ping to ourself - partial(send_stanza, - ""), - 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, - ""), - 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, - ""), - 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, in a muc where we’re not - partial(send_stanza, - ""), - # 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-acceptable"), - - # Send a ping to ourself, in a muc where we are, but not this resource - partial(send_stanza, - ""), - # 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-acceptable"), - ]), - Scenario("self_ping_on_real_channel", - [ - handshake_sequence(), - partial(send_stanza, - ""), - connection_sequence("irc.localhost", '{jid_one}/{resource_one}'), - partial(expect_stanza, - "/message/body[text()='Mode #foo [+nt] by {irc_host_one}']"), - partial(expect_stanza, - ("/presence[@to='{jid_one}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_one}']/muc_user:x/muc_user:item[@affiliation='admin'][@role='moderator']", - "/presence/muc_user:x/muc_user:status[@code='110']") - ), - partial(expect_stanza, "/message[@from='#foo%{irc_server_one}'][@type='groupchat']/subject[not(text())]"), - - # Send a ping to ourself - partial(send_stanza, - ""), - partial(expect_stanza, - "/iq[@from='#foo%{irc_server_one}/{nick_one}'][@type='result'][@to='{jid_one}/{resource_one}'][@id='first_ping']"), - - # Now join the same room, from the same bare JID, behind the same nick - partial(send_stanza, - ""), - partial(expect_stanza, - ("/presence[@to='{jid_one}/{resource_two}'][@from='#foo%{irc_server_one}/{nick_one}']/muc_user:x/muc_user:item[@affiliation='admin'][@role='moderator']", - "/presence/muc_user:x/muc_user:status[@code='110']") - ), - partial(expect_stanza, "/message[@from='#foo%{irc_server_one}'][@type='groupchat'][@to='{jid_one}/{resource_two}']/subject[not(text())]"), - - # And re-send a self ping - partial(send_stanza, - ""), - partial(expect_stanza, - "/iq[@from='#foo%{irc_server_one}/{nick_one}'][@type='result'][@to='{jid_one}/{resource_one}'][@id='second_ping']"), - ## And re-do exactly the same thing, just change the resource initiating the self ping - partial(send_stanza, - ""), - partial(expect_stanza, - "/iq[@from='#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, - ""), - 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, - ""), - 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, - ""), - connection_sequence("irc.localhost", '{jid_one}/{resource_one}'), - partial(expect_stanza, "/message"), - partial(expect_stanza, "/presence/muc_user:x/muc_user:status[@code='110']"), - partial(expect_stanza, "/message[@type='groupchat']/subject"), - - # Second user joins - partial(send_stanza, - ""), - connection_sequence("irc.localhost", '{jid_two}/{resource_one}'), - partial(expect_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",), - ]), - - # demonstrate bug https://lab.louiz.org/louiz/biboumi/issues/3291 - # First user joins an other channel - partial(send_stanza, - ""), - 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, - ""), - partial(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 - partial(send_stanza, - "reported"), - 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, "QUIT bye bye"), - 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, - ""), - connection_sequence("irc.localhost", '{jid_one}/{resource_one}'), - partial(expect_stanza, "/message"), - partial(expect_stanza, "/presence/muc_user:x/muc_user:status[@code='110']"), - partial(expect_stanza, "/message[@type='groupchat']/subject"), - - # Second user joins - partial(send_stanza, - ""), - connection_sequence("irc.localhost", '{jid_two}/{resource_one}'), - partial(expect_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, - "/mode +v {nick_two}"), - 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, - ""), - 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, - ""), - 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, - ""), - partial(expect_stanza, "/iq[@type='error']"), - - # using an iq, without the rights to do it - partial(send_stanza, - ""), - partial(expect_unordered, [ - ("/iq[@type='error']",), - ("/message[@type='chat'][@to='{jid_two}/{resource_one}']",), - ]), - - # using an iq, with an unknown mode - partial(send_stanza, - ""), - 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, - ""), - connection_sequence("irc.localhost", '{jid_one}/{resource_one}'), - partial(expect_stanza, "/message"), - partial(expect_stanza, "/presence/muc_user:x/muc_user:status[@code='110']"), - partial(expect_stanza, "/message[@type='groupchat']/subject"), - - # Second user joins, from two resources - partial(send_stanza, - ""), - connection_sequence("irc.localhost", '{jid_two}/{resource_one}'), - partial(expect_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, - ""), - 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, - "reported"), - 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, - ""), - connection_sequence("irc.localhost", '{jid_one}/{resource_one}'), - partial(expect_stanza, - "/message/body[text()='Mode #foo [+nt] by {irc_host_one}']"), - partial(expect_stanza, - ("/presence[@to='{jid_one}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_one}']/muc_user:x/muc_user:item[@affiliation='admin'][@role='moderator']", - "/presence/muc_user:x/muc_user:status[@code='110']") - ), - partial(expect_stanza, "/message[@from='#foo%{irc_server_one}'][@type='groupchat']/subject[not(text())]"), - - # Send a version request to ourself - partial(send_stanza, - ""), - # We receive our own request, - partial(expect_stanza, - "/iq[@from='{lower_nick_one}%{irc_server_one}'][@type='get'][@to='{jid_one}/{resource_one}']", - after = partial(save_value, "id", partial(extract_attribute, "/iq", 'id'))), - # Respond to the request - partial(send_stanza, - "e2e test1.0Fedora"), - partial(expect_stanza, - "/iq[@from='#foo%{irc_server_one}/{nick_one}'][@type='result'][@to='{jid_one}/{resource_one}'][@id='first_version']/version:query/version:name[text()='e2e test (through the biboumi gateway) 1.0 Fedora']"), - - # Now join the same room, from the same bare JID, behind the same nick - partial(send_stanza, - ""), - partial(expect_stanza, - ("/presence[@to='{jid_one}/{resource_two}'][@from='#foo%{irc_server_one}/{nick_one}']/muc_user:x/muc_user:item[@affiliation='admin'][@role='moderator']", - "/presence/muc_user:x/muc_user:status[@code='110']") - ), - partial(expect_stanza, "/message[@from='#foo%{irc_server_one}'][@type='groupchat'][@to='{jid_one}/{resource_two}']/subject[not(text())]"), - - # And re-send a self ping - partial(send_stanza, - ""), - # We receive our own request. Note that we don't know the to value, it could be one of our two resources. - partial(expect_stanza, - "/iq[@from='{lower_nick_one}%{irc_server_one}'][@type='get'][@to]", - after = (partial(save_value, "to", partial(extract_attribute, "/iq", "to")), - partial(save_value, "id", partial(extract_attribute, "/iq", "id")))), - # Respond to the request, using the extracted 'to' value as our 'from' - partial(send_stanza, - "e2e test1.0Fedora"), - partial(expect_stanza, - "/iq[@from='#foo%{irc_server_one}/{nick_one}'][@type='result'][@to='{jid_one}/{resource_two}'][@id='second_version']"), - - # And do exactly the same thing, but initiated by the other resource - partial(send_stanza, - ""), - # We receive our own request. Note that we don't know the to value, it could be one of our two resources. - partial(expect_stanza, - "/iq[@from='{lower_nick_one}%{irc_server_one}'][@type='get'][@to]", - after = (partial(save_value, "to", partial(extract_attribute, "/iq", "to")), - partial(save_value, "id", partial(extract_attribute, "/iq", "id")))), - # Respond to the request, using the extracted 'to' value as our 'from' - partial(send_stanza, - "e2e test1.0Fedora"), - partial(expect_stanza, - "/iq[@from='#foo%{irc_server_one}/{nick_one}'][@type='result'][@to='{jid_one}/{resource_one}'][@id='second_version']"), - ]), - Scenario("version_on_global_nick", - [ - handshake_sequence(), - partial(send_stanza, - ""), - connection_sequence("irc.localhost", '{jid_one}/{resource_one}'), - partial(expect_stanza, - "/message/body[text()='Mode #foo [+nt] by {irc_host_one}']"), - partial(expect_stanza, - ("/presence[@to='{jid_one}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_one}']/muc_user:x/muc_user:item[@affiliation='admin'][@role='moderator']", - "/presence/muc_user:x/muc_user:status[@code='110']") - ), - partial(expect_stanza, "/message[@from='#foo%{irc_server_one}'][@type='groupchat']/subject[not(text())]"), - - partial(send_stanza, - ""), - - 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, - "e2e test1.0Fedora"), - 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, - ""), - 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, - ""), - partial(expect_stanza, - "/message/body[text()='{nick_one} is already on channel #foo']") - ]), - Scenario("client_error", - [ - handshake_sequence(), - # First resource - partial(send_stanza, - ""), - connection_sequence("irc.localhost", '{jid_one}/{resource_one}'), - partial(expect_stanza, - "/message/body[text()='Mode #foo [+nt] by {irc_host_one}']"), - partial(expect_stanza, - ("/presence[@to='{jid_one}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_one}']/muc_user:x/muc_user:item[@affiliation='admin'][@role='moderator']", - "/presence/muc_user:x/muc_user:status[@code='110']") - ), - partial(expect_stanza, "/message[@from='#foo%{irc_server_one}'][@type='groupchat']/subject[not(text())]"), - - # Second resource, same channel - partial(send_stanza, - ""), - partial(expect_stanza, - ("/presence[@to='{jid_one}/{resource_two}'][@from='#foo%{irc_server_one}/{nick_one}']/muc_user:x/muc_user:item[@affiliation='admin'][@role='moderator']", - "/presence/muc_user:x/muc_user:status[@code='110']") - ), - partial(expect_stanza, "/message[@from='#foo%{irc_server_one}'][@type='groupchat'][@to='{jid_one}/{resource_two}']/subject[not(text())]"), - - # Now the first resource has an error - partial(send_stanza, - ""), - # 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, - ""), - 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, "coucou"), - 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, "coucou 2"), - 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, ""), - - 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, """ - - - urn:xmpp:mam:2 - 2000-06-07T00:00:00Z - - """), - - 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, """ - - - urn:xmpp:mam:2 - 3016-06-07T00:00:00Z - - """), - - 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, "1"), - - 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, - ""), - 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, "coucou"), - 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, "coucou 2"), - # 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, "coucou 3"), - partial(send_stanza, "coucou 4"), - 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, """ - - - urn:xmpp:mam:2 - {first_timestamp} - {second_timestamp} - - - """), - - - 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(), - - # Disable the throttling because the test is based on timings - partial(send_stanza, ""), - 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, "" - "" - "" - "6667" - "66976670" - "9999" - ""), - partial(expect_stanza, "/iq[@type='result']/commands:command[@node='configure'][@status='completed']/commands:note[@type='info'][text()='Configuration successfully applied.']"), - - - partial(send_stanza, - ""), - connection_sequence("irc.localhost", '{jid_one}/{resource_one}'), - partial(expect_stanza, - "/message/body[text()='Mode #foo [+nt] by {irc_host_one}']"), - partial(expect_stanza, - ("/presence[@to='{jid_one}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_one}']/muc_user:x/muc_user:item[@affiliation='admin'][@role='moderator']", - "/presence/muc_user:x/muc_user:status[@code='110']") - ), - partial(expect_stanza, "/message[@from='#foo%{irc_server_one}'][@type='groupchat']/subject[not(text())]"), - - # Send two channel messages - partial(send_stanza, "coucou"), - 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, "coucou 2"), - # 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, "coucou 3"), - partial(send_stanza, "coucou 4"), - 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, - ""), - 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, ""), - partial(expect_stanza, "/presence[@type='unavailable']"), - - partial(sleep_for, 0.2), - - # Rejoin #foo, with some history limit - partial(send_stanza, - ""), - partial(expect_stanza, "/message"), - partial(expect_stanza, "/presence/muc_user:x/muc_user:status[@code='110']"), - partial(expect_stanza, "/message/subject"), - - partial(send_stanza, ""), - partial(expect_stanza, "/presence[@type='unavailable']"), - - partial(sleep_for, 0.2), - - # Rejoin #foo, with some history limit - partial(send_stanza, - ""), - 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, ""), - partial(expect_stanza, "/presence[@type='unavailable']"), - - - # Rejoin #foo, with some history limit - partial(send_stanza, - ""), - 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, ""), - partial(expect_stanza, "/presence[@type='unavailable']"), - - # Rejoin #foo, with some history limit - partial(send_stanza, - ""), - 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, ""), - partial(expect_stanza, "/presence[@type='unavailable']"), - - # Rejoin #foo, with some history limit - partial(send_stanza, - ""), - 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, ""), - partial(expect_stanza, "/presence[@type='unavailable']"), - - ]), - Scenario("mam_on_fixed_server", - [ - handshake_sequence(), - - partial(send_stanza, - ""), - 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, "coucou"), - partial(expect_stanza, "/message[@from='#foo@{biboumi_host}/{nick_one}'][@to='{jid_one}/{resource_one}'][@type='groupchat']/body[text()='coucou']"), - - partial(send_stanza, "coucou 2"), - 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, ""), - - 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(), - - # Disable the throttling, otherwise it’s way too long - partial(send_stanza, ""), - 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, "" - "" - "" - "6667" - "66976670" - "9999" - ""), - partial(expect_stanza, "/iq[@type='result']/commands:command[@node='configure'][@status='completed']/commands:note[@type='info'][text()='Configuration successfully applied.']"), - - partial(send_stanza, - ""), - connection_sequence("irc.localhost", '{jid_one}/{resource_one}'), - partial(expect_stanza, - "/message/body[text()='Mode #foo [+nt] by {irc_host_one}']"), - partial(expect_stanza, - ("/presence[@to='{jid_one}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_one}']/muc_user:x/muc_user:item[@affiliation='admin'][@role='moderator']", - "/presence/muc_user:x/muc_user:status[@code='110']") - ), - partial(expect_stanza, "/message[@from='#foo%{irc_server_one}'][@type='groupchat']/subject[not(text())]", - after = partial(save_value, "counter", lambda x: 0)), - ] + [ - partial(send_stanza, "{counter}"), - 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, ""), - 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, "{last_uuid}"), - - 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, "DUMMY_ID"), - 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, ""), - - 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, "{last_uuid}2"), - 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, "0"), - - 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, - ""), - 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='{lower_nick_one}@{biboumi_host}/~{nick_one}@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, "coucou"), - 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, - ""), - partial(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 - 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, - ""), - 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='{lower_nick_one}%{irc_server_one}/~{nick_one}@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, "coucou"), - 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, - ""), - 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='{lower_nick_one}%{irc_server_one}/~{nick_one}@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, - ""), - 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, - ""), - 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, ""), - 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, - ""), - 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, - ""), - 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, - ""), - 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, - ""), - 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, "0"), - - # 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, "2"), - 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, "12"), - 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, "#bar%{irc_server_one}1"), - 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, "#coucou%{irc_server_one}1"), - 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, "#foo%{irc_server_one}1"), - 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(), - - # Disable the throttling, otherwise it’s way too long - partial(send_stanza, ""), - 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, "" - "" - "" - "6667" - "66976670" - "9999" - ""), - partial(expect_stanza, "/iq[@type='result']/commands:command[@node='configure'][@status='completed']/commands:note[@type='info'][text()='Configuration successfully applied.']"), - - partial(send_stanza, - ""), - 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, - ""), - 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, ""), - # 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, - ""), - connection_sequence("irc.localhost", '{jid_one}/{resource_one}'), - partial(expect_stanza, "/message"), - partial(expect_stanza, "/presence"), - partial(expect_stanza, "/message"), - - partial(send_stanza, - ""), - partial(expect_stanza, "/message"), - partial(expect_stanza, "/presence"), - partial(expect_stanza, "/message"), - - partial(send_stanza, - ""), - partial(expect_stanza, "/message"), - partial(expect_stanza, "/presence"), - partial(expect_stanza, "/message"), - - partial(send_stanza, - ""), - partial(expect_stanza, "/message"), - partial(expect_stanza, "/presence"), - partial(expect_stanza, "/message"), - - partial(send_stanza, - ""), - partial(expect_stanza, "/message"), - partial(expect_stanza, "/presence"), - partial(expect_stanza, "/message"), - - partial(send_stanza, - ""), - partial(expect_stanza, "/message"), - partial(expect_stanza, "/presence"), - partial(expect_stanza, "/message"), - - partial(send_stanza, - ""), - partial(expect_stanza, "/message"), - partial(expect_stanza, "/presence"), - partial(expect_stanza, "/message"), - - partial(send_stanza, - ""), - partial(expect_stanza, "/message"), - partial(expect_stanza, "/presence"), - partial(expect_stanza, "/message"), - - partial(send_stanza, - ""), - partial(expect_stanza, "/message"), - partial(expect_stanza, "/presence"), - partial(expect_stanza, "/message"), - - partial(send_stanza, - ""), - partial(expect_stanza, "/message"), - partial(expect_stanza, "/presence"), - partial(expect_stanza, "/message"), - - partial(send_stanza, "3"), - 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, "#ccc%{irc_server_one}3"), - 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, "#fff%{irc_server_one}3"), - 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, "#iii%{irc_server_one}3"), - 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, ""), - partial(send_stanza, ""), - partial(send_stanza, ""), - partial(send_stanza, ""), - partial(send_stanza, ""), - partial(send_stanza, ""), - partial(send_stanza, ""), - partial(send_stanza, ""), - partial(send_stanza, ""), - partial(send_stanza, ""), - 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, - ""), - 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, - ""), - 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']", - "/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 - partial(send_stanza, - ""), - connection_sequence("irc.localhost", '{jid_one}/{resource_one}'), - partial(expect_stanza, - "/message/body[text()='Mode #foo [+nt] by {irc_host_one}']"), - partial(expect_stanza, - ("/presence[@to='{jid_one}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_one}']/muc_user:x/muc_user:item[@affiliation='admin'][@role='moderator']", - "/presence/muc_user:x/muc_user:status[@code='110']") - ), - partial(expect_stanza, "/message[@from='#foo%{irc_server_one}'][@type='groupchat']/subject[not(text())]"), - - partial(send_stanza, - ""), - partial(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']" - )), - ]), - Scenario("fixed_muc_disco_info", - [ - handshake_sequence(), - - partial(send_stanza, - ""), - 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, ""), - connection_sequence("irc.localhost", '{jid_one}/{resource_one}'), - partial(expect_stanza, "/message"), - partial(expect_stanza, "/presence"), - partial(expect_stanza, "/message"), - - partial(send_stanza, "WHOIS {nick_one}"), - 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, ""), - 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, "WHOIS {nick_one}"), - 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, ""), - 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, ""), - connection_sequence("irc.localhost", '{jid_one}/{resource_one}'), - partial(expect_stanza, "/message"), - partial(expect_stanza, "/presence"), - partial(expect_stanza, "/message"), - - partial(send_stanza, ""), - connection_sequence("irc.localhost", '{jid_two}/{resource_two}'), - partial(expect_stanza, "/message"), - partial(expect_stanza, "/presence"), - partial(expect_stanza, "/message"), - partial(send_stanza, ""), - 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, ""), - 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, ""), - 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:complete", - ), - after = partial(save_value, "sessionid", partial(extract_attribute, "/iq[@type='result']/commands:command[@node='configure']", "sessionid")) - ), - partial(send_stanza, "042"), - partial(expect_stanza, "/iq[@type='result']/commands:command[@node='configure'][@status='completed']/commands:note[@type='info'][text()='Configuration successfully applied.']"), - - partial(send_stanza, ""), - 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:complete", - ), - after = partial(save_value, "sessionid", partial(extract_attribute, "/iq[@type='result']/commands:command[@node='configure']", "sessionid")) - ), - partial(send_stanza, ""), - partial(expect_stanza, "/iq[@type='result']/commands:command[@node='configure'][@status='canceled']"), - ]), - Scenario("global_configure_fixed", - [ - handshake_sequence(), - partial(send_stanza, ""), - 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:complete", - ), - after = partial(save_value, "sessionid", partial(extract_attribute, "/iq[@type='result']/commands:command[@node='global-configure']", "sessionid")) - ), - partial(send_stanza, "042"), - partial(expect_stanza, "/iq[@type='result']/commands:command[@node='global-configure'][@status='completed']/commands:note[@type='info'][text()='Configuration successfully applied.']"), - - partial(send_stanza, ""), - 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:complete", - ), - after = partial(save_value, "sessionid", partial(extract_attribute, "/iq[@type='result']/commands:command[@node='global-configure']", "sessionid")) - ), - partial(send_stanza, ""), - partial(expect_stanza, "/iq[@type='result']/commands:command[@node='global-configure'][@status='canceled']"), - - partial(send_stanza, ""), - 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, ""), - 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:complete", - ), - ), - ],conf='persistent_by_default'), - Scenario("irc_server_configure", - [ - handshake_sequence(), - partial(send_stanza, ""), - 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-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='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 = partial(save_value, "sessionid", partial(extract_attribute, "/iq[@type='result']/commands:command[@node='configure']", "sessionid")) - ), - partial(send_stanza, "" - "" - "" - "" - "66976698" - "1" - "12:12:12" - "coucou" - "first commandsecond command" - "my_nickname" - "username" - "42" - "realname" - "UTF-8" - "latin-1" - ""), - partial(expect_stanza, "/iq[@type='result']/commands:command[@node='configure'][@status='completed']/commands:note[@type='info'][text()='Configuration successfully applied.']"), - - partial(send_stanza, ""), - 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='throttle_limit']/dataform:value[text()='42']", - "/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 = partial(save_value, "sessionid", partial(extract_attribute, "/iq[@type='result']/commands:command[@node='configure']", "sessionid")) - ), - partial(send_stanza, ""), - partial(expect_stanza, "/iq[@type='result']/commands:command[@node='configure'][@status='canceled']"), - - # Same thing, but try to empty some values - partial(send_stanza, ""), - 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, "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - ""), - partial(expect_stanza, "/iq[@type='result']/commands:command[@node='configure'][@status='completed']/commands:note[@type='info'][text()='Configuration successfully applied.']"), - - partial(send_stanza, ""), - 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:complete", - "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='text-single'][@var='throttle_limit']/dataform:value[text()='-1']", # An invalid value sets this field to -1, aka disabled - ), - after = partial(save_value, "sessionid", partial(extract_attribute, "/iq[@type='result']/commands:command[@node='configure']", "sessionid")) - ), - partial(send_stanza, ""), - partial(expect_stanza, "/iq[@type='result']/commands:command[@node='configure'][@status='canceled']"), - - ]), - Scenario("irc_channel_configure", - [ - handshake_sequence(), - partial(send_stanza, ""), - 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']", - "!/iq/commands:command/commands:dummy", - ), - after = partial(save_value, "sessionid", partial(extract_attribute, "/iq[@type='result']/commands:command[@node='configure']", "sessionid")) - ), - partial(send_stanza, "" - "" - "" - "" - "UTF-8" - "latin-1" - "true" - ""), - partial(expect_stanza, "/iq[@type='result']/commands:command[@node='configure'][@status='completed']/commands:note[@type='info'][text()='Configuration successfully applied.']"), - - partial(send_stanza, ""), - 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:complete", - ), - after = partial(save_value, "sessionid", partial(extract_attribute, "/iq[@type='result']/commands:command[@node='configure']", "sessionid")) - ), - partial(send_stanza, ""), - partial(expect_stanza, "/iq[@type='result']/commands:command[@node='configure'][@status='canceled']"), - ]), - Scenario("irc_channel_configure_xep0045", - [ - handshake_sequence(), - partial(send_stanza, ""), - 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, "" - "" - "" - "" - "UTF-8" - "latin-1" - ""), - partial(expect_stanza, "/iq[@type='result']"), - partial(send_stanza, " "), - partial(expect_stanza, "/iq[@type='result']"), - ]), - Scenario("irc_channel_configure_fixed", - [ - handshake_sequence(), - partial(send_stanza, ""), - 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, "" - "" - "" - "" - "UTF-8" - "latin-1" - ""), - partial(expect_stanza, "/iq[@type='result']/commands:command[@node='configure'][@status='completed']/commands:note[@type='info'][text()='Configuration successfully applied.']"), - - partial(send_stanza, ""), - 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:complete", - ), - after = partial(save_value, "sessionid", partial(extract_attribute, "/iq[@type='result']/commands:command[@node='configure']", "sessionid")) - ), - partial(send_stanza, ""), - 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, ""), - 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, "" - "" - "" - "" - "7778" - "0" - "my_special_nickname" - ""), - partial(expect_stanza, "/iq[@type='result']/commands:command[@node='configure'][@status='completed']/commands:note[@type='info'][text()='Configuration successfully applied.']"), - - partial(send_stanza, - ""), - 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, ""), - partial(expect_stanza, "/iq/commands:command/commands:note[text()='You are not connected to the IRC server irc.localhost']"), - - partial(send_stanza, - ""), - connection_sequence("irc.localhost", '{jid_one}/{resource_one}'), - partial(expect_stanza, "/message"), - partial(expect_stanza, "/presence"), - partial(expect_stanza, "/message"), - - partial(send_stanza, ""), - 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, ""), - partial(expect_stanza, "/iq/commands:command/commands:note[text()='You are not connected to the IRC server irc.localhost']"), - - partial(send_stanza, - ""), - 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, ""), - 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, ""), - partial(expect_stanza, "/presence[@to='{jid_one}'][@from='{irc_server_one}'][@type='subscribed']") - ]), - Scenario("fixed_irc_server_presence_subscription", - [ - handshake_sequence(), - partial(send_stanza, ""), - partial(expect_stanza, "/presence[@to='{jid_one}'][@from='{biboumi_host}'][@type='subscribed']") - ], conf='fixed_server'), - Scenario("leave_unjoined_chan", - [ - handshake_sequence(), - partial(send_stanza, ""), - connection_sequence("irc.localhost", '{jid_one}/{resource_one}'), - partial(expect_stanza, "/message"), - partial(expect_stanza, "/presence"), - partial(expect_stanza, "/message"), - - partial(send_stanza, ""), - 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, "") - ]), - Scenario("basic_subscribe_unsubscribe", - [ - handshake_sequence(), - - # Mutual subscription exchange - partial(send_stanza, ""), - 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, ""), - - - # Unsubscribe - partial(send_stanza, ""), - partial(expect_stanza, "/presence[@type='unavailable']"), - partial(expect_stanza, "/presence[@type='unsubscribed']"), - partial(expect_stanza, "/presence[@type='unsubscribe']"), - partial(send_stanza, ""), - partial(send_stanza, ""), - ]), - Scenario("resource_is_removed_from_server_when_last_chan_is_left", - [ - # Join the channel - handshake_sequence(), - partial(send_stanza, - ""), - connection_sequence("irc.localhost", '{jid_one}/{resource_one}'), - partial(expect_stanza, - "/message/body[text()='Mode #foo [+nt] by {irc_host_one}']"), - partial(expect_stanza, - ("/presence[@to='{jid_one}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_one}']/muc_user:x/muc_user:item[@affiliation='admin'][@role='moderator']", - "/presence/muc_user:x/muc_user:status[@code='110']") - ), - partial(expect_stanza, "/message[@from='#foo%{irc_server_one}'][@type='groupchat'][@to='{jid_one}/{resource_one}']/subject[not(text())]"), - - # Make it persistent - partial(send_stanza, ""), - partial(expect_stanza, "/iq[@type='result']/muc_owner:query/dataform:x/dataform:field[@var='persistent'][@type='boolean']/dataform:value[text()='false']"), - partial(send_stanza, "true"), - partial(expect_stanza, "/iq[@type='result']"), - - partial(send_stanza, ""), - 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, - ""), - 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, - ""), - 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, "kikoo"), - partial(send_stanza, "second kikoo"), - - # 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, ""), - partial(expect_stanza, "/presence[@type='subscribed'][@id='subid1']"), - - partial(expect_stanza, "/presence[@type='subscribe']"), - partial(send_stanza, ""), - - # Join a channel on that server - partial(send_stanza, - ""), - - # 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, ""), - 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 + provided_scenar_names = sys.argv[1:] + scenarios = get_scenarios(os.path.abspath(os.path.dirname(__file__)), provided_scenar_names) - scenar_list = sys.argv[1:] irc_output = open("irc_output.txt", "w") irc = IrcServerRunner() print("Starting irc server…") @@ -3407,12 +362,12 @@ 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." % @@ -3430,3 +385,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..fe5aea6 --- /dev/null +++ b/tests/end_to_end/functions.py @@ -0,0 +1,166 @@ +from functools import partial +import collections +import datetime +import asyncio +import time +import lxml +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 + xmpp.stanza_checker = partial(check_func, [xpath.format_map(replacements) for xpath in xpaths], xmpp, after) + 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) + 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..8875166 --- /dev/null +++ b/tests/end_to_end/scenarios/basic_handshake_success.py @@ -0,0 +1,5 @@ +from scenarios import * + +scenario = ( + sequences.handshake(), +) 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..cbcfeb6 --- /dev/null +++ b/tests/end_to_end/scenarios/basic_subscribe_unsubscribe.py @@ -0,0 +1,23 @@ +from scenarios import * + +scenario = ( + sequences.handshake(), + + # Mutual subscription exchange + send_stanza(""), + 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(""), + + # Unsubscribe + send_stanza(""), + expect_stanza("/presence[@type='unavailable']"), + expect_stanza("/presence[@type='unsubscribed']"), + expect_stanza("/presence[@type='unsubscribe']"), + send_stanza(""), + send_stanza(""), +) 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("TOPIC TEST"), + 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(""), + 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(""), + 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("coucou"), + 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(""), + 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("coucou"), + expect_stanza("/message[@from='#foo@{biboumi_host}/{nick_one}'][@to='{jid_one}/{resource_one}'][@type='groupchat']/body[text()='coucou']"), + + # Second user joins + send_stanza(""), + 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..88bdaa2 --- /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 = ( + sequences.handshake(), + send_stanza(""), + 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..7005f66 --- /dev/null +++ b/tests/end_to_end/scenarios/channel_join_with_different_nick.py @@ -0,0 +1,15 @@ +from scenarios import * + +from scenarios.simple_channel_join import expect_self_join_presence + +scenario = ( + sequences.handshake(), + send_stanza(""), + 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(""), + # 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("/mode +k SECRET"), + 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(""), + 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(" SECRET"), + 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(""), + 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..31676a2 --- /dev/null +++ b/tests/end_to_end/scenarios/channel_list_escaping.py @@ -0,0 +1,12 @@ +from scenarios import * + +scenario = ( + sequences.handshake(), + + send_stanza(""), + 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(""), + 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(""), + 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("0"), + + # 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("2"), + 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("12"), + 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("#bar%{irc_server_one}1"), + 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("#coucou%{irc_server_one}1"), + 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("#foo%{irc_server_one}1"), + 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(""), + 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("coucou"), + # 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("coucou in private"), + # 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("yes"), + # 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(""), + 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("re in private"), + + # 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("re"), + # 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(""), + 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("hihihoho"), + 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(""), + 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(""), + # 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..1d4c9dc --- /dev/null +++ b/tests/end_to_end/scenarios/complete_channel_list_with_pages_of_3.py @@ -0,0 +1,108 @@ +from scenarios import * + +scenario = ( + sequences.handshake(), + + send_stanza(""), + sequences.connection("irc.localhost", '{jid_one}/{resource_one}'), + expect_stanza("/message"), + expect_stanza("/presence"), + expect_stanza("/message"), + + send_stanza(""), + expect_stanza("/message"), + expect_stanza("/presence"), + expect_stanza("/message"), + + send_stanza(""), + expect_stanza("/message"), + expect_stanza("/presence"), + expect_stanza("/message"), + + send_stanza(""), + expect_stanza("/message"), + expect_stanza("/presence"), + expect_stanza("/message"), + + send_stanza(""), + expect_stanza("/message"), + expect_stanza("/presence"), + expect_stanza("/message"), + + send_stanza(""), + expect_stanza("/message"), + expect_stanza("/presence"), + expect_stanza("/message"), + + send_stanza(""), + expect_stanza("/message"), + expect_stanza("/presence"), + expect_stanza("/message"), + + send_stanza(""), + expect_stanza("/message"), + expect_stanza("/presence"), + expect_stanza("/message"), + + send_stanza(""), + expect_stanza("/message"), + expect_stanza("/presence"), + expect_stanza("/message"), + + send_stanza(""), + expect_stanza("/message"), + expect_stanza("/presence"), + expect_stanza("/message"), + + send_stanza("3"), + 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("#ccc%{irc_server_one}3"), + 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("#fff%{irc_server_one}3"), + 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("#iii%{irc_server_one}3"), + 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(""), + send_stanza(""), + send_stanza(""), + send_stanza(""), + send_stanza(""), + send_stanza(""), + send_stanza(""), + send_stanza(""), + send_stanza(""), + send_stanza(""), + 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/default_channel_list_limit.py b/tests/end_to_end/scenarios/default_channel_list_limit.py new file mode 100644 index 0000000..831c4c8 --- /dev/null +++ b/tests/end_to_end/scenarios/default_channel_list_limit.py @@ -0,0 +1,54 @@ +from scenarios import * + +def incr_counter(): + counter = -1 + def f(stanza): + nonlocal counter + counter += 1 + return counter + return f + +counter = incr_counter() + +scenario = ( + sequences.handshake(), + + # Disable the throttling, otherwise it’s way too long + send_stanza(""), + expect_stanza("/iq[@type='result']", + after = save_value("sessionid", extract_attribute("/iq[@type='result']/commands:command[@node='configure']", "sessionid"))), + send_stanza("" + "" + "" + "6667" + "66976670" + "9999" + ""), + 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(""), + sequences.connection(), + + scenarios.simple_channel_join.expect_self_join_presence(jid = '{jid_one}/{resource_one}', chan = "#foo", nick = "{nick_one}"), + + + ( + send_stanza(""), + expect_stanza("/message"), + expect_stanza("/presence", + after = save_value("counter", counter)), + expect_stanza("/message"), + ) * 110, + + send_stanza(""), + # 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..7f9e101 --- /dev/null +++ b/tests/end_to_end/scenarios/default_mam_limit.py @@ -0,0 +1,107 @@ +from scenarios import * + +scenario = ( + sequences.handshake(), + + # Disable the throttling, otherwise it’s way too long + send_stanza(""), + expect_stanza("/iq[@type='result']", + after = save_value("sessionid", extract_attribute("/iq[@type='result']/commands:command[@node='configure']", "sessionid"))), + send_stanza("" + "" + "" + "6667" + "66976670" + "9999" + ""), + expect_stanza("/iq[@type='result']/commands:command[@node='configure'][@status='completed']/commands:note[@type='info'][text()='Configuration successfully applied.']"), + + send_stanza(""), + 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("{counter}"), + 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(""), + 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("{last_uuid}"), + + 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("DUMMY_ID"), + 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(""), + + 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("{last_uuid}2"), + 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("0"), + + 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..062bdab --- /dev/null +++ b/tests/end_to_end/scenarios/encoded_channel_join.py @@ -0,0 +1,11 @@ +from scenarios import * + +scenario = ( + sequences.handshake(), + send_stanza(""), + 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..87eb213 --- /dev/null +++ b/tests/end_to_end/scenarios/execute_admin_disconnect_from_server_adhoc_command.py @@ -0,0 +1,70 @@ +from scenarios import * + +from scenarios.simple_channel_join import expect_self_join_presence + +scenario = ( + sequences.handshake(), + + # Admin connects to first server + send_stanza(""), + 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(""), + 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(""), + 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(""), + 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("{jid_one}e2e test one"), + 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("localhostDisconnected by e2e"), + # 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(""), + 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("irc.localhostDisconnected by e2e"), + 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..25d5f6b --- /dev/null +++ b/tests/end_to_end/scenarios/execute_disconnect_user_adhoc_command.py @@ -0,0 +1,20 @@ +from scenarios import * + +from scenarios.simple_channel_join import expect_self_join_presence + +scenario = ( + sequences.handshake(), + send_stanza(""), + sequences.connection("irc.localhost", '{jid_admin}/{resource_one}'), + expect_self_join_presence(jid = '{jid_admin}/{resource_one}', chan = "#foo", nick = "{nick_one}"), + + send_stanza(""), + 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("{jid_admin}Disconnected by e2e"), + 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..8785dd9 --- /dev/null +++ b/tests/end_to_end/scenarios/execute_forbidden_adhoc_command.py @@ -0,0 +1,8 @@ +from scenarios import * + +scenario = ( + sequences.handshake(), + send_stanza(""), + 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..3fd2fb6 --- /dev/null +++ b/tests/end_to_end/scenarios/execute_hello_adhoc_command.py @@ -0,0 +1,15 @@ +from scenarios import * + +scenario = ( + sequences.handshake(), + send_stanza(""), + 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("COUCOU"), + 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..3035ec0 --- /dev/null +++ b/tests/end_to_end/scenarios/execute_incomplete_hello_adhoc_command.py @@ -0,0 +1,12 @@ +from scenarios import * + +scenario = ( + sequences.handshake(), + send_stanza(""), + 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(""), + 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..2af30ee --- /dev/null +++ b/tests/end_to_end/scenarios/execute_ping_adhoc_command.py @@ -0,0 +1,7 @@ +from scenarios import * + +scenario = ( + sequences.handshake(), + send_stanza(""), + 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..2cced19 --- /dev/null +++ b/tests/end_to_end/scenarios/execute_reload_adhoc_command.py @@ -0,0 +1,7 @@ +from scenarios import * + +scenario = ( + sequences.handshake(), + send_stanza(""), + 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..ecac07e --- /dev/null +++ b/tests/end_to_end/scenarios/fixed_irc_server_subscription.py @@ -0,0 +1,9 @@ +from scenarios import * + +conf = 'fixed_server' + +scenario = ( + sequences.handshake(), + send_stanza(""), + 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..7a5f0b9 --- /dev/null +++ b/tests/end_to_end/scenarios/fixed_muc_disco_info.py @@ -0,0 +1,16 @@ +from scenarios import * + +conf = 'fixed_server' + +scenario = ( + sequences.handshake(), + + send_stanza(""), + 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..ab30c02 --- /dev/null +++ b/tests/end_to_end/scenarios/get_irc_connection_info.py @@ -0,0 +1,17 @@ +from scenarios import * + +scenario = ( + sequences.handshake(), + + send_stanza(""), + expect_stanza("/iq/commands:command/commands:note[text()='You are not connected to the IRC server irc.localhost']"), + + send_stanza(""), + sequences.connection("irc.localhost", '{jid_one}/{resource_one}'), + expect_stanza("/message"), + expect_stanza("/presence"), + expect_stanza("/message"), + + send_stanza(""), + 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..f90fa6a --- /dev/null +++ b/tests/end_to_end/scenarios/get_irc_connection_info_fixed.py @@ -0,0 +1,19 @@ +from scenarios import * + +conf = 'fixed_server' + +scenario = ( + sequences.handshake(), + + send_stanza(""), + expect_stanza("/iq/commands:command/commands:note[text()='You are not connected to the IRC server irc.localhost']"), + + send_stanza(""), + sequences.connection("irc.localhost", '{jid_one}/{resource_one}', fixed_irc_server=True), + expect_stanza("/message"), + expect_stanza("/presence"), + expect_stanza("/message"), + + send_stanza(""), + 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..3c88fe6 --- /dev/null +++ b/tests/end_to_end/scenarios/global_configure.py @@ -0,0 +1,28 @@ +from scenarios import * + +scenario = ( + sequences.handshake(), + send_stanza(""), + 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("042"), + expect_stanza("/iq[@type='result']/commands:command[@node='configure'][@status='completed']/commands:note[@type='info'][text()='Configuration successfully applied.']"), + + send_stanza(""), + 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(""), + 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..718a09e --- /dev/null +++ b/tests/end_to_end/scenarios/global_configure_fixed.py @@ -0,0 +1,33 @@ +from scenarios import * + +conf = 'fixed_server' + +scenario = ( + sequences.handshake(), + send_stanza(""), + 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("042"), + expect_stanza("/iq[@type='result']/commands:command[@node='global-configure'][@status='completed']/commands:note[@type='info'][text()='Configuration successfully applied.']"), + + send_stanza(""), + 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(""), + expect_stanza("/iq[@type='result']/commands:command[@node='global-configure'][@status='canceled']"), + + send_stanza(""), + 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..0c56dc2 --- /dev/null +++ b/tests/end_to_end/scenarios/global_configure_persistent_by_default.py @@ -0,0 +1,16 @@ +from scenarios import * + +conf='persistent_by_default' + +scenario = ( + sequences.handshake(), + send_stanza(""), + 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..a75508d --- /dev/null +++ b/tests/end_to_end/scenarios/invite_other.py @@ -0,0 +1,22 @@ +from scenarios import * + +scenario = ( + sequences.handshake(), + send_stanza(""), + sequences.connection("irc.localhost", '{jid_one}/{resource_one}'), + expect_stanza("/message"), + expect_stanza("/presence"), + expect_stanza("/message"), + + send_stanza(""), + sequences.connection("irc.localhost", '{jid_two}/{resource_two}'), + expect_stanza("/message"), + expect_stanza("/presence"), + expect_stanza("/message"), + send_stanza(""), + 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(""), + 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..15647d1 --- /dev/null +++ b/tests/end_to_end/scenarios/irc_channel_configure.py @@ -0,0 +1,36 @@ +from scenarios import * + +scenario = ( + sequences.handshake(), + send_stanza(""), + 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("" + "" + "" + "" + "UTF-8" + "latin-1" + "true" + ""), + expect_stanza("/iq[@type='result']/commands:command[@node='configure'][@status='completed']/commands:note[@type='info'][text()='Configuration successfully applied.']"), + + send_stanza(""), + 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(""), + 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..3e72865 --- /dev/null +++ b/tests/end_to_end/scenarios/irc_channel_configure_fixed.py @@ -0,0 +1,34 @@ +from scenarios import * + +conf = 'fixed_server' + +scenario = ( + sequences.handshake(), + send_stanza(""), + 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("" + "" + "" + "" + "UTF-8" + "latin-1" + ""), + expect_stanza("/iq[@type='result']/commands:command[@node='configure'][@status='completed']/commands:note[@type='info'][text()='Configuration successfully applied.']"), + + send_stanza(""), + 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(""), + 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..22c4ac9 --- /dev/null +++ b/tests/end_to_end/scenarios/irc_channel_configure_xep0045.py @@ -0,0 +1,21 @@ +from scenarios import * + +scenario = ( + sequences.handshake(), + send_stanza(""), + 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("" + "" + "" + "" + "UTF-8" + "latin-1" + ""), + expect_stanza("/iq[@type='result']"), + send_stanza(" "), + 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..608364f --- /dev/null +++ b/tests/end_to_end/scenarios/irc_server_configure.py @@ -0,0 +1,103 @@ +from scenarios import * + +scenario = ( + sequences.handshake(), + send_stanza(""), + 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='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("" + "" + "" + "" + "66976698" + "1" + "12:12:12" + "coucou" + "first commandsecond command" + "my_nickname" + "username" + "42" + "realname" + "UTF-8" + "latin-1" + ""), + expect_stanza("/iq[@type='result']/commands:command[@node='configure'][@status='completed']/commands:note[@type='info'][text()='Configuration successfully applied.']"), + + send_stanza(""), + 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='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(""), + expect_stanza("/iq[@type='result']/commands:command[@node='configure'][@status='canceled']"), + + # Same thing, but try to empty some values + send_stanza(""), + expect_stanza("/iq[@type='result']", + after = save_value("sessionid", extract_attribute("/iq[@type='result']/commands:command[@node='configure']", "sessionid")) + ), + send_stanza("" + "" + "" + "" + "" + "" + "" + "" + "" + "" + ""), + expect_stanza("/iq[@type='result']/commands:command[@node='configure'][@status='completed']/commands:note[@type='info'][text()='Configuration successfully applied.']"), + + send_stanza(""), + 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()='-1']", # An invalid value sets this field to -1, aka disabled + after = save_value("sessionid", extract_attribute("/iq[@type='result']/commands:command[@node='configure']", "sessionid")) + ), + send_stanza(""), + 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..d44839a --- /dev/null +++ b/tests/end_to_end/scenarios/irc_server_connection.py @@ -0,0 +1,8 @@ +from scenarios import * + +scenario = ( + sequences.handshake(), + send_stanza(""), + 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..aa02104 --- /dev/null +++ b/tests/end_to_end/scenarios/irc_server_connection_failure.py @@ -0,0 +1,12 @@ +from scenarios import * + +scenario = ( + sequences.handshake(), + send_stanza(""), + 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..2bb7a27 --- /dev/null +++ b/tests/end_to_end/scenarios/irc_server_presence_in_roster.py @@ -0,0 +1,27 @@ +from scenarios import * + +scenario = ( + sequences.handshake(), + + # Mutual subscription exchange + send_stanza(""), + expect_stanza("/presence[@type='subscribed'][@id='subid1']"), + + expect_stanza("/presence[@type='subscribe']"), + send_stanza(""), + + # Join a channel on that server + send_stanza(""), + + # 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(""), + 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..59bd6e4 --- /dev/null +++ b/tests/end_to_end/scenarios/irc_server_presence_subscription.py @@ -0,0 +1,7 @@ +from scenarios import * + +scenario = ( + sequences.handshake(), + send_stanza(""), + 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..3faa74f --- /dev/null +++ b/tests/end_to_end/scenarios/irc_tls_connection.py @@ -0,0 +1,26 @@ +from scenarios import * + +scenario = ( + sequences.handshake(), + # 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(""), + expect_stanza("/iq[@type='result']", + after = save_value("sessionid", extract_attribute("/iq[@type='result']/commands:command[@node='configure']", "sessionid"))), + send_stanza("" + "" + "" + "" + "7778" + "0" + "my_special_nickname" + ""), + expect_stanza("/iq[@type='result']/commands:command[@node='configure'][@status='completed']/commands:note[@type='info'][text()='Configuration successfully applied.']"), + + send_stanza(""), + 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..6291c4d --- /dev/null +++ b/tests/end_to_end/scenarios/join_history_limit.py @@ -0,0 +1,110 @@ +from scenarios import * + +scenario = ( + sequences.handshake(), + + # Disable the throttling because the test is based on timings + send_stanza(""), + expect_stanza("/iq[@type='result']", + after = save_value("sessionid", extract_attribute("/iq[@type='result']/commands:command[@node='configure']", "sessionid"))), + send_stanza("" + "" + "" + "6667" + "66976670" + "9999" + ""), + expect_stanza("/iq[@type='result']/commands:command[@node='configure'][@status='completed']/commands:note[@type='info'][text()='Configuration successfully applied.']"), + + + send_stanza(""), + 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("coucou"), + 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("coucou 2"), + # 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("coucou 3"), + send_stanza("coucou 4"), + 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(""), + expect_stanza("/message"), + expect_stanza("/presence/muc_user:x/muc_user:status[@code='110']"), + expect_stanza("/message/subject"), + + # Leave #foo + send_stanza(""), + expect_stanza("/presence[@type='unavailable']"), + + sleep_for(0.2), + + # Rejoin #foo, with some history limit + send_stanza(""), + expect_stanza("/message"), + expect_stanza("/presence/muc_user:x/muc_user:status[@code='110']"), + expect_stanza("/message/subject"), + + send_stanza(""), + expect_stanza("/presence[@type='unavailable']"), + + sleep_for(0.2), + + # Rejoin #foo, with some history limit + send_stanza(""), + 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(""), + expect_stanza("/presence[@type='unavailable']"), + + # Rejoin #foo, with some history limit + send_stanza(""), + 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(""), + expect_stanza("/presence[@type='unavailable']"), + + # Rejoin #foo, with some history limit + send_stanza(""), + 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(""), + expect_stanza("/presence[@type='unavailable']"), + + # Rejoin #foo, with some history limit + send_stanza(""), + 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(""), + 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..322dd5d --- /dev/null +++ b/tests/end_to_end/scenarios/leave_unjoined_chan.py @@ -0,0 +1,17 @@ +from scenarios import * + +scenario = ( + sequences.handshake(), + send_stanza(""), + sequences.connection("irc.localhost", '{jid_one}/{resource_one}'), + expect_stanza("/message"), + expect_stanza("/presence"), + expect_stanza("/message"), + + send_stanza(""), + 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("") +) 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..057cc7a --- /dev/null +++ b/tests/end_to_end/scenarios/list_adhoc.py @@ -0,0 +1,11 @@ +from scenarios import * + +scenario = ( + sequences.handshake(), + send_stanza(""), + 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..0898e15 --- /dev/null +++ b/tests/end_to_end/scenarios/list_adhoc_fixed_server.py @@ -0,0 +1,13 @@ +from scenarios import * + +conf = "fixed_server" + +scenario = ( + sequences.handshake(), + send_stanza(""), + 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..e8acb28 --- /dev/null +++ b/tests/end_to_end/scenarios/list_adhoc_irc.py @@ -0,0 +1,9 @@ +from scenarios import * + +scenario = ( + sequences.handshake(), + send_stanza(""), + 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..9e1ec17 --- /dev/null +++ b/tests/end_to_end/scenarios/list_admin_adhoc.py @@ -0,0 +1,10 @@ +from scenarios import * + +scenario = ( + sequences.handshake(), + send_stanza(""), + 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..4fac959 --- /dev/null +++ b/tests/end_to_end/scenarios/list_admin_adhoc_fixed_server.py @@ -0,0 +1,13 @@ +from scenarios import * + +conf = "fixed_server" + +scenario = ( + sequences.handshake(), + send_stanza(""), + 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..cf7b494 --- /dev/null +++ b/tests/end_to_end/scenarios/list_muc_user_adhoc.py @@ -0,0 +1,7 @@ +from scenarios import * + +scenario = ( + sequences.handshake(), + send_stanza(""), + 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("coucou"), + expect_stanza("/message[@from='#foo@{biboumi_host}/{nick_one}'][@to='{jid_one}/{resource_one}'][@type='groupchat']/body[text()='coucou']"), + + send_stanza("coucou 2"), + 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(""), + + 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..f8d1a06 --- /dev/null +++ b/tests/end_to_end/scenarios/mam_with_timestamps.py @@ -0,0 +1,43 @@ +from scenarios import * + +scenario = ( + scenarios.simple_channel_join.scenario, + + # Send two channel messages + send_stanza("coucou"), + 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("coucou 2"), + # 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("coucou 3"), + send_stanza("coucou 4"), + 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(""" + + + urn:xmpp:mam:2 + {first_timestamp} + {second_timestamp} + + + """), + + 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"), +) 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("/mode +v {nick_two}"), + 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(""), + 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(""), + 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(""), + expect_stanza("/iq[@type='error']"), + + # using an iq, without the rights to do it + send_stanza(""), + expect_unordered( + ["/iq[@type='error']"], + ["/message[@type='chat'][@to='{jid_two}/{resource_one}']"] + ), + + # using an iq, with an unknown mode + send_stanza(""), + 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..85cac4b --- /dev/null +++ b/tests/end_to_end/scenarios/muc_disco_info.py @@ -0,0 +1,30 @@ +from scenarios import * + +scenario = ( + sequences.handshake(), + + send_stanza(""), + 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(""), + 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(""), + 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("coucou"), + # 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("coucou"), + 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..94a1120 --- /dev/null +++ b/tests/end_to_end/scenarios/muc_traffic_info.py @@ -0,0 +1,8 @@ +from scenarios import * + +scenario = ( + sequences.handshake(), + + send_stanza(""), + 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("un\ndeux\ntrois"), + # 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("hello"), + + # 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(""), + 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("a\nb\nc"), + # 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("FIRST LINE\nSECOND LINE."), + # 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..1cc84ee --- /dev/null +++ b/tests/end_to_end/scenarios/multiple_channels_join.py @@ -0,0 +1,20 @@ +from scenarios import * + +from scenarios.simple_channel_join import expect_self_join_presence + +scenario = ( + sequences.handshake(), + + # Join 3 rooms, on the same server, with three different nicks + send_stanza(""), + send_stanza(""), + send_stanza(" SECRET"), + + 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(""), + 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(""), + 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("reported"), + 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..43cc31b --- /dev/null +++ b/tests/end_to_end/scenarios/multisessionnick.py @@ -0,0 +1,127 @@ +from scenarios import * + +from scenarios.simple_channel_join import expect_self_join_presence + +scenario = ( + sequences.handshake(), + + # Resource one joins a channel + send_stanza(""), + 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(""), + + # 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(""), + 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("RELLO"), + + # 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(""), + 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(""), + 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(""), + # 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("first"), + send_stanza("second"), + + # 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..3af0273 --- /dev/null +++ b/tests/end_to_end/scenarios/nick_change_in_join.py @@ -0,0 +1,19 @@ +from scenarios import * + +from scenarios.simple_channel_join import expect_self_join_presence + +scenario = ( + sequences.handshake(), + send_stanza(""), + sequences.connection(), + expect_self_join_presence(jid = '{jid_one}/{resource_one}', chan = "#foo", nick = "{nick_one}"), + + send_stanza(""), + 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..ed83d6b --- /dev/null +++ b/tests/end_to_end/scenarios/not_connected_error.py @@ -0,0 +1,13 @@ +from scenarios import * + +from scenarios.simple_channel_join import expect_self_join_presence + +scenario = ( + sequences.handshake(), + send_stanza(""), + # Fixme: what is the purpose of this test? Check that we don’t receive anything here…? + + send_stanza(""), + 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("NOTICE {nick_one} :[#foo] Hello in a notice."), + 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(""), + expect_stanza("/iq[@type='result']/muc_owner:query/dataform:x/dataform:field[@var='persistent'][@type='boolean']/dataform:value[text()='false']"), + send_stanza("true"), + expect_stanza("/iq[@type='result']"), + + # Check that the value is now effectively true + send_stanza(""), + 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(""), + 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(""), + + # 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("coucou"), + + # 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(""), + 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("QUIT bye bye"), + 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..56586d1 --- /dev/null +++ b/tests/end_to_end/scenarios/raw_message.py @@ -0,0 +1,13 @@ +from scenarios import * + +scenario = ( + sequences.handshake(), + send_stanza(""), + sequences.connection("irc.localhost", '{jid_one}/{resource_one}'), + expect_stanza("/message"), + expect_stanza("/presence"), + expect_stanza("/message"), + + send_stanza("WHOIS {nick_one}"), + 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..6443ccb --- /dev/null +++ b/tests/end_to_end/scenarios/raw_message_fixed_irc_server.py @@ -0,0 +1,17 @@ +from scenarios import * + +conf = 'fixed_server' + +scenario = ( + sequences.handshake(), + + send_stanza(""), + sequences.connection("irc.localhost", '{jid_one}/{resource_one}', fixed_irc_server=True), + expect_stanza("/message"), + expect_stanza("/presence"), + expect_stanza("/message"), + + send_stanza("WHOIS {nick_one}"), + 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("NAMES"), + 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..60e91fe --- /dev/null +++ b/tests/end_to_end/scenarios/resource_is_removed_from_server_when_last_chan_is_left.py @@ -0,0 +1,43 @@ +from scenarios import * + +scenario = ( + # Join the channel + sequences.handshake(), + send_stanza(""), + 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(""), + expect_stanza("/iq[@type='result']/muc_owner:query/dataform:x/dataform:field[@var='persistent'][@type='boolean']/dataform:value[text()='false']"), + send_stanza("true"), + expect_stanza("/iq[@type='result']"), + + send_stanza(""), + 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(""), + 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(""), + 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("kikoo"), + send_stanza("second kikoo"), + + # 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..7ea30bf --- /dev/null +++ b/tests/end_to_end/scenarios/self_disco_info.py @@ -0,0 +1,12 @@ +from scenarios import * + +scenario = ( + sequences.handshake(), + send_stanza(""), + 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(""), + 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(""), + 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(""), + # 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(""), + # 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(""), + 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(""), + 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(""), + 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(""), + 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(""), + 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(""), + 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(""), + # 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("e2e test1.0Fedora"), + 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(""), + 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(""), + # 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("e2e test1.0Fedora"), + 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(""), + 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("e2e test1.0Fedora"), + 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..4147dbd --- /dev/null +++ b/tests/end_to_end/scenarios/simple_channel_join.py @@ -0,0 +1,22 @@ +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 = ( + sequences.handshake(), + send_stanza(""), + 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..88bdaa2 --- /dev/null +++ b/tests/end_to_end/scenarios/simple_channel_join_fixed.py @@ -0,0 +1,13 @@ +from scenarios import * + +conf = "fixed_server" + +scenario = ( + sequences.handshake(), + send_stanza(""), + 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(""), + 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(""), + 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(""), + 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("reported"), + 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("QUIT bye bye"), + 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("coucou"), + 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("coucou 2"), + 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(""), + + 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(""" + + + urn:xmpp:mam:2 + 2000-06-07T00:00:00Z + + """), + + 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(""" + + + urn:xmpp:mam:2 + 3016-06-07T00:00:00Z + + """), + + 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("1"), + + 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("/me rit en IRC"), + # 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("") + ) + +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) + -- cgit v1.2.3