summaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests')
-rw-r--r--tests/config.cpp2
-rw-r--r--tests/database.cpp50
-rw-r--r--tests/end_to_end/__main__.py964
-rw-r--r--tests/end_to_end/ircd.conf9
-rw-r--r--tests/iid.cpp6
-rw-r--r--tests/jid.cpp22
-rw-r--r--tests/network.cpp44
-rw-r--r--tests/utils.cpp31
-rw-r--r--tests/xmpp.cpp26
9 files changed, 1031 insertions, 123 deletions
diff --git a/tests/config.cpp b/tests/config.cpp
index a6fa92a..ec9844f 100644
--- a/tests/config.cpp
+++ b/tests/config.cpp
@@ -8,7 +8,7 @@
TEST_CASE("Config basic")
{
// Disable all output for this test
- IoTester<std::ostream> out(std::cout);
+ IoTester<std::ostream> out(std::cerr);
// Write a value in the config file
Config::read_conf("test.cfg");
Config::set("coucou", "bonjour", true);
diff --git a/tests/database.cpp b/tests/database.cpp
index 4e2be14..f49220a 100644
--- a/tests/database.cpp
+++ b/tests/database.cpp
@@ -8,24 +8,22 @@ TEST_CASE("Database")
{
#ifdef USE_DATABASE
Database::open(":memory:");
- Database::set_verbose(false);
SECTION("Basic retrieve and update")
{
auto o = Database::get_irc_server_options("zouzou@example.com", "irc.example.com");
- o.update();
+ o.save(Database::db);
auto a = Database::get_irc_server_options("zouzou@example.com", "irc.example.com");
auto b = Database::get_irc_server_options("moumou@example.com", "irc.example.com");
// b does not yet exist in the db, the object is created but not yet
// inserted
- CHECK(1 == Database::count<db::IrcServerOptions>());
+ CHECK(1 == Database::count(Database::irc_server_options));
- b.update();
- CHECK(2 == Database::count<db::IrcServerOptions>());
+ b.save(Database::db);
+ CHECK(2 == Database::count(Database::irc_server_options));
- CHECK(b.pass == "");
- CHECK(b.pass.value() == "");
+ CHECK(b.col<Database::Pass>() == "");
}
SECTION("channel options")
@@ -33,16 +31,20 @@ TEST_CASE("Database")
Config::set("db_name", ":memory:");
auto o = Database::get_irc_channel_options("zouzou@example.com", "irc.example.com", "#foo");
- CHECK(o.encodingIn == "");
- o.encodingIn = "ISO-8859-1";
- o.update();
+ CHECK(o.col<Database::EncodingIn>() == "");
+ o.col<Database::EncodingIn>() = "ISO-8859-1";
+ CHECK(o.col<Database::RecordHistoryOptional>().is_set == false);
+ o.col<Database::RecordHistoryOptional>().set_value(false);
+ o.save(Database::db);
auto b = Database::get_irc_channel_options("zouzou@example.com", "irc.example.com", "#foo");
- CHECK(o.encodingIn == "ISO-8859-1");
+ CHECK(o.col<Database::EncodingIn>() == "ISO-8859-1");
+ CHECK(o.col<Database::RecordHistoryOptional>().is_set == true);
+ CHECK(o.col<Database::RecordHistoryOptional>().value == false);
}
SECTION("Channel options with server default")
{
- const std::string owner{"zouzou@example.com"};
+ const std::string owner{"CACA@example.com"};
const std::string server{"irc.example.com"};
const std::string chan1{"#foo"};
@@ -51,43 +53,43 @@ TEST_CASE("Database")
GIVEN("An option defined for the channel but not the server")
{
- c.encodingIn = "channelEncoding";
- c.update();
+ c.col<Database::EncodingIn>() = "channelEncoding";
+ c.save(Database::db);
WHEN("we fetch that option")
{
auto r = Database::get_irc_channel_options_with_server_default(owner, server, chan1);
THEN("we get the channel option")
- CHECK(r.encodingIn == "channelEncoding");
+ CHECK(r.col<Database::EncodingIn>() == "channelEncoding");
}
}
GIVEN("An option defined for the server but not the channel")
{
- s.encodingIn = "serverEncoding";
- s.update();
+ s.col<Database::EncodingIn>() = "serverEncoding";
+ s.save(Database::db);
WHEN("we fetch that option")
{
auto r = Database::get_irc_channel_options_with_server_default(owner, server, chan1);
THEN("we get the server option")
- CHECK(r.encodingIn == "serverEncoding");
+ CHECK(r.col<Database::EncodingIn>() == "serverEncoding");
}
}
GIVEN("An option defined for both the server and the channel")
{
- s.encodingIn = "serverEncoding";
- s.update();
- c.encodingIn = "channelEncoding";
- c.update();
+ s.col<Database::EncodingIn>() = "serverEncoding";
+ s.save(Database::db);
+ c.col<Database::EncodingIn>() = "channelEncoding";
+ c.save(Database::db);
WHEN("we fetch that option")
{
auto r = Database::get_irc_channel_options_with_server_default(owner, server, chan1);
THEN("we get the channel option")
- CHECK(r.encodingIn == "channelEncoding");
+ CHECK(r.col<Database::EncodingIn>() == "channelEncoding");
}
WHEN("we fetch that option, with no channel specified")
{
auto r = Database::get_irc_channel_options_with_server_default(owner, server, "");
THEN("we get the server option")
- CHECK(r.encodingIn == "serverEncoding");
+ CHECK(r.col<Database::EncodingIn>() == "serverEncoding");
}
}
}
diff --git a/tests/end_to_end/__main__.py b/tests/end_to_end/__main__.py
index 7658d92..580f8e4 100644
--- a/tests/end_to_end/__main__.py
+++ b/tests/end_to_end/__main__.py
@@ -1,12 +1,14 @@
#!/usr/bin/env python3
import collections
+import lxml.etree
+import datetime
import slixmpp
import asyncio
import logging
import signal
import atexit
-import lxml.etree
+import time
import sys
import io
import os
@@ -95,7 +97,11 @@ class XMPPComponent(slixmpp.BaseXMPP):
def run_scenario(self):
if self.scenario.steps:
step = self.scenario.steps.pop(0)
- step(self, self.biboumi)
+ try:
+ step(self, self.biboumi)
+ except Exception as e:
+ self.error(e)
+ self.run_scenario()
else:
if self.biboumi:
self.biboumi.stop()
@@ -113,6 +119,7 @@ def match(stanza, xpath):
tree = lxml.etree.parse(io.StringIO(str(stanza)))
matched = tree.xpath(xpath, namespaces={'re': 'http://exslt.org/regular-expressions',
'muc_user': 'http://jabber.org/protocol/muc#user',
+ 'muc_owner': 'http://jabber.org/protocol/muc#owner',
'muc': 'http://jabber.org/protocol/muc',
'disco_info': 'http://jabber.org/protocol/disco#info',
'muc_traffic': 'http://jabber.org/protocol/muc#traffic',
@@ -120,22 +127,29 @@ def match(stanza, xpath):
'commands': 'http://jabber.org/protocol/commands',
'dataform': 'jabber:x:data',
'version': 'jabber:iq:version',
- 'mam': 'urn:xmpp:mam:1',
+ 'mam': 'urn:xmpp:mam:2',
'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'})
+ 'stanza': 'urn:ietf:params:xml:ns:xmpp-stanzas',
+ 'stable_id': 'urn:xmpp:sid:0'})
return matched
def check_xpath(xpaths, xmpp, after, stanza):
for xpath in xpaths:
+ expected = True
+ real_xpath = xpath
+ # We can check that a stanza DOESN’T match, by adding a ! before it.
+ if xpath.startswith('!'):
+ expected = False
+ xpath = xpath[1:]
matched = match(stanza, xpath)
- if not matched:
- raise StanzaError("Received stanza “%s” did not match expected xpath “%s”" % (stanza, xpath))
+ if (expected and not matched) or (not expected and matched):
+ raise StanzaError("Received stanza “%s” did not match expected xpath “%s”" % (stanza, real_xpath))
if after:
if isinstance(after, collections.Iterable):
for af in after:
@@ -261,6 +275,14 @@ def expect_stanza(xpaths, xmpp, biboumi, optional=False, after=None):
else:
print("Warning, from argument type passed to expect_stanza: %s" % (type(xpaths)))
+def save_current_timestamp_plus_delta(key, delta, message, xmpp):
+ now_plus_delta = datetime.datetime.utcnow() + delta
+ xmpp.saved_values[key] = now_plus_delta.strftime("%FT%T.967Z")
+
+def sleep_for(duration, xmpp, biboumi):
+ time.sleep(duration)
+ asyncio.get_event_loop().call_soon(xmpp.run_scenario)
+
# list_of_xpaths: [(xpath, xpath), (xpath, xpath), (xpath)]
def expect_unordered(list_of_xpaths, xmpp, biboumi):
formatted_list_of_xpaths = []
@@ -275,10 +297,6 @@ def expect_unordered(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,))
- asyncio.get_event_loop().call_soon(xmpp.run_scenario)
-
class BiboumiTest:
"""
@@ -345,7 +363,9 @@ confs = {
password=coucou
db_name=e2e_test.sqlite
port=8811
-admin=admin@example.com""",
+admin=admin@example.com
+identd_port=1113
+outgoing_bind=127.0.0.1""",
'fixed_server':
"""hostname=biboumi.localhost
@@ -354,11 +374,14 @@ db_name=e2e_test.sqlite
port=8811
fixed_irc_server=irc.localhost
admin=admin@example.com
+identd_port=1113
"""}
common_replacements = {
'irc_server_one': 'irc.localhost@biboumi.localhost',
+ 'irc_server_two': 'localhost@biboumi.localhost',
'irc_host_one': 'irc.localhost',
+ 'irc_host_two': 'localhost',
'biboumi_host': 'biboumi.localhost',
'resource_one': 'resource1',
'resource_two': 'resource2',
@@ -380,8 +403,8 @@ def handshake_sequence():
def connection_begin_sequence(irc_host, jid):
jid = jid.format_map(common_replacements)
- xpath = "/message[@to='" + jid + "'][@from='irc.localhost@biboumi.localhost']/body[text()='%s']"
- xpath_re = "/message[@to='" + jid + "'][@from='irc.localhost@biboumi.localhost']/body[re:test(text(), '%s')]"
+ xpath = "/message[@to='" + jid + "'][@from='" + irc_host + "@biboumi.localhost']/body[text()='%s']"
+ xpath_re = "/message[@to='" + jid + "'][@from='" + irc_host + "@biboumi.localhost']/body[re:test(text(), '%s')]"
return (
partial(expect_stanza,
xpath % ('Connecting to %s:6697 (encrypted)' % irc_host)),
@@ -395,25 +418,47 @@ def connection_begin_sequence(irc_host, jid):
xpath % ('Connecting to %s:6667 (not encrypted)' % irc_host)),
partial(expect_stanza,
xpath % 'Connected to IRC server.'),
- # These two messages can be receive in any order
+ # These five messages can be receive in any order
partial(expect_stanza,
- xpath_re % (r'^%s: \*\*\* (Checking Ident|Looking up your hostname...)$' % irc_host)),
+ xpath_re % (r'^%s: (\*\*\* Checking Ident|\*\*\* Looking up your hostname\.\.\.|\*\*\* Found your hostname: .*|ACK multi-prefix|\*\*\* Got Ident response)$' % 'irc.localhost')),
partial(expect_stanza,
- xpath_re % (r'^%s: \*\*\* (Checking Ident|Looking up your hostname...)$' % irc_host)),
- # These three messages can be received in any order
+ xpath_re % (r'^%s: (\*\*\* Checking Ident|\*\*\* Looking up your hostname\.\.\.|\*\*\* Found your hostname: .*|ACK multi-prefix|\*\*\* Got Ident response)$' % 'irc.localhost')),
partial(expect_stanza,
- xpath_re % (r'^%s: (\*\*\* Found your hostname: .*|ACK multi-prefix|\*\*\* No Ident response)$' % irc_host)),
+ xpath_re % (r'^%s: (\*\*\* Checking Ident|\*\*\* Looking up your hostname\.\.\.|\*\*\* Found your hostname: .*|ACK multi-prefix|\*\*\* Got Ident response)$' % 'irc.localhost')),
partial(expect_stanza,
- xpath_re % (r'^%s: (\*\*\* Found your hostname: .*|ACK multi-prefix|\*\*\* No Ident response)$' % irc_host)),
+ xpath_re % (r'^%s: (\*\*\* Checking Ident|\*\*\* Looking up your hostname\.\.\.|\*\*\* Found your hostname: .*|ACK multi-prefix|\*\*\* Got Ident response)$' % 'irc.localhost')),
partial(expect_stanza,
- xpath_re % (r'^%s: (\*\*\* Found your hostname: .*|ACK multi-prefix|\*\*\* No Ident response)$' % irc_host)),
+ xpath_re % (r'^%s: (\*\*\* Checking Ident|\*\*\* Looking up your hostname\.\.\.|\*\*\* Found your hostname: .*|ACK multi-prefix|\*\*\* Got Ident response)$' % 'irc.localhost')),
)
+def connection_tls_begin_sequence(irc_host, jid):
+ jid = jid.format_map(common_replacements)
+ xpath = "/message[@to='" + jid + "'][@from='" + irc_host + "@biboumi.localhost']/body[text()='%s']"
+ xpath_re = "/message[@to='" + jid + "'][@from='" + irc_host + "@biboumi.localhost']/body[re:test(text(), '%s')]"
+ irc_host = 'irc.localhost'
+ return (
+ partial(expect_stanza,
+ xpath % ('Connecting to %s:7778 (encrypted)' % irc_host)),
+ partial(expect_stanza,
+ xpath % 'Connected to IRC server (encrypted).'),
+ # These five messages can be receive in any order
+ partial(expect_stanza,
+ xpath_re % (r'^%s: (\*\*\* Checking Ident|\*\*\* Looking up your hostname\.\.\.|\*\*\* Found your hostname: .*|ACK multi-prefix|\*\*\* Got Ident response)$' % irc_host)),
+ partial(expect_stanza,
+ xpath_re % (r'^%s: (\*\*\* Checking Ident|\*\*\* Looking up your hostname\.\.\.|\*\*\* Found your hostname: .*|ACK multi-prefix|\*\*\* Got Ident response)$' % irc_host)),
+ partial(expect_stanza,
+ xpath_re % (r'^%s: (\*\*\* Checking Ident|\*\*\* Looking up your hostname\.\.\.|\*\*\* Found your hostname: .*|ACK multi-prefix|\*\*\* Got Ident response)$' % irc_host)),
+ partial(expect_stanza,
+ xpath_re % (r'^%s: (\*\*\* Checking Ident|\*\*\* Looking up your hostname\.\.\.|\*\*\* Found your hostname: .*|ACK multi-prefix|\*\*\* Got Ident response)$' % irc_host)),
+ partial(expect_stanza,
+ xpath_re % (r'^%s: (\*\*\* Checking Ident|\*\*\* Looking up your hostname\.\.\.|\*\*\* Found your hostname: .*|ACK multi-prefix|\*\*\* Got Ident response)$' % irc_host)),
+ )
def connection_end_sequence(irc_host, jid):
jid = jid.format_map(common_replacements)
- xpath = "/message[@to='" + jid + "'][@from='irc.localhost@biboumi.localhost']/body[text()='%s']"
- xpath_re = "/message[@to='" + jid + "'][@from='irc.localhost@biboumi.localhost']/body[re:test(text(), '%s')]"
+ xpath = "/message[@to='" + jid + "'][@from='" + irc_host + "@biboumi.localhost']/body[text()='%s']"
+ xpath_re = "/message[@to='" + jid + "'][@from='" + irc_host + "@biboumi.localhost']/body[re:test(text(), '%s')]"
+ irc_host = 'irc.localhost'
return (
partial(expect_stanza,
xpath_re % (r'^%s: Your host is .*$' % irc_host)),
@@ -436,18 +481,39 @@ def connection_end_sequence(irc_host, jid):
partial(expect_stanza,
xpath % "- This is charybdis MOTD you might replace it, but if not your friends will\n- laugh at you.\n"),
partial(expect_stanza,
- xpath_re % r'^User mode for \w+ is \[\+i\]$'),
+ xpath_re % r'^User mode for \w+ is \[\+Z?i\]$'),
+ )
+
+def connection_middle_sequence(irc_host, jid):
+ xpath_re = "/message[@to='" + jid + "'][@from='" + irc_host + "@biboumi.localhost']/body[re:test(text(), '%s')]"
+ irc_host = 'irc.localhost'
+ return (
+ partial(expect_stanza, xpath_re % (r'^%s: \*\*\* You are exempt from flood limits$' % irc_host)),
)
def connection_sequence(irc_host, jid):
- return connection_begin_sequence(irc_host, jid) + connection_end_sequence(irc_host, jid)
+ return connection_begin_sequence(irc_host, jid) +\
+ connection_middle_sequence(irc_host, jid) +\
+ connection_end_sequence(irc_host, jid)
+
+def connection_tls_sequence(irc_host, jid):
+ return connection_tls_begin_sequence(irc_host, jid) + \
+ connection_middle_sequence(irc_host, jid) +\
+ connection_end_sequence(irc_host, jid)
def extract_attribute(xpath, name, stanza):
matched = match(stanza, xpath)
return matched[0].get(name)
+def chan_name_from_jid(jid):
+ return jid[1:jid.find('%')]
+
+
+def extract_text(xpath, stanza):
+ matched = match(stanza, xpath)
+ return matched[0].text
def save_value(name, func, stanza, xmpp):
xmpp.saved_values[name] = func(stanza)
@@ -471,6 +537,20 @@ if __name__ == '__main__':
"<presence from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}/{nick_one}' />"),
connection_sequence("irc.localhost", '{jid_one}/{resource_one}'),
]),
+ Scenario("irc_server_connection_failure",
+ [
+ handshake_sequence(),
+ partial(send_stanza,
+ "<presence from='{jid_one}/{resource_one}' to='#foo%doesnotexist@{biboumi_host}/{nick_one}' />"),
+ partial(expect_stanza,
+ "/message/body[text()='Connecting to doesnotexist:6697 (encrypted)']"),
+ partial(expect_stanza,
+ "/message/body[re:test(text(), 'Connection failed: (Domain name not found|Name or service not known)')]"),
+ partial(expect_stanza,
+ ("/presence[@from='#foo%doesnotexist@{biboumi_host}/{nick_one}']/muc:x",
+ "/presence/error[@type='cancel']/stanza:item-not-found",
+ "/presence/error[@type='cancel']/stanza:text[re:test(text(), '(Domain name not found|Name or service not known)')]")),
+ ]),
Scenario("simple_channel_join",
[
handshake_sequence(),
@@ -485,12 +565,50 @@ if __name__ == '__main__':
),
partial(expect_stanza, "/message[@from='#foo%{irc_server_one}'][@type='groupchat']/subject[not(text())]"),
]),
+ Scenario("multiple_channels_join",
+ [
+ handshake_sequence(),
+ partial(send_stanza,
+ "<presence from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}/{nick_one}' />"),
+ partial(send_stanza,
+ "<presence from='{jid_one}/{resource_one}' to='#bar%{irc_server_one}/{nick_one}' />"),
+ partial(send_stanza,
+ "<presence from='{jid_one}/{resource_one}' to='#baz%{irc_server_one}/{nick_one}'> <x xmlns='http://jabber.org/protocol/muc'><password>SECRET</password></x></presence>"),
+
+ connection_sequence("irc.localhost", '{jid_one}/{resource_one}'),
+
+ partial(expect_stanza,
+ "/message/body[text()='Mode #foo [+nt] by {irc_host_one}']"),
+ partial(expect_stanza,
+ ("/presence[@to='{jid_one}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_one}']/muc_user:x/muc_user:item[@affiliation='admin'][@role='moderator']",
+ "/presence/muc_user:x/muc_user:status[@code='110']")
+ ),
+ partial(expect_stanza, "/message[@from='#foo%{irc_server_one}'][@type='groupchat']/subject[not(text())]"),
+
+ partial(expect_stanza,
+ "/message/body[text()='Mode #bar [+nt] by {irc_host_one}']"),
+ partial(expect_stanza,
+ ("/presence[@to='{jid_one}/{resource_one}'][@from='#bar%{irc_server_one}/{nick_one}']/muc_user:x/muc_user:item[@affiliation='admin'][@role='moderator']",
+ "/presence/muc_user:x/muc_user:status[@code='110']")
+ ),
+ partial(expect_stanza, "/message[@from='#bar%{irc_server_one}'][@type='groupchat']/subject[not(text())]"),
+
+ partial(expect_stanza,
+ "/message/body[text()='Mode #baz [+nt] by {irc_host_one}']"),
+ partial(expect_stanza,
+ ("/presence[@to='{jid_one}/{resource_one}'][@from='#baz%{irc_server_one}/{nick_one}']/muc_user:x/muc_user:item[@affiliation='admin'][@role='moderator']",
+ "/presence/muc_user:x/muc_user:status[@code='110']")
+ ),
+ partial(expect_stanza, "/message[@from='#baz%{irc_server_one}'][@type='groupchat']/subject[not(text())]"),
+ ]),
Scenario("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}'),
+ connection_middle_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']")
@@ -502,12 +620,30 @@ if __name__ == '__main__':
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("not_connected_error",
+ [
+ handshake_sequence(),
+ partial(send_stanza,
+ "<presence type='unavailable' from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}/{nick_one}' />"),
+ partial(send_stanza,
+ "<presence from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}/{nick_one}' />"),
+ connection_sequence("irc.localhost", '{jid_one}/{resource_one}'),
+ partial(expect_stanza,
+ "/message/body[text()='Mode #foo [+nt] by {irc_host_one}']"),
+ partial(expect_stanza,
+ ("/presence[@to='{jid_one}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_one}']/muc_user:x/muc_user:item[@affiliation='admin'][@role='moderator']",
+ "/presence/muc_user:x/muc_user:status[@code='110']")
+ ),
+ partial(expect_stanza, "/message[@from='#foo%{irc_server_one}'][@type='groupchat']/subject[not(text())]"),
+ ]),
Scenario("irc_server_disconnection",
[
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}'),
+ connection_middle_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']")
@@ -534,8 +670,6 @@ if __name__ == '__main__':
[
handshake_sequence(),
# First user joins
- partial(log_message,
- "First user joins"),
partial(send_stanza,
"<presence from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}/{nick_one}' />"),
connection_sequence("irc.localhost", '{jid_one}/{resource_one}'),
@@ -548,8 +682,6 @@ if __name__ == '__main__':
partial(expect_stanza, "/message[@from='#foo%{irc_server_one}'][@type='groupchat']/subject[not(text())]"),
# Second user joins
- partial(log_message,
- "Second user joins"),
partial(send_stanza,
"<presence from='{jid_two}/{resource_one}' to='#foo%{irc_server_one}/{nick_two}' />"),
connection_sequence("irc.localhost", '{jid_two}/{resource_one}'),
@@ -561,6 +693,48 @@ if __name__ == '__main__':
("/message[@from='#foo%{irc_server_one}'][@type='groupchat']/subject[not(text())]",),
]),
]),
+ Scenario("channel_join_with_password",
+ [
+ handshake_sequence(),
+ # First user joins
+ partial(send_stanza,
+ "<presence from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}/{nick_one}' />"),
+ connection_sequence("irc.localhost", '{jid_one}/{resource_one}'),
+ partial(expect_stanza,
+ "/message/body[text()='Mode #foo [+nt] by {irc_host_one}']"),
+ partial(expect_stanza,
+ ("/presence[@to='{jid_one}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_one}']/muc_user:x/muc_user:item[@affiliation='admin'][@jid='~nick@localhost'][@role='moderator']",
+ "/presence/muc_user:x/muc_user:status[@code='110']")
+ ),
+ partial(expect_stanza, "/message[@from='#foo%{irc_server_one}'][@type='groupchat']/subject[not(text())]"),
+
+ # Set a password in the room, by using /mode +k
+ partial(send_stanza, "<message from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}' type='groupchat'><body>/mode +k SECRET</body></message>"),
+ partial(expect_stanza, "/message[@from='#foo%{irc_server_one}'][@to='{jid_one}/{resource_one}'][@type='groupchat']/body[text()='Mode #foo [+k SECRET] by {nick_one}']"),
+
+ # Second user tries to join, without a password
+ partial(send_stanza,
+ "<presence from='{jid_two}/{resource_one}' to='#foo%{irc_server_one}/{nick_two}'/>"),
+ connection_sequence("irc.localhost", '{jid_two}/{resource_one}'),
+
+ partial(expect_stanza, "/message/body[text()='{irc_host_one}: #foo: Cannot join channel (+k) - bad key']"),
+ partial(expect_stanza,
+ "/presence[@type='error'][@from='#foo%{irc_server_one}/{nick_two}']/error[@type='auth']/stanza:not-authorized",
+ ),
+
+ # Second user joins, with a password
+ partial(send_stanza,
+ "<presence from='{jid_two}/{resource_one}' to='#foo%{irc_server_one}/{nick_two}'> <x xmlns='http://jabber.org/protocol/muc'><password>SECRET</password></x></presence>"),
+ # connection_sequence("irc.localhost", '{jid_two}/{resource_one}'),
+ partial(expect_unordered, [
+ ("/presence[@to='{jid_one}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_two}']/muc_user:x/muc_user:item[@affiliation='none'][@role='participant'][@jid='~bobby@localhost']",),
+ ("/presence[@to='{jid_two}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_one}']/muc_user:x/muc_user:item[@affiliation='admin'][@role='moderator']",),
+ ("/presence[@to='{jid_two}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_two}']/muc_user:x/muc_user:item[@affiliation='none'][@jid='~bobby@localhost'][@role='participant']",
+ "/presence/muc_user:x/muc_user:status[@code='110']",),
+ ("/message[@from='#foo%{irc_server_one}'][@type='groupchat']/subject[not(text())]",),
+ ]),
+
+ ]),
Scenario("channel_custom_topic",
[
handshake_sequence(),
@@ -598,6 +772,23 @@ if __name__ == '__main__':
),
partial(expect_stanza, "/message[@from='#foo%{irc_server_one}/{nick_one}'][@type='groupchat']/subject[text()='TOPIC TEST']"),
]),
+ Scenario("multiline_topic",
+ [
+ handshake_sequence(),
+ # User joins
+ partial(send_stanza,
+ "<presence from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}/{nick_one}' />"),
+ connection_sequence("irc.localhost", '{jid_one}/{resource_one}'),
+ partial(expect_stanza,
+ "/message/body"),
+ partial(expect_stanza, "/presence"),
+ partial(expect_stanza, "/message[@from='#foo%{irc_server_one}'][@type='groupchat']/subject[not(text())]"),
+
+ # User tries to set a multiline topic
+ partial(send_stanza,
+ "<message from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}' type='groupchat'><subject>FIRST LINE\nSECOND LINE.</subject></message>"),
+ partial(expect_stanza, "/message[@from='#foo%{irc_server_one}/{nick_one}'][@type='groupchat'][@to='{jid_one}/{resource_one}']/subject[text()='FIRST LINE SECOND LINE.']"),
+ ]),
Scenario("channel_basic_join_on_fixed_irc_server",
[
handshake_sequence(),
@@ -641,8 +832,6 @@ if __name__ == '__main__':
partial(expect_stanza, ("/iq[@type='result']/disco_items:query[@node='http://jabber.org/protocol/commands']",
"/iq/disco_items:query/disco_items:item[5]")),
], conf='fixed_server'),
-
-
Scenario("list_adhoc_irc",
[
handshake_sequence(),
@@ -676,11 +865,34 @@ if __name__ == '__main__':
"/iq/commands:command/commands:actions/commands:next",
),
after = partial(save_value, "sessionid", partial(extract_attribute, "/iq[@type='result']/commands:command[@node='hello']", "sessionid"))
-
),
partial(send_stanza, "<iq type='set' id='hello-command2' from='{jid_one}/{resource_one}' to='{biboumi_host}'><command xmlns='http://jabber.org/protocol/commands' node='hello' sessionid='{sessionid}' action='next'><x xmlns='jabber:x:data' type='submit'><field var='name'><value>COUCOU</value></field></x></command></iq>"),
partial(expect_stanza, "/iq[@type='result']/commands:command[@node='hello'][@status='completed']/commands:note[@type='info'][text()='Hello COUCOU!']")
]),
+ Scenario("execute_incomplete_hello_adhoc_command",
+ [
+ handshake_sequence(),
+ partial(send_stanza, "<iq type='set' id='hello-command1' from='{jid_one}/{resource_one}' to='{biboumi_host}'><command xmlns='http://jabber.org/protocol/commands' node='hello' action='execute' /></iq>"),
+ partial(expect_stanza, ("/iq[@type='result']/commands:command[@node='hello'][@sessionid][@status='executing']",
+ "/iq/commands:command/commands:actions/commands:next",
+ ),
+ after = partial(save_value, "sessionid", partial(extract_attribute, "/iq[@type='result']/commands:command[@node='hello']", "sessionid"))
+ ),
+ partial(send_stanza, "<iq type='set' id='hello-command2' from='{jid_one}/{resource_one}' to='{biboumi_host}'><command xmlns='http://jabber.org/protocol/commands' node='hello' sessionid='{sessionid}' action='next'><x xmlns='jabber:x:data' type='submit'></x></command></iq>"),
+ partial(expect_stanza, "/iq[@type='error']")
+ ]),
+ Scenario("execute_ping_adhoc_command",
+ [
+ handshake_sequence(),
+ partial(send_stanza, "<iq type='set' id='ping-command1' from='{jid_one}/{resource_one}' to='{biboumi_host}'><command xmlns='http://jabber.org/protocol/commands' node='ping' action='execute' /></iq>"),
+ partial(expect_stanza, "/iq[@type='result']/commands:command[@node='ping'][@status='completed']/commands:note[@type='info'][text()='Pong']")
+ ]),
+ Scenario("execute_reload_adhoc_command",
+ [
+ handshake_sequence(),
+ partial(send_stanza, "<iq type='set' id='ping-command1' from='{jid_admin}/{resource_one}' to='{biboumi_host}'><command xmlns='http://jabber.org/protocol/commands' node='reload' action='execute' /></iq>"),
+ partial(expect_stanza, "/iq[@type='result']/commands:command[@node='reload'][@status='completed']/commands:note[@type='info'][text()='Configuration reloaded.']")
+ ]),
Scenario("execute_forbidden_adhoc_command",
[
handshake_sequence(),
@@ -692,7 +904,6 @@ if __name__ == '__main__':
[
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}']"),
@@ -710,6 +921,75 @@ if __name__ == '__main__':
# Note, charybdis ignores our QUIT message, so we can't test it
partial(expect_stanza, "/presence[@type='unavailable'][@to='{jid_admin}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_one}']"),
]),
+ Scenario("execute_admin_disconnect_from_server_adhoc_command",
+ [
+ handshake_sequence(),
+
+ # Admin connects to first server
+ partial(send_stanza, "<presence from='{jid_admin}/{resource_one}' to='#bar%{irc_server_one}/{nick_one}' />"),
+ connection_sequence("irc.localhost", '{jid_admin}/{resource_one}'),
+ partial(expect_stanza, "/message/body[text()='Mode #bar [+nt] by {irc_host_one}']"),
+ partial(expect_stanza, "/presence"),
+ partial(expect_stanza, "/message"),
+
+ # Non-Admin connects to first server
+ partial(send_stanza, "<presence from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}/{nick_two}' />"),
+ connection_sequence("irc.localhost", '{jid_one}/{resource_one}'),
+ partial(expect_stanza, "/message/body[text()='Mode #foo [+nt] by {irc_host_one}']"),
+ partial(expect_stanza, "/presence"),
+ partial(expect_stanza, "/message"),
+
+ # Non-admin connects to second server
+ partial(send_stanza, "<presence from='{jid_one}/{resource_one}' to='#bon%{irc_server_two}/{nick_three}' />"),
+ connection_sequence("localhost", '{jid_one}/{resource_one}'),
+ partial(expect_stanza, "/message/body[text()='Mode #bon [+nt] by {irc_host_one}']"),
+ partial(expect_stanza, "/presence"),
+ partial(expect_stanza, "/message"),
+
+ # Execute as admin
+ partial(send_stanza, "<iq type='set' id='command1' from='{jid_admin}/{resource_one}' to='{biboumi_host}'><command xmlns='http://jabber.org/protocol/commands' node='disconnect-from-irc-server' action='execute' /></iq>"),
+ partial(expect_stanza, ("/iq[@type='result']/commands:command[@node='disconnect-from-irc-server'][@sessionid][@status='executing']",
+ "/iq/commands:command/dataform:x[@type='form']/dataform:field[@var='jid'][@type='list-single']/dataform:option[@label='{jid_one}']/dataform:value[text()='{jid_one}']",
+ "/iq/commands:command/dataform:x[@type='form']/dataform:field[@var='jid'][@type='list-single']/dataform:option[@label='{jid_admin}']/dataform:value[text()='{jid_admin}']",
+ "/iq/commands:command/commands:actions/commands:next",
+ ),
+ after = partial(save_value, "sessionid", partial(extract_attribute, "/iq/commands:command[@node='disconnect-from-irc-server']", "sessionid"))
+ ),
+ partial(send_stanza, "<iq type='set' id='command2' from='{jid_admin}/{resource_one}' to='{biboumi_host}'><command xmlns='http://jabber.org/protocol/commands' node='disconnect-from-irc-server' sessionid='{sessionid}' action='next'><x xmlns='jabber:x:data' type='submit'><field var='jid'><value>{jid_one}</value></field><field var='quit-message'><value>e2e test one</value></field></x></command></iq>"),
+
+ partial(expect_stanza, ("/iq[@type='result']/commands:command[@node='disconnect-from-irc-server'][@sessionid][@status='executing']",
+ "/iq/commands:command/dataform:x[@type='form']/dataform:field[@var='quit-message'][@type='text-single']",
+ "/iq/commands:command/dataform:x[@type='form']/dataform:field[@var='irc-servers'][@type='list-multi']",
+ "/iq/commands:command/dataform:x[@type='form']/dataform:field[@var='irc-servers']/dataform:option[@label='localhost']/dataform:value[text()='localhost']",
+ "/iq/commands:command/dataform:x[@type='form']/dataform:field[@var='irc-servers']/dataform:option[@label='irc.localhost']/dataform:value[text()='irc.localhost']",
+ "/iq/commands:command/commands:actions/commands:next",
+ ),
+ after = partial(save_value, "sessionid", partial(extract_attribute, "/iq/commands:command[@node='disconnect-from-irc-server']", "sessionid"))
+ ),
+ partial(send_stanza, "<iq type='set' id='command2' from='{jid_admin}/{resource_one}' to='{biboumi_host}'><command xmlns='http://jabber.org/protocol/commands' node='disconnect-from-irc-server' sessionid='{sessionid}' action='next'><x xmlns='jabber:x:data' type='submit'><field var='irc-servers'><value>localhost</value></field><field var='quit-message'><value>Disconnected by e2e</value></field></x></command></iq>"),
+ partial(expect_unordered, [("/presence[@type='unavailable'][@to='{jid_one}/{resource_one}'][@from='#bon%{irc_server_two}/{nick_three}']",),
+ ("/iq[@type='result']/commands:command[@node='disconnect-from-irc-server'][@status='completed']/commands:note[@type='info'][text()='{jid_one} was disconnected from 1 IRC server.']",),
+ ("/message[@to='{jid_one}/{resource_one}']/body[text()='ERROR: Disconnected by e2e']",),
+ ]),
+
+
+ # Execute as non-admin (this skips the first step)
+ partial(send_stanza, "<iq type='set' id='command1' from='{jid_one}/{resource_one}' to='{biboumi_host}'><command xmlns='http://jabber.org/protocol/commands' node='disconnect-from-irc-server' action='execute' /></iq>"),
+
+ partial(expect_stanza, ("/iq[@type='result']/commands:command[@node='disconnect-from-irc-server'][@sessionid][@status='executing']",
+ "/iq/commands:command/dataform:x[@type='form']/dataform:field[@var='quit-message'][@type='text-single']",
+ "/iq/commands:command/dataform:x[@type='form']/dataform:field[@var='irc-servers'][@type='list-multi']",
+ "/iq/commands:command/dataform:x[@type='form']/dataform:field[@var='irc-servers']/dataform:option[@label='irc.localhost']/dataform:value[text()='irc.localhost']",
+ "/iq/commands:command/commands:actions/commands:next",
+ ),
+ after = partial(save_value, "sessionid", partial(extract_attribute, "/iq/commands:command[@node='disconnect-from-irc-server']", "sessionid"))
+ ),
+ partial(send_stanza, "<iq type='set' id='command2' from='{jid_one}/{resource_one}' to='{biboumi_host}'><command xmlns='http://jabber.org/protocol/commands' node='disconnect-from-irc-server' sessionid='{sessionid}' action='next'><x xmlns='jabber:x:data' type='submit'><field var='irc-servers'><value>irc.localhost</value></field><field var='quit-message'><value>Disconnected by e2e</value></field></x></command></iq>"),
+ partial(expect_unordered, [("/presence[@type='unavailable'][@to='{jid_one}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_two}']",),
+ ("/iq[@type='result']/commands:command[@node='disconnect-from-irc-server'][@status='completed']/commands:note[@type='info'][text()='{jid_one}/{resource_one} was disconnected from 1 IRC server.']",),
+ ("/message[@to='{jid_one}/{resource_one}']/body[text()='ERROR: Disconnected by e2e']",),
+ ]),
+ ]),
Scenario("multisessionnick",
[
handshake_sequence(),
@@ -759,7 +1039,6 @@ if __name__ == '__main__':
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, [
@@ -769,7 +1048,6 @@ if __name__ == '__main__':
("/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, [
@@ -808,6 +1086,46 @@ if __name__ == '__main__':
("/message[@from='{lower_nick_two}%{irc_server_one}'][@to='{jid_one}/{resource_one}'][@type='chat']/body[text()='second']",),
]),
]),
+ Scenario("channel_join_with_different_nick",
+ [
+ handshake_sequence(),
+ partial(send_stanza,
+ "<presence from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}/{nick_one}' />"),
+ connection_sequence("irc.localhost", '{jid_one}/{resource_one}'),
+ partial(expect_stanza,
+ "/message/body[text()='Mode #foo [+nt] by {irc_host_one}']"),
+ partial(expect_stanza,
+ ("/presence[@to='{jid_one}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_one}']/muc_user:x/muc_user:item[@affiliation='admin'][@role='moderator']",
+ "/presence/muc_user:x/muc_user:status[@code='110']")
+ ),
+ partial(expect_stanza, "/message[@from='#foo%{irc_server_one}'][@type='groupchat'][@to='{jid_one}/{resource_one}']/subject[not(text())]"),
+
+ # The same resource joins a different channel with a different nick
+ partial(send_stanza,
+ "<presence from='{jid_one}/{resource_one}' to='#bar%{irc_server_one}/{nick_two}' />"),
+
+ # We must receive a join presence in response, without any nick change (nick_two) must be ignored
+ partial(expect_stanza,
+ "/message/body[text()='Mode #bar [+nt] by {irc_host_one}']"),
+ partial(expect_stanza,
+ ("/presence[@to='{jid_one}/{resource_one}'][@from='#bar%{irc_server_one}/{nick_one}']/muc_user:x/muc_user:item[@affiliation='admin'][@role='moderator']",
+ "/presence/muc_user:x/muc_user:status[@code='110']")
+ ),
+ partial(expect_stanza, "/message[@from='#bar%{irc_server_one}'][@type='groupchat'][@to='{jid_one}/{resource_one}']/subject[not(text())]"),
+ ]),
+ Scenario("notices",
+ [
+ handshake_sequence(),
+ partial(send_stanza,
+ "<presence from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}/{nick_one}' />"),
+ connection_sequence("irc.localhost", '{jid_one}/{resource_one}'),
+ partial(expect_stanza, "/message"),
+ partial(expect_stanza, "/presence"),
+ partial(expect_stanza, "/message"),
+
+ partial(send_stanza, "<message from='{jid_one}/{resource_one}' to='{irc_server_one}' type='chat'><body>NOTICE {nick_one} :[#foo] Hello in a notice.</body></message>"),
+ partial(expect_stanza, "/message[@from='#foo%{irc_server_one}/{nick_one}'][@type='groupchat']/body[text()='[notice] [#foo] Hello in a notice.']"),
+ ]),
Scenario("channel_messages",
[
handshake_sequence(),
@@ -901,6 +1219,72 @@ if __name__ == '__main__':
),
partial(expect_stanza, "/message[@from='#biboumi\\40louiz.org\\3a80%{irc_server_one}'][@type='groupchat']/subject[not(text())]"),
]),
+ Scenario("self_ping_with_error",
+ [
+ handshake_sequence(),
+ partial(send_stanza,
+ "<presence from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}/{nick_one}' />"),
+ connection_sequence("irc.localhost", '{jid_one}/{resource_one}'),
+ partial(expect_stanza,
+ "/message/body[text()='Mode #foo [+nt] by {irc_host_one}']"),
+ partial(expect_stanza,
+ ("/presence[@to='{jid_one}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_one}']/muc_user:x/muc_user:item[@affiliation='admin'][@role='moderator']",
+ "/presence/muc_user:x/muc_user:status[@code='110']")
+ ),
+ partial(expect_stanza, "/message[@from='#foo%{irc_server_one}'][@type='groupchat']/subject[not(text())]"),
+
+ # Send a ping to ourself
+ partial(send_stanza,
+ "<iq type='get' from='{jid_one}/{resource_one}' id='first_ping' to='#foo%{irc_server_one}/{nick_one}'><ping xmlns='urn:xmpp:ping' /></iq>"),
+ # We receive our own ping request,
+ partial(expect_stanza,
+ "/iq[@from='{lower_nick_one}%{irc_server_one}'][@type='get'][@to='{jid_one}/{resource_one}'][@id='gnip_tsrif']"),
+ # Respond to the request with an error
+ partial(send_stanza,
+ "<iq from='{jid_one}/{resource_one}' id='gnip_tsrif' to='{lower_nick_one}%{irc_server_one}' type='error'><error type='cancel'><feature-not-implemented xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/></error></iq>"),
+ partial(expect_stanza,
+ "/iq[@from='#foo%{irc_server_one}/{nick_one}'][@type='result'][@to='{jid_one}/{resource_one}'][@id='first_ping']"),
+
+ # Send a ping to ourself
+ partial(send_stanza,
+ "<iq type='get' from='{jid_one}/{resource_one}' id='first_ping' to='#foo%{irc_server_one}/{nick_one}'><ping xmlns='urn:xmpp:ping' /></iq>"),
+ # We receive our own ping request,
+ partial(expect_stanza,
+ "/iq[@from='{lower_nick_one}%{irc_server_one}'][@type='get'][@to='{jid_one}/{resource_one}'][@id='gnip_tsrif']"),
+ # Respond to the request with an error
+ partial(send_stanza,
+ "<iq from='{jid_one}/{resource_one}' id='gnip_tsrif' to='{lower_nick_one}%{irc_server_one}' type='error'><error type='cancel'><service-unavailable xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/></error></iq>"),
+ partial(expect_stanza,
+ "/iq[@from='#foo%{irc_server_one}/{nick_one}'][@type='result'][@to='{jid_one}/{resource_one}'][@id='first_ping']"),
+ ]),
+ Scenario("self_ping_not_in_muc",
+ [
+ handshake_sequence(),
+ partial(send_stanza,
+ "<presence from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}/{nick_one}' />"),
+ connection_sequence("irc.localhost", '{jid_one}/{resource_one}'),
+ partial(expect_stanza,
+ "/message/body[text()='Mode #foo [+nt] by {irc_host_one}']"),
+ partial(expect_stanza,
+ ("/presence[@to='{jid_one}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_one}']/muc_user:x/muc_user:item[@affiliation='admin'][@role='moderator']",
+ "/presence/muc_user:x/muc_user:status[@code='110']")
+ ),
+ partial(expect_stanza, "/message[@from='#foo%{irc_server_one}'][@type='groupchat']/subject[not(text())]"),
+
+ # Send a ping to ourself, in a muc where we’re not
+ partial(send_stanza,
+ "<iq type='get' from='{jid_one}/{resource_one}' id='first_ping' to='#nil%{irc_server_one}/{nick_one}'><ping xmlns='urn:xmpp:ping' /></iq>"),
+ # Immediately receive an error
+ partial(expect_stanza,
+ "/iq[@from='#nil%{irc_server_one}/{nick_one}'][@type='error'][@to='{jid_one}/{resource_one}'][@id='first_ping']/error/stanza:not-allowed"),
+
+ # Send a ping to ourself, in a muc where we are, but not this resource
+ partial(send_stanza,
+ "<iq type='get' from='{jid_one}/{resource_two}' id='first_ping' to='#foo%{irc_server_one}/{nick_one}'><ping xmlns='urn:xmpp:ping' /></iq>"),
+ # Immediately receive an error
+ partial(expect_stanza,
+ "/iq[@from='#foo%{irc_server_one}/{nick_one}'][@type='error'][@to='{jid_one}/{resource_two}'][@id='first_ping']/error/stanza:not-allowed"),
+ ]),
Scenario("self_ping_on_real_channel",
[
handshake_sequence(),
@@ -960,6 +1344,31 @@ if __name__ == '__main__':
"/iq[@from='#foo%{irc_server_one}/{nick_one}'][@type='result'][@to='{jid_one}/{resource_two}'][@id='third_ping']"),
]),
+ Scenario("self_ping_fixed_server", [
+ handshake_sequence(),
+ partial(send_stanza,
+ "<presence from='{jid_one}/{resource_one}' to='#foo@{biboumi_host}/{nick_one}' />"),
+ connection_sequence("irc.localhost", '{jid_one}/{resource_one}'),
+ partial(expect_stanza,
+ "/message/body[text()='Mode #foo [+nt] by {irc_host_one}']"),
+ partial(expect_stanza,
+ ("/presence[@to='{jid_one}/{resource_one}'][@from='#foo@{biboumi_host}/{nick_one}']/muc_user:x/muc_user:item[@affiliation='admin'][@role='moderator']",
+ "/presence/muc_user:x/muc_user:status[@code='110']")
+ ),
+ partial(expect_stanza, "/message[@from='#foo@{biboumi_host}'][@type='groupchat']/subject[not(text())]"),
+
+ # Send a ping to ourself
+ partial(send_stanza,
+ "<iq type='get' from='{jid_one}/{resource_one}' id='first_ping' to='#foo@{biboumi_host}/{nick_one}'><ping xmlns='urn:xmpp:ping' /></iq>"),
+ # We receive our own ping request,
+ partial(expect_stanza,
+ "/iq[@from='{lower_nick_one}@{biboumi_host}'][@type='get'][@to='{jid_one}/{resource_one}'][@id='gnip_tsrif']"),
+ # Respond to the request
+ partial(send_stanza,
+ "<iq type='result' to='{lower_nick_one}@{biboumi_host}' id='gnip_tsrif' from='{jid_one}/{resource_one}'/>"),
+ partial(expect_stanza,
+ "/iq[@from='#foo@{biboumi_host}/{nick_one}'][@type='result'][@to='{jid_one}/{resource_one}'][@id='first_ping']"),
+ ], conf="fixed_server"),
Scenario("simple_kick",
[
handshake_sequence(),
@@ -983,10 +1392,8 @@ if __name__ == '__main__':
]),
# 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_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']",
@@ -1000,6 +1407,82 @@ if __name__ == '__main__':
("/iq[@id='kick1'][@type='result']",),
]),
]),
+ Scenario("mode_change",
+ [
+ handshake_sequence(),
+ # First user joins
+ partial(send_stanza,
+ "<presence from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}/{nick_one}' />"),
+ connection_sequence("irc.localhost", '{jid_one}/{resource_one}'),
+ partial(expect_stanza, "/message"),
+ partial(expect_stanza, "/presence/muc_user:x/muc_user:status[@code='110']"),
+ partial(expect_stanza, "/message[@type='groupchat']/subject"),
+
+ # Second user joins
+ partial(send_stanza,
+ "<presence from='{jid_two}/{resource_one}' to='#foo%{irc_server_one}/{nick_two}' />"),
+ connection_sequence("irc.localhost", '{jid_two}/{resource_one}'),
+ partial(expect_unordered, [
+ ("/presence/muc_user:x/muc_user:item[@affiliation='none'][@role='participant']",),
+ ("/presence/muc_user:x/muc_user:item[@affiliation='admin'][@role='moderator']",),
+ ("/presence/muc_user:x/muc_user:status[@code='110']",),
+ ("/message/subject",),
+ ]),
+
+ # Change a user mode with a message starting with /mode
+ partial(send_stanza,
+ "<message from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}' type='groupchat'><body>/mode +v {nick_two}</body></message>"),
+ partial(expect_unordered, [
+ ("/message[@to='{jid_one}/{resource_one}']/body[text()='Mode #foo [+v {nick_two}] by {nick_one}']",),
+ ("/message[@to='{jid_two}/{resource_one}']/body[text()='Mode #foo [+v {nick_two}] by {nick_one}']",),
+ ("/presence[@to='{jid_two}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_two}']/muc_user:x/muc_user:item[@affiliation='member'][@role='participant']",),
+ ("/presence[@to='{jid_one}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_two}']/muc_user:x/muc_user:item[@affiliation='member'][@role='participant']",)
+ ]),
+
+ # using an iq
+ partial(send_stanza,
+ "<iq from='{jid_one}/{resource_one}' id='id1' to='#foo%{irc_server_one}' type='set'><query xmlns='http://jabber.org/protocol/muc#admin'><item affiliation='admin' nick='{nick_two}'/></query></iq>"),
+ partial(expect_unordered, [
+ ("/message[@to='{jid_one}/{resource_one}']/body[text()='Mode #foo [+o {nick_two}] by {nick_one}']",),
+ ("/message[@to='{jid_two}/{resource_one}']/body[text()='Mode #foo [+o {nick_two}] by {nick_one}']",),
+ ("/presence[@to='{jid_two}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_two}']/muc_user:x/muc_user:item[@affiliation='admin'][@role='moderator']",),
+ ("/presence[@to='{jid_one}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_two}']/muc_user:x/muc_user:item[@affiliation='admin'][@role='moderator']",),
+ ("/iq[@id='id1'][@type='result'][@to='{jid_one}/{resource_one}'][@from='#foo%{irc_server_one}']",),
+ ]),
+
+ # remove the mode
+ partial(send_stanza,
+ "<iq from='{jid_one}/{resource_one}' id='id1' to='#foo%{irc_server_one}' type='set'><query xmlns='http://jabber.org/protocol/muc#admin'><item affiliation='member' nick='{nick_two}' role='participant'/></query></iq>"),
+ partial(expect_unordered, [
+ ("/message[@to='{jid_one}/{resource_one}']/body[text()='Mode #foo [+v-o {nick_two} {nick_two}] by {nick_one}']",),
+ ("/message[@to='{jid_two}/{resource_one}']/body[text()='Mode #foo [+v-o {nick_two} {nick_two}] by {nick_one}']",),
+ ("/presence[@to='{jid_two}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_two}']/muc_user:x/muc_user:item[@affiliation='member'][@role='participant']",),
+ ("/presence[@to='{jid_one}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_two}']/muc_user:x/muc_user:item[@affiliation='member'][@role='participant']",),
+ ("/iq[@id='id1'][@type='result'][@to='{jid_one}/{resource_one}'][@from='#foo%{irc_server_one}']",),
+ ]),
+
+ # using an iq, an a non-existant nick
+ partial(send_stanza,
+ "<iq from='{jid_one}/{resource_one}' id='id1' to='#foo%{irc_server_one}' type='set'><query xmlns='http://jabber.org/protocol/muc#admin'><item affiliation='admin' nick='blectre'/></query></iq>"),
+ partial(expect_stanza, "/iq[@type='error']"),
+
+ # using an iq, without the rights to do it
+ partial(send_stanza,
+ "<iq from='{jid_two}/{resource_one}' id='id1' to='#foo%{irc_server_one}' type='set'><query xmlns='http://jabber.org/protocol/muc#admin'><item affiliation='admin' nick='{nick_one}'/></query></iq>"),
+ partial(expect_unordered, [
+ ("/iq[@type='error']",),
+ ("/message[@type='chat'][@to='{jid_two}/{resource_one}']",),
+ ]),
+
+ # using an iq, with an unknown mode
+ partial(send_stanza,
+ "<iq from='{jid_two}/{resource_one}' id='id1' to='#foo%{irc_server_one}' type='set'><query xmlns='http://jabber.org/protocol/muc#admin'><item affiliation='owner' nick='{nick_one}'/></query></iq>"),
+ partial(expect_unordered, [
+ ("/iq[@type='error']",),
+ ("/message[@type='chat'][@to='{jid_two}/{resource_one}']",),
+ ]),
+
+ ]),
Scenario("multisession_kick",
[
handshake_sequence(),
@@ -1033,10 +1516,8 @@ if __name__ == '__main__':
partial(expect_stanza, "/message[@from='#foo%{irc_server_one}'][@type='groupchat'][@to='{jid_two}/{resource_two}']/subject[not(text())]"),
# Moderator kicks participant
- partial(log_message, "Moderator kicks participant"),
partial(send_stanza,
"<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_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']",
@@ -1121,7 +1602,6 @@ if __name__ == '__main__':
]),
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}' />"),
@@ -1134,15 +1614,12 @@ if __name__ == '__main__':
),
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,
@@ -1216,13 +1693,16 @@ if __name__ == '__main__':
# 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(expect_stanza,
+ ("/message[@from='#foo%{irc_server_one}/{nick_one}'][@to='{jid_one}/{resource_one}'][@type='groupchat']/body[text()='coucou']",
+ "/message/stable_id:stanza-id[@by='#foo%{irc_server_one}'][@id]",)
+ ),
partial(send_stanza, "<message from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}' type='groupchat'><body>coucou 2</body></message>"),
partial(expect_stanza, "/message[@from='#foo%{irc_server_one}/{nick_one}'][@to='{jid_one}/{resource_one}'][@type='groupchat']/body[text()='coucou 2']"),
# Retrieve the complete archive
- partial(send_stanza, "<iq to='#foo%{irc_server_one}' from='{jid_one}/{resource_one}' type='set' id='id1'><query xmlns='urn:xmpp:mam:1' queryid='qid1' /></iq>"),
+ partial(send_stanza, "<iq to='#foo%{irc_server_one}' from='{jid_one}/{resource_one}' type='set' id='id1'><query xmlns='urn:xmpp:mam:2' queryid='qid1' /></iq>"),
partial(expect_stanza,
("/message/mam:result[@queryid='qid1']/forward:forwarded/delay:delay",
@@ -1238,9 +1718,9 @@ if __name__ == '__main__':
# 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'>
+ <query xmlns='urn:xmpp:mam:2' queryid='qid2'>
<x xmlns='jabber:x:data' type='submit'>
- <field var='FORM_TYPE' type='hidden'> <value>urn:xmpp:mam:1</value></field>
+ <field var='FORM_TYPE' type='hidden'> <value>urn:xmpp:mam:2</value></field>
<field var='end'><value>2000-06-07T00:00:00Z</value></field>
</x>
</query></iq>"""),
@@ -1251,16 +1731,87 @@ if __name__ == '__main__':
# 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'>
+ <query xmlns='urn:xmpp:mam:2' queryid='qid3'>
<x xmlns='jabber:x:data' type='submit'>
- <field var='FORM_TYPE' type='hidden'> <value>urn:xmpp:mam:1</value></field>
+ <field var='FORM_TYPE' type='hidden'> <value>urn:xmpp:mam:2</value></field>
<field var='start'><value>3016-06-07T00:00:00Z</value></field>
</x>
</query></iq>"""),
partial(expect_stanza,
"/iq[@type='result'][@id='id3'][@from='#foo%{irc_server_one}'][@to='{jid_one}/{resource_one}']"),
+
+ # Retrieve a limited archive
+ partial(send_stanza, "<iq to='#foo%{irc_server_one}' from='{jid_one}/{resource_one}' type='set' id='id4'><query xmlns='urn:xmpp:mam:2' queryid='qid4'><set xmlns='http://jabber.org/protocol/rsm'><max>1</max></set></query></iq>"),
+
+ partial(expect_stanza,
+ ("/message/mam:result[@queryid='qid4']/forward:forwarded/delay:delay",
+ "/message/mam:result[@queryid='qid4']/forward:forwarded/client:message[@from='#foo%{irc_server_one}/{nick_one}'][@type='groupchat']/client:body[text()='coucou 2']")
+ ),
+
+ partial(expect_stanza,
+ "/iq[@type='result'][@id='id4'][@from='#foo%{irc_server_one}'][@to='{jid_one}/{resource_one}']"),
+
]),
+ Scenario("mam_with_timestamps",
+ [
+ handshake_sequence(),
+ partial(send_stanza,
+ "<presence from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}/{nick_one}' />"),
+ connection_sequence("irc.localhost", '{jid_one}/{resource_one}'),
+ partial(expect_stanza,
+ "/message/body[text()='Mode #foo [+nt] by {irc_host_one}']"),
+ partial(expect_stanza,
+ ("/presence[@to='{jid_one}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_one}']/muc_user:x/muc_user:item[@affiliation='admin'][@role='moderator']",
+ "/presence/muc_user:x/muc_user:status[@code='110']")
+ ),
+ partial(expect_stanza, "/message[@from='#foo%{irc_server_one}'][@type='groupchat']/subject[not(text())]"),
+
+ # Send two channel messages
+ partial(send_stanza, "<message from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}' type='groupchat'><body>coucou</body></message>"),
+ partial(expect_stanza,
+ ("/message[@from='#foo%{irc_server_one}/{nick_one}'][@to='{jid_one}/{resource_one}'][@type='groupchat']/body[text()='coucou']",
+ "/message/stable_id:stanza-id[@by='#foo%{irc_server_one}'][@id]",)
+ ),
+
+ partial(send_stanza, "<message from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}' type='groupchat'><body>coucou 2</body></message>"),
+ # Record the current time
+ partial(expect_stanza, "/message[@from='#foo%{irc_server_one}/{nick_one}'][@to='{jid_one}/{resource_one}'][@type='groupchat']/body[text()='coucou 2']",
+ after = partial(save_current_timestamp_plus_delta, "first_timestamp", datetime.timedelta(seconds=1))),
+
+ # Wait two seconds before sending two new messages
+ partial(sleep_for, 2),
+ partial(send_stanza, "<message from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}' type='groupchat'><body>coucou 3</body></message>"),
+ partial(send_stanza, "<message from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}' type='groupchat'><body>coucou 4</body></message>"),
+ partial(expect_stanza, "/message[@type='groupchat']/body[text()='coucou 3']"),
+ partial(expect_stanza, "/message[@type='groupchat']/body[text()='coucou 4']",
+ after = partial(save_current_timestamp_plus_delta, "second_timestamp", datetime.timedelta(seconds=1))),
+
+ # Retrieve the archive, after our saved datetime
+ partial(send_stanza, """<iq to='#foo%{irc_server_one}' from='{jid_one}/{resource_one}' type='set' id='id8'>
+ <query xmlns='urn:xmpp:mam:2' queryid='qid16'>
+ <x type='submit' xmlns='jabber:x:data'>
+ <field var='FORM_TYPE' xmlns='jabber:x:data'><value xmlns='jabber:x:data'>urn:xmpp:mam:2</value></field>
+ <field var='start' xmlns='jabber:x:data'><value xmlns='jabber:x:data'>{first_timestamp}</value></field>
+ <field var='end' xmlns='jabber:x:data'><value xmlns='jabber:x:data'>{second_timestamp}</value></field>
+ </x>
+ </query>
+ </iq>"""),
+
+
+ partial(expect_stanza,
+ ("/message/mam:result[@queryid='qid16']/forward:forwarded/delay:delay",
+ "/message/mam:result/forward:forwarded/client:message[@from='#foo%{irc_server_one}/{nick_one}'][@type='groupchat']/client:body[text()='coucou 3']")
+ ),
+
+ partial(expect_stanza,
+ ("/message/mam:result[@queryid='qid16']/forward:forwarded/delay:delay",
+ "/message/mam:result/forward:forwarded/client:message[@from='#foo%{irc_server_one}/{nick_one}'][@type='groupchat']/client:body[text()='coucou 4']")
+ ),
+
+ partial(expect_stanza,
+ "/iq[@type='result'][@id='id8'][@from='#foo%{irc_server_one}'][@to='{jid_one}/{resource_one}']"),
+ ]),
Scenario("mam_on_fixed_server",
[
handshake_sequence(),
@@ -1283,7 +1834,7 @@ if __name__ == '__main__':
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(send_stanza, "<iq to='#foo@{biboumi_host}' from='{jid_one}/{resource_one}' type='set' id='id1'><query xmlns='urn:xmpp:mam:2' queryid='qid1' /></iq>"),
partial(expect_stanza,
("/message/mam:result[@queryid='qid1']/forward:forwarded/delay:delay",
@@ -1294,6 +1845,51 @@ if __name__ == '__main__':
"/message/mam:result[@queryid='qid1']/forward:forwarded/client:message[@from='#foo@{biboumi_host}/{nick_one}'][@type='groupchat']/client:body[text()='coucou 2']")
),
], conf="fixed_server"),
+ Scenario("default_mam_limit",
+ [
+ handshake_sequence(),
+ partial(send_stanza,
+ "<presence from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}/{nick_one}' />"),
+ connection_sequence("irc.localhost", '{jid_one}/{resource_one}'),
+ partial(expect_stanza,
+ "/message/body[text()='Mode #foo [+nt] by {irc_host_one}']"),
+ partial(expect_stanza,
+ ("/presence[@to='{jid_one}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_one}']/muc_user:x/muc_user:item[@affiliation='admin'][@role='moderator']",
+ "/presence/muc_user:x/muc_user:status[@code='110']")
+ ),
+ partial(expect_stanza, "/message[@from='#foo%{irc_server_one}'][@type='groupchat']/subject[not(text())]",
+ after = partial(save_value, "counter", lambda x: 0)),
+ ] + [
+ partial(send_stanza, "<message from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}' type='groupchat'><body>{counter}</body></message>"),
+ partial(expect_stanza,
+ "/message[@from='#foo%{irc_server_one}/{nick_one}'][@to='{jid_one}/{resource_one}'][@type='groupchat']/body[text()='{counter}']",
+ after = partial(save_value, "counter", lambda stanza: str(1 + int(extract_text("/message/body", stanza))))
+ ),
+ ] * 150 + [
+ # Retrieve the archive, without any restriction
+ partial(send_stanza, "<iq to='#foo%{irc_server_one}' from='{jid_one}/{resource_one}' type='set' id='id1'><query xmlns='urn:xmpp:mam:2' queryid='qid1' /></iq>"),
+ # Since we should only receive the last 100 messages from the archive,
+ # it should start with message "50"
+ 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()='50']")
+ ),
+ ] + [
+ # followed by 98 more messages
+ partial(expect_stanza,
+ ("/message/mam:result[@queryid='qid1']/forward:forwarded/delay:delay",
+ "/message/mam:result[@queryid='qid1']/forward:forwarded/client:message[@from='#foo%{irc_server_one}/{nick_one}'][@type='groupchat']/client:body")
+ ),
+ ] * 98 + [
+ # and finally the message "149"
+ 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()='149']")
+ ),
+ partial(expect_stanza,
+ "/iq[@type='result'][@id='id1'][@from='#foo%{irc_server_one}'][@to='{jid_one}/{resource_one}']"),
+
+ ]),
Scenario("channel_history_on_fixed_server",
[
handshake_sequence(),
@@ -1368,7 +1964,6 @@ if __name__ == '__main__':
[
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}'),
@@ -1380,7 +1975,6 @@ if __name__ == '__main__':
),
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,
@@ -1388,7 +1982,6 @@ if __name__ == '__main__':
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",
@@ -1400,7 +1993,6 @@ if __name__ == '__main__':
[
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}'),
@@ -1412,7 +2004,6 @@ if __name__ == '__main__':
),
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,
@@ -1420,7 +2011,6 @@ if __name__ == '__main__':
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,
@@ -1428,24 +2018,27 @@ if __name__ == '__main__':
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"),
+ # Ask for 0 item
partial(send_stanza, "<iq from='{jid_one}/{resource_one}' id='id1' to='{irc_server_one}' type='get'><query xmlns='http://jabber.org/protocol/disco#items'><set xmlns='http://jabber.org/protocol/rsm'><max>0</max></set></query></iq>"),
+
+ # Get 0 item
partial(expect_stanza, (
"/iq[@type='result']/disco_items:query",
)),
- partial(log_message, "Request with max=2"),
+ # Ask for 2 (of 3) items We don’t have the count,
+ # because biboumi doesn’t have the complete list when
+ # it sends us the 2 items
partial(send_stanza, "<iq from='{jid_one}/{resource_one}' id='id1' to='{irc_server_one}' type='get'><query xmlns='http://jabber.org/protocol/disco#items'><set xmlns='http://jabber.org/protocol/rsm'><max>2</max></set></query></iq>"),
partial(expect_stanza, (
"/iq[@type='result']/disco_items:query",
"/iq/disco_items:query/disco_items:item[@jid='#bar%{irc_server_one}']",
"/iq/disco_items:query/disco_items:item[@jid='#coucou%{irc_server_one}']",
"/iq/disco_items:query/rsm:set/rsm:first[text()='#bar%{irc_server_one}'][@index='0']",
- "/iq/disco_items:query/rsm:set/rsm:last[text()='#coucou%{irc_server_one}']",
- "/iq/disco_items:query/rsm:set/rsm:count[text()='3']"
+ "/iq/disco_items:query/rsm:set/rsm:last[text()='#coucou%{irc_server_one}']"
)),
- partial(log_message, "Request with max=12"),
+ # Ask for 12 (of 3) items. We get the whole list, and thus we have the count included.
partial(send_stanza, "<iq from='{jid_one}/{resource_one}' id='id1' to='{irc_server_one}' type='get'><query xmlns='http://jabber.org/protocol/disco#items'><set xmlns='http://jabber.org/protocol/rsm'><max>12</max></set></query></iq>"),
partial(expect_stanza, (
"/iq[@type='result']/disco_items:query",
@@ -1457,7 +2050,10 @@ if __name__ == '__main__':
"/iq/disco_items:query/rsm:set/rsm:count[text()='3']"
)),
- partial(log_message, "Request with max=1 after=#bar"),
+ # Ask for 1 item, AFTER the first item (so,
+ # the second). Since we don’t invalidate the cache
+ # with this request, we should have the count
+ # included.
partial(send_stanza, "<iq from='{jid_one}/{resource_one}' id='id1' to='{irc_server_one}' type='get'><query xmlns='http://jabber.org/protocol/disco#items'><set xmlns='http://jabber.org/protocol/rsm'><after>#bar%{irc_server_one}</after><max>1</max></set></query></iq>"),
partial(expect_stanza, (
"/iq[@type='result']/disco_items:query",
@@ -1467,21 +2063,57 @@ if __name__ == '__main__':
"/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>"),
+ # Ask for 1 item, AFTER the second item (so,
+ # the third).
+ partial(send_stanza, "<iq from='{jid_one}/{resource_one}' id='id1' to='{irc_server_one}' type='get'><query xmlns='http://jabber.org/protocol/disco#items'><set xmlns='http://jabber.org/protocol/rsm'><after>#coucou%{irc_server_one}</after><max>1</max></set></query></iq>"),
partial(expect_stanza, (
"/iq[@type='result']/disco_items:query",
- "/iq/disco_items:query/disco_items:item[@jid='#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/disco_items:item[@jid='#foo%{irc_server_one}']",
+ "/iq/disco_items:query/rsm:set/rsm:first[text()='#foo%{irc_server_one}'][@index='2']",
+ "/iq/disco_items:query/rsm:set/rsm:last[text()='#foo%{irc_server_one}']",
"/iq/disco_items:query/rsm:set/rsm:count[text()='3']"
- ))
+ )),
+
+ # Ask for 1 item, AFTER the third item (so,
+ # the fourth). Since it doesn't exist, we get 0 item
+ partial(send_stanza, "<iq from='{jid_one}/{resource_one}' id='id1' to='{irc_server_one}' type='get'><query xmlns='http://jabber.org/protocol/disco#items'><set xmlns='http://jabber.org/protocol/rsm'><after>#foo%{irc_server_one}</after><max>1</max></set></query></iq>"),
+ partial(expect_stanza, (
+ "/iq[@type='result']/disco_items:query",
+ "/iq/disco_items:query/rsm:set/rsm:count[text()='3']"
+ )),
+ ]),
+ Scenario("default_channel_list_limit",
+ [
+ handshake_sequence(),
+ partial(send_stanza,
+ "<presence from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}/{nick_one}' />"),
+ connection_sequence("irc.localhost", '{jid_one}/{resource_one}'),
+ partial(expect_stanza, "/message"),
+ partial(expect_stanza, "/presence"),
+ partial(expect_stanza, "/message",
+ after = partial(save_value, "counter", lambda x: 0)),
+ ] + [
+ partial(send_stanza,
+ "<presence from='{jid_one}/{resource_one}' to='#{counter}%{irc_server_one}/{nick_one}' />"),
+ partial(expect_stanza, "/message"),
+ partial(expect_stanza, "/presence",
+ after = partial(save_value, "counter", lambda stanza: str(1 + int(chan_name_from_jid(extract_attribute("/presence", "from", stanza)))))),
+ partial(expect_stanza, "/message")
+ ] * 110 + [
+ partial(send_stanza, "<iq from='{jid_one}/{resource_one}' id='id1' to='{irc_server_one}' type='get'><query xmlns='http://jabber.org/protocol/disco#items'/></iq>"),
+ # charybdis sends the list in alphabetic order, so #foo is the last, and #99 is after #120
+ partial(expect_stanza, ("/iq/disco_items:query/disco_items:item[@jid='#0%{irc_server_one}']",
+ "/iq/disco_items:query/disco_items:item[@jid='#1%{irc_server_one}']",
+ "/iq/disco_items:query/disco_items:item[@jid='#109%{irc_server_one}']",
+ "/iq/disco_items:query/disco_items:item[@jid='#9%{irc_server_one}']",
+ "!/iq/disco_items:query/disco_items:item[@jid='#foo%{irc_server_one}']",
+ "!/iq/disco_items:query/disco_items:item[@jid='#99%{irc_server_one}']",
+ "!/iq/disco_items:query/disco_items:item[@jid='#90%{irc_server_one}']")),
]),
Scenario("complete_channel_list_with_pages_of_3",
[
handshake_sequence(),
- partial(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}'),
@@ -1543,7 +2175,6 @@ if __name__ == '__main__':
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",
@@ -1554,7 +2185,6 @@ if __name__ == '__main__':
"/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",
@@ -1584,7 +2214,6 @@ if __name__ == '__main__':
"/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' />"),
@@ -1612,8 +2241,40 @@ if __name__ == '__main__':
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']"),
+ partial(expect_stanza, "/iq[@from='#foo%{irc_server_one}'][@to='{jid_one}/{resource_one}'][@type='result']/disco_info:query[@node='http://jabber.org/protocol/muc#traffic']"),
+ ]),
+ Scenario("muc_disco_info",
+ [
+ handshake_sequence(),
+
+ partial(send_stanza,
+ "<iq from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}' id='1' type='get'><query xmlns='http://jabber.org/protocol/disco#info'/></iq>"),
+ partial(expect_stanza,
+ ("/iq[@from='#foo%{irc_server_one}'][@to='{jid_one}/{resource_one}'][@type='result']/disco_info:query",
+ "/iq[@type='result']/disco_info:query/disco_info:identity[@category='conference'][@type='irc'][@name='IRC channel #foo from server {irc_host_one} over biboumi']",
+ "/iq/disco_info:query/disco_info:feature[@var='jabber:iq:version']",
+ "/iq/disco_info:query/disco_info:feature[@var='http://jabber.org/protocol/commands']",
+ "/iq/disco_info:query/disco_info:feature[@var='urn:xmpp:ping']",
+ "/iq/disco_info:query/disco_info:feature[@var='urn:xmpp:mam:2']",
+ "/iq/disco_info:query/disco_info:feature[@var='jabber:iq:version']",
+ )),
]),
+ Scenario("fixed_muc_disco_info",
+ [
+ handshake_sequence(),
+
+ partial(send_stanza,
+ "<iq from='{jid_one}/{resource_one}' to='#foo@{biboumi_host}' id='1' type='get'><query xmlns='http://jabber.org/protocol/disco#info'/></iq>"),
+ partial(expect_stanza,
+ ("/iq[@from='#foo@{biboumi_host}'][@to='{jid_one}/{resource_one}'][@type='result']/disco_info:query",
+ "/iq[@type='result']/disco_info:query/disco_info:identity[@category='conference'][@type='irc'][@name='IRC channel #foo from server {irc_host_one} over biboumi']",
+ "/iq/disco_info:query/disco_info:feature[@var='jabber:iq:version']",
+ "/iq/disco_info:query/disco_info:feature[@var='http://jabber.org/protocol/commands']",
+ "/iq/disco_info:query/disco_info:feature[@var='urn:xmpp:ping']",
+ "/iq/disco_info:query/disco_info:feature[@var='urn:xmpp:mam:2']",
+ "/iq/disco_info:query/disco_info:feature[@var='jabber:iq:version']",
+ )),
+ ], conf='fixed_server'),
Scenario("raw_message",
[
handshake_sequence(),
@@ -1635,7 +2296,7 @@ if __name__ == '__main__':
"/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='urn:xmpp:mam:2']",
"/iq/disco_info:query/disco_info:feature[@var='jabber:iq:version']",
)),
]),
@@ -1663,6 +2324,8 @@ if __name__ == '__main__':
partial(send_stanza,
"<presence from='{jid_one}/{resource_one}' to='%{irc_server_one}/{nick_one}' />"),
connection_begin_sequence("irc.localhost", '{jid_one}/{resource_one}'),
+ connection_middle_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']")
@@ -1680,7 +2343,6 @@ if __name__ == '__main__':
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, [
@@ -1698,13 +2360,11 @@ if __name__ == '__main__':
]),
- 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)']"),
@@ -1732,6 +2392,7 @@ if __name__ == '__main__':
"/iq/commands:command/dataform:x[@type='form']/dataform:instructions[text()='Edit the form, to configure your global settings for the component.']",
"/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='text-single'][@var='max_history_length']/dataform:value[text()='42']",
"/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='boolean'][@var='record_history']/dataform:value[text()='false']",
+ "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='boolean'][@var='persistent']/dataform:value[text()='false']",
"/iq/commands:command/commands:actions/commands:next",
),
after = partial(save_value, "sessionid", partial(extract_attribute, "/iq[@type='result']/commands:command[@node='configure']", "sessionid"))
@@ -1756,7 +2417,7 @@ if __name__ == '__main__':
"/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/dataform:x[@type='form']/dataform:field[@type='text-single'][@var='encoding_out']",
"/iq/commands:command/commands:actions/commands:next",
),
after = partial(save_value, "sessionid", partial(extract_attribute, "/iq[@type='result']/commands:command[@node='configure']", "sessionid"))
@@ -1799,15 +2460,126 @@ if __name__ == '__main__':
partial(send_stanza, "<iq type='set' id='id4' from='{jid_one}/{resource_one}' to='{irc_server_one}'><command xmlns='http://jabber.org/protocol/commands' action='cancel' node='configure' sessionid='{sessionid}' /></iq>"),
partial(expect_stanza, "/iq[@type='result']/commands:command[@node='configure'][@status='canceled']"),
]),
+ Scenario("irc_channel_configure",
+ [
+ handshake_sequence(),
+ partial(send_stanza, "<iq type='set' id='id1' from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}'><command xmlns='http://jabber.org/protocol/commands' node='configure' action='execute' /></iq>"),
+ partial(expect_stanza, ("/iq[@type='result']/commands:command[@node='configure'][@sessionid][@status='executing']",
+ "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='text-single'][@var='encoding_in']",
+ "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='text-single'][@var='encoding_out']",
+ "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='list-single'][@var='record_history']/dataform:value[text()='unset']",
+ ),
+ after = partial(save_value, "sessionid", partial(extract_attribute, "/iq[@type='result']/commands:command[@node='configure']", "sessionid"))
+ ),
+ partial(send_stanza, "<iq type='set' id='id2' from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}'>"
+ "<command xmlns='http://jabber.org/protocol/commands' node='configure' sessionid='{sessionid}' action='next'>"
+ "<x xmlns='jabber:x:data' type='submit'>"
+ "<field var='ports' />"
+ "<field var='encoding_out'><value>UTF-8</value></field>"
+ "<field var='encoding_in'><value>latin-1</value></field>"
+ "<field var='record_history'><value>true</value></field>"
+ "</x></command></iq>"),
+ partial(expect_stanza, "/iq[@type='result']/commands:command[@node='configure'][@status='completed']/commands:note[@type='info'][text()='Configuration successfully applied.']"),
+
+ partial(send_stanza, "<iq type='set' id='id3' from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}'><command xmlns='http://jabber.org/protocol/commands' node='configure' action='execute' /></iq>"),
+ partial(expect_stanza, ("/iq[@type='result']/commands:command[@node='configure'][@sessionid][@status='executing']",
+ "/iq/commands:command/dataform:x[@type='form']/dataform:title[text()='Configure the IRC channel #foo on server irc.localhost']",
+ "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='text-single'][@var='encoding_in']/dataform:value[text()='latin-1']",
+ "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='text-single'][@var='encoding_out']/dataform:value[text()='UTF-8']",
+ "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='list-single'][@var='record_history']/dataform:value[text()='true']",
+ "/iq/commands:command/commands:actions/commands:next",
+ ),
+ after = partial(save_value, "sessionid", partial(extract_attribute, "/iq[@type='result']/commands:command[@node='configure']", "sessionid"))
+ ),
+ partial(send_stanza, "<iq type='set' id='id4' from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}'><command xmlns='http://jabber.org/protocol/commands' action='cancel' node='configure' sessionid='{sessionid}' /></iq>"),
+ partial(expect_stanza, "/iq[@type='result']/commands:command[@node='configure'][@status='canceled']"),
+ ]),
+ Scenario("irc_channel_configure_xep0045",
+ [
+ handshake_sequence(),
+ partial(send_stanza, "<iq type='get' id='id1' from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}'><query xmlns='http://jabber.org/protocol/muc#owner'/></iq>"),
+ partial(expect_stanza, ("/iq[@type='result']/muc_owner:query",
+ "/iq/muc_owner:query/dataform:x[@type='form']/dataform:field[@type='text-single'][@var='encoding_in']",
+ "/iq/muc_owner:query/dataform:x[@type='form']/dataform:field[@type='text-single'][@var='encoding_out']",
+ ),
+ ),
+ partial(send_stanza, "<iq type='set' id='id2' from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}'>"
+ "<query xmlns='http://jabber.org/protocol/muc#owner'>"
+ "<x xmlns='jabber:x:data' type='submit'>"
+ "<field var='ports' />"
+ "<field var='encoding_out'><value>UTF-8</value></field>"
+ "<field var='encoding_in'><value>latin-1</value></field>"
+ "</x></query></iq>"),
+ partial(expect_stanza, "/iq[@type='result']"),
+ partial(send_stanza, "<iq type='set' id='id3' from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}'><query xmlns='http://jabber.org/protocol/muc#owner'> <x xmlns='jabber:x:data' type='cancel'/></query></iq>"),
+ partial(expect_stanza, "/iq[@type='result']"),
+ ]),
+ Scenario("irc_channel_configure_fixed",
+ [
+ handshake_sequence(),
+ partial(send_stanza, "<iq type='set' id='id1' from='{jid_one}/{resource_one}' to='#foo@{biboumi_host}'><command xmlns='http://jabber.org/protocol/commands' node='configure' action='execute' /></iq>"),
+ partial(expect_stanza, ("/iq[@type='result']/commands:command[@node='configure'][@sessionid][@status='executing']",
+ "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='text-single'][@var='encoding_in']",
+ "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='text-single'][@var='encoding_out']",
+ ),
+ after = partial(save_value, "sessionid", partial(extract_attribute, "/iq[@type='result']/commands:command[@node='configure']", "sessionid"))
+ ),
+ partial(send_stanza, "<iq type='set' id='id2' from='{jid_one}/{resource_one}' to='#foo@{biboumi_host}'>"
+ "<command xmlns='http://jabber.org/protocol/commands' node='configure' sessionid='{sessionid}' action='next'>"
+ "<x xmlns='jabber:x:data' type='submit'>"
+ "<field var='ports' />"
+ "<field var='encoding_out'><value>UTF-8</value></field>"
+ "<field var='encoding_in'><value>latin-1</value></field>"
+ "</x></command></iq>"),
+ partial(expect_stanza, "/iq[@type='result']/commands:command[@node='configure'][@status='completed']/commands:note[@type='info'][text()='Configuration successfully applied.']"),
+
+ partial(send_stanza, "<iq type='set' id='id3' from='{jid_one}/{resource_one}' to='#foo@{biboumi_host}'><command xmlns='http://jabber.org/protocol/commands' node='configure' action='execute' /></iq>"),
+ partial(expect_stanza, ("/iq[@type='result']/commands:command[@node='configure'][@sessionid][@status='executing']",
+ "/iq/commands:command/dataform:x[@type='form']/dataform:title[text()='Configure the IRC channel #foo on server irc.localhost']",
+ "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='text-single'][@var='encoding_in']/dataform:value[text()='latin-1']",
+ "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='text-single'][@var='encoding_out']/dataform:value[text()='UTF-8']",
+ "/iq/commands:command/commands:actions/commands:next",
+ ),
+ after = partial(save_value, "sessionid", partial(extract_attribute, "/iq[@type='result']/commands:command[@node='configure']", "sessionid"))
+ ),
+ partial(send_stanza, "<iq type='set' id='id4' from='{jid_one}/{resource_one}' to='#foo@{biboumi_host}'><command xmlns='http://jabber.org/protocol/commands' action='cancel' node='configure' sessionid='{sessionid}' /></iq>"),
+ partial(expect_stanza, "/iq[@type='result']/commands:command[@node='configure'][@status='canceled']"),
+ ], conf='fixed_server'),
+ Scenario("irc_tls_connection",
+ [
+ handshake_sequence(),
+ # First, use an adhoc command to configure how we connect to the irc server, configure
+ # only one TLS port, and disable the cert verification.
+ partial(send_stanza, "<iq type='set' id='id1' from='{jid_one}/{resource_one}' to='{irc_server_one}'><command xmlns='http://jabber.org/protocol/commands' node='configure' action='execute' /></iq>"),
+ partial(expect_stanza, "/iq[@type='result']",
+ after = partial(save_value, "sessionid", partial(extract_attribute, "/iq[@type='result']/commands:command[@node='configure']", "sessionid"))),
+ partial(send_stanza, "<iq type='set' id='id2' from='{jid_one}/{resource_one}' to='{irc_server_one}'>"
+ "<command xmlns='http://jabber.org/protocol/commands' node='configure' sessionid='{sessionid}' action='next'>"
+ "<x xmlns='jabber:x:data' type='submit'>"
+ "<field var='ports' />"
+ "<field var='tls_ports'><value>7778</value></field>"
+ "<field var='verify_cert'><value>0</value></field>"
+ "</x></command></iq>"),
+ partial(expect_stanza, "/iq[@type='result']/commands:command[@node='configure'][@status='completed']/commands:note[@type='info'][text()='Configuration successfully applied.']"),
+
+ partial(send_stanza,
+ "<presence from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}/{nick_one}' />"),
+ connection_tls_sequence("irc.localhost", '{jid_one}/{resource_one}'),
+ partial(expect_stanza,
+ "/message/body[text()='Mode #foo [+nt] by {irc_host_one}']"),
+ partial(expect_stanza,
+ ("/presence[@to='{jid_one}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_one}']/muc_user:x/muc_user:item[@affiliation='admin'][@role='moderator']",
+ "/presence/muc_user:x/muc_user:status[@code='110']")
+ ),
+ partial(expect_stanza, "/message[@from='#foo%{irc_server_one}'][@type='groupchat']/subject[not(text())]"),
+ ]),
Scenario("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}'),
@@ -1822,11 +2594,9 @@ if __name__ == '__main__':
[
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}'),
@@ -1853,9 +2623,36 @@ if __name__ == '__main__':
"/presence/muc:x",
"/presence/error/stanza:text")),
], conf='fixed_server'),
+ Scenario("irc_server_presence_subscription",
+ [
+ handshake_sequence(),
+ partial(send_stanza, "<presence type='subscribe' from='{jid_one}/{resource_one}' to='{irc_server_one}' id='sub1' />"),
+ partial(expect_stanza, "/presence[@to='{jid_one}'][@from='{irc_server_one}'][@type='subscribed']")
+ ]),
+ Scenario("fixed_irc_server_presence_subscription",
+ [
+ handshake_sequence(),
+ partial(send_stanza, "<presence type='subscribe' from='{jid_one}/{resource_one}' to='{biboumi_host}' id='sub1' />"),
+ partial(expect_stanza, "/presence[@to='{jid_one}'][@from='{biboumi_host}'][@type='subscribed']")
+ ], conf='fixed_server'),
+ Scenario("leave_unjoined_chan",
+ [
+ handshake_sequence(),
+ partial(send_stanza, "<presence from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}/{nick_one}' />"),
+ connection_sequence("irc.localhost", '{jid_one}/{resource_one}'),
+ partial(expect_stanza, "/message"),
+ partial(expect_stanza, "/presence"),
+ partial(expect_stanza, "/message"),
+
+ partial(send_stanza, "<presence from='{jid_two}/{resource_two}' to='#foo%{irc_server_one}/{nick_one}' />"),
+ connection_begin_sequence("irc.localhost", '{jid_two}/{resource_two}'),
+
+ partial(expect_stanza, "/message[@to='{jid_two}/{resource_two}'][@type='chat']/body[text()='irc.localhost: {nick_one}: Nickname is already in use.']"),
+ partial(expect_stanza, "/presence[@type='error']/error[@type='cancel'][@code='409']/stanza:conflict"),
+ partial(send_stanza, "<presence from='{jid_two}/{resource_two}' to='#foo%{irc_server_one}/{nick_one}' type='unavailable' />")
+ ])
)
-
failures = 0
scenar_list = sys.argv[1:]
@@ -1872,7 +2669,8 @@ if __name__ == '__main__':
if b"now running in foreground mode" in res:
break
print("irc server started.")
- print("Running %s checks for biboumi." % (len(scenarios)))
+ checks = len([s for s in scenarios if s.name in scenar_list]) if scenar_list else len(scenarios)
+ print("Running %s checks for biboumi." % checks)
for s in scenarios:
if scenar_list and s.name not in scenar_list:
diff --git a/tests/end_to_end/ircd.conf b/tests/end_to_end/ircd.conf
index 7327531..ccfbd90 100644
--- a/tests/end_to_end/ircd.conf
+++ b/tests/end_to_end/ircd.conf
@@ -156,7 +156,7 @@ listen {
*/
#host = "192.0.2.6";
port = 5000, 6665 .. 6669;
- # sslport = 6697;
+ sslport = 7778;
/* Listen on IPv6 (if you used host= above). */
#host = "2001:db8:2::6";
@@ -221,6 +221,7 @@ auth {
auth {
user = "*@*";
class = "users";
+ flags = flood_exempt;
};
/* privset {} blocks MUST be specified before anything that uses them. That
@@ -352,8 +353,8 @@ channel {
use_knock = yes;
knock_delay = 5 minutes;
knock_delay_channel = 1 minute;
- max_chans_per_user = 15;
- max_chans_per_user_large = 60;
+ max_chans_per_user = 140;
+ max_chans_per_user_large = 200;
max_bans = 100;
max_bans_large = 500;
default_split_user_count = 0;
@@ -497,7 +498,7 @@ general {
reject_after_count = 3;
reject_duration = 5 minutes;
throttle_duration = 60;
- throttle_count = 4;
+ throttle_count = 8888;
max_ratelimit_tokens = 30;
away_interval = 30;
certfp_method = sha1;
diff --git a/tests/iid.cpp b/tests/iid.cpp
index b42b9e5..3da0396 100644
--- a/tests/iid.cpp
+++ b/tests/iid.cpp
@@ -83,6 +83,12 @@ TEST_CASE("Iid creation")
CHECK(iid6.get_local() == "##channel");
CHECK(iid6.get_server() == "");
CHECK(iid6.type == Iid::Type::Channel);
+
+ Iid iid7("", chantypes);
+ CHECK(std::to_string(iid7) == "");
+ CHECK(iid7.get_local() == "");
+ CHECK(iid7.get_server() == "");
+ CHECK(iid7.type == Iid::Type::None);
}
TEST_CASE("Iid creation in fixed_server mode")
diff --git a/tests/jid.cpp b/tests/jid.cpp
index 9015afd..480827b 100644
--- a/tests/jid.cpp
+++ b/tests/jid.cpp
@@ -1,7 +1,7 @@
#include "catch.hpp"
#include <xmpp/jid.hpp>
-#include <louloulibs.h>
+#include <biboumi.h>
TEST_CASE("Jid")
{
@@ -15,7 +15,10 @@ TEST_CASE("Jid")
CHECK(jid2.local == "");
CHECK(jid2.domain == "ツ.coucou");
CHECK(jid2.resource == "coucou@coucou/coucou");
+}
+TEST_CASE("jidprep")
+{
// Jidprep
const std::string badjid("~zigougou™@EpiK-7D9D1FDE.poez.io/Boujour/coucou/slt™");
const std::string correctjid = jidprep(badjid);
@@ -26,13 +29,18 @@ TEST_CASE("Jid")
CHECK(jidprep(badjid) == "~zigougoutm@epik-7d9d1fde.poez.io/Boujour/coucou/sltTM");
CHECK(jidprep(badjid) == "~zigougoutm@epik-7d9d1fde.poez.io/Boujour/coucou/sltTM");
- const std::string badjid2("Zigougou@poez.io");
- const std::string correctjid2 = jidprep(badjid2);
- CHECK(correctjid2 == "zigougou@poez.io");
+ CHECK(jidprep("Zigougou@poez.io") == "zigougou@poez.io");
+
+ CHECK(jidprep("~Bisous@88.123.43.45") == "~bisous@88.123.43.45");
+
+ CHECK(jidprep("~Bisous@::ffff:42.156.139.46") == "~bisous@[::ffff:42.156.139.46]");
+
+ CHECK(jidprep("louiz!6bf74289@2001:bc8:38e7::") == "louiz!6bf74289@[2001:bc8:38e7::]");
- const std::string crappy("~Bisous@7ea8beb1:c5fd2849:da9a048e:ip");
- const std::string fixed_crappy = jidprep(crappy);
- CHECK(fixed_crappy == "~bisous@7ea8beb1-c5fd2849-da9a048e-ip");
+ CHECK(jidprep("louiz@+:::::----coucou.com78--.") == "louiz@coucou.com78");
+ CHECK(jidprep("louiz@coucou.com78--.") == "louiz@coucou.com78");
+ CHECK(jidprep("louiz@+:::::----coucou.com78") == "louiz@coucou.com78");
+ CHECK(jidprep("louiz@:::::") == "louiz@empty");
#else // Without libidn, jidprep always returns an empty string
CHECK(jidprep(badjid) == "");
#endif
diff --git a/tests/network.cpp b/tests/network.cpp
new file mode 100644
index 0000000..33cf023
--- /dev/null
+++ b/tests/network.cpp
@@ -0,0 +1,44 @@
+#include "catch.hpp"
+#include <network/tls_policy.hpp>
+
+#ifdef BOTAN_FOUND
+TEST_CASE("tls_policy")
+{
+ BiboumiTLSPolicy policy;
+ const auto default_minimum_signature_strength = policy.minimum_signature_strength();
+ const auto default_session_ticket_lifetime = policy.session_ticket_lifetime();
+ const auto default_minimum_rsa_bits = policy.minimum_rsa_bits();
+
+ policy.load("does not exist");
+ WHEN("we fail to load the file")
+ {
+ THEN("all values are the default ones")
+ {
+ CHECK(policy.minimum_signature_strength() == default_minimum_signature_strength);
+ CHECK(policy.minimum_rsa_bits() == default_minimum_rsa_bits);
+ }
+ AND_WHEN("we load a valid first file")
+ {
+ std::istringstream iss("minimum_signature_strength = 128\nminimum_rsa_bits=12\n");
+ policy.load(iss);
+ THEN("the specified values are updated, and the rest is still the default")
+ {
+ CHECK(policy.minimum_signature_strength() == 128);
+ CHECK(policy.minimum_rsa_bits() == 12);
+ CHECK(policy.session_ticket_lifetime() == default_session_ticket_lifetime);
+ }
+ AND_WHEN("we load a second file")
+ {
+ std::istringstream iss("minimum_signature_strength = 15");
+ policy.load(iss);
+ THEN("the specified values are updated, and the rest is untouched")
+ {
+ CHECK(policy.minimum_signature_strength() == 15);
+ CHECK(policy.minimum_rsa_bits() == 12);
+ CHECK(policy.session_ticket_lifetime() == default_session_ticket_lifetime);
+ }
+ }
+ }
+ }
+}
+#endif
diff --git a/tests/utils.cpp b/tests/utils.cpp
index 48951da..c5ef7e7 100644
--- a/tests/utils.cpp
+++ b/tests/utils.cpp
@@ -8,6 +8,9 @@
#include <utils/empty_if_fixed_server.hpp>
#include <utils/get_first_non_empty.hpp>
#include <utils/time.hpp>
+#include <utils/system.hpp>
+#include <utils/scopeguard.hpp>
+#include <utils/dirname.hpp>
using namespace std::string_literals;
@@ -140,3 +143,31 @@ TEST_CASE("parse_datetime")
CHECK(utils::parse_datetime("1970-01-02T00:00:12*00:00") == -1);
CHECK(utils::parse_datetime("1970-01-02T00:00:12+0000") == -1);
}
+
+TEST_CASE("scope_guard")
+{
+ bool res = false;
+ {
+ auto guard = utils::make_scope_guard([&res](){ res = true; });
+ CHECK(!res);
+ }
+ CHECK(res);
+}
+
+TEST_CASE("system_name")
+{
+ CHECK(utils::get_system_name() != "Unknown");
+ CHECK(!utils::get_system_name().empty());
+}
+
+TEST_CASE("dirname")
+{
+ CHECK(utils::dirname("/") == "/");
+ CHECK(utils::dirname("coucou.txt") == "./");
+ CHECK(utils::dirname("../coucou.txt") == "../");
+ CHECK(utils::dirname("/etc/biboumi/coucou.txt") == "/etc/biboumi/");
+ CHECK(utils::dirname("..") == "..");
+ CHECK(utils::dirname("../") == "../");
+ CHECK(utils::dirname(".") == ".");
+ CHECK(utils::dirname("./") == "./");
+}
diff --git a/tests/xmpp.cpp b/tests/xmpp.cpp
index 37d2b54..14c51da 100644
--- a/tests/xmpp.cpp
+++ b/tests/xmpp.cpp
@@ -30,20 +30,21 @@ TEST_CASE("Test basic XML parsing")
// And do the same checks on moved-constructed stanza
Stanza moved(std::move(copy));
});
- xml.feed(doc.data(), doc.size(), true);
+ CHECK(doc.size() <= std::numeric_limits<int>::max());
+ xml.feed(doc.data(), static_cast<int>(doc.size()), true);
const std::string doc2 = "<stream xmlns='s'><stanza>coucou\r\n\a</stanza></stream>";
xml.add_stanza_callback([](const Stanza& stanza)
{
CHECK(stanza.get_inner() == "coucou\r\n");
});
-
- xml.feed(doc2.data(), doc.size(), true);
+ CHECK(doc.size() <= std::numeric_limits<int>::max());
+ xml.feed(doc2.data(), static_cast<int>(doc.size()), true);
}
TEST_CASE("XML escape")
{
- const std::string unescaped = "'coucou'<cc>/&\"gaga\"";
+ const std::string unescaped = R"('coucou'<cc>/&"gaga")";
CHECK(xml_escape(unescaped) == "&apos;coucou&apos;&lt;cc&gt;/&amp;&quot;gaga&quot;");
}
@@ -52,3 +53,20 @@ TEST_CASE("handshake_digest")
const auto res = get_handshake_digest("id1234", "S4CR3T");
CHECK(res == "c92901b5d376ad56269914da0cce3aab976847df");
}
+
+TEST_CASE("substanzas")
+{
+ Stanza a("a");
+ {
+ XmlSubNode b(a, "b");
+ {
+ CHECK(!a.has_children());
+ XmlSubNode c(b, "c");
+ XmlSubNode d(b, "d");
+ CHECK(!c.has_children());
+ CHECK(!d.has_children());
+ }
+ CHECK(b.has_children());
+ }
+ CHECK(a.has_children());
+}