summaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests')
-rw-r--r--tests/config.cpp5
-rw-r--r--tests/dns.cpp91
-rw-r--r--tests/encoding.cpp2
-rw-r--r--tests/end_to_end/__main__.py1124
-rw-r--r--tests/end_to_end/ircd.conf4
-rw-r--r--tests/iid.cpp64
-rw-r--r--tests/timed_events.cpp1
-rw-r--r--tests/utils.cpp40
-rw-r--r--tests/xmpp.cpp7
9 files changed, 1078 insertions, 260 deletions
diff --git a/tests/config.cpp b/tests/config.cpp
index ddea151..a6fa92a 100644
--- a/tests/config.cpp
+++ b/tests/config.cpp
@@ -1,9 +1,14 @@
#include "catch.hpp"
+#include "io_tester.hpp"
+
+#include <iostream>
#include <config/config.hpp>
TEST_CASE("Config basic")
{
+ // Disable all output for this test
+ IoTester<std::ostream> out(std::cout);
// Write a value in the config file
Config::read_conf("test.cfg");
Config::set("coucou", "bonjour", true);
diff --git a/tests/dns.cpp b/tests/dns.cpp
deleted file mode 100644
index c3eda7b..0000000
--- a/tests/dns.cpp
+++ /dev/null
@@ -1,91 +0,0 @@
-#include "catch.hpp"
-
-#include <network/dns_handler.hpp>
-#include <network/resolver.hpp>
-#include <network/poller.hpp>
-
-#include <utils/timed_events.hpp>
-
-TEST_CASE("DNS resolver")
-{
- Resolver resolver;
- Resolver resolver2;
- Resolver resolver3;
-
- /**
- * If we are using cares, we need to run a poller loop until each
- * resolution is finished. Without cares we get the result before
- * resolve() returns because it’s blocking.
- */
-#ifdef CARES_FOUND
- auto p = std::make_shared<Poller>();
-
- const auto loop = [&p]()
- {
- do
- {
- DNSHandler::instance.watch_dns_sockets(p);
- }
- while (p->poll(utils::no_timeout) != -1);
- };
-#else
- // We don’t need to do anything if we are not using cares.
- const auto loop = [](){};
-#endif
-
- std::string hostname;
- std::string port = "6667";
-
- bool success = true;
-
- const auto error_cb = [&success](const std::string& hostname)
- {
- return [&success, hostname](const char *msg)
- {
- INFO("Failed to resolve " << hostname << ":" << msg);
- success = false;
- };
- };
- const auto success_cb = [&success](const std::string& hostname)
- {
- return [&success, hostname](const struct addrinfo *addr)
- {
- INFO("Successfully resolved " << hostname << ": " << addr_to_string(addr));
- success = true;
- };
- };
-
- hostname = "example.com";
- resolver.resolve(hostname, port,
- success_cb(hostname), error_cb(hostname));
- hostname = "poez.io";
- resolver2.resolve(hostname, port,
- success_cb(hostname), error_cb(hostname));
- hostname = "louiz.org";
- resolver3.resolve(hostname, port,
- success_cb(hostname), error_cb(hostname));
- loop();
- CHECK(success);
-
- hostname = "this.should.fail.because.it.is..misformatted";
- resolver.resolve(hostname, port,
- success_cb(hostname), error_cb(hostname));
- loop();
- CHECK(!success);
-
- hostname = "this.should.fail.because.it.is.does.not.exist.invalid";
- resolver.resolve(hostname, port,
- success_cb(hostname), error_cb(hostname));
- loop();
- CHECK(!success);
-
- hostname = "localhost";
- resolver.resolve(hostname, port,
- success_cb(hostname), error_cb(hostname));
- loop();
- CHECK(success);
-
-#ifdef CARES_FOUND
- DNSHandler::instance.destroy();
-#endif
-}
diff --git a/tests/encoding.cpp b/tests/encoding.cpp
index 389cf23..b5192ff 100644
--- a/tests/encoding.cpp
+++ b/tests/encoding.cpp
@@ -11,7 +11,6 @@ TEST_CASE("UTF-8 validation")
CHECK_FALSE(utils::is_valid_utf8("\xFE\xFE\xFF\xFF"));
std::string in = "Biboumi ╯°□°)╯︵ ┻━┻";
- INFO(in);
CHECK(utils::is_valid_utf8(in.data()));
}
@@ -49,7 +48,6 @@ TEST_CASE("Remove invalid XML chars")
{
std::string without_ctrl_char("𤭢€¢$");
std::string in = "Biboumi ╯°□°)╯︵ ┻━┻";
- INFO(in);
CHECK(utils::remove_invalid_xml_chars(without_ctrl_char) == without_ctrl_char);
CHECK(utils::remove_invalid_xml_chars(in) == in);
CHECK(utils::remove_invalid_xml_chars("\acouco\u0008u\uFFFEt\uFFFFe\r\n♥") == "coucoute\r\n♥");
diff --git a/tests/end_to_end/__main__.py b/tests/end_to_end/__main__.py
index 4348197..7658d92 100644
--- a/tests/end_to_end/__main__.py
+++ b/tests/end_to_end/__main__.py
@@ -13,6 +13,8 @@ import os
from functools import partial
from slixmpp.xmlstream.matcher.base import MatcherBase
+if not hasattr(asyncio, "ensure_future"):
+ asyncio.ensure_future = getattr(asyncio, "async")
class MatchAll(MatcherBase):
"""match everything"""
@@ -57,7 +59,7 @@ class XMPPComponent(slixmpp.BaseXMPP):
self.add_event_handler("session_end", self.on_end_session)
- asyncio.async(self.accept_routine())
+ asyncio.ensure_future(self.accept_routine())
self.scenario = scenario
self.biboumi = biboumi
@@ -74,7 +76,7 @@ class XMPPComponent(slixmpp.BaseXMPP):
self.scenario.steps = []
self.failed = True
- def on_end_session(self, event):
+ def on_end_session(self, _):
self.loop.stop()
def handle_incoming_stanza(self, stanza):
@@ -91,16 +93,17 @@ class XMPPComponent(slixmpp.BaseXMPP):
self.run_scenario()
def run_scenario(self):
- if scenario.steps:
- step = scenario.steps.pop(0)
+ if self.scenario.steps:
+ step = self.scenario.steps.pop(0)
step(self, self.biboumi)
else:
- self.biboumi.stop()
+ if self.biboumi:
+ self.biboumi.stop()
@asyncio.coroutine
def accept_routine(self):
self.accepting_server = yield from self.loop.create_server(lambda: self,
- "127.0.0.1", "8811", reuse_address=True)
+ "127.0.0.1", 8811, reuse_address=True)
def check_stanza_against_all_expected_xpaths(self):
pass
@@ -110,15 +113,26 @@ 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': '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'})
+ 'version': 'jabber:iq:version',
+ 'mam': 'urn:xmpp:mam:1',
+ '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'})
return matched
def check_xpath(xpaths, xmpp, after, stanza):
- for i, xpath in enumerate(xpaths):
+ for xpath in xpaths:
matched = match(stanza, xpath)
if not matched:
raise StanzaError("Received stanza “%s” did not match expected xpath “%s”" % (stanza, xpath))
@@ -129,6 +143,28 @@ def check_xpath(xpaths, xmpp, after, stanza):
else:
after(stanza, xmpp)
+def all_xpaths_match(stanza, xpaths):
+ for xpath in xpaths:
+ matched = match(stanza, xpath)
+ if not matched:
+ return False
+ return True
+
+def check_list_of_xpath(list_of_xpaths, xmpp, stanza):
+ found = None
+ for i, xpaths in enumerate(list_of_xpaths):
+ if all_xpaths_match(stanza, xpaths):
+ found = True
+ list_of_xpaths.pop(i)
+ break
+
+ if not found:
+ raise StanzaError("Received stanza “%s” did not match any of the expected xpaths:\n%s" % (stanza, list_of_xpaths))
+
+ if list_of_xpaths:
+ step = partial(expect_unordered_already_formatted, list_of_xpaths)
+ xmpp.scenario.steps.insert(0, step)
+
def check_xpath_optional(xpaths, xmpp, after, stanza):
try:
@@ -187,10 +223,11 @@ class ProcessRunner:
class BiboumiRunner(ProcessRunner):
- def __init__(self, name, with_valgrind):
+ def __init__(self, name):
super().__init__()
self.name = name
self.fd = open("biboumi_%s_output.txt" % (name,), "w")
+ with_valgrind = os.environ.get("E2E_WITH_VALGRIND") is not None
if with_valgrind:
self.create = asyncio.create_subprocess_exec("valgrind", "--suppressions=" + (os.environ.get("E2E_BIBOUMI_SUPP_DIR") or "") + "biboumi.supp", "--leak-check=full", "--show-leak-kinds=all",
"--errors-for-leak-kinds=all", "--error-exitcode=16",
@@ -224,6 +261,19 @@ def expect_stanza(xpaths, xmpp, biboumi, optional=False, after=None):
else:
print("Warning, from argument type passed to expect_stanza: %s" % (type(xpaths)))
+# list_of_xpaths: [(xpath, xpath), (xpath, xpath), (xpath)]
+def expect_unordered(list_of_xpaths, xmpp, biboumi):
+ formatted_list_of_xpaths = []
+ for xpaths in list_of_xpaths:
+ formatted_xpaths = []
+ for xpath in xpaths:
+ formatted_xpath = xpath.format_map(common_replacements)
+ formatted_xpaths.append(formatted_xpath)
+ formatted_list_of_xpaths.append(tuple(formatted_xpaths))
+ expect_unordered_already_formatted(formatted_list_of_xpaths, xmpp, biboumi)
+
+def expect_unordered_already_formatted(formatted_list_of_xpaths, xmpp, biboumi):
+ xmpp.stanza_checker = partial(check_list_of_xpath, formatted_list_of_xpaths, xmpp)
def log_message(message, xmpp, biboumi):
print("%s" % (message,))
@@ -241,7 +291,8 @@ class BiboumiTest:
self.scenario = scenario
self.expected_code = expected_code
- def run(self, with_valgrind=True):
+ def run(self):
+ with_valgrind = os.environ.get("E2E_WITH_VALGRIND") is not None
print("Running scenario: %s%s" % (self.scenario.name, " (with valgrind)" if with_valgrind else ''))
# Redirect the slixmpp logging into a specific file
output_filename = "slixmpp_%s_output.txt" % (self.scenario.name,)
@@ -252,10 +303,15 @@ class BiboumiTest:
filename=output_filename)
with open("test.conf", "w") as fd:
- fd.write(confs[scenario.conf])
+ fd.write(confs[self.scenario.conf])
+
+ try:
+ os.remove("e2e_test.sqlite")
+ except FileNotFoundError:
+ pass
# Start the XMPP component and biboumi
- biboumi = BiboumiRunner(scenario.name, with_valgrind)
+ biboumi = BiboumiRunner(self.scenario.name)
xmpp = XMPPComponent(self.scenario, biboumi)
asyncio.get_event_loop().run_until_complete(biboumi.start())
@@ -264,7 +320,7 @@ class BiboumiTest:
xmpp.process()
code = asyncio.get_event_loop().run_until_complete(biboumi.wait())
xmpp.biboumi = None
- scenario.steps.clear()
+ self.scenario.steps.clear()
failed = False
if not xmpp.failed:
if code != self.expected_code:
@@ -311,6 +367,7 @@ common_replacements = {
'jid_two': 'second@example.com',
'jid_admin': 'admin@example.com',
'nick_two': 'Bobby',
+ 'nick_three': 'Bernard',
'lower_nick_one': 'nick',
'lower_nick_two': 'bobby',
}
@@ -428,7 +485,24 @@ if __name__ == '__main__':
),
partial(expect_stanza, "/message[@from='#foo%{irc_server_one}'][@type='groupchat']/subject[not(text())]"),
]),
- Scenario("virtual_channel_join",
+ Scenario("virtual_channel",
+ [
+ handshake_sequence(),
+ partial(send_stanza,
+ "<presence from='{jid_one}/{resource_one}' to='%{irc_server_one}/{nick_one}' />"),
+ connection_begin_sequence("irc.localhost", '{jid_one}/{resource_one}'),
+ partial(expect_stanza,
+ ("/presence[@to='{jid_one}/{resource_one}'][@from='%{irc_server_one}/{nick_one}']/muc_user:x/muc_user:item[@affiliation='none'][@role='participant']",
+ "/presence/muc_user:x/muc_user:status[@code='110']")
+ ),
+ partial(expect_stanza, "/message[@from='%{irc_server_one}'][@type='groupchat']/subject[re:test(text(), '^This is a virtual channel.*$')]"),
+ connection_end_sequence("irc.localhost", '{jid_one}/{resource_one}'),
+ partial(send_stanza, "<presence type='unavailable' from='{jid_one}/{resource_one}' to='%{irc_server_one}/{nick_one}' />"),
+ partial(expect_stanza, "/presence[@type='unavailable'][@from='%{irc_server_one}/{nick_one}']"),
+ partial(expect_stanza, "/message[@from='{irc_server_one}']/body[text()='ERROR: Closing Link: localhost (Client Quit)']"),
+ partial(expect_stanza, "/message[@from='{irc_server_one}']/body[text()='ERROR: Connection closed.']"),
+ ]),
+ Scenario("irc_server_disconnection",
[
handshake_sequence(),
partial(send_stanza,
@@ -440,6 +514,21 @@ if __name__ == '__main__':
),
partial(expect_stanza, "/message[@from='%{irc_server_one}'][@type='groupchat']/subject[re:test(text(), '^This is a virtual channel.*$')]"),
connection_end_sequence("irc.localhost", '{jid_one}/{resource_one}'),
+
+ partial(send_stanza, "<presence from='{jid_one}/{resource_one}' to='%{irc_server_one}/{nick_two}' />"),
+
+ partial(expect_unordered, [
+ ("/presence[@from='%{irc_server_one}/{nick_one}'][@to='{jid_one}/{resource_one}'][@type='unavailable']/muc_user:x/muc_user:item[@nick='{nick_two}']",
+ "/presence/muc_user:x/muc_user:status[@code='110']",
+ "/presence/muc_user:x/muc_user:status[@code='303']"),
+ ("/presence[@from='%{irc_server_one}/{nick_two}'][@to='{jid_one}/{resource_one}']",
+ "/presence/muc_user:x/muc_user:status[@code='110']"),
+ ]),
+
+ partial(send_stanza, "<presence type='unavailable' from='{jid_one}/{resource_one}' to='%{irc_server_one}/{nick_two}' />"),
+ partial(expect_stanza, "/presence[@type='unavailable'][@from='%{irc_server_one}/{nick_two}']"),
+ partial(expect_stanza, "/message[@from='{irc_server_one}']/body[text()='ERROR: Closing Link: localhost (Client Quit)']"),
+ partial(expect_stanza, "/message[@from='{irc_server_one}']/body[text()='ERROR: Connection closed.']"),
]),
Scenario("channel_join_with_two_users",
[
@@ -464,24 +553,13 @@ if __name__ == '__main__':
partial(send_stanza,
"<presence from='{jid_two}/{resource_one}' to='#foo%{irc_server_one}/{nick_two}' />"),
connection_sequence("irc.localhost", '{jid_two}/{resource_one}'),
- # Our presence, sent to the other user
- partial(log_message,
- "Our presence sent to the other user"),
- partial(expect_stanza,
- ("/presence[@to='{jid_one}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_two}']/muc_user:x/muc_user:item[@affiliation='none'][@jid='~bobby@localhost'][@role='participant']",)),
- # The other user presence
- partial(log_message,
- "The other user presence"),
- partial(expect_stanza,
- "/presence[@to='{jid_second}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_one}']/muc_user:x/muc_user:item[@affiliation='none'][@jid='~nick@localhost'][@role='participant']"),
- # Our own presence
- partial(log_message,
- "Our own presence"),
- partial(expect_stanza,
- ("/presence[@to='{jid_two}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_two}']/muc_user:x/muc_user:item[@affiliation='none'][@jid='~bobby@localhost'][@role='participant']",
- "/presence/muc_user:x/muc_user:status[@code='110']")
- ),
- partial(expect_stanza, "/message[@from='#foo%{irc_server_one}'][@type='groupchat']/subject[not(text())]"),
+ partial(expect_unordered, [
+ ("/presence[@to='{jid_one}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_two}']/muc_user:x/muc_user:item[@affiliation='none'][@role='participant'][@jid='~bobby@localhost']",),
+ ("/presence[@to='{jid_two}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_one}']/muc_user:x/muc_user:item[@affiliation='admin'][@role='moderator']",),
+ ("/presence[@to='{jid_two}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_two}']/muc_user:x/muc_user:item[@affiliation='none'][@jid='~bobby@localhost'][@role='participant']",
+ "/presence/muc_user:x/muc_user:status[@code='110']",),
+ ("/message[@from='#foo%{irc_server_one}'][@type='groupchat']/subject[not(text())]",),
+ ]),
]),
Scenario("channel_custom_topic",
[
@@ -512,7 +590,7 @@ if __name__ == '__main__':
("/presence[@to='{jid_one}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_two}']/muc_user:x/muc_user:item[@affiliation='none'][@jid='~bobby@localhost'][@role='participant']",)),
# The other user presence
partial(expect_stanza,
- "/presence[@to='{jid_second}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_one}']/muc_user:x/muc_user:item[@affiliation='admin'][@jid='~nick@localhost'][@role='moderator']"),
+ "/presence[@to='{jid_two}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_one}']/muc_user:x/muc_user:item[@affiliation='admin'][@role='moderator']"),
# Our own presence
partial(expect_stanza,
("/presence[@to='{jid_two}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_two}']/muc_user:x/muc_user:item[@affiliation='none'][@jid='~bobby@localhost'][@role='participant']",
@@ -554,7 +632,7 @@ if __name__ == '__main__':
handshake_sequence(),
partial(send_stanza, "<iq type='get' id='idwhatever' from='{jid_one}/{resource_one}' to='{biboumi_host}'><query xmlns='http://jabber.org/protocol/disco#items' node='http://jabber.org/protocol/commands' /></iq>"),
partial(expect_stanza, ("/iq[@type='result']/disco_items:query[@node='http://jabber.org/protocol/commands']",
- "/iq/disco_items:query/disco_items:item[3]")),
+ "/iq/disco_items:query/disco_items:item[5]")),
], conf='fixed_server'),
Scenario("list_admin_adhoc_fixed_server",
[
@@ -570,7 +648,7 @@ if __name__ == '__main__':
handshake_sequence(),
partial(send_stanza, "<iq type='get' id='idwhatever' from='{jid_one}/{resource_one}' to='{irc_host_one}@{biboumi_host}'><query xmlns='http://jabber.org/protocol/disco#items' node='http://jabber.org/protocol/commands' /></iq>"),
partial(expect_stanza, ("/iq[@type='result']/disco_items:query[@node='http://jabber.org/protocol/commands']",
- "/iq/disco_items:query/disco_items:item[1]")),
+ "/iq/disco_items:query/disco_items:item[2]")),
]),
Scenario("list_adhoc_irc_fixed_server",
[
@@ -603,6 +681,35 @@ if __name__ == '__main__':
partial(send_stanza, "<iq type='set' id='hello-command2' from='{jid_one}/{resource_one}' to='{biboumi_host}'><command xmlns='http://jabber.org/protocol/commands' node='hello' sessionid='{sessionid}' action='next'><x xmlns='jabber:x:data' type='submit'><field var='name'><value>COUCOU</value></field></x></command></iq>"),
partial(expect_stanza, "/iq[@type='result']/commands:command[@node='hello'][@status='completed']/commands:note[@type='info'][text()='Hello COUCOU!']")
]),
+ Scenario("execute_forbidden_adhoc_command",
+ [
+ handshake_sequence(),
+ partial(send_stanza, "<iq type='set' id='command1' from='{jid_one}/{resource_one}' to='{biboumi_host}'><command xmlns='http://jabber.org/protocol/commands' node='disconnect-user' action='execute' /></iq>"),
+ partial(expect_stanza, ("/iq[@type='error'][@id='command1']/commands:command[@node='disconnect-user']",
+ "/iq/commands:command/commands:error[@type='cancel']/stanza:forbidden")),
+ ]),
+ Scenario("execute_disconnect_user_adhoc_command",
+ [
+ handshake_sequence(),
+
+ partial(log_message, "Join a channel"),
+ partial(send_stanza, "<presence from='{jid_admin}/{resource_one}' to='#foo%{irc_server_one}/{nick_one}' />"),
+ connection_sequence("irc.localhost", '{jid_admin}/{resource_one}'),
+ partial(expect_stanza, "/message/body[text()='Mode #foo [+nt] by {irc_host_one}']"),
+ partial(expect_stanza, "/presence"),
+ partial(expect_stanza, "/message"),
+
+ partial(send_stanza, "<iq type='set' id='command1' from='{jid_admin}/{resource_one}' to='{biboumi_host}'><command xmlns='http://jabber.org/protocol/commands' node='disconnect-user' action='execute' /></iq>"),
+ partial(expect_stanza, ("/iq[@type='result']/commands:command[@node='disconnect-user'][@sessionid][@status='executing']",
+ "/iq/commands:command/commands:actions/commands:next",
+ ),
+ after = partial(save_value, "sessionid", partial(extract_attribute, "/iq/commands:command[@node='disconnect-user']", "sessionid"))
+ ),
+ partial(send_stanza, "<iq type='set' id='command2' from='{jid_admin}/{resource_one}' to='{biboumi_host}'><command xmlns='http://jabber.org/protocol/commands' node='disconnect-user' sessionid='{sessionid}' action='next'><x xmlns='jabber:x:data' type='submit'><field var='jids'><value>{jid_admin}</value></field><field var='quit-message'><value>Disconnected by e2e</value></field></x></command></iq>"),
+ partial(expect_stanza, "/iq[@type='result']/commands:command[@node='disconnect-user'][@status='completed']/commands:note[@type='info'][text()='1 user has been disconnected.']"),
+ # Note, charybdis ignores our QUIT message, so we can't test it
+ partial(expect_stanza, "/presence[@type='unavailable'][@to='{jid_admin}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_one}']"),
+ ]),
Scenario("multisessionnick",
[
handshake_sequence(),
@@ -621,51 +728,85 @@ if __name__ == '__main__':
partial(send_stanza,
"<presence from='{jid_one}/{resource_two}' to='#foo%{irc_server_one}/{nick_one}' />"),
# We receive our own join
- partial(expect_stanza,
- ("/presence[@to='{jid_one}/{resource_two}'][@from='#foo%{irc_server_one}/{nick_one}']/muc_user:x/muc_user:item[@affiliation='admin'][@role='moderator']",
- "/presence/muc_user:x/muc_user:status[@code='110']")
+ partial(expect_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())]",)]
),
- partial(expect_stanza, "/message[@from='#foo%{irc_server_one}'][@type='groupchat'][@to='{jid_one}/{resource_two}']/subject[not(text())]"),
# A different user joins the same room
partial(send_stanza,
"<presence from='{jid_two}/{resource_one}' to='#foo%{irc_server_one}/{nick_two}' />"),
connection_sequence("irc.localhost", '{jid_two}/{resource_one}'),
- partial(expect_stanza,
- ("/presence[@to='{jid_one}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_two}']",)),
- partial(expect_stanza,
- ("/presence[@to='{jid_one}/{resource_two}'][@from='#foo%{irc_server_one}/{nick_two}']",)),
+ partial(expect_unordered, [
+ ("/presence[@to='{jid_one}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_two}']",),
+ ("/presence[@to='{jid_one}/{resource_two}'][@from='#foo%{irc_server_one}/{nick_two}']",),
+ ("/presence[@to='{jid_two}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_two}']",
+ "/presence/muc_user:x/muc_user:status[@code='110']",),
+ ("/presence[@to='{jid_two}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_one}']",),
+ ]
+ ),
- partial(expect_stanza,
- "/presence[@to='{jid_second}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_one}']"),
- partial(expect_stanza,
- ("/presence[@to='{jid_two}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_two}']",
- "/presence/muc_user:x/muc_user:status[@code='110']")
- ),
partial(expect_stanza, "/message[@from='#foo%{irc_server_one}'][@type='groupchat']/subject[not(text())]"),
# That second user sends a private message to the first one
partial(send_stanza, "<message from='{jid_two}/{resource_one}' to='#foo%{irc_server_one}/{nick_one}' type='chat'><body>RELLO</body></message>"),
# Message is received with a server-wide JID, by the two resources behind nick_one
- partial(expect_stanza, "/message[@from='{lower_nick_two}!{irc_server_one}'][@to='{jid_one}/{resource_one}'][@type='chat']/body[text()='RELLO']"),
- partial(expect_stanza, "/message[@from='{lower_nick_two}!{irc_server_one}'][@to='{jid_one}/{resource_two}'][@type='chat']/body[text()='RELLO']"),
+ 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")),
+ partial(expect_stanza, "/message[@from='{lower_nick_two}%{irc_server_one}'][@to='{jid_one}/{resource_two}'][@type='chat']/body[text()='RELLO']"),
+
+
+ partial(log_message, "Nickname conflict"),
+ # First occupant (with the two resources) changes her/his nick
+ partial(send_stanza, "<presence from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}/{nick_two}' />"),
+ partial(expect_unordered, [
+ ("/message[@to='{jid_one}/{resource_one}'][@type='chat']/body[text()='irc.localhost: Bobby: Nickname is already in use.']",),
+ ("/message[@to='{jid_one}/{resource_two}'][@type='chat']/body[text()='irc.localhost: Bobby: Nickname is already in use.']",),
+ ("/presence[@to='{jid_one}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_two}'][@type='error']",),
+ ("/presence[@to='{jid_one}/{resource_two}'][@from='#foo%{irc_server_one}/{nick_two}'][@type='error']",),
+ ]),
+
+ # partial(log_message, "Nickname change"),
+ # First occupant (with the two resources) changes her/his nick
+ partial(send_stanza, "<presence from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}/{nick_three}' />"),
+ partial(expect_unordered, [
+ ("/presence[@from='#foo%{irc_server_one}/{nick_one}'][@to='{jid_two}/{resource_one}'][@type='unavailable']/muc_user:x/muc_user:item[@nick='Bernard']",
+ "/presence/muc_user:x/muc_user:status[@code='303']"),
+ ("/presence[@from='#foo%{irc_server_one}/{nick_three}'][@to='{jid_two}/{resource_one}']",),
+ ("/presence[@from='#foo%{irc_server_one}/{nick_one}'][@to='{jid_one}/{resource_one}'][@type='unavailable']/muc_user:x/muc_user:item[@nick='Bernard']",
+ "/presence/muc_user:x/muc_user:status[@code='303']",
+ "/presence/muc_user:x/muc_user:status[@code='110']"),
+ ("/presence[@from='#foo%{irc_server_one}/{nick_three}'][@to='{jid_one}/{resource_one}']",
+ "/presence/muc_user:x/muc_user:status[@code='110']"),
+
+ ("/presence[@from='#foo%{irc_server_one}/{nick_one}'][@to='{jid_one}/{resource_two}'][@type='unavailable']/muc_user:x/muc_user:item[@nick='Bernard']",
+ "/presence/muc_user:x/muc_user:status[@code='303']",
+ "/presence/muc_user:x/muc_user:status[@code='110']"),
+ ("/presence[@from='#foo%{irc_server_one}/{nick_three}'][@to='{jid_one}/{resource_two}']",
+ "/presence/muc_user:x/muc_user:status[@code='110']"),
+ ]),
# One resource leaves the server entirely.
partial(send_stanza, "<presence type='unavailable' from='{jid_one}/{resource_two}' to='#foo%{irc_server_one}/{nick_one}' />"),
# The leave is forwarded only to us
partial(expect_stanza,
("/presence[@type='unavailable']/muc_user:x/muc_user:status[@code='110']",
- "/presence/status[text()='Biboumi note: 1 resources are still in this channel.']")
+ "/presence/status[text()='Biboumi note: 1 resources are still in this channel.']",
+ )
),
+
# The second user sends two new private messages to the first user
- partial(send_stanza, "<message from='{jid_two}/{resource_one}' to='#foo%{irc_server_one}/{nick_one}' type='chat'><body>first</body></message>"),
- partial(send_stanza, "<message from='{jid_two}/{resource_one}' to='#foo%{irc_server_one}/{nick_one}' type='chat'><body>second</body></message>"),
+ partial(send_stanza, "<message from='{jid_two}/{resource_one}' to='#foo%{irc_server_one}/{nick_three}' type='chat'><body>first</body></message>"),
+ partial(send_stanza, "<message from='{jid_two}/{resource_one}' to='#foo%{irc_server_one}/{nick_three}' type='chat'><body>second</body></message>"),
# The first user receives the two messages, on the connected resource, once each
- partial(expect_stanza, "/message[@from='{lower_nick_two}!{irc_server_one}'][@to='{jid_one}/{resource_one}'][@type='chat']/body[text()='first']"),
- partial(expect_stanza, "/message[@from='{lower_nick_two}!{irc_server_one}'][@to='{jid_one}/{resource_one}'][@type='chat']/body[text()='second']"),
-
+ 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("channel_messages",
[
@@ -677,7 +818,7 @@ if __name__ == '__main__':
partial(expect_stanza,
"/message/body[text()='Mode #foo [+nt] by {irc_host_one}']"),
partial(expect_stanza,
- ("/presence[@to='{jid_one}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_one}']/muc_user:x/muc_user:item[@affiliation='admin'][@jid='~nick@localhost'][@role='moderator']",
+ ("/presence[@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())]"),
@@ -687,31 +828,29 @@ if __name__ == '__main__':
"<presence from='{jid_two}/{resource_one}' to='#foo%{irc_server_one}/{nick_two}' />"),
connection_sequence("irc.localhost", '{jid_two}/{resource_one}'),
# Our presence, sent to the other user
- partial(expect_stanza,
- ("/presence[@to='{jid_one}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_two}']/muc_user:x/muc_user:item[@affiliation='none'][@jid='~bobby@localhost'][@role='participant']",)),
- # The other user presence
- partial(expect_stanza,
- "/presence[@to='{jid_second}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_one}']/muc_user:x/muc_user:item[@affiliation='none'][@jid='~nick@localhost'][@role='participant']"),
- # Our own presence
- partial(expect_stanza,
- ("/presence[@to='{jid_two}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_two}']/muc_user:x/muc_user:item[@affiliation='none'][@jid='~bobby@localhost'][@role='participant']",
- "/presence/muc_user:x/muc_user:status[@code='110']")
- ),
- partial(expect_stanza, "/message[@from='#foo%{irc_server_one}'][@type='groupchat']/subject[not(text())]"),
+ partial(expect_unordered, [
+ ("/presence[@to='{jid_one}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_two}']/muc_user:x/muc_user:item[@affiliation='none'][@jid='~bobby@localhost'][@role='participant']",),
+ ("/presence[@to='{jid_two}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_one}']/muc_user:x/muc_user:item[@affiliation='admin'][@role='moderator']",),
+ ("/presence[@to='{jid_two}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_two}']/muc_user:x/muc_user:item[@affiliation='none'][@jid='~bobby@localhost'][@role='participant']",
+ "/presence/muc_user:x/muc_user:status[@code='110']"),
+ ("/message[@from='#foo%{irc_server_one}'][@type='groupchat']/subject[not(text())]",)
+ ]),
# Send a channel message
partial(send_stanza, "<message from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}' type='groupchat'><body>coucou</body></message>"),
# Receive the message, forwarded to the two users
- partial(expect_stanza, "/message[@from='#foo%{irc_server_one}/{nick_one}'][@to='{jid_one}/{resource_one}'][@type='groupchat']/body[text()='coucou']"),
- partial(expect_stanza, "/message[@from='#foo%{irc_server_one}/{nick_one}'][@to='{jid_two}/{resource_one}'][@type='groupchat']/body[text()='coucou']"),
+ partial(expect_unordered, [
+ ("/message[@from='#foo%{irc_server_one}/{nick_one}'][@to='{jid_one}/{resource_one}'][@type='groupchat']/body[text()='coucou']",),
+ ("/message[@from='#foo%{irc_server_one}/{nick_one}'][@to='{jid_two}/{resource_one}'][@type='groupchat']/body[text()='coucou']",)
+ ]),
# Send a private message, to a in-room JID
partial(send_stanza, "<message from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}/{nick_two}' type='chat'><body>coucou in private</body></message>"),
# Message is received with a server-wide JID
- partial(expect_stanza, "/message[@from='{lower_nick_one}!{irc_server_one}'][@to='{jid_two}/{resource_one}'][@type='chat']/body[text()='coucou in private']"),
+ partial(expect_stanza, "/message[@from='{lower_nick_one}%{irc_server_one}'][@to='{jid_two}/{resource_one}'][@type='chat']/body[text()='coucou in private']"),
# Respond to the message, to the server-wide JID
- partial(send_stanza, "<message from='{jid_two}/{resource_one}' to='{lower_nick_one}!{irc_server_one}' type='chat'><body>yes</body></message>"),
+ partial(send_stanza, "<message from='{jid_two}/{resource_one}' to='{lower_nick_one}%{irc_server_one}' type='chat'><body>yes</body></message>"),
# The response is received from the in-room JID
partial(expect_stanza, "/message[@from='#foo%{irc_server_one}/{nick_two}'][@to='{jid_one}/{resource_one}'][@type='chat']/body[text()='yes']"),
@@ -729,10 +868,10 @@ if __name__ == '__main__':
# Send a private message, to a in-room JID
partial(send_stanza, "<message from='{jid_one}/{resource_one}' to='%{irc_server_one}/{nick_two}' type='chat'><body>re in private</body></message>"),
# Message is received with a server-wide JID
- partial(expect_stanza, "/message[@from='{lower_nick_one}!{irc_server_one}'][@to='{jid_two}/{resource_one}'][@type='chat']/body[text()='re in private']"),
+ partial(expect_stanza, "/message[@from='{lower_nick_one}%{irc_server_one}'][@to='{jid_two}/{resource_one}'][@type='chat']/body[text()='re in private']"),
# Respond to the message, to the server-wide JID
- partial(send_stanza, "<message from='{jid_two}/{resource_one}' to='{lower_nick_one}!{irc_server_one}' type='chat'><body>re</body></message>"),
+ partial(send_stanza, "<message from='{jid_two}/{resource_one}' to='{lower_nick_one}%{irc_server_one}' type='chat'><body>re</body></message>"),
# The response is received from the in-room JID
partial(expect_stanza, "/message[@from='%{irc_server_one}/{nick_two}'][@to='{jid_one}/{resource_one}'][@type='chat']/body[text()='re']"),
@@ -743,9 +882,9 @@ if __name__ == '__main__':
"/presence[@type='unavailable']/muc_user:x/muc_user:status[@code='110']"),
# The private messages from this nick should now come (again) from the server-wide JID
- partial(send_stanza, "<message from='{jid_two}/{resource_one}' to='{lower_nick_one}!{irc_server_one}' type='chat'><body>hihihoho</body></message>"),
+ partial(send_stanza, "<message from='{jid_two}/{resource_one}' to='{lower_nick_one}%{irc_server_one}' type='chat'><body>hihihoho</body></message>"),
partial(expect_stanza,
- "/message[@from='{lower_nick_two}!{irc_server_one}'][@to='{jid_one}/{resource_one}']"),
+ "/message[@from='{lower_nick_two}%{irc_server_one}'][@to='{jid_one}/{resource_one}']"),
]
),
Scenario("encoded_channel_join",
@@ -781,10 +920,10 @@ if __name__ == '__main__':
"<iq type='get' from='{jid_one}/{resource_one}' id='first_ping' to='#foo%{irc_server_one}/{nick_one}'><ping xmlns='urn:xmpp:ping' /></iq>"),
# We receive our own ping request,
partial(expect_stanza,
- "/iq[@from='{lower_nick_one}!{irc_server_one}'][@type='get'][@to='{jid_one}/{resource_one}'][@id='gnip_tsrif']"),
+ "/iq[@from='{lower_nick_one}%{irc_server_one}'][@type='get'][@to='{jid_one}/{resource_one}'][@id='gnip_tsrif']"),
# Respond to the request
partial(send_stanza,
- "<iq type='result' to='{lower_nick_one}!{irc_server_one}' id='gnip_tsrif' from='{jid_one}/{resource_one}'/>"),
+ "<iq type='result' to='{lower_nick_one}%{irc_server_one}' id='gnip_tsrif' from='{jid_one}/{resource_one}'/>"),
partial(expect_stanza,
"/iq[@from='#foo%{irc_server_one}/{nick_one}'][@type='result'][@to='{jid_one}/{resource_one}'][@id='first_ping']"),
@@ -802,21 +941,21 @@ if __name__ == '__main__':
"<iq type='get' from='{jid_one}/{resource_one}' id='second_ping' to='#foo%{irc_server_one}/{nick_one}'><ping xmlns='urn:xmpp:ping' /></iq>"),
# We receive our own ping request. Note that we don't know the to value, it could be one of our two resources.
partial(expect_stanza,
- "/iq[@from='{lower_nick_one}!{irc_server_one}'][@type='get'][@to][@id='gnip_dnoces']",
+ "/iq[@from='{lower_nick_one}%{irc_server_one}'][@type='get'][@to][@id='gnip_dnoces']",
after = partial(save_value, "to", partial(extract_attribute, "/iq", "to"))),
# Respond to the request, using the extracted 'to' value as our 'from'
partial(send_stanza,
- "<iq type='result' to='{lower_nick_one}!{irc_server_one}' id='gnip_dnoces' from='{to}'/>"),
+ "<iq type='result' to='{lower_nick_one}%{irc_server_one}' id='gnip_dnoces' from='{to}'/>"),
partial(expect_stanza,
"/iq[@from='#foo%{irc_server_one}/{nick_one}'][@type='result'][@to='{jid_one}/{resource_one}'][@id='second_ping']"),
## And re-do exactly the same thing, just change the resource initiating the self ping
partial(send_stanza,
"<iq type='get' from='{jid_one}/{resource_two}' id='third_ping' to='#foo%{irc_server_one}/{nick_one}'><ping xmlns='urn:xmpp:ping' /></iq>"),
partial(expect_stanza,
- "/iq[@from='{lower_nick_one}!{irc_server_one}'][@type='get'][@to][@id='gnip_driht']",
+ "/iq[@from='{lower_nick_one}%{irc_server_one}'][@type='get'][@to][@id='gnip_driht']",
after = partial(save_value, "to", partial(extract_attribute, "/iq", "to"))),
partial(send_stanza,
- "<iq type='result' to='{lower_nick_one}!{irc_server_one}' id='gnip_driht' from='{to}'/>"),
+ "<iq type='result' to='{lower_nick_one}%{irc_server_one}' id='gnip_driht' from='{to}'/>"),
partial(expect_stanza,
"/iq[@from='#foo%{irc_server_one}/{nick_one}'][@type='result'][@to='{jid_one}/{resource_two}'][@id='third_ping']"),
@@ -836,34 +975,30 @@ if __name__ == '__main__':
partial(send_stanza,
"<presence from='{jid_two}/{resource_one}' to='#foo%{irc_server_one}/{nick_two}' />"),
connection_sequence("irc.localhost", '{jid_two}/{resource_one}'),
- partial(expect_stanza,
- "/presence/muc_user:x/muc_user:item[@affiliation='none'][@role='participant']",),
-
- partial(expect_stanza,
- "/presence/muc_user:x/muc_user:item[@affiliation='admin'][@role='moderator']"),
- partial(expect_stanza,
- "/presence/muc_user:x/muc_user:status[@code='110']"),
- partial(expect_stanza, "/message/subject"),
+ partial(expect_unordered, [
+ ("/presence/muc_user:x/muc_user:item[@affiliation='none'][@role='participant']",),
+ ("/presence/muc_user:x/muc_user:item[@affiliation='admin'][@role='moderator']",),
+ ("/presence/muc_user:x/muc_user:status[@code='110']",),
+ ("/message/subject",),
+ ]),
# Moderator kicks participant
partial(log_message, "Moderator kicks participant"),
partial(send_stanza,
"<iq id='kick1' to='#foo%{irc_server_one}' from='{jid_one}/{resource_one}' type='set'><query xmlns='http://jabber.org/protocol/muc#admin'><item nick='{nick_two}' role='none'><reason>reported</reason></item></query></iq>"),
partial(log_message, "Presence is sent to everyone"),
- partial(expect_stanza,
- ("/presence[@type='unavailable'][@to='{jid_second}/{resource_one}']/muc_user:x/muc_user:item[@role='none']/muc_user:actor[@nick='{nick_one}']",
+ 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']"
- )),
- partial(expect_stanza,
- ("/presence[@type='unavailable']/muc_user:x/muc_user:item[@role='none']/muc_user:actor[@nick='{nick_one}']",
+ ),
+ ("/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']",
),
- ),
- partial(expect_stanza,
- "/iq[@id='kick1'][@type='result']"),
+ ("/iq[@id='kick1'][@type='result']",),
+ ]),
]),
Scenario("multisession_kick",
[
@@ -876,18 +1011,16 @@ if __name__ == '__main__':
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
+ # Second user joins, fprom two resources
partial(send_stanza,
"<presence from='{jid_two}/{resource_one}' to='#foo%{irc_server_one}/{nick_two}' />"),
connection_sequence("irc.localhost", '{jid_two}/{resource_one}'),
- partial(expect_stanza,
- "/presence/muc_user:x/muc_user:item[@affiliation='none'][@role='participant']",),
-
- partial(expect_stanza,
- "/presence/muc_user:x/muc_user:item[@affiliation='admin'][@role='moderator']"),
- partial(expect_stanza,
- "/presence/muc_user:x/muc_user:status[@code='110']"),
- partial(expect_stanza, "/message/subject"),
+ partial(expect_unordered, [
+ ("/presence/muc_user:x/muc_user:item[@affiliation='none'][@role='participant']",),
+ ("/presence/muc_user:x/muc_user:item[@affiliation='admin'][@role='moderator']",),
+ ("/presence/muc_user:x/muc_user:status[@code='110']",),
+ ("/message/subject",),
+ ]),
partial(send_stanza,
"<presence from='{jid_two}/{resource_two}' to='#foo%{irc_server_one}/{nick_two}' />"),
@@ -904,26 +1037,23 @@ if __name__ == '__main__':
partial(send_stanza,
"<iq id='kick1' to='#foo%{irc_server_one}' from='{jid_one}/{resource_one}' type='set'><query xmlns='http://jabber.org/protocol/muc#admin'><item nick='{nick_two}' role='none'><reason>reported</reason></item></query></iq>"),
partial(log_message, "Unavailable presence is sent to the two resources"),
- partial(expect_stanza,
- ("/presence[@type='unavailable'][@to='{jid_second}/{resource_one}']/muc_user:x/muc_user:item[@role='none']/muc_user:actor[@nick='{nick_one}']",
+ 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']"
- )),
- partial(expect_stanza,
- ("/presence[@type='unavailable'][@to='{jid_second}/{resource_two}']/muc_user:x/muc_user:item[@role='none']/muc_user:actor[@nick='{nick_one}']",
+ ),
+ ("/presence[@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']"
- )),
- partial(expect_stanza,
+ ),
("/presence[@type='unavailable']/muc_user:x/muc_user:item[@role='none']/muc_user:actor[@nick='{nick_one}']",
"/presence/muc_user:x/muc_user:item/muc_user:reason[text()='reported']",
"/presence/muc_user:x/muc_user:status[@code='307']",
),
- ),
- partial(expect_stanza,
- "/iq[@id='kick1'][@type='result']"),
+ ("/iq[@id='kick1'][@type='result']",),
+ ]),
]),
Scenario("self_version",
[
@@ -944,11 +1074,11 @@ if __name__ == '__main__':
"<iq type='get' from='{jid_one}/{resource_one}' id='first_version' to='#foo%{irc_server_one}/{nick_one}'><query xmlns='jabber:iq:version' /></iq>"),
# We receive our own request,
partial(expect_stanza,
- "/iq[@from='{lower_nick_one}!{irc_server_one}'][@type='get'][@to='{jid_one}/{resource_one}']",
+ "/iq[@from='{lower_nick_one}%{irc_server_one}'][@type='get'][@to='{jid_one}/{resource_one}']",
after = partial(save_value, "id", partial(extract_attribute, "/iq", 'id'))),
# Respond to the request
partial(send_stanza,
- "<iq type='result' to='{lower_nick_one}!{irc_server_one}' id='{id}' from='{jid_one}/{resource_one}'><query xmlns='jabber:iq:version'><name>e2e test</name><version>1.0</version><os>Fedora</os></query></iq>"),
+ "<iq type='result' to='{lower_nick_one}%{irc_server_one}' id='{id}' from='{jid_one}/{resource_one}'><query xmlns='jabber:iq:version'><name>e2e test</name><version>1.0</version><os>Fedora</os></query></iq>"),
partial(expect_stanza,
"/iq[@from='#foo%{irc_server_one}/{nick_one}'][@type='result'][@to='{jid_one}/{resource_one}'][@id='first_version']/version:query/version:name[text()='e2e test (through the biboumi gateway) 1.0 Fedora']"),
@@ -966,12 +1096,12 @@ if __name__ == '__main__':
"<iq type='get' from='{jid_one}/{resource_two}' id='second_version' to='#foo%{irc_server_one}/{nick_one}'><query xmlns='jabber:iq:version' /></iq>"),
# We receive our own request. Note that we don't know the to value, it could be one of our two resources.
partial(expect_stanza,
- "/iq[@from='{lower_nick_one}!{irc_server_one}'][@type='get'][@to]",
+ "/iq[@from='{lower_nick_one}%{irc_server_one}'][@type='get'][@to]",
after = (partial(save_value, "to", partial(extract_attribute, "/iq", "to")),
partial(save_value, "id", partial(extract_attribute, "/iq", "id")))),
# Respond to the request, using the extracted 'to' value as our 'from'
partial(send_stanza,
- "<iq type='result' to='{lower_nick_one}!{irc_server_one}' id='{id}' from='{to}'><query xmlns='jabber:iq:version'><name>e2e test</name><version>1.0</version><os>Fedora</os></query></iq>"),
+ "<iq type='result' to='{lower_nick_one}%{irc_server_one}' id='{id}' from='{to}'><query xmlns='jabber:iq:version'><name>e2e test</name><version>1.0</version><os>Fedora</os></query></iq>"),
partial(expect_stanza,
"/iq[@from='#foo%{irc_server_one}/{nick_one}'][@type='result'][@to='{jid_one}/{resource_two}'][@id='second_version']"),
@@ -980,19 +1110,755 @@ if __name__ == '__main__':
"<iq type='get' from='{jid_one}/{resource_one}' id='second_version' to='#foo%{irc_server_one}/{nick_one}'><query xmlns='jabber:iq:version' /></iq>"),
# We receive our own request. Note that we don't know the to value, it could be one of our two resources.
partial(expect_stanza,
- "/iq[@from='{lower_nick_one}!{irc_server_one}'][@type='get'][@to]",
+ "/iq[@from='{lower_nick_one}%{irc_server_one}'][@type='get'][@to]",
after = (partial(save_value, "to", partial(extract_attribute, "/iq", "to")),
partial(save_value, "id", partial(extract_attribute, "/iq", "id")))),
# Respond to the request, using the extracted 'to' value as our 'from'
partial(send_stanza,
- "<iq type='result' to='{lower_nick_one}!{irc_server_one}' id='{id}' from='{to}'><query xmlns='jabber:iq:version'><name>e2e test</name><version>1.0</version><os>Fedora</os></query></iq>"),
+ "<iq type='result' to='{lower_nick_one}%{irc_server_one}' id='{id}' from='{to}'><query xmlns='jabber:iq:version'><name>e2e test</name><version>1.0</version><os>Fedora</os></query></iq>"),
partial(expect_stanza,
"/iq[@from='#foo%{irc_server_one}/{nick_one}'][@type='result'][@to='{jid_one}/{resource_one}'][@id='second_version']"),
]),
+ Scenario("version_on_global_nick",
+ [
+ partial(log_message, "Joining the channel"),
+ handshake_sequence(),
+ partial(send_stanza,
+ "<presence from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}/{nick_one}' />"),
+ connection_sequence("irc.localhost", '{jid_one}/{resource_one}'),
+ partial(expect_stanza,
+ "/message/body[text()='Mode #foo [+nt] by {irc_host_one}']"),
+ partial(expect_stanza,
+ ("/presence[@to='{jid_one}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_one}']/muc_user:x/muc_user:item[@affiliation='admin'][@role='moderator']",
+ "/presence/muc_user:x/muc_user:status[@code='110']")
+ ),
+ partial(expect_stanza, "/message[@from='#foo%{irc_server_one}'][@type='groupchat']/subject[not(text())]"),
+
+ partial(log_message, "Send a version request to ourself"),
+ partial(send_stanza,
+ "<iq type='get' from='{jid_one}/{resource_one}' id='first_version' to='{lower_nick_one}%{irc_server_one}'><query xmlns='jabber:iq:version' /></iq>"),
+
+ partial(log_message, "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'))),
+ partial(log_message, "Respond to the request"),
+ partial(send_stanza,
+ "<iq type='result' to='{lower_nick_one}%{irc_server_one}' id='{id}' from='{jid_one}/{resource_one}'><query xmlns='jabber:iq:version'><name>e2e test</name><version>1.0</version><os>Fedora</os></query></iq>"),
+ partial(expect_stanza,
+ "/iq[@from='{lower_nick_one}%{irc_server_one}'][@type='result'][@to='{jid_one}/{resource_one}'][@id='first_version']/version:query/version:name[text()='e2e test (through the biboumi gateway) 1.0 Fedora']"),
+
+ ]),
+ Scenario("self_invite",
+ [
+ handshake_sequence(),
+ partial(send_stanza,
+ "<presence from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}/{nick_one}' />"),
+ connection_sequence("irc.localhost", '{jid_one}/{resource_one}'),
+ partial(expect_stanza,
+ "/message/body[text()='Mode #foo [+nt] by {irc_host_one}']"),
+ partial(expect_stanza,
+ ("/presence[@to='{jid_one}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_one}']/muc_user:x/muc_user:item[@affiliation='admin'][@role='moderator']",
+ "/presence/muc_user:x/muc_user:status[@code='110']")
+ ),
+ partial(expect_stanza, "/message[@from='#foo%{irc_server_one}'][@type='groupchat']/subject[not(text())]"),
+ partial(send_stanza,
+ "<message from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}'><x xmlns='http://jabber.org/protocol/muc#user'><invite to='{nick_one}'/></x></message>"),
+ partial(expect_stanza,
+ "/message/body[text()='{nick_one} is already on channel #foo']")
+ ]),
+ Scenario("client_error",
+ [
+ handshake_sequence(),
+ # First resource
+ partial(send_stanza,
+ "<presence from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}/{nick_one}' />"),
+ connection_sequence("irc.localhost", '{jid_one}/{resource_one}'),
+ partial(expect_stanza,
+ "/message/body[text()='Mode #foo [+nt] by {irc_host_one}']"),
+ partial(expect_stanza,
+ ("/presence[@to='{jid_one}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_one}']/muc_user:x/muc_user:item[@affiliation='admin'][@role='moderator']",
+ "/presence/muc_user:x/muc_user:status[@code='110']")
+ ),
+ partial(expect_stanza, "/message[@from='#foo%{irc_server_one}'][@type='groupchat']/subject[not(text())]"),
+
+ # Second resource, same channel
+ partial(send_stanza,
+ "<presence from='{jid_one}/{resource_two}' to='#foo%{irc_server_one}/{nick_one}' />"),
+ partial(expect_stanza,
+ ("/presence[@to='{jid_one}/{resource_two}'][@from='#foo%{irc_server_one}/{nick_one}']/muc_user:x/muc_user:item[@affiliation='admin'][@role='moderator']",
+ "/presence/muc_user:x/muc_user:status[@code='110']")
+ ),
+ partial(expect_stanza, "/message[@from='#foo%{irc_server_one}'][@type='groupchat'][@to='{jid_one}/{resource_two}']/subject[not(text())]"),
+
+ # Now the first resource has an error
+ partial(send_stanza,
+ "<message from='{jid_one}/{resource_one}' to='#foo%%{irc_server_one}/{nick_one}' type='error'><error type='cancel'><recipient-unavailable xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/></error></message>"),
+ # Receive a leave only to the leaving resource
+ partial(expect_stanza,
+ ("/presence[@type='unavailable'][@from='#foo%{irc_server_one}/{nick_one}'][@to='{jid_one}/{resource_one}']/muc_user:x/muc_user:status[@code='110']",
+ "/presence/status[text()='Biboumi note: 1 resources are still in this channel.']")
+ ),
+ ]),
+ Scenario("simple_mam",
+ [
+ handshake_sequence(),
+ partial(send_stanza,
+ "<presence from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}/{nick_one}' />"),
+ connection_sequence("irc.localhost", '{jid_one}/{resource_one}'),
+ partial(expect_stanza,
+ "/message/body[text()='Mode #foo [+nt] by {irc_host_one}']"),
+ partial(expect_stanza,
+ ("/presence[@to='{jid_one}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_one}']/muc_user:x/muc_user:item[@affiliation='admin'][@role='moderator']",
+ "/presence/muc_user:x/muc_user:status[@code='110']")
+ ),
+ partial(expect_stanza, "/message[@from='#foo%{irc_server_one}'][@type='groupchat']/subject[not(text())]"),
+
+ # Send two channel messages
+ partial(send_stanza, "<message from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}' type='groupchat'><body>coucou</body></message>"),
+ partial(expect_stanza, "/message[@from='#foo%{irc_server_one}/{nick_one}'][@to='{jid_one}/{resource_one}'][@type='groupchat']/body[text()='coucou']"),
+
+ partial(send_stanza, "<message from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}' type='groupchat'><body>coucou 2</body></message>"),
+ partial(expect_stanza, "/message[@from='#foo%{irc_server_one}/{nick_one}'][@to='{jid_one}/{resource_one}'][@type='groupchat']/body[text()='coucou 2']"),
+
+ # Retrieve the complete archive
+ partial(send_stanza, "<iq to='#foo%{irc_server_one}' from='{jid_one}/{resource_one}' type='set' id='id1'><query xmlns='urn:xmpp:mam:1' queryid='qid1' /></iq>"),
+
+ partial(expect_stanza,
+ ("/message/mam:result[@queryid='qid1']/forward:forwarded/delay:delay",
+ "/message/mam:result[@queryid='qid1']/forward:forwarded/client:message[@from='#foo%{irc_server_one}/{nick_one}'][@type='groupchat']/client:body[text()='coucou']")
+ ),
+ partial(expect_stanza,
+ ("/message/mam:result[@queryid='qid1']/forward:forwarded/delay:delay",
+ "/message/mam:result[@queryid='qid1']/forward:forwarded/client:message[@from='#foo%{irc_server_one}/{nick_one}'][@type='groupchat']/client:body[text()='coucou 2']")
+ ),
+
+ partial(expect_stanza,
+ "/iq[@type='result'][@id='id1'][@from='#foo%{irc_server_one}'][@to='{jid_one}/{resource_one}']"),
+
+ # Retrieve an empty archive by specifying an early “end” date
+ partial(send_stanza, """<iq to='#foo%{irc_server_one}' from='{jid_one}/{resource_one}' type='set' id='id2'>
+ <query xmlns='urn:xmpp:mam:1' queryid='qid2'>
+ <x xmlns='jabber:x:data' type='submit'>
+ <field var='FORM_TYPE' type='hidden'> <value>urn:xmpp:mam:1</value></field>
+ <field var='end'><value>2000-06-07T00:00:00Z</value></field>
+ </x>
+ </query></iq>"""),
+
+ partial(expect_stanza,
+ "/iq[@type='result'][@id='id2'][@from='#foo%{irc_server_one}'][@to='{jid_one}/{resource_one}']"),
+
+ # Retrieve an empty archive by specifying a late “start” date
+ # (note that this test will break in ~1000 years)
+ partial(send_stanza, """<iq to='#foo%{irc_server_one}' from='{jid_one}/{resource_one}' type='set' id='id3'>
+ <query xmlns='urn:xmpp:mam:1' queryid='qid3'>
+ <x xmlns='jabber:x:data' type='submit'>
+ <field var='FORM_TYPE' type='hidden'> <value>urn:xmpp:mam:1</value></field>
+ <field var='start'><value>3016-06-07T00:00:00Z</value></field>
+ </x>
+ </query></iq>"""),
+
+ partial(expect_stanza,
+ "/iq[@type='result'][@id='id3'][@from='#foo%{irc_server_one}'][@to='{jid_one}/{resource_one}']"),
+ ]),
+ Scenario("mam_on_fixed_server",
+ [
+ handshake_sequence(),
+
+ partial(send_stanza,
+ "<presence from='{jid_one}/{resource_one}' to='#foo@{biboumi_host}/{nick_one}' />"),
+ connection_sequence("irc.localhost", '{jid_one}/{resource_one}'),
+ partial(expect_stanza,
+ "/message/body[text()='Mode #foo [+nt] by {irc_host_one}']"),
+ partial(expect_stanza,
+ ("/presence[@to='{jid_one}/{resource_one}'][@from='#foo@{biboumi_host}/{nick_one}']/muc_user:x/muc_user:item[@affiliation='admin'][@role='moderator']",
+ "/presence/muc_user:x/muc_user:status[@code='110']")
+ ),
+ partial(expect_stanza, "/message[@from='#foo@{biboumi_host}'][@type='groupchat']/subject[not(text())]"),
+
+ partial(send_stanza, "<message from='{jid_one}/{resource_one}' to='#foo@{biboumi_host}' type='groupchat'><body>coucou</body></message>"),
+ partial(expect_stanza, "/message[@from='#foo@{biboumi_host}/{nick_one}'][@to='{jid_one}/{resource_one}'][@type='groupchat']/body[text()='coucou']"),
+
+ partial(send_stanza, "<message from='{jid_one}/{resource_one}' to='#foo@{biboumi_host}' type='groupchat'><body>coucou 2</body></message>"),
+ partial(expect_stanza, "/message[@from='#foo@{biboumi_host}/{nick_one}'][@to='{jid_one}/{resource_one}'][@type='groupchat']/body[text()='coucou 2']"),
+
+ # Retrieve the complete archive
+ partial(send_stanza, "<iq to='#foo@{biboumi_host}' from='{jid_one}/{resource_one}' type='set' id='id1'><query xmlns='urn:xmpp:mam:1' queryid='qid1' /></iq>"),
+
+ partial(expect_stanza,
+ ("/message/mam:result[@queryid='qid1']/forward:forwarded/delay:delay",
+ "/message/mam:result[@queryid='qid1']/forward:forwarded/client:message[@from='#foo@{biboumi_host}/{nick_one}'][@type='groupchat']/client:body[text()='coucou']")
+ ),
+ partial(expect_stanza,
+ ("/message/mam:result[@queryid='qid1']/forward:forwarded/delay:delay",
+ "/message/mam:result[@queryid='qid1']/forward:forwarded/client:message[@from='#foo@{biboumi_host}/{nick_one}'][@type='groupchat']/client:body[text()='coucou 2']")
+ ),
+ ], conf="fixed_server"),
+ Scenario("channel_history_on_fixed_server",
+ [
+ handshake_sequence(),
+ # First user join
+ partial(send_stanza,
+ "<presence from='{jid_one}/{resource_one}' to='#foo@{biboumi_host}/{nick_one}' />"),
+ connection_sequence("irc.localhost", '{jid_one}/{resource_one}'),
+ partial(expect_stanza,
+ "/message/body[text()='Mode #foo [+nt] by {irc_host_one}']"),
+ partial(expect_stanza,
+ ("/presence[@to='{jid_one}/{resource_one}'][@from='#foo@{biboumi_host}/{nick_one}']/muc_user:x/muc_user:item[@affiliation='admin'][@jid='~nick@localhost'][@role='moderator']",
+ "/presence/muc_user:x/muc_user:status[@code='110']")
+ ),
+ partial(expect_stanza, "/message[@from='#foo@{biboumi_host}'][@type='groupchat']/subject[not(text())]"),
+
+ # Send one channel message
+ partial(send_stanza, "<message from='{jid_one}/{resource_one}' to='#foo@{biboumi_host}' type='groupchat'><body>coucou</body></message>"),
+ partial(expect_stanza, "/message[@from='#foo@{biboumi_host}/{nick_one}'][@to='{jid_one}/{resource_one}'][@type='groupchat']/body[text()='coucou']"),
+
+ # Second user joins
+ partial(send_stanza,
+ "<presence from='{jid_one}/{resource_two}' to='#foo@{biboumi_host}/{nick_one}' />"),
+ # connection_sequence("irc.localhost", '{jid_one}/{resource_two}'),
+ # partial(expect_stanza,
+ # "/message/body[text()='Mode #foo [+nt] by {irc_host_one}']"),
+ partial(expect_stanza,
+ ("/presence[@to='{jid_one}/{resource_two}'][@from='#foo@{biboumi_host}/{nick_one}']/muc_user:x/muc_user:item[@affiliation='admin'][@jid='~nick@localhost'][@role='moderator']",
+ "/presence/muc_user:x/muc_user:status[@code='110']")
+ ),
+ # Receive the history message
+ partial(expect_stanza, ("/message[@from='#foo@{biboumi_host}/{nick_one}']/body[text()='coucou']",
+ "/message/delay:delay[@from='#foo@{biboumi_host}']")),
+
+ partial(expect_stanza, "/message[@from='#foo@{biboumi_host}'][@type='groupchat']/subject[not(text())]"),
+ ], conf="fixed_server"),
+ Scenario("channel_history",
+ [
+ handshake_sequence(),
+ # First user join
+ partial(send_stanza,
+ "<presence from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}/{nick_one}' />"),
+ connection_sequence("irc.localhost", '{jid_one}/{resource_one}'),
+ partial(expect_stanza,
+ "/message/body[text()='Mode #foo [+nt] by {irc_host_one}']"),
+ partial(expect_stanza,
+ ("/presence[@to='{jid_one}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_one}']/muc_user:x/muc_user:item[@affiliation='admin'][@jid='~nick@localhost'][@role='moderator']",
+ "/presence/muc_user:x/muc_user:status[@code='110']")
+ ),
+ partial(expect_stanza, "/message[@from='#foo%{irc_server_one}'][@type='groupchat']/subject[not(text())]"),
+
+ # Send one channel message
+ partial(send_stanza, "<message from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}' type='groupchat'><body>coucou</body></message>"),
+ partial(expect_stanza, "/message[@from='#foo%{irc_server_one}/{nick_one}'][@to='{jid_one}/{resource_one}'][@type='groupchat']/body[text()='coucou']"),
+
+ # Second user joins
+ partial(send_stanza,
+ "<presence from='{jid_one}/{resource_two}' to='#foo%{irc_server_one}/{nick_one}' />"),
+ # connection_sequence("irc.localhost", '{jid_one}/{resource_two}'),
+ # partial(expect_stanza,
+ # "/message/body[text()='Mode #foo [+nt] by {irc_host_one}']"),
+ partial(expect_stanza,
+ ("/presence[@to='{jid_one}/{resource_two}'][@from='#foo%{irc_server_one}/{nick_one}']/muc_user:x/muc_user:item[@affiliation='admin'][@jid='~nick@localhost'][@role='moderator']",
+ "/presence/muc_user:x/muc_user:status[@code='110']")
+ ),
+ # Receive the history message
+ partial(expect_stanza, ("/message[@from='#foo%{irc_server_one}/{nick_one}']/body[text()='coucou']",
+ "/message/delay:delay[@from='#foo%{irc_server_one}']")),
+
+ partial(expect_stanza, "/message[@from='#foo%{irc_server_one}'][@type='groupchat']/subject[not(text())]"),
+ ]),
+ Scenario("simple_channel_list",
+ [
+ handshake_sequence(),
+
+ partial(log_message, "Join first channel #foo"),
+ partial(send_stanza,
+ "<presence from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}/{nick_one}' />"),
+ connection_sequence("irc.localhost", '{jid_one}/{resource_one}'),
+ partial(expect_stanza,
+ "/message/body[text()='Mode #foo [+nt] by {irc_host_one}']"),
+ partial(expect_stanza,
+ ("/presence[@to='{jid_one}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_one}']/muc_user:x/muc_user:item[@affiliation='admin'][@role='moderator']",
+ "/presence/muc_user:x/muc_user:status[@code='110']")
+ ),
+ partial(expect_stanza, "/message[@from='#foo%{irc_server_one}'][@type='groupchat']/subject[not(text())]"),
+
+ partial(log_message, "Join second channel #bar"),
+ partial(send_stanza,
+ "<presence from='{jid_one}/{resource_one}' to='#bar%{irc_server_one}/{nick_one}' />"),
+ partial(expect_stanza,
+ "/message/body[text()='Mode #bar [+nt] by {irc_host_one}']"),
+ partial(expect_stanza, "/presence"),
+ partial(expect_stanza, "/message[@from='#bar%{irc_server_one}'][@type='groupchat']/subject[not(text())]"),
+
+ partial(log_message, "Request the whole channel list"),
+ partial(send_stanza, "<iq from='{jid_one}/{resource_one}' id='id1' to='{irc_server_one}' type='get'><query xmlns='http://jabber.org/protocol/disco#items'/></iq>"),
+ partial(expect_stanza, (
+ "/iq[@type='result']/disco_items:query",
+ "/iq/disco_items:query/disco_items:item[@jid='#foo%{irc_server_one}']",
+ "/iq/disco_items:query/disco_items:item[@jid='#bar%{irc_server_one}']"
+ ))
+ ]),
+ Scenario("channel_list_with_rsm",
+ [
+ handshake_sequence(),
+
+ partial(log_message, "Join first channel #foo"),
+ partial(send_stanza,
+ "<presence from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}/{nick_one}' />"),
+ connection_sequence("irc.localhost", '{jid_one}/{resource_one}'),
+ partial(expect_stanza,
+ "/message/body[text()='Mode #foo [+nt] by {irc_host_one}']"),
+ partial(expect_stanza,
+ ("/presence[@to='{jid_one}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_one}']/muc_user:x/muc_user:item[@affiliation='admin'][@role='moderator']",
+ "/presence/muc_user:x/muc_user:status[@code='110']")
+ ),
+ partial(expect_stanza, "/message[@from='#foo%{irc_server_one}'][@type='groupchat']/subject[not(text())]"),
+
+ partial(log_message, "Join second channel #bar"),
+ partial(send_stanza,
+ "<presence from='{jid_one}/{resource_one}' to='#bar%{irc_server_one}/{nick_one}' />"),
+ partial(expect_stanza,
+ "/message/body[text()='Mode #bar [+nt] by {irc_host_one}']"),
+ partial(expect_stanza, "/presence"),
+ partial(expect_stanza, "/message[@from='#bar%{irc_server_one}'][@type='groupchat']/subject[not(text())]"),
+
+ partial(log_message, "Join third channel #coucou"),
+ partial(send_stanza,
+ "<presence from='{jid_one}/{resource_one}' to='#coucou%{irc_server_one}/{nick_one}' />"),
+ partial(expect_stanza,
+ "/message/body[text()='Mode #coucou [+nt] by {irc_host_one}']"),
+ partial(expect_stanza, "/presence"),
+ partial(expect_stanza, "/message[@from='#coucou%{irc_server_one}'][@type='groupchat']/subject[not(text())]"),
+
+ partial(log_message, "Request with max=0"),
+ partial(send_stanza, "<iq from='{jid_one}/{resource_one}' id='id1' to='{irc_server_one}' type='get'><query xmlns='http://jabber.org/protocol/disco#items'><set xmlns='http://jabber.org/protocol/rsm'><max>0</max></set></query></iq>"),
+ partial(expect_stanza, (
+ "/iq[@type='result']/disco_items:query",
+ )),
+
+ partial(log_message, "Request with max=2"),
+ partial(send_stanza, "<iq from='{jid_one}/{resource_one}' id='id1' to='{irc_server_one}' type='get'><query xmlns='http://jabber.org/protocol/disco#items'><set xmlns='http://jabber.org/protocol/rsm'><max>2</max></set></query></iq>"),
+ partial(expect_stanza, (
+ "/iq[@type='result']/disco_items:query",
+ "/iq/disco_items:query/disco_items:item[@jid='#bar%{irc_server_one}']",
+ "/iq/disco_items:query/disco_items:item[@jid='#coucou%{irc_server_one}']",
+ "/iq/disco_items:query/rsm:set/rsm:first[text()='#bar%{irc_server_one}'][@index='0']",
+ "/iq/disco_items:query/rsm:set/rsm:last[text()='#coucou%{irc_server_one}']",
+ "/iq/disco_items:query/rsm:set/rsm:count[text()='3']"
+ )),
+
+ partial(log_message, "Request with max=12"),
+ partial(send_stanza, "<iq from='{jid_one}/{resource_one}' id='id1' to='{irc_server_one}' type='get'><query xmlns='http://jabber.org/protocol/disco#items'><set xmlns='http://jabber.org/protocol/rsm'><max>12</max></set></query></iq>"),
+ partial(expect_stanza, (
+ "/iq[@type='result']/disco_items:query",
+ "/iq/disco_items:query/disco_items:item[@jid='#bar%{irc_server_one}']",
+ "/iq/disco_items:query/disco_items:item[@jid='#coucou%{irc_server_one}']",
+ "/iq/disco_items:query/disco_items:item[@jid='#foo%{irc_server_one}']",
+ "/iq/disco_items:query/rsm:set/rsm:first[text()='#bar%{irc_server_one}'][@index='0']",
+ "/iq/disco_items:query/rsm:set/rsm:last[text()='#foo%{irc_server_one}']",
+ "/iq/disco_items:query/rsm:set/rsm:count[text()='3']"
+ )),
+
+ partial(log_message, "Request with max=1 after=#bar"),
+ partial(send_stanza, "<iq from='{jid_one}/{resource_one}' id='id1' to='{irc_server_one}' type='get'><query xmlns='http://jabber.org/protocol/disco#items'><set xmlns='http://jabber.org/protocol/rsm'><after>#bar%{irc_server_one}</after><max>1</max></set></query></iq>"),
+ partial(expect_stanza, (
+ "/iq[@type='result']/disco_items:query",
+ "/iq/disco_items:query/disco_items:item[@jid='#coucou%{irc_server_one}']",
+ "/iq/disco_items:query/rsm:set/rsm:first[text()='#coucou%{irc_server_one}'][@index='1']",
+ "/iq/disco_items:query/rsm:set/rsm:last[text()='#coucou%{irc_server_one}']",
+ "/iq/disco_items:query/rsm:set/rsm:count[text()='3']"
+ )),
+
+ partial(log_message, "Request with max=1 after=#bar"),
+ partial(send_stanza, "<iq from='{jid_one}/{resource_one}' id='id1' to='{irc_server_one}' type='get'><query xmlns='http://jabber.org/protocol/disco#items'><set xmlns='http://jabber.org/protocol/rsm'><after>#bar%{irc_server_one}</after><max>1</max></set></query></iq>"),
+ partial(expect_stanza, (
+ "/iq[@type='result']/disco_items:query",
+ "/iq/disco_items:query/disco_items:item[@jid='#coucou%{irc_server_one}']",
+ "/iq/disco_items:query/rsm:set/rsm:first[text()='#coucou%{irc_server_one}'][@index='1']",
+ "/iq/disco_items:query/rsm:set/rsm:last[text()='#coucou%{irc_server_one}']",
+ "/iq/disco_items:query/rsm:set/rsm:count[text()='3']"
+ ))
+ ]),
+ Scenario("complete_channel_list_with_pages_of_3",
+ [
+ handshake_sequence(),
+
+ partial(log_message, "Join 10 channels"),
+ partial(send_stanza,
+ "<presence from='{jid_one}/{resource_one}' to='#aaa%{irc_server_one}/{nick_one}' />"),
+ connection_sequence("irc.localhost", '{jid_one}/{resource_one}'),
+ partial(expect_stanza, "/message"),
+ partial(expect_stanza, "/presence"),
+ partial(expect_stanza, "/message"),
+
+ partial(send_stanza,
+ "<presence from='{jid_one}/{resource_one}' to='#bbb%{irc_server_one}/{nick_one}' />"),
+ partial(expect_stanza, "/message"),
+ partial(expect_stanza, "/presence"),
+ partial(expect_stanza, "/message"),
+
+ partial(send_stanza,
+ "<presence from='{jid_one}/{resource_one}' to='#ccc%{irc_server_one}/{nick_one}' />"),
+ partial(expect_stanza, "/message"),
+ partial(expect_stanza, "/presence"),
+ partial(expect_stanza, "/message"),
+
+ partial(send_stanza,
+ "<presence from='{jid_one}/{resource_one}' to='#ddd%{irc_server_one}/{nick_one}' />"),
+ partial(expect_stanza, "/message"),
+ partial(expect_stanza, "/presence"),
+ partial(expect_stanza, "/message"),
+
+ partial(send_stanza,
+ "<presence from='{jid_one}/{resource_one}' to='#eee%{irc_server_one}/{nick_one}' />"),
+ partial(expect_stanza, "/message"),
+ partial(expect_stanza, "/presence"),
+ partial(expect_stanza, "/message"),
+
+ partial(send_stanza,
+ "<presence from='{jid_one}/{resource_one}' to='#fff%{irc_server_one}/{nick_one}' />"),
+ partial(expect_stanza, "/message"),
+ partial(expect_stanza, "/presence"),
+ partial(expect_stanza, "/message"),
+
+ partial(send_stanza,
+ "<presence from='{jid_one}/{resource_one}' to='#ggg%{irc_server_one}/{nick_one}' />"),
+ partial(expect_stanza, "/message"),
+ partial(expect_stanza, "/presence"),
+ partial(expect_stanza, "/message"),
+
+ partial(send_stanza,
+ "<presence from='{jid_one}/{resource_one}' to='#hhh%{irc_server_one}/{nick_one}' />"),
+ partial(expect_stanza, "/message"),
+ partial(expect_stanza, "/presence"),
+ partial(expect_stanza, "/message"),
+
+ partial(send_stanza,
+ "<presence from='{jid_one}/{resource_one}' to='#iii%{irc_server_one}/{nick_one}' />"),
+ partial(expect_stanza, "/message"),
+ partial(expect_stanza, "/presence"),
+ partial(expect_stanza, "/message"),
+
+ partial(send_stanza,
+ "<presence from='{jid_one}/{resource_one}' to='#jjj%{irc_server_one}/{nick_one}' />"),
+ partial(expect_stanza, "/message"),
+ partial(expect_stanza, "/presence"),
+ partial(expect_stanza, "/message"),
+
+ partial(log_message, "Request the first page, with a limit of 3"),
+ partial(send_stanza, "<iq from='{jid_one}/{resource_one}' id='id' to='{irc_server_one}' type='get'><query xmlns='http://jabber.org/protocol/disco#items'><set xmlns='http://jabber.org/protocol/rsm'><max>3</max></set></query></iq>"),
+ partial(expect_stanza, (
+ "/iq[@type='result']/disco_items:query",
+ "/iq/disco_items:query/disco_items:item[@jid='#aaa%{irc_server_one}']",
+ "/iq/disco_items:query/disco_items:item[@jid='#bbb%{irc_server_one}']",
+ "/iq/disco_items:query/disco_items:item[@jid='#ccc%{irc_server_one}']",
+ "/iq/disco_items:query/rsm:set/rsm:first[text()='#aaa%{irc_server_one}'][@index='0']",
+ "/iq/disco_items:query/rsm:set/rsm:last[text()='#ccc%{irc_server_one}']"
+ )),
+
+ partial(log_message, "Request subsequent pages"),
+ partial(send_stanza, "<iq from='{jid_one}/{resource_one}' id='id' to='{irc_server_one}' type='get'><query xmlns='http://jabber.org/protocol/disco#items'><set xmlns='http://jabber.org/protocol/rsm'><after>#ccc%{irc_server_one}</after><max>3</max></set></query></iq>"),
+ partial(expect_stanza, (
+ "/iq[@type='result']/disco_items:query",
+ "/iq/disco_items:query/disco_items:item[@jid='#ddd%{irc_server_one}']",
+ "/iq/disco_items:query/disco_items:item[@jid='#eee%{irc_server_one}']",
+ "/iq/disco_items:query/disco_items:item[@jid='#fff%{irc_server_one}']",
+ "/iq/disco_items:query/rsm:set/rsm:first[text()='#ddd%{irc_server_one}'][@index='3']",
+ "/iq/disco_items:query/rsm:set/rsm:last[text()='#fff%{irc_server_one}']"
+ )),
+
+ partial(send_stanza, "<iq from='{jid_one}/{resource_one}' id='id' to='{irc_server_one}' type='get'><query xmlns='http://jabber.org/protocol/disco#items'><set xmlns='http://jabber.org/protocol/rsm'><after>#fff%{irc_server_one}</after><max>3</max></set></query></iq>"),
+ partial(expect_stanza, (
+ "/iq[@type='result']/disco_items:query",
+ "/iq/disco_items:query/disco_items:item[@jid='#ggg%{irc_server_one}']",
+ "/iq/disco_items:query/disco_items:item[@jid='#hhh%{irc_server_one}']",
+ "/iq/disco_items:query/disco_items:item[@jid='#iii%{irc_server_one}']",
+ "/iq/disco_items:query/rsm:set/rsm:first[text()='#ggg%{irc_server_one}'][@index='6']",
+ "/iq/disco_items:query/rsm:set/rsm:last[text()='#iii%{irc_server_one}']"
+ )),
+
+ partial(send_stanza, "<iq from='{jid_one}/{resource_one}' id='id' to='{irc_server_one}' type='get'><query xmlns='http://jabber.org/protocol/disco#items'><set xmlns='http://jabber.org/protocol/rsm'><after>#iii%{irc_server_one}</after><max>3</max></set></query></iq>"),
+ partial(expect_stanza, (
+ "/iq[@type='result']/disco_items:query",
+ "/iq/disco_items:query/disco_items:item[@jid='#jjj%{irc_server_one}']",
+ "/iq/disco_items:query/rsm:set/rsm:first[text()='#jjj%{irc_server_one}'][@index='9']",
+ "/iq/disco_items:query/rsm:set/rsm:last[text()='#jjj%{irc_server_one}']",
+ "/iq/disco_items:query/rsm:set/rsm:count[text()='10']"
+ )),
+
+ partial(log_message, "Leaving the 10 channels"),
+ partial(send_stanza, "<presence from='{jid_one}/{resource_one}' to='#aaa%{irc_server_one}/{nick_one}' type='unavailable' />"),
+ partial(send_stanza, "<presence from='{jid_one}/{resource_one}' to='#bbb%{irc_server_one}/{nick_one}' type='unavailable' />"),
+ partial(send_stanza, "<presence from='{jid_one}/{resource_one}' to='#ccc%{irc_server_one}/{nick_one}' type='unavailable' />"),
+ partial(send_stanza, "<presence from='{jid_one}/{resource_one}' to='#ddd%{irc_server_one}/{nick_one}' type='unavailable' />"),
+ partial(send_stanza, "<presence from='{jid_one}/{resource_one}' to='#eee%{irc_server_one}/{nick_one}' type='unavailable' />"),
+ partial(send_stanza, "<presence from='{jid_one}/{resource_one}' to='#fff%{irc_server_one}/{nick_one}' type='unavailable' />"),
+ partial(send_stanza, "<presence from='{jid_one}/{resource_one}' to='#ggg%{irc_server_one}/{nick_one}' type='unavailable' />"),
+ partial(send_stanza, "<presence from='{jid_one}/{resource_one}' to='#hhh%{irc_server_one}/{nick_one}' type='unavailable' />"),
+ partial(send_stanza, "<presence from='{jid_one}/{resource_one}' to='#iii%{irc_server_one}/{nick_one}' type='unavailable' />"),
+ partial(send_stanza, "<presence from='{jid_one}/{resource_one}' to='#jjj%{irc_server_one}/{nick_one}' type='unavailable' />"),
+ partial(expect_stanza, "/presence[@type='unavailable']"),
+ partial(expect_stanza, "/presence[@type='unavailable']"),
+ partial(expect_stanza, "/presence[@type='unavailable']"),
+ partial(expect_stanza, "/presence[@type='unavailable']"),
+ partial(expect_stanza, "/presence[@type='unavailable']"),
+ partial(expect_stanza, "/presence[@type='unavailable']"),
+ partial(expect_stanza, "/presence[@type='unavailable']"),
+ partial(expect_stanza, "/presence[@type='unavailable']"),
+ partial(expect_stanza, "/presence[@type='unavailable']"),
+ partial(expect_stanza, "/presence[@type='unavailable']")
+ ]),
+ Scenario("muc_traffic_info",
+ [
+ handshake_sequence(),
+
+ partial(send_stanza,
+ "<iq from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}' id='1' type='get'><query xmlns='http://jabber.org/protocol/disco#info' node='http://jabber.org/protocol/muc#traffic'/></iq>"),
+ partial(expect_stanza, "/iq[@type='result']/disco_info:query[@node='http://jabber.org/protocol/muc#traffic']"),
+ ]),
+ Scenario("raw_message",
+ [
+ handshake_sequence(),
+ partial(send_stanza, "<presence from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}/{nick_one}' />"),
+ connection_sequence("irc.localhost", '{jid_one}/{resource_one}'),
+ partial(expect_stanza, "/message"),
+ partial(expect_stanza, "/presence"),
+ partial(expect_stanza, "/message"),
+
+ partial(send_stanza, "<message from='{jid_one}/{resource_one}' to='{irc_server_one}' type='chat'><body>WHOIS {nick_one}</body></message>"),
+ partial(expect_stanza, "/message[@from='{irc_server_one}'][@type='chat']/body[text()='irc.localhost: {nick_one} ~{nick_one} localhost * {nick_one}']"),
+ ]),
+ Scenario("self_disco_info",
+ [
+ handshake_sequence(),
+ partial(send_stanza, "<iq type='get' id='get1' from='{jid_one}/{resource_one}' to='{biboumi_host}'><query xmlns='http://jabber.org/protocol/disco#info'/></iq>"),
+ partial(expect_stanza,
+ ("/iq[@type='result']/disco_info:query/disco_info:identity[@category='conference'][@type='irc'][@name='Biboumi XMPP-IRC gateway']",
+ "/iq/disco_info:query/disco_info:feature[@var='jabber:iq:version']",
+ "/iq/disco_info:query/disco_info:feature[@var='http://jabber.org/protocol/commands']",
+ "/iq/disco_info:query/disco_info:feature[@var='urn:xmpp:ping']",
+ "/iq/disco_info:query/disco_info:feature[@var='urn:xmpp:mam:1']",
+ "/iq/disco_info:query/disco_info:feature[@var='jabber:iq:version']",
+ )),
+ ]),
+ Scenario("invite_other",
+ [
+ handshake_sequence(),
+ partial(send_stanza, "<presence from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}/{nick_one}' />"),
+ connection_sequence("irc.localhost", '{jid_one}/{resource_one}'),
+ partial(expect_stanza, "/message"),
+ partial(expect_stanza, "/presence"),
+ partial(expect_stanza, "/message"),
+
+ partial(send_stanza, "<presence from='{jid_two}/{resource_two}' to='#bar%{irc_server_one}@{biboumi_host}/{nick_two}' />"),
+ connection_sequence("irc.localhost", '{jid_two}/{resource_two}'),
+ partial(expect_stanza, "/message"),
+ partial(expect_stanza, "/presence"),
+ partial(expect_stanza, "/message"),
+ partial(send_stanza, "<message from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}'><x xmlns='http://jabber.org/protocol/muc#user'><invite to='{nick_two}'/></x></message>"),
+ partial(expect_stanza, "/message/body[text()='{nick_two} has been invited to #foo']"),
+ partial(expect_stanza, "/message[@to='{jid_two}/{resource_two}'][@from='#foo%{irc_server_one}']/muc_user:x/muc_user:invite[@from='#foo%{irc_server_one}/{nick_one}']"),
+ ]),
+ Scenario("virtual_channel_multisession",
+ [
+ handshake_sequence(),
+ partial(send_stanza,
+ "<presence from='{jid_one}/{resource_one}' to='%{irc_server_one}/{nick_one}' />"),
+ connection_begin_sequence("irc.localhost", '{jid_one}/{resource_one}'),
+ partial(expect_stanza,
+ ("/presence[@to='{jid_one}/{resource_one}'][@from='%{irc_server_one}/{nick_one}']/muc_user:x/muc_user:item[@affiliation='none'][@role='participant']",
+ "/presence/muc_user:x/muc_user:status[@code='110']")
+ ),
+ partial(expect_stanza, "/message[@from='%{irc_server_one}'][@type='groupchat']/subject[re:test(text(), '^This is a virtual channel.*$')]"),
+ connection_end_sequence("irc.localhost", '{jid_one}/{resource_one}'),
+
+ partial(send_stanza,
+ "<presence from='{jid_one}/{resource_two}' to='%{irc_server_one}/{nick_one}' />"),
+
+ partial(expect_stanza,
+ ("/presence[@to='{jid_one}/{resource_two}'][@from='%{irc_server_one}/{nick_one}']/muc_user:x/muc_user:item[@affiliation='none'][@role='participant']",
+ "/presence/muc_user:x/muc_user:status[@code='110']")
+ ),
+ partial(expect_stanza, "/message[@to='{jid_one}/{resource_two}'][@from='%{irc_server_one}'][@type='groupchat']/subject[re:test(text(), '^This is a virtual channel.*$')]"),
+
+
+ partial(log_message, "Nick change"),
+ partial(send_stanza, "<presence from='{jid_one}/{resource_one}' to='%{irc_server_one}/{nick_two}' />"),
+
+ partial(expect_unordered, [
+ ("/presence[@from='%{irc_server_one}/{nick_one}'][@to='{jid_one}/{resource_two}'][@type='unavailable']/muc_user:x/muc_user:item[@nick='Bobby']",
+ "/presence/muc_user:x/muc_user:status[@code='303']",
+ "/presence/muc_user:x/muc_user:status[@code='110']"),
+ ("/presence[@from='%{irc_server_one}/{nick_two}'][@to='{jid_one}/{resource_two}']",
+ "/presence/muc_user:x/muc_user:status[@code='110']"),
+
+ ("/presence[@from='%{irc_server_one}/{nick_one}'][@to='{jid_one}/{resource_one}'][@type='unavailable']/muc_user:x/muc_user:item[@nick='Bobby']",
+ "/presence/muc_user:x/muc_user:status[@code='303']",
+ "/presence/muc_user:x/muc_user:status[@code='110']"),
+ ("/presence[@from='%{irc_server_one}/{nick_two}'][@to='{jid_one}/{resource_one}']",
+ "/presence/muc_user:x/muc_user:status[@code='110']"),
+ ]),
+
+
+ partial(log_message, "First resource leaves."),
+ partial(send_stanza, "<presence type='unavailable' from='{jid_one}/{resource_one}' to='%{irc_server_one}/{nick_two}' />"),
+ partial(expect_stanza, ("/presence[@type='unavailable'][@from='%{irc_server_one}/{nick_two}'][@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.']",)
+ ),
+
+ partial(log_message, "Second resource leaves."),
+ partial(send_stanza, "<presence type='unavailable' from='{jid_one}/{resource_two}' to='%{irc_server_one}/{nick_two}' />"),
+ partial(expect_stanza, "/presence[@type='unavailable'][@from='%{irc_server_one}/{nick_two}']"),
+ partial(expect_stanza, "/message[@from='{irc_server_one}']/body[text()='ERROR: Closing Link: localhost (Client Quit)']"),
+ partial(expect_stanza, "/message[@from='{irc_server_one}']/body[text()='ERROR: Connection closed.']"),
+ ]),
+ Scenario("global_configure",
+ [
+ handshake_sequence(),
+ partial(send_stanza, "<iq type='set' id='id1' from='{jid_one}/{resource_one}' to='{biboumi_host}'><command xmlns='http://jabber.org/protocol/commands' node='configure' action='execute' /></iq>"),
+ partial(expect_stanza, ("/iq[@type='result']/commands:command[@node='configure'][@sessionid][@status='executing']",
+ "/iq/commands:command/dataform:x[@type='form']/dataform:title[text()='Configure some global default settings.']",
+ "/iq/commands:command/dataform:x[@type='form']/dataform:instructions[text()='Edit the form, to configure your global settings for the component.']",
+ "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='text-single'][@var='max_history_length']/dataform:value[text()='20']",
+ "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='boolean'][@var='record_history']/dataform:value[text()='true']",
+ "/iq/commands:command/commands:actions/commands:next",
+ ),
+ after = partial(save_value, "sessionid", partial(extract_attribute, "/iq[@type='result']/commands:command[@node='configure']", "sessionid"))
+ ),
+ partial(send_stanza, "<iq type='set' id='id2' from='{jid_one}/{resource_one}' to='{biboumi_host}'><command xmlns='http://jabber.org/protocol/commands' node='configure' sessionid='{sessionid}' action='next'><x xmlns='jabber:x:data' type='submit'><field var='record_history'><value>0</value></field><field var='max_history_length'><value>42</value></field></x></command></iq>"),
+ partial(expect_stanza, "/iq[@type='result']/commands:command[@node='configure'][@status='completed']/commands:note[@type='info'][text()='Configuration successfully applied.']"),
+
+ partial(send_stanza, "<iq type='set' id='id3' from='{jid_one}/{resource_one}' to='{biboumi_host}'><command xmlns='http://jabber.org/protocol/commands' node='configure' action='execute' /></iq>"),
+ partial(expect_stanza, ("/iq[@type='result']/commands:command[@node='configure'][@sessionid][@status='executing']",
+ "/iq/commands:command/dataform:x[@type='form']/dataform:title[text()='Configure some global default settings.']",
+ "/iq/commands:command/dataform:x[@type='form']/dataform:instructions[text()='Edit the form, to configure your global settings for the component.']",
+ "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='text-single'][@var='max_history_length']/dataform:value[text()='42']",
+ "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='boolean'][@var='record_history']/dataform:value[text()='false']",
+ "/iq/commands:command/commands:actions/commands:next",
+ ),
+ after = partial(save_value, "sessionid", partial(extract_attribute, "/iq[@type='result']/commands:command[@node='configure']", "sessionid"))
+ ),
+ partial(send_stanza, "<iq type='set' id='id4' from='{jid_one}/{resource_one}' to='{biboumi_host}'><command xmlns='http://jabber.org/protocol/commands' action='cancel' node='configure' sessionid='{sessionid}' /></iq>"),
+ partial(expect_stanza, "/iq[@type='result']/commands:command[@node='configure'][@status='canceled']"),
+ ]),
+ Scenario("irc_server_configure",
+ [
+ handshake_sequence(),
+ partial(send_stanza, "<iq type='set' id='id1' from='{jid_one}/{resource_one}' to='{irc_server_one}'><command xmlns='http://jabber.org/protocol/commands' node='configure' action='execute' /></iq>"),
+ partial(expect_stanza, ("/iq[@type='result']/commands:command[@node='configure'][@sessionid][@status='executing']",
+ "/iq/commands:command/dataform:x[@type='form']/dataform:title[text()='Configure the IRC server irc.localhost']",
+ "/iq/commands:command/dataform:x[@type='form']/dataform:instructions[text()='Edit the form, to configure the settings of the IRC server irc.localhost']",
+ "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='text-multi'][@var='ports']/dataform:value[text()='6667']",
+ "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='text-multi'][@var='tls_ports']/dataform:value[text()='6670']",
+ "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='text-multi'][@var='tls_ports']/dataform:value[text()='6697']",
+ "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='boolean'][@var='verify_cert']/dataform:value[text()='true']",
+ "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='text-single'][@var='fingerprint']",
+ "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='text-private'][@var='pass']",
+ "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='text-single'][@var='after_connect_command']",
+ "/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']/dataform:value[text()='ISO-8859-1']",
+ "/iq/commands:command/commands:actions/commands:next",
+ ),
+ after = partial(save_value, "sessionid", partial(extract_attribute, "/iq[@type='result']/commands:command[@node='configure']", "sessionid"))
+ ),
+ partial(send_stanza, "<iq type='set' id='id2' from='{jid_one}/{resource_one}' to='{irc_server_one}'>"
+ "<command xmlns='http://jabber.org/protocol/commands' node='configure' sessionid='{sessionid}' action='next'>"
+ "<x xmlns='jabber:x:data' type='submit'>"
+ "<field var='ports' />"
+ "<field var='tls_ports'><value>6697</value><value>6698</value></field>"
+ "<field var='verify_cert'><value>1</value></field>"
+ "<field var='fingerprint'><value>12:12:12</value></field>"
+ "<field var='pass'><value>coucou</value></field>"
+ "<field var='after_connect_command'><value>INVALID command</value></field>"
+ "<field var='username'><value>username</value></field>"
+ "<field var='realname'><value>realname</value></field>"
+ "<field var='encoding_out'><value>UTF-8</value></field>"
+ "<field var='encoding_in'><value>latin-1</value></field>"
+ "</x></command></iq>"),
+ partial(expect_stanza, "/iq[@type='result']/commands:command[@node='configure'][@status='completed']/commands:note[@type='info'][text()='Configuration successfully applied.']"),
+
+ partial(send_stanza, "<iq type='set' id='id3' from='{jid_one}/{resource_one}' to='{irc_server_one}'><command xmlns='http://jabber.org/protocol/commands' node='configure' action='execute' /></iq>"),
+ partial(expect_stanza, ("/iq[@type='result']/commands:command[@node='configure'][@sessionid][@status='executing']",
+ "/iq/commands:command/dataform:x[@type='form']/dataform:title[text()='Configure the IRC server irc.localhost']",
+ "/iq/commands:command/dataform:x[@type='form']/dataform:instructions[text()='Edit the form, to configure the settings of the IRC server irc.localhost']",
+ "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='text-multi']",
+ "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='text-multi'][@var='tls_ports']/dataform:value[text()='6697']",
+ "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='text-multi'][@var='tls_ports']/dataform:value[text()='6698']",
+ "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='boolean'][@var='verify_cert']/dataform:value[text()='true']",
+ "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='text-single'][@var='fingerprint']/dataform:value[text()='12:12:12']",
+ "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='text-private'][@var='pass']/dataform:value[text()='coucou']",
+ "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='text-single'][@var='after_connect_command']/dataform:value[text()='INVALID command']",
+ "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='text-single'][@var='username']/dataform:value[text()='username']",
+ "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='text-single'][@var='realname']/dataform:value[text()='realname']",
+ "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='text-single'][@var='encoding_in']/dataform:value[text()='latin-1']",
+ "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='text-single'][@var='encoding_out']/dataform:value[text()='UTF-8']",
+ "/iq/commands:command/commands:actions/commands:next",
+ ),
+ after = partial(save_value, "sessionid", partial(extract_attribute, "/iq[@type='result']/commands:command[@node='configure']", "sessionid"))
+ ),
+ partial(send_stanza, "<iq type='set' id='id4' from='{jid_one}/{resource_one}' to='{irc_server_one}'><command xmlns='http://jabber.org/protocol/commands' action='cancel' node='configure' sessionid='{sessionid}' /></iq>"),
+ partial(expect_stanza, "/iq[@type='result']/commands:command[@node='configure'][@status='canceled']"),
+ ]),
+ Scenario("get_irc_connection_info",
+ [
+ handshake_sequence(),
+
+ partial(log_message, "Not connected yet"),
+ partial(send_stanza, "<iq type='set' id='command1' from='{jid_one}/{resource_one}' to='{irc_server_one}'><command xmlns='http://jabber.org/protocol/commands' node='get-irc-connection-info' action='execute' /></iq>"),
+ partial(expect_stanza, "/iq/commands:command/commands:note[text()='You are not connected to the IRC server irc.localhost']"),
+
+ partial(log_message, "Join one room"),
+ partial(send_stanza,
+ "<presence from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}/{nick_one}' />"),
+ connection_sequence("irc.localhost", '{jid_one}/{resource_one}'),
+ partial(expect_stanza, "/message"),
+ partial(expect_stanza, "/presence"),
+ partial(expect_stanza, "/message"),
+
+ partial(send_stanza, "<iq type='set' id='command2' from='{jid_one}/{resource_one}' to='{irc_server_one}'><command xmlns='http://jabber.org/protocol/commands' node='get-irc-connection-info' action='execute' /></iq>"),
+ partial(expect_stanza, r"/iq/commands:command/commands:note[re:test(text(), 'Connected to IRC server irc.localhost on port 6667 since \d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d \(\d+ seconds ago\)\.\n#foo from 1 resource: {resource_one}.*')]"),
+ ]),
+ Scenario("get_irc_connection_info_fixed",
+ [
+ handshake_sequence(),
+
+ partial(log_message, "Not connected yet"),
+ partial(send_stanza, "<iq type='set' id='command1' from='{jid_one}/{resource_one}' to='{biboumi_host}'><command xmlns='http://jabber.org/protocol/commands' node='get-irc-connection-info' action='execute' /></iq>"),
+ partial(expect_stanza, "/iq/commands:command/commands:note[text()='You are not connected to the IRC server irc.localhost']"),
+
+ partial(log_message, "Join one room"),
+ partial(send_stanza,
+ "<presence from='{jid_one}/{resource_one}' to='#foo@{biboumi_host}/{nick_one}' />"),
+ connection_sequence("irc.localhost", '{jid_one}/{resource_one}'),
+ partial(expect_stanza, "/message"),
+ partial(expect_stanza, "/presence"),
+ partial(expect_stanza, "/message"),
+
+ partial(send_stanza, "<iq type='set' id='command2' from='{jid_one}/{resource_one}' to='{biboumi_host}'><command xmlns='http://jabber.org/protocol/commands' node='get-irc-connection-info' action='execute' /></iq>"),
+ partial(expect_stanza, r"/iq/commands:command/commands:note[re:test(text(), 'Connected to IRC server irc.localhost on port 6667 since \d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d \(\d+ seconds ago\)\.\n#foo from 1 resource: {resource_one}.*')]"),
+ ], conf='fixed_server'),
+ Scenario("invalid_room_jid",
+ [
+ handshake_sequence(),
+ partial(send_stanza, "<presence from='{jid_one}/{resource_one}' to='invalid%{irc_server_one}/{nick_one}' />"),
+ partial(expect_stanza, ("/presence[@type='error'][@to='{jid_one}/{resource_one}'][@from='invalid%{irc_server_one}/{nick_one}']/error[@type='cancel']/stanza:item-not-found",
+ "/presence/muc:x",
+ "/presence/error/stanza:text")),
+ ]),
+ Scenario("invalid_room_jid_fixed",
+ [
+ handshake_sequence(),
+ partial(send_stanza, "<presence from='{jid_one}/{resource_one}' to='invalid@{biboumi_host}/{nick_one}' />"),
+ partial(expect_stanza, ("/presence[@type='error'][@to='{jid_one}/{resource_one}'][@from='invalid@{biboumi_host}/{nick_one}']/error[@type='cancel']/stanza:item-not-found",
+ "/presence/muc:x",
+ "/presence/error/stanza:text")),
+ ], conf='fixed_server'),
)
+
failures = 0
+ scenar_list = sys.argv[1:]
irc_output = open("irc_output.txt", "w")
irc = IrcServerRunner()
print("Starting irc server…")
@@ -1008,11 +1874,13 @@ if __name__ == '__main__':
print("irc server started.")
print("Running %s checks for biboumi." % (len(scenarios)))
- for scenario in scenarios:
- test = BiboumiTest(scenario)
- if not test.run(os.getenv("E2E_BIBOUMI_VALGRIND") is not None):
+ 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." %
- (scenario.name, scenario.name))
+ (s.name, s.name))
failures += 1
print("Waiting for irc server to exit…")
diff --git a/tests/end_to_end/ircd.conf b/tests/end_to_end/ircd.conf
index 7edb3a8..7327531 100644
--- a/tests/end_to_end/ircd.conf
+++ b/tests/end_to_end/ircd.conf
@@ -477,8 +477,8 @@ general {
operspy_admin_only = no;
operspy_dont_care_user_info = no;
caller_id_wait = 1 minute;
- pace_wait_simple = 1 second;
- pace_wait = 10 seconds;
+ pace_wait_simple = 0 second;
+ pace_wait = 0 seconds;
short_motd = no;
ping_cookie = no;
connect_timeout = 30 seconds;
diff --git a/tests/iid.cpp b/tests/iid.cpp
index 74d010d..b42b9e5 100644
--- a/tests/iid.cpp
+++ b/tests/iid.cpp
@@ -47,66 +47,60 @@ namespace Catch
TEST_CASE("Iid creation")
{
- Iid iid1("foo!irc.example.org");
- CHECK(std::to_string(iid1) == "foo!irc.example.org");
+ const std::set<char> chantypes {'#', '&'};
+ Iid iid1("foo%irc.example.org", chantypes);
+ CHECK(std::to_string(iid1) == "foo%irc.example.org");
CHECK(iid1.get_local() == "foo");
CHECK(iid1.get_server() == "irc.example.org");
- CHECK(!iid1.is_channel);
- CHECK(iid1.is_user);
+ CHECK(iid1.type == Iid::Type::User);
- Iid iid2("#test%irc.example.org");
+ Iid iid2("#test%irc.example.org", chantypes);
CHECK(std::to_string(iid2) == "#test%irc.example.org");
CHECK(iid2.get_local() == "#test");
CHECK(iid2.get_server() == "irc.example.org");
- CHECK(iid2.is_channel);
- CHECK(!iid2.is_user);
+ CHECK(iid2.type == Iid::Type::Channel);
- Iid iid3("%irc.example.org");
+ Iid iid3("%irc.example.org", chantypes);
CHECK(std::to_string(iid3) == "%irc.example.org");
- CHECK(iid3.get_local() == "");
+ CHECK(iid3.get_local().empty());
CHECK(iid3.get_server() == "irc.example.org");
- CHECK(iid3.is_channel);
- CHECK(!iid3.is_user);
+ CHECK(iid3.type == Iid::Type::Channel);
- Iid iid4("irc.example.org");
+ Iid iid4("irc.example.org", chantypes);
CHECK(std::to_string(iid4) == "irc.example.org");
CHECK(iid4.get_local() == "");
CHECK(iid4.get_server() == "irc.example.org");
- CHECK(!iid4.is_channel);
- CHECK(!iid4.is_user);
+ CHECK(iid4.type == Iid::Type::Server);
- Iid iid5("nick!");
- CHECK(std::to_string(iid5) == "nick!");
+ Iid iid5("nick%", chantypes);
+ CHECK(std::to_string(iid5) == "nick%");
CHECK(iid5.get_local() == "nick");
CHECK(iid5.get_server() == "");
- CHECK(!iid5.is_channel);
- CHECK(iid5.is_user);
+ CHECK(iid5.type == Iid::Type::User);
- Iid iid6("##channel%");
+ Iid iid6("##channel%", chantypes);
CHECK(std::to_string(iid6) == "##channel%");
CHECK(iid6.get_local() == "##channel");
CHECK(iid6.get_server() == "");
- CHECK(iid6.is_channel);
- CHECK(!iid6.is_user);
+ CHECK(iid6.type == Iid::Type::Channel);
}
TEST_CASE("Iid creation in fixed_server mode")
{
Config::set("fixed_irc_server", "fixed.example.com", false);
- Iid iid1("foo!irc.example.org");
- CHECK(std::to_string(iid1) == "foo!");
- CHECK(iid1.get_local() == "foo");
+ const std::set<char> chantypes {'#', '&'};
+ Iid iid1("foo%irc.example.org", chantypes);
+ CHECK(std::to_string(iid1) == "foo%irc.example.org");
+ CHECK(iid1.get_local() == "foo%irc.example.org");
CHECK(iid1.get_server() == "fixed.example.com");
- CHECK(!iid1.is_channel);
- CHECK(iid1.is_user);
+ CHECK(iid1.type == Iid::Type::User);
- Iid iid2("#test%irc.example.org");
+ Iid iid2("#test%irc.example.org", chantypes);
CHECK(std::to_string(iid2) == "#test%irc.example.org");
CHECK(iid2.get_local() == "#test%irc.example.org");
CHECK(iid2.get_server() == "fixed.example.com");
- CHECK(iid2.is_channel);
- CHECK(!iid2.is_user);
+ CHECK(iid2.type == Iid::Type::Channel);
// Note that it is impossible to adress the IRC server directly, or to
// use the virtual channel, in that mode
@@ -114,17 +108,15 @@ TEST_CASE("Iid creation in fixed_server mode")
// Iid iid3("%irc.example.org");
// Iid iid4("irc.example.org");
- Iid iid5("nick!");
- CHECK(std::to_string(iid5) == "nick!");
+ Iid iid5("nick", chantypes);
+ CHECK(std::to_string(iid5) == "nick");
CHECK(iid5.get_local() == "nick");
CHECK(iid5.get_server() == "fixed.example.com");
- CHECK(!iid5.is_channel);
- CHECK(iid5.is_user);
+ CHECK(iid5.type == Iid::Type::User);
- Iid iid6("##channel%");
+ Iid iid6("##channel%", chantypes);
CHECK(std::to_string(iid6) == "##channel%");
CHECK(iid6.get_local() == "##channel%");
CHECK(iid6.get_server() == "fixed.example.com");
- CHECK(iid6.is_channel);
- CHECK(!iid6.is_user);
+ CHECK(iid6.type == Iid::Type::Channel);
}
diff --git a/tests/timed_events.cpp b/tests/timed_events.cpp
index d63abef..fece422 100644
--- a/tests/timed_events.cpp
+++ b/tests/timed_events.cpp
@@ -38,7 +38,6 @@ TEST_CASE("Test timed event expiration")
CHECK(TimedEventsManager::instance().execute_expired_events() == 0);
std::chrono::milliseconds timoute = TimedEventsManager::instance().get_timeout();
- INFO("Sleeping for " << timoute.count() << "ms");
std::this_thread::sleep_for(timoute + 1ms);
// Event is now expired
diff --git a/tests/utils.cpp b/tests/utils.cpp
index 01d070e..48951da 100644
--- a/tests/utils.cpp
+++ b/tests/utils.cpp
@@ -6,6 +6,10 @@
#include <utils/split.hpp>
#include <utils/xdg.hpp>
#include <utils/empty_if_fixed_server.hpp>
+#include <utils/get_first_non_empty.hpp>
+#include <utils/time.hpp>
+
+using namespace std::string_literals;
TEST_CASE("String split")
{
@@ -100,3 +104,39 @@ TEST_CASE("string cut")
CHECK(res[0] == "rhello, ");
CHECK(res[1] == "♥");
}
+
+TEST_CASE("first non-empty string")
+{
+ CHECK(get_first_non_empty(""s, ""s, "hello"s, "world"s) == "hello"s);
+ CHECK(get_first_non_empty(""s, ""s, ""s, ""s) == ""s);
+ CHECK(get_first_non_empty("first"s) == "first"s);
+ CHECK(get_first_non_empty(0, 1, 2, 3) == 1);
+}
+
+TEST_CASE("time_to_string")
+{
+ const std::time_t stamp = 1472480968;
+ const std::string result = "2016-08-29T14:29:28Z";
+ CHECK(utils::to_string(stamp) == result);
+ CHECK(utils::to_string(stamp).size() == result.size());
+ CHECK(utils::to_string(0) == "1970-01-01T00:00:00Z");
+}
+
+TEST_CASE("parse_datetime")
+{
+ CHECK(utils::parse_datetime("1970-01-01T00:00:00Z") == 0);
+
+ const int twenty_three_hours = 82800;
+ CHECK(utils::parse_datetime("1970-01-01T23:00:12Z") == twenty_three_hours + 12);
+ CHECK(utils::parse_datetime("1970-01-01T23:00:12Z") == utils::parse_datetime("1970-01-01T23:00:12+00:00"));
+ CHECK(utils::parse_datetime("1970-01-01T23:00:12Z") == utils::parse_datetime("1970-01-01T23:00:12-00:00"));
+ CHECK(utils::parse_datetime("1970-01-02T00:00:12Z") == utils::parse_datetime("1970-01-01T23:00:12-01:00"));
+ CHECK(utils::parse_datetime("1970-01-02T00:00:12Z") == utils::parse_datetime("1970-01-02T01:00:12+01:00"));
+
+ CHECK(utils::parse_datetime("2016-08-29T14:29:29Z") == 1472480969);
+
+ CHECK(utils::parse_datetime("blah") == -1);
+ CHECK(utils::parse_datetime("1970-01-02T00:00:12B") == -1);
+ CHECK(utils::parse_datetime("1970-01-02T00:00:12*00:00") == -1);
+ CHECK(utils::parse_datetime("1970-01-02T00:00:12+0000") == -1);
+}
diff --git a/tests/xmpp.cpp b/tests/xmpp.cpp
index 6aab8c4..37d2b54 100644
--- a/tests/xmpp.cpp
+++ b/tests/xmpp.cpp
@@ -1,6 +1,7 @@
#include "catch.hpp"
#include <xmpp/xmpp_parser.hpp>
+#include <xmpp/auth.hpp>
TEST_CASE("Test basic XML parsing")
{
@@ -45,3 +46,9 @@ TEST_CASE("XML escape")
const std::string unescaped = "'coucou'<cc>/&\"gaga\"";
CHECK(xml_escape(unescaped) == "&apos;coucou&apos;&lt;cc&gt;/&amp;&quot;gaga&quot;");
}
+
+TEST_CASE("handshake_digest")
+{
+ const auto res = get_handshake_digest("id1234", "S4CR3T");
+ CHECK(res == "c92901b5d376ad56269914da0cce3aab976847df");
+}