__main__.py 97.2 KB
Newer Older
1 2
#!/usr/bin/env python3

3
import collections
4 5 6 7 8
import slixmpp
import asyncio
import logging
import signal
import atexit
9
import lxml.etree
10
import sys
11
import io
louiz’'s avatar
louiz’ committed
12
import os
13
from functools import partial
louiz’'s avatar
louiz’ committed
14
from slixmpp.xmlstream.matcher.base import MatcherBase
15 16


louiz’'s avatar
louiz’ committed
17
class MatchAll(MatcherBase):
18 19 20 21 22 23
    """match everything"""

    def match(self, xml):
        return True


24
class StanzaError(Exception):
25 26 27 28 29 30 31 32 33 34 35
    """
    Raised when a step fails.
    """
    pass


class SkipStepError(Exception):
    """
    Raised by a step when it needs to be skiped, by running
    the next available step immediately.
    """
36 37 38
    pass


39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
class XMPPComponent(slixmpp.BaseXMPP):
    """
    XMPPComponent sending a “scenario” of stanzas, checking that the responses
    match the expected results.
    """

    def __init__(self, scenario, biboumi):
        super().__init__(jid="biboumi.localhost", default_ns="jabber:component:accept")
        self.is_component = True
        self.stream_header = '<stream:stream %s %s from="%s" id="%s">' % (
            'xmlns="jabber:component:accept"',
            'xmlns:stream="%s"' % self.stream_ns,
            self.boundjid, self.get_id())
        self.stream_footer = "</stream:stream>"

        self.register_handler(slixmpp.Callback('Match All',
                                               MatchAll(None),
                                               self.handle_incoming_stanza))

        self.add_event_handler("session_end", self.on_end_session)

        asyncio.async(self.accept_routine())

        self.scenario = scenario
        self.biboumi = biboumi
64 65 66
        # A callable, taking a stanza as argument and raising a StanzaError
        # exception if the test should fail.
        self.stanza_checker = None
67 68 69
        self.failed = False
        self.accepting_server = None

70 71
        self.saved_values = {}

72 73 74 75 76
    def error(self, message):
        print("Failure: %s" % (message,))
        self.scenario.steps = []
        self.failed = True

77
    def on_end_session(self, _):
78 79 80
        self.loop.stop()

    def handle_incoming_stanza(self, stanza):
81 82 83 84 85
        if self.stanza_checker:
            try:
                self.stanza_checker(stanza)
            except StanzaError as e:
                self.error(e)
86 87 88 89
            except SkipStepError:
                # Run the next step and then re-handle this same stanza
                self.run_scenario()
                return self.handle_incoming_stanza(stanza)
90
            self.stanza_checker = None
91 92 93
        self.run_scenario()

    def run_scenario(self):
louiz’'s avatar
louiz’ committed
94 95
        if self.scenario.steps:
            step = self.scenario.steps.pop(0)
96 97
            step(self, self.biboumi)
        else:
louiz’'s avatar
louiz’ committed
98 99
            if self.biboumi:
                self.biboumi.stop()
100 101 102 103

    @asyncio.coroutine
    def accept_routine(self):
        self.accepting_server = yield from self.loop.create_server(lambda: self,
104
                                                                   "127.0.0.1", 8811, reuse_address=True)
105

106 107
    def check_stanza_against_all_expected_xpaths(self):
        pass
louiz’'s avatar
louiz’ committed
108

louiz’'s avatar
louiz’ committed
109

110 111 112 113
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',
louiz’'s avatar
louiz’ committed
114 115
                                            'disco_info': 'http://jabber.org/protocol/disco#info',
                                            'muc_traffic': 'http://jabber.org/protocol/muc#traffic',
116
                                            'disco_items': 'http://jabber.org/protocol/disco#items',
117
                                            'commands': 'http://jabber.org/protocol/commands',
118
                                            'dataform': 'jabber:x:data',
119 120 121 122
                                            'version': 'jabber:iq:version',
                                            'mam': 'urn:xmpp:mam:1',
                                            'delay': 'urn:xmpp:delay',
                                            'forward': 'urn:xmpp:forward:0',
123
                                            'client': 'jabber:client',
124 125 126
                                            'rsm': 'http://jabber.org/protocol/rsm',
                                            'carbon': 'urn:xmpp:carbons:2',
                                            'hints': 'urn:xmpp:hints'})
127 128 129 130 131 132
    return matched


def check_xpath(xpaths, xmpp, after, stanza):
    for i, xpath in enumerate(xpaths):
        matched = match(stanza, xpath)
133 134
        if not matched:
            raise StanzaError("Received stanza “%s” did not match expected xpath “%s”" % (stanza, xpath))
135
    if after:
136 137 138 139 140
        if isinstance(after, collections.Iterable):
            for af in after:
                af(stanza, xmpp)
        else:
            after(stanza, xmpp)
141

142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164
def all_xpaths_match(stanza, xpaths):
    for xpath in xpaths:
        matched = match(stanza, xpath)
        if not matched:
            return False
    return True

def check_list_of_xpath(list_of_xpaths, xmpp, stanza):
    found = None
    for i, xpaths in enumerate(list_of_xpaths):
        if all_xpaths_match(stanza, xpaths):
            found = i
            break

    if found is None:
        raise StanzaError("Received stanza “%s” did not match any of the expected xpaths:\n%s" % (stanza, list_of_xpaths))

    list_of_xpaths.pop(i)

    if list_of_xpaths:
        step = partial(expect_unordered_already_formatted, list_of_xpaths)
        xmpp.scenario.steps.insert(0, step)

louiz’'s avatar
louiz’ committed
165

166
def check_xpath_optional(xpaths, xmpp, after, stanza):
167
    try:
168
        check_xpath(xpaths, xmpp, after, stanza)
169 170 171 172
    except StanzaError:
        raise SkipStepError()


173 174 175 176 177 178 179 180
class Scenario:
    """Defines a list of actions that are executed in sequence, until one of
    them throws an exception, or until the end.  An action can be something
    like “send a stanza”, “receive the next stanza and check that it matches
    the given XPath”, “send a signal”, “wait for the end of the process”,
    etc
    """

181
    def __init__(self, name, steps, conf="basic"):
182 183 184 185 186
        """
        Steps is a list of 2-tuple:
        [(action, answer), (action, answer)]
        """
        self.name = name
187
        self.steps = []
188
        self.conf = conf
189 190 191 192 193 194
        for elem in steps:
            if isinstance(elem, collections.Iterable):
                for step in elem:
                    self.steps.append(step)
            else:
                self.steps.append(elem)
195

louiz’'s avatar
louiz’ committed
196

197 198
class ProcessRunner:
    def __init__(self):
199 200
        self.process = None
        self.signal_sent = False
201
        self.create = None
202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217

    @asyncio.coroutine
    def start(self):
        self.process = yield from self.create

    @asyncio.coroutine
    def wait(self):
        code = yield from self.process.wait()
        return code

    def stop(self):
        if not self.signal_sent:
            self.signal_sent = True
            if self.process:
                self.process.send_signal(signal.SIGINT)

218 219 220
    def __del__(self):
        self.stop()

221

222
class BiboumiRunner(ProcessRunner):
louiz’'s avatar
louiz’ committed
223
    def __init__(self, name):
224 225 226
        super().__init__()
        self.name = name
        self.fd = open("biboumi_%s_output.txt" % (name,), "w")
louiz’'s avatar
louiz’ committed
227
        with_valgrind = os.environ.get("E2E_WITH_VALGRIND") is not None
228
        if with_valgrind:
louiz’'s avatar
louiz’ committed
229
            self.create = asyncio.create_subprocess_exec("valgrind", "--suppressions=" + (os.environ.get("E2E_BIBOUMI_SUPP_DIR") or "") + "biboumi.supp", "--leak-check=full", "--show-leak-kinds=all",
230 231 232 233 234 235 236
                                                         "--errors-for-leak-kinds=all", "--error-exitcode=16",
                                                         "./biboumi", "test.conf", stdin=None, stdout=self.fd,
                                                         stderr=self.fd, loop=None, limit=None)
        else:
            self.create = asyncio.create_subprocess_exec("./biboumi", "test.conf", stdin=None, stdout=self.fd,
                                                         stderr=self.fd, loop=None, limit=None)

louiz’'s avatar
louiz’ committed
237

238 239 240
class IrcServerRunner(ProcessRunner):
    def __init__(self):
        super().__init__()
louiz’'s avatar
louiz’ committed
241
        self.create = asyncio.create_subprocess_exec("charybdis", "-foreground", "-configfile", os.getcwd() + "/../tests/end_to_end/ircd.conf",
242
                                                     stderr=asyncio.subprocess.PIPE)
243

louiz’'s avatar
louiz’ committed
244

245
def send_stanza(stanza, xmpp, biboumi):
246 247 248
    replacements = common_replacements
    replacements.update(xmpp.saved_values)
    xmpp.send_raw(stanza.format_map(replacements))
249 250 251
    asyncio.get_event_loop().call_soon(xmpp.run_scenario)


252
def expect_stanza(xpaths, xmpp, biboumi, optional=False, after=None):
253
    check_func = check_xpath if not optional else check_xpath_optional
254
    if isinstance(xpaths, str):
255
        xmpp.stanza_checker = partial(check_func, [xpaths.format_map(common_replacements)], xmpp, after)
256
    elif isinstance(xpaths, tuple):
257
        xmpp.stanza_checker = partial(check_func, [xpath.format_map(common_replacements) for xpath in xpaths], xmpp, after)
258 259
    else:
        print("Warning, from argument type passed to expect_stanza: %s" % (type(xpaths)))
260

261 262 263 264 265 266 267 268 269 270 271 272 273 274 275
# list_of_xpaths: [(xpath, xpath), (xpath, xpath), (xpath)]
def expect_unordered(list_of_xpaths, xmpp, biboumi):
    formatted_list_of_xpaths = []
    for xpaths in list_of_xpaths:
        formatted_xpaths = []
        for xpath in xpaths:
            formatted_xpath = xpath.format_map(common_replacements)
            formatted_xpaths.append(formatted_xpath)
        formatted_list_of_xpaths.append(tuple(formatted_xpaths))
    # formatted_list_of_xpaths = [tuple(xpath.format_map(common_replacements) for xpath in xpaths) for xpaths in list_of_xpaths]

    expect_unordered_already_formatted(formatted_list_of_xpaths, xmpp, biboumi)

def expect_unordered_already_formatted(formatted_list_of_xpaths, xmpp, biboumi):
    xmpp.stanza_checker = partial(check_list_of_xpath, formatted_list_of_xpaths, xmpp)
louiz’'s avatar
louiz’ committed
276

louiz’'s avatar
louiz’ committed
277 278 279 280 281
def log_message(message, xmpp, biboumi):
    print("%s" % (message,))
    asyncio.get_event_loop().call_soon(xmpp.run_scenario)


282 283 284 285 286 287 288 289 290
class BiboumiTest:
    """
    Spawns a biboumi process and a fake XMPP Component that will run a
    Scenario.  It redirects the outputs of the subprocess into separated
    files, and detects any failure in the running of the scenario.
    """

    def __init__(self, scenario, expected_code=0):
        self.scenario = scenario
291
        self.expected_code = expected_code
292

louiz’'s avatar
louiz’ committed
293 294
    def run(self):
        with_valgrind = os.environ.get("E2E_WITH_VALGRIND") is not None
295 296 297 298 299 300 301 302 303
        print("Running scenario: %s%s" % (self.scenario.name, " (with valgrind)" if with_valgrind else ''))
        # Redirect the slixmpp logging into a specific file
        output_filename = "slixmpp_%s_output.txt" % (self.scenario.name,)
        with open(output_filename, "w"):
            pass
        logging.basicConfig(level=logging.DEBUG,
                            format='%(levelname)-8s %(message)s',
                            filename=output_filename)

304
        with open("test.conf", "w") as fd:
louiz’'s avatar
louiz’ committed
305
            fd.write(confs[self.scenario.conf])
306

307 308 309 310
        try:
            os.remove("e2e_test.sqlite")
        except FileNotFoundError:
            pass
311

312
        # Start the XMPP component and biboumi
louiz’'s avatar
louiz’ committed
313
        biboumi = BiboumiRunner(self.scenario.name)
314 315 316 317 318 319 320
        xmpp = XMPPComponent(self.scenario, biboumi)
        asyncio.get_event_loop().run_until_complete(biboumi.start())

        asyncio.get_event_loop().call_soon(xmpp.run_scenario)

        xmpp.process()
        code = asyncio.get_event_loop().run_until_complete(biboumi.wait())
louiz’'s avatar
louiz’ committed
321
        xmpp.biboumi = None
louiz’'s avatar
louiz’ committed
322
        self.scenario.steps.clear()
323 324 325 326 327 328 329 330 331 332
        failed = False
        if not xmpp.failed:
            if code != self.expected_code:
                xmpp.error("Wrong return code from biboumi's process: %d" % (code,))
                failed = True
            else:
                print("Success!")
        else:
            failed = True

333 334
        xmpp.saved_values.clear()

335 336 337 338 339
        if xmpp.server:
            xmpp.accepting_server.close()

        return not failed

louiz’'s avatar
louiz’ committed
340

341 342
confs = {
'basic':
343 344
"""hostname=biboumi.localhost
password=coucou
345
db_name=e2e_test.sqlite
346 347
port=8811
admin=admin@example.com""",
348 349 350 351

'fixed_server':
"""hostname=biboumi.localhost
password=coucou
352
db_name=e2e_test.sqlite
353 354
port=8811
fixed_irc_server=irc.localhost
355
admin=admin@example.com
356
"""}
357

358 359 360
common_replacements = {
    'irc_server_one': 'irc.localhost@biboumi.localhost',
    'irc_host_one': 'irc.localhost',
361
    'biboumi_host': 'biboumi.localhost',
362
    'resource_one': 'resource1',
363
    'resource_two': 'resource2',
364 365 366
    'nick_one': 'Nick',
    'jid_one': 'first@example.com',
    'jid_two': 'second@example.com',
367
    'jid_admin': 'admin@example.com',
368
    'nick_two': 'Bobby',
369
    'lower_nick_one': 'nick',
370
    'lower_nick_two': 'bobby',
371 372 373 374 375
}


def handshake_sequence():
    return (partial(expect_stanza, "//handshake"),
376
            partial(send_stanza, "<handshake xmlns='jabber:component:accept'/>"))
377 378


379
def connection_begin_sequence(irc_host, jid):
380 381 382 383 384
    jid = jid.format_map(common_replacements)
    xpath    = "/message[@to='" + jid + "'][@from='irc.localhost@biboumi.localhost']/body[text()='%s']"
    xpath_re = "/message[@to='" + jid + "'][@from='irc.localhost@biboumi.localhost']/body[re:test(text(), '%s')]"
    return (
    partial(expect_stanza,
385
            xpath % ('Connecting to %s:6697 (encrypted)' % irc_host)),
386
    partial(expect_stanza,
387
            xpath % 'Connection failed: Connection refused'),
388
    partial(expect_stanza,
389
            xpath % ('Connecting to %s:6670 (encrypted)' % irc_host)),
390
    partial(expect_stanza,
391
            xpath % 'Connection failed: Connection refused'),
392
    partial(expect_stanza,
393
            xpath % ('Connecting to %s:6667 (not encrypted)' % irc_host)),
394
    partial(expect_stanza,
395 396
            xpath % 'Connected to IRC server.'),
    # These two messages can be receive in any order
397
    partial(expect_stanza,
398
            xpath_re % (r'^%s: \*\*\* (Checking Ident|Looking up your hostname...)$' % irc_host)),
399
    partial(expect_stanza,
400
            xpath_re % (r'^%s: \*\*\* (Checking Ident|Looking up your hostname...)$' % irc_host)),
401 402
    # These three messages can be received in any order
    partial(expect_stanza,
403
            xpath_re % (r'^%s: (\*\*\* Found your hostname: .*|ACK multi-prefix|\*\*\* No Ident response)$' % irc_host)),
404
    partial(expect_stanza,
405
            xpath_re % (r'^%s: (\*\*\* Found your hostname: .*|ACK multi-prefix|\*\*\* No Ident response)$' % irc_host)),
406
    partial(expect_stanza,
407 408 409 410 411 412 413 414 415
            xpath_re % (r'^%s: (\*\*\* Found your hostname: .*|ACK multi-prefix|\*\*\* No Ident response)$' % irc_host)),
    )


def connection_end_sequence(irc_host, jid):
    jid = jid.format_map(common_replacements)
    xpath    = "/message[@to='" + jid + "'][@from='irc.localhost@biboumi.localhost']/body[text()='%s']"
    xpath_re = "/message[@to='" + jid + "'][@from='irc.localhost@biboumi.localhost']/body[re:test(text(), '%s')]"
    return (
416
    partial(expect_stanza,
417
            xpath_re % (r'^%s: Your host is .*$' % irc_host)),
418
    partial(expect_stanza,
419
            xpath_re % (r'^%s: This server was created .*$' % irc_host)),
420
    partial(expect_stanza,
421
            xpath_re % (r'^%s: There are \d+ users and \d+ invisible on \d+ servers$' % irc_host)),
422 423
    partial(expect_stanza,
            xpath_re % (r'^%s: \d+ unknown connection\(s\)$' % irc_host), optional=True),
424
    partial(expect_stanza,
425
            xpath_re % (r'^%s: \d+ channels formed$' % irc_host), optional=True),
426
    partial(expect_stanza,
427
            xpath_re % (r'^%s: I have \d+ clients and \d+ servers$' % irc_host)),
428
    partial(expect_stanza,
429
            xpath_re % (r'^%s: \d+ \d+ Current local users \d+, max \d+$' % irc_host)),
430
    partial(expect_stanza,
431
            xpath_re % (r'^%s: \d+ \d+ Current global users \d+, max \d+$' % irc_host)),
432
    partial(expect_stanza,
433
            xpath_re % (r'^%s: Highest connection count: \d+ \(\d+ clients\) \(\d+ connections received\)$' % irc_host)),
434 435
    partial(expect_stanza,
            xpath % "- This is charybdis MOTD you might replace it, but if not your friends will\n- laugh at you.\n"),
436
    partial(expect_stanza,
437
            xpath_re % r'^User mode for \w+ is \[\+i\]$'),
438 439 440
    )


441 442 443 444
def connection_sequence(irc_host, jid):
    return connection_begin_sequence(irc_host, jid) + connection_end_sequence(irc_host, jid)


445 446 447 448 449 450 451 452 453
def extract_attribute(xpath, name, stanza):
    matched = match(stanza, xpath)
    return matched[0].get(name)


def save_value(name, func, stanza, xmpp):
    xmpp.saved_values[name] = func(stanza)


454 455 456 457 458 459 460 461 462
if __name__ == '__main__':

    atexit.register(asyncio.get_event_loop().close)

    # Start the test component, accepting connections on the configured
    # port.
    scenarios = (
        Scenario("basic_handshake_success",
                 [
463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478
                     handshake_sequence()
                 ]),
        Scenario("irc_server_connection",
                 [
                     handshake_sequence(),
                     partial(send_stanza,
                             "<presence from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}/{nick_one}' />"),
                     connection_sequence("irc.localhost", '{jid_one}/{resource_one}'),
                 ]),
        Scenario("simple_channel_join",
                 [
                     handshake_sequence(),
                     partial(send_stanza,
                             "<presence from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}/{nick_one}' />"),
                     connection_sequence("irc.localhost", '{jid_one}/{resource_one}'),
                     partial(expect_stanza,
479 480 481
                             "/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']",
482 483 484
                             "/presence/muc_user:x/muc_user:status[@code='110']")
                             ),
                     partial(expect_stanza, "/message[@from='#foo%{irc_server_one}'][@type='groupchat']/subject[not(text())]"),
485
                 ]),
louiz’'s avatar
louiz’ committed
486 487 488 489 490 491 492 493 494 495 496 497 498
        Scenario("virtual_channel_join",
                 [
                     handshake_sequence(),
                     partial(send_stanza,
                             "<presence from='{jid_one}/{resource_one}' to='%{irc_server_one}/{nick_one}' />"),
                     connection_begin_sequence("irc.localhost", '{jid_one}/{resource_one}'),
                     partial(expect_stanza,
                             ("/presence[@to='{jid_one}/{resource_one}'][@from='%{irc_server_one}/{nick_one}']/muc_user:x/muc_user:item[@affiliation='none'][@role='participant']",
                              "/presence/muc_user:x/muc_user:status[@code='110']")
                             ),
                     partial(expect_stanza, "/message[@from='%{irc_server_one}'][@type='groupchat']/subject[re:test(text(), '^This is a virtual channel.*$')]"),
                     connection_end_sequence("irc.localhost", '{jid_one}/{resource_one}'),
                 ]),
499
        Scenario("channel_join_with_two_users",
500
                 [
501 502
                     handshake_sequence(),
                     # First user joins
louiz’'s avatar
louiz’ committed
503 504
                     partial(log_message,
                             "First user joins"),
louiz’'s avatar
louiz’ committed
505
                     partial(send_stanza,
506 507 508
                             "<presence from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}/{nick_one}' />"),
                     connection_sequence("irc.localhost", '{jid_one}/{resource_one}'),
                     partial(expect_stanza,
509 510 511
                             "/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']",
512 513 514 515 516
                             "/presence/muc_user:x/muc_user:status[@code='110']")
                             ),
                     partial(expect_stanza, "/message[@from='#foo%{irc_server_one}'][@type='groupchat']/subject[not(text())]"),

                     # Second user joins
louiz’'s avatar
louiz’ committed
517 518
                     partial(log_message,
                             "Second user joins"),
519 520
                     partial(send_stanza,
                             "<presence from='{jid_two}/{resource_one}' to='#foo%{irc_server_one}/{nick_two}' />"),
louiz’'s avatar
louiz’ committed
521
                     connection_sequence("irc.localhost", '{jid_two}/{resource_one}'),
522 523 524 525 526 527 528
                     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())]",),
                         ]),
louiz’'s avatar
louiz’ committed
529 530 531 532 533 534 535 536 537
                 ]),
        Scenario("channel_custom_topic",
                 [
                     handshake_sequence(),
                     # First user joins
                     partial(send_stanza,
                             "<presence from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}/{nick_one}' />"),
                     connection_sequence("irc.localhost", '{jid_one}/{resource_one}'),
                     partial(expect_stanza,
538 539 540 541
                             "/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']")
louiz’'s avatar
louiz’ committed
542 543 544 545 546
                             ),
                     partial(expect_stanza, "/message[@from='#foo%{irc_server_one}'][@type='groupchat']/subject[not(text())]"),

                     # First user sets the topic
                     partial(send_stanza,
547
                             "<message from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}' type='groupchat'><subject>TOPIC TEST</subject></message>"),
548
                     partial(expect_stanza, "/message[@from='#foo%{irc_server_one}/{nick_one}'][@type='groupchat'][@to='{jid_one}/{resource_one}']/subject[text()='TOPIC TEST']"),
549 550 551 552 553 554 555 556 557 558

                     # Second user joins
                     partial(send_stanza,
                             "<presence from='{jid_two}/{resource_one}' to='#foo%{irc_server_one}/{nick_two}' />"),
                     connection_sequence("irc.localhost", '{jid_two}/{resource_one}'),
                     # Our presence, sent to the other user
                     partial(expect_stanza,
                             ("/presence[@to='{jid_one}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_two}']/muc_user:x/muc_user:item[@affiliation='none'][@jid='~bobby@localhost'][@role='participant']",)),
                     # The other user presence
                     partial(expect_stanza,
559
                             "/presence[@to='{jid_two}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_one}']/muc_user:x/muc_user:item[@affiliation='admin'][@role='moderator']"),
560 561 562 563 564
                     # Our own presence
                     partial(expect_stanza,
                             ("/presence[@to='{jid_two}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_two}']/muc_user:x/muc_user:item[@affiliation='none'][@jid='~bobby@localhost'][@role='participant']",
                              "/presence/muc_user:x/muc_user:status[@code='110']")
                             ),
565
                     partial(expect_stanza, "/message[@from='#foo%{irc_server_one}/{nick_one}'][@type='groupchat']/subject[text()='TOPIC TEST']"),
566
                 ]),
567 568 569 570 571 572 573 574 575 576 577 578 579 580 581
        Scenario("channel_basic_join_on_fixed_irc_server",
                 [
                     handshake_sequence(),
                     partial(send_stanza,
                     "<presence from='{jid_one}/{resource_one}' to='#zgeg@{biboumi_host}/{nick_one}' />"),
                     connection_sequence("irc.localhost", '{jid_one}/{resource_one}'),
                     partial(expect_stanza,
                     "/message/body[text()='Mode #zgeg [+nt] by {irc_host_one}']"),
                     partial(expect_stanza,
                     ("/presence[@to='{jid_one}/{resource_one}'][@from='#zgeg@{biboumi_host}/{nick_one}']/muc_user:x/muc_user:item[@affiliation='admin'][@role='moderator']",
                     "/presence/muc_user:x/muc_user:status[@code='110']")
                     ),
                     partial(expect_stanza, "/message[@from='#zgeg@{biboumi_host}'][@type='groupchat']/subject[not(text())]"),
                 ], conf='fixed_server'
                 ),
582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609
        Scenario("list_adhoc",
                 [
                     handshake_sequence(),
                     partial(send_stanza, "<iq type='get' id='idwhatever' from='{jid_one}/{resource_one}' to='{biboumi_host}'><query xmlns='http://jabber.org/protocol/disco#items' node='http://jabber.org/protocol/commands' /></iq>"),
                     partial(expect_stanza, ("/iq[@type='result']/disco_items:query[@node='http://jabber.org/protocol/commands']",
                                             "/iq/disco_items:query/disco_items:item[3]")),
                 ]),
        Scenario("list_admin_adhoc",
                 [
                     handshake_sequence(),
                     partial(send_stanza, "<iq type='get' id='idwhatever' from='{jid_admin}/{resource_one}' to='{biboumi_host}'><query xmlns='http://jabber.org/protocol/disco#items' node='http://jabber.org/protocol/commands' /></iq>"),
                     partial(expect_stanza, ("/iq[@type='result']/disco_items:query[@node='http://jabber.org/protocol/commands']",
                                             "/iq/disco_items:query/disco_items:item[5]")),
                 ]),
        Scenario("list_adhoc_fixed_server",
                 [
                     handshake_sequence(),
                     partial(send_stanza, "<iq type='get' id='idwhatever' from='{jid_one}/{resource_one}' to='{biboumi_host}'><query xmlns='http://jabber.org/protocol/disco#items' node='http://jabber.org/protocol/commands' /></iq>"),
                     partial(expect_stanza, ("/iq[@type='result']/disco_items:query[@node='http://jabber.org/protocol/commands']",
                                             "/iq/disco_items:query/disco_items:item[3]")),
                 ], conf='fixed_server'),
        Scenario("list_admin_adhoc_fixed_server",
                 [
                     handshake_sequence(),
                     partial(send_stanza, "<iq type='get' id='idwhatever' from='{jid_admin}/{resource_one}' to='{biboumi_host}'><query xmlns='http://jabber.org/protocol/disco#items' node='http://jabber.org/protocol/commands' /></iq>"),
                     partial(expect_stanza, ("/iq[@type='result']/disco_items:query[@node='http://jabber.org/protocol/commands']",
                                             "/iq/disco_items:query/disco_items:item[5]")),
                 ], conf='fixed_server'),
610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632


        Scenario("list_adhoc_irc",
                 [
                     handshake_sequence(),
                     partial(send_stanza, "<iq type='get' id='idwhatever' from='{jid_one}/{resource_one}' to='{irc_host_one}@{biboumi_host}'><query xmlns='http://jabber.org/protocol/disco#items' node='http://jabber.org/protocol/commands' /></iq>"),
                     partial(expect_stanza, ("/iq[@type='result']/disco_items:query[@node='http://jabber.org/protocol/commands']",
                                             "/iq/disco_items:query/disco_items:item[1]")),
                 ]),
        Scenario("list_adhoc_irc_fixed_server",
                 [
                     handshake_sequence(),
                     partial(send_stanza, "<iq type='get' id='idwhatever' from='{jid_one}/{resource_one}' to='{biboumi_host}'><query xmlns='http://jabber.org/protocol/disco#items' node='http://jabber.org/protocol/commands' /></iq>"),
                     partial(expect_stanza, ("/iq[@type='result']/disco_items:query[@node='http://jabber.org/protocol/commands']",
                                             "/iq/disco_items:query/disco_items:item[4]")),
                 ], conf='fixed_server'),
        Scenario("list_admin_adhoc_irc_fixed_server",
                 [
                     handshake_sequence(),
                     partial(send_stanza, "<iq type='get' id='idwhatever' from='{jid_admin}/{resource_one}' to='{biboumi_host}'><query xmlns='http://jabber.org/protocol/disco#items' node='http://jabber.org/protocol/commands' /></iq>"),
                     partial(expect_stanza, ("/iq[@type='result']/disco_items:query[@node='http://jabber.org/protocol/commands']",
                                             "/iq/disco_items:query/disco_items:item[6]")),
                 ], conf='fixed_server'),
633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649

        Scenario("execute_hello_adhoc_command",
                 [
                     handshake_sequence(),
                     partial(send_stanza, "<iq type='set' id='hello-command1' from='{jid_one}/{resource_one}' to='{biboumi_host}'><command xmlns='http://jabber.org/protocol/commands' node='hello' action='execute' /></iq>"),
                     partial(expect_stanza, ("/iq[@type='result']/commands:command[@node='hello'][@sessionid][@status='executing']",
                                             "/iq/commands:command/dataform:x[@type='form']/dataform:title[text()='Configure your name.']",
                                             "/iq/commands:command/dataform:x[@type='form']/dataform:instructions[text()='Please provide your name.']",
                                             "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='text-single']/dataform:required",
                                             "/iq/commands:command/commands:actions/commands:next",
                                             ),
                                             after = partial(save_value, "sessionid", partial(extract_attribute, "/iq[@type='result']/commands:command[@node='hello']", "sessionid"))

                             ),
                     partial(send_stanza, "<iq type='set' id='hello-command2' from='{jid_one}/{resource_one}' to='{biboumi_host}'><command xmlns='http://jabber.org/protocol/commands' node='hello' sessionid='{sessionid}' action='next'><x xmlns='jabber:x:data' type='submit'><field var='name'><value>COUCOU</value></field></x></command></iq>"),
                     partial(expect_stanza, "/iq[@type='result']/commands:command[@node='hello'][@status='completed']/commands:note[@type='info'][text()='Hello COUCOU!']")
                 ]),
650
        Scenario("multisessionnick",
651 652 653 654 655 656 657 658 659 660 661
                 [
                     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']")
                             ),
662
                     partial(expect_stanza, "/message[@from='#foo%{irc_server_one}'][@type='groupchat'][@to='{jid_one}/{resource_one}']/subject[not(text())]"),
663 664 665 666 667

                     # The other resources joins the same room, with the same nick
                     partial(send_stanza,
                             "<presence from='{jid_one}/{resource_two}' to='#foo%{irc_server_one}/{nick_one}' />"),
                     # We receive our own join
668 669 670 671
                     partial(expect_unordered,
                             [("/presence[@to='{jid_one}/{resource_two}'][@from='#foo%{irc_server_one}/{nick_one}']/muc_user:x/muc_user:item[@affiliation='admin'][@role='moderator']",
                              "/presence/muc_user:x/muc_user:status[@code='110']"),
                              ("/message[@from='#foo%{irc_server_one}'][@type='groupchat'][@to='{jid_one}/{resource_two}']/subject[not(text())]",)]
672
                             ),
673 674 675 676 677 678

                     # A different user joins the same room
                     partial(send_stanza,
                             "<presence from='{jid_two}/{resource_one}' to='#foo%{irc_server_one}/{nick_two}' />"),
                     connection_sequence("irc.localhost", '{jid_two}/{resource_one}'),

679 680 681 682 683
                     partial(expect_unordered, [
                                ("/presence[@to='{jid_one}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_two}']",),
                                ("/presence[@to='{jid_one}/{resource_two}'][@from='#foo%{irc_server_one}/{nick_two}']",)
                                ]
                             ),
684

685 686 687 688 689 690
                     partial(expect_unordered, [
                         ("/presence[@to='{jid_two}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_one}']",),
                         ("/presence[@to='{jid_two}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_two}']",
                          "/presence/muc_user:x/muc_user:status[@code='110']",),
                          ]
                         ),
691 692 693 694 695
                     partial(expect_stanza, "/message[@from='#foo%{irc_server_one}'][@type='groupchat']/subject[not(text())]"),

                     # That second user sends a private message to the first one
                     partial(send_stanza, "<message from='{jid_two}/{resource_one}' to='#foo%{irc_server_one}/{nick_one}' type='chat'><body>RELLO</body></message>"),
                     # Message is received with a server-wide JID, by the two resources behind nick_one
696 697 698
                     partial(expect_stanza, ("/message[@from='{lower_nick_two}%{irc_server_one}'][@to='{jid_one}/{resource_one}'][@type='chat']/body[text()='RELLO']",
                                             "/message/hints:no-copy",
                                             "/message/carbon:private")),
699
                     partial(expect_stanza, "/message[@from='{lower_nick_two}%{irc_server_one}'][@to='{jid_one}/{resource_two}'][@type='chat']/body[text()='RELLO']"),
700 701 702 703 704 705 706 707 708 709 710 711

                     # One resource leaves the server entirely.
                     partial(send_stanza, "<presence type='unavailable' from='{jid_one}/{resource_two}' to='#foo%{irc_server_one}/{nick_one}' />"),
                     # The leave is forwarded only to us
                     partial(expect_stanza,
                             ("/presence[@type='unavailable']/muc_user:x/muc_user:status[@code='110']",
                             "/presence/status[text()='Biboumi note: 1 resources are still in this channel.']")
                             ),
                     # The second user sends two new private messages to the first user
                     partial(send_stanza, "<message from='{jid_two}/{resource_one}' to='#foo%{irc_server_one}/{nick_one}' type='chat'><body>first</body></message>"),
                     partial(send_stanza, "<message from='{jid_two}/{resource_one}' to='#foo%{irc_server_one}/{nick_one}' type='chat'><body>second</body></message>"),
                     # The first user receives the two messages, on the connected resource, once each
712 713
                     partial(expect_stanza, "/message[@from='{lower_nick_two}%{irc_server_one}'][@to='{jid_one}/{resource_one}'][@type='chat']/body[text()='first']"),
                     partial(expect_stanza, "/message[@from='{lower_nick_two}%{irc_server_one}'][@to='{jid_one}/{resource_one}'][@type='chat']/body[text()='second']"),
714
                 ]),
715 716 717 718 719
        Scenario("channel_messages",
                 [
                     handshake_sequence(),
                     # First user joins
                     partial(send_stanza,
720
                     "<presence from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}/{nick_one}' />"),
721 722 723 724
                     connection_sequence("irc.localhost", '{jid_one}/{resource_one}'),
                     partial(expect_stanza,
                     "/message/body[text()='Mode #foo [+nt] by {irc_host_one}']"),
                     partial(expect_stanza,
725
                     ("/presence[@to='{jid_one}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_one}']/muc_user:x/muc_user:item[@affiliation='admin'][@role='moderator']",
726 727 728 729 730 731 732 733 734
                     "/presence/muc_user:x/muc_user:status[@code='110']")
                     ),
                     partial(expect_stanza, "/message[@from='#foo%{irc_server_one}'][@type='groupchat']/subject[not(text())]"),

                     # Second user joins
                     partial(send_stanza,
                     "<presence from='{jid_two}/{resource_one}' to='#foo%{irc_server_one}/{nick_two}' />"),
                     connection_sequence("irc.localhost", '{jid_two}/{resource_one}'),
                     # Our presence, sent to the other user
735 736 737 738 739 740 741
                     partial(expect_unordered, [
                         ("/presence[@to='{jid_one}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_two}']/muc_user:x/muc_user:item[@affiliation='none'][@jid='~bobby@localhost'][@role='participant']",),
                         ("/presence[@to='{jid_two}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_one}']/muc_user:x/muc_user:item[@affiliation='admin'][@role='moderator']",),
                         ("/presence[@to='{jid_two}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_two}']/muc_user:x/muc_user:item[@affiliation='none'][@jid='~bobby@localhost'][@role='participant']",
                          "/presence/muc_user:x/muc_user:status[@code='110']"),
                         ("/message[@from='#foo%{irc_server_one}'][@type='groupchat']/subject[not(text())]",)
                     ]),
742

743 744 745
                     # Send a channel message
                     partial(send_stanza, "<message from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}' type='groupchat'><body>coucou</body></message>"),
                     # Receive the message, forwarded to the two users
746 747 748 749
                     partial(expect_unordered, [
                         ("/message[@from='#foo%{irc_server_one}/{nick_one}'][@to='{jid_one}/{resource_one}'][@type='groupchat']/body[text()='coucou']",),
                         ("/message[@from='#foo%{irc_server_one}/{nick_one}'][@to='{jid_two}/{resource_one}'][@type='groupchat']/body[text()='coucou']",)
                         ]),
750 751 752 753

                     # Send a private message, to a in-room JID
                     partial(send_stanza, "<message from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}/{nick_two}' type='chat'><body>coucou in private</body></message>"),
                     # Message is received with a server-wide JID
754
                     partial(expect_stanza, "/message[@from='{lower_nick_one}%{irc_server_one}'][@to='{jid_two}/{resource_one}'][@type='chat']/body[text()='coucou in private']"),
755 756

                     # Respond to the message, to the server-wide JID
757
                     partial(send_stanza, "<message from='{jid_two}/{resource_one}' to='{lower_nick_one}%{irc_server_one}' type='chat'><body>yes</body></message>"),
758 759
                     # The response is received from the in-room JID
                     partial(expect_stanza, "/message[@from='#foo%{irc_server_one}/{nick_two}'][@to='{jid_one}/{resource_one}'][@type='chat']/body[text()='yes']"),
760 761 762 763 764 765 766 767 768 769 770 771 772 773 774

                     ## Do the exact same thing, from a different chan,
                     # to check if the response comes from the right JID

                     # Join the virtual channel
                     partial(send_stanza,
                     "<presence from='{jid_one}/{resource_one}' to='%{irc_server_one}/{nick_one}' />"),
                     partial(expect_stanza,
                     "/presence/muc_user:x/muc_user:status[@code='110']"),
                     partial(expect_stanza, "/message[@from='%{irc_server_one}'][@type='groupchat']/subject"),


                     # Send a private message, to a in-room JID
                     partial(send_stanza, "<message from='{jid_one}/{resource_one}' to='%{irc_server_one}/{nick_two}' type='chat'><body>re in private</body></message>"),
                     # Message is received with a server-wide JID
775
                     partial(expect_stanza, "/message[@from='{lower_nick_one}%{irc_server_one}'][@to='{jid_two}/{resource_one}'][@type='chat']/body[text()='re in private']"),
776 777

                     # Respond to the message, to the server-wide JID
778
                     partial(send_stanza, "<message from='{jid_two}/{resource_one}' to='{lower_nick_one}%{irc_server_one}' type='chat'><body>re</body></message>"),
779 780
                     # The response is received from the in-room JID
                     partial(expect_stanza, "/message[@from='%{irc_server_one}/{nick_two}'][@to='{jid_one}/{resource_one}'][@type='chat']/body[text()='re']"),
781 782 783 784 785 786 787 788

                     # Now we leave the room, to check if the subsequent private messages are still received properly
                     partial(send_stanza,
                     "<presence from='{jid_one}/{resource_one}' to='%{irc_server_one}/{nick_one}' type='unavailable' />"),
                     partial(expect_stanza,
                     "/presence[@type='unavailable']/muc_user:x/muc_user:status[@code='110']"),

                     # The private messages from this nick should now come (again) from the server-wide JID
789
                     partial(send_stanza, "<message from='{jid_two}/{resource_one}' to='{lower_nick_one}%{irc_server_one}' type='chat'><body>hihihoho</body></message>"),
790
                     partial(expect_stanza,
791
                     "/message[@from='{lower_nick_two}%{irc_server_one}'][@to='{jid_one}/{resource_one}']"),
792
                 ]
793 794 795 796 797 798 799 800 801 802 803 804 805 806 807
                 ),
                Scenario("encoded_channel_join",
                 [
                     handshake_sequence(),
                     partial(send_stanza,
                             "<presence from='{jid_one}/{resource_one}' to='#biboumi\\40louiz.org\\3a80%{irc_server_one}/{nick_one}' />"),
                     connection_sequence("irc.localhost", '{jid_one}/{resource_one}'),
                     partial(expect_stanza,
                             "/message/body[text()='Mode #biboumi@louiz.org:80 [+nt] by {irc_host_one}']"),
                     partial(expect_stanza,
                             ("/presence[@to='{jid_one}/{resource_one}'][@from='#biboumi\\40louiz.org\\3a80%{irc_server_one}/{nick_one}']/muc_user:x/muc_user:item[@affiliation='admin'][@role='moderator']",
                             "/presence/muc_user:x/muc_user:status[@code='110']")
                             ),
                     partial(expect_stanza, "/message[@from='#biboumi\\40louiz.org\\3a80%{irc_server_one}'][@type='groupchat']/subject[not(text())]"),
                 ]),
808 809 810 811 812 813 814 815 816 817 818 819 820
                Scenario("self_ping_on_real_channel",
                 [
                     handshake_sequence(),
                     partial(send_stanza,
                             "<presence from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}/{nick_one}' />"),
                     connection_sequence("irc.localhost", '{jid_one}/{resource_one}'),
                     partial(expect_stanza,
                             "/message/body[text()='Mode #foo [+nt] by {irc_host_one}']"),
                     partial(expect_stanza,
                             ("/presence[@to='{jid_one}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_one}']/muc_user:x/muc_user:item[@affiliation='admin'][@role='moderator']",
                              "/presence/muc_user:x/muc_user:status[@code='110']")
                             ),
                     partial(expect_stanza, "/message[@from='#foo%{irc_server_one}'][@type='groupchat']/subject[not(text())]"),
821

822 823 824 825 826
                     # 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,
827
                             "/iq[@from='{lower_nick_one}%{irc_server_one}'][@type='get'][@to='{jid_one}/{resource_one}'][@id='gnip_tsrif']"),
828 829
                     # Respond to the request
                     partial(send_stanza,
830
                             "<iq type='result' to='{lower_nick_one}%{irc_server_one}' id='gnip_tsrif' from='{jid_one}/{resource_one}'/>"),
831 832
                     partial(expect_stanza,
                             "/iq[@from='#foo%{irc_server_one}/{nick_one}'][@type='result'][@to='{jid_one}/{resource_one}'][@id='first_ping']"),
833 834 835 836 837 838 839 840 841 842 843 844 845 846 847

                     # Now join the same room, from the same bare JID, behind the same nick
                     partial(send_stanza,
                             "<presence from='{jid_one}/{resource_two}' to='#foo%{irc_server_one}/{nick_one}' />"),
                     partial(expect_stanza,
                             ("/presence[@to='{jid_one}/{resource_two}'][@from='#foo%{irc_server_one}/{nick_one}']/muc_user:x/muc_user:item[@affiliation='admin'][@role='moderator']",
                              "/presence/muc_user:x/muc_user:status[@code='110']")
                             ),
                     partial(expect_stanza, "/message[@from='#foo%{irc_server_one}'][@type='groupchat'][@to='{jid_one}/{resource_two}']/subject[not(text())]"),

                     # And re-send a self ping
                     partial(send_stanza,
                             "<iq type='get' from='{jid_one}/{resource_one}' id='second_ping' to='#foo%{irc_server_one}/{nick_one}'><ping xmlns='urn:xmpp:ping' /></iq>"),
                     # We receive our own ping request. Note that we don't know the to value, it could be one of our two resources.
                     partial(expect_stanza,
848
                             "/iq[@from='{lower_nick_one}%{irc_server_one}'][@type='get'][@to][@id='gnip_dnoces']",
849 850 851
                             after = partial(save_value, "to", partial(extract_attribute, "/iq", "to"))),
                     # Respond to the request, using the extracted 'to' value as our 'from'
                     partial(send_stanza,
852
                             "<iq type='result' to='{lower_nick_one}%{irc_server_one}' id='gnip_dnoces' from='{to}'/>"),
853 854 855 856 857 858
                     partial(expect_stanza,
                             "/iq[@from='#foo%{irc_server_one}/{nick_one}'][@type='result'][@to='{jid_one}/{resource_one}'][@id='second_ping']"),
                     ## And re-do exactly the same thing, just change the resource initiating the self ping
                     partial(send_stanza,
                             "<iq type='get' from='{jid_one}/{resource_two}' id='third_ping' to='#foo%{irc_server_one}/{nick_one}'><ping xmlns='urn:xmpp:ping' /></iq>"),
                     partial(expect_stanza,
859
                             "/iq[@from='{lower_nick_one}%{irc_server_one}'][@type='get'][@to][@id='gnip_driht']",
860 861
                             after = partial(save_value, "to", partial(extract_attribute, "/iq", "to"))),
                     partial(send_stanza,
862
                             "<iq type='result' to='{lower_nick_one}%{irc_server_one}' id='gnip_driht' from='{to}'/>"),
863 864 865
                     partial(expect_stanza,
                             "/iq[@from='#foo%{irc_server_one}/{nick_one}'][@type='result'][@to='{jid_one}/{resource_two}'][@id='third_ping']"),

866
                 ]),
louiz’'s avatar
louiz’ committed
867 868 869 870 871 872 873 874 875 876 877 878 879 880 881
                Scenario("simple_kick",
                [
                     handshake_sequence(),
                     # First user joins
                     partial(send_stanza,
                     "<presence from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}/{nick_one}' />"),
                     connection_sequence("irc.localhost", '{jid_one}/{resource_one}'),
                     partial(expect_stanza, "/message"),
                     partial(expect_stanza, "/presence/muc_user:x/muc_user:status[@code='110']"),
                     partial(expect_stanza, "/message[@type='groupchat']/subject"),

                     # Second user joins
                     partial(send_stanza,
                     "<presence from='{jid_two}/{resource_one}' to='#foo%{irc_server_one}/{nick_two}' />"),
                     connection_sequence("irc.localhost", '{jid_two}/{resource_one}'),
882 883 884 885 886 887
                     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",),
                         ]),
louiz’'s avatar
louiz’ committed
888 889 890 891 892 893

                     # 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"),
894 895
                     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}']",
louiz’'s avatar
louiz’ committed
896 897 898
                              "/presence/muc_user:x/muc_user:item/muc_user:reason[text()='reported']",
                              "/presence/muc_user:x/muc_user:status[@code='307']",
                              "/presence/muc_user:x/muc_user:status[@code='110']"
899 900
                             ),
                             ("/presence[@type='unavailable'][@to='{jid_one}/{resource_one}']/muc_user:x/muc_user:item[@role='none']/muc_user:actor[@nick='{nick_one}']",
louiz’'s avatar
louiz’ committed
901 902 903
                              "/presence/muc_user:x/muc_user:item/muc_user:reason[text()='reported']",
                              "/presence/muc_user:x/muc_user:status[@code='307']",
                              ),
904 905
                             ("/iq[@id='kick1'][@type='result']",),
                     ]),
louiz’'s avatar
louiz’ committed
906
                ]),
907 908 909 910 911 912 913 914 915 916 917
                Scenario("multisession_kick",
                 [
                     handshake_sequence(),
                     # First user joins
                     partial(send_stanza,
                             "<presence from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}/{nick_one}' />"),
                     connection_sequence("irc.localhost", '{jid_one}/{resource_one}'),
                     partial(expect_stanza, "/message"),
                     partial(expect_stanza, "/presence/muc_user:x/muc_user:status[@code='110']"),
                     partial(expect_stanza, "/message[@type='groupchat']/subject"),

918
                     # Second user joins, fprom two resources
919 920 921
                     partial(send_stanza,
                             "<presence from='{jid_two}/{resource_one}' to='#foo%{irc_server_one}/{nick_two}' />"),
                     connection_sequence("irc.localhost", '{jid_two}/{resource_one}'),
922 923 924 925 926 927
                     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",),
                     ]),
928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943

                     partial(send_stanza,
                             "<presence from='{jid_two}/{resource_two}' to='#foo%{irc_server_one}/{nick_two}' />"),
                     partial(expect_stanza,
                             "/presence[@to='{jid_two}/{resource_two}'][@from='#foo%{irc_server_one}/{nick_one}']"),
                     partial(expect_stanza,
                             ("/presence[@to='{jid_two}/{resource_two}'][@from='#foo%{irc_server_one}/{nick_two}']",
                              "/presence/muc_user:x/muc_user:status[@code='110']")
                             ),
                     partial(expect_stanza, "/message[@from='#foo%{irc_server_one}'][@type='groupchat'][@to='{jid_two}/{resource_two}']/subject[not(text())]"),

                     # Moderator kicks participant
                     partial(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"),
944 945
                     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}']",
946 947 948
                              "/presence/muc_user:x/muc_user:item/muc_user:reason[text()='reported']",
                              "/presence/muc_user:x/muc_user:status[@code='307']",
                              "/presence/muc_user:x/muc_user:status[@code='110']"
949 950
                              ),
                             ("/presence[@type='unavailable'][@to='{jid_two}/{resource_two}']/muc_user:x/muc_user:item[@role='none']/muc_user:actor[@nick='{nick_one}']",
951 952 953
                              "/presence/muc_user:x/muc_user:item/muc_user:reason[text()='reported']",
                              "/presence/muc_user:x/muc_user:status[@code='307']",
                              "/presence/muc_user:x/muc_user:status[@code='110']"
954
                              ),
955 956 957 958
                             ("/presence[@type='unavailable']/muc_user:x/muc_user:item[@role='none']/muc_user:actor[@nick='{nick_one}']",
                              "/presence/muc_user:x/muc_user:item/muc_user:reason[text()='reported']",
                              "/presence/muc_user:x/muc_user:status[@code='307']",
                              ),
959 960
                             ("/iq[@id='kick1'][@type='result']",),
                             ]),
961
                 ]),
962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980
                Scenario("self_version",
                 [
                     handshake_sequence(),
                     partial(send_stanza,
                             "<presence from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}/{nick_one}' />"),
                     connection_sequence("irc.localhost", '{jid_one}/{resource_one}'),
                     partial(expect_stanza,
                             "/message/body[text()='Mode #foo [+nt] by {irc_host_one}']"),
                     partial(expect_stanza,
                             ("/presence[@to='{jid_one}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_one}']/muc_user:x/muc_user:item[@affiliation='admin'][@role='moderator']",
                              "/presence/muc_user:x/muc_user:status[@code='110']")
                             ),
                     partial(expect_stanza, "/message[@from='#foo%{irc_server_one}'][@type='groupchat']/subject[not(text())]"),

                     # Send a version request to ourself
                     partial(send_stanza,
                             "<iq type='get' from='{jid_one}/{resource_one}' id='first_version' to='#foo%{irc_server_one}/{nick_one}'><query xmlns='jabber:iq:version' /></iq>"),
                     # We receive our own request,
                     partial(expect_stanza,
981
                             "/iq[@from='{lower_nick_one}%{irc_server_one}'][@type='get'][@to='{jid_one}/{resource_one}']",
982 983 984
                             after = partial(save_value, "id", partial(extract_attribute, "/iq", 'id'))),
                     # Respond to the request
                     partial(send_stanza,
985
                             "<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>"),
986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002
                     partial(expect_stanza,
                             "/iq[@from='#foo%{irc_server_one}/{nick_one}'][@type='result'][@to='{jid_one}/{resource_one}'][@id='first_version']/version:query/version:name[text()='e2e test (through the biboumi gateway) 1.0 Fedora']"),

                     # Now join the same room, from the same bare JID, behind the same nick
                     partial(send_stanza,
                             "<presence from='{jid_one}/{resource_two}' to='#foo%{irc_server_one}/{nick_one}' />"),
                     partial(expect_stanza,
                             ("/presence[@to='{jid_one}/{resource_two}'][@from='#foo%{irc_server_one}/{nick_one}']/muc_user:x/muc_user:item[@affiliation='admin'][@role='moderator']",
                              "/presence/muc_user:x/muc_user:status[@code='110']")
                             ),
                     partial(expect_stanza, "/message[@from='#foo%{irc_server_one}'][@type='groupchat'][@to='{jid_one}/{resource_two}']/subject[not(text())]"),

                     # And re-send a self ping
                     partial(send_stanza,
                             "<iq type='get' from='{jid_one}/{resource_two}' id='second_version' to='#foo%{irc_server_one}/{nick_one}'><query xmlns='jabber:iq:version' /></iq>"),
                     # We receive our own request. Note that we don't know the to value, it could be one of our two resources.
                     partial(expect_stanza,
1003
                             "/iq[@from='{lower_nick_one}%{irc_server_one}'][@type='get'][@to]",
1004 1005 1006 1007
                             after = (partial(save_value, "to", partial(extract_attribute, "/iq", "to")),
                                      partial(save_value, "id", partial(extract_attribute, "/iq", "id")))),
                     # Respond to the request, using the extracted 'to' value as our 'from'
                     partial(send_stanza,
1008
                             "<iq type='result' to='{lower_nick_one}%{irc_server_one}' id='{id}' from='{to}'><query xmlns='jabber:iq:version'><name>e2e test</name><version>1.0</version><os>Fedora</os></query></iq>"),
1009 1010 1011 1012 1013 1014 1015 1016
                     partial(expect_stanza,
                             "/iq[@from='#foo%{irc_server_one}/{nick_one}'][@type='result'][@to='{jid_one}/{resource_two}'][@id='second_version']"),

                     # And do exactly the same thing, but initiated by the other resource
                     partial(send_stanza,
                             "<iq type='get' from='{jid_one}/{resource_one}' id='second_version' to='#foo%{irc_server_one}/{nick_one}'><query xmlns='jabber:iq:version' /></iq>"),
                     # We receive our own request. Note that we don't know the to value, it could be one of our two resources.
                     partial(expect_stanza,
1017
                             "/iq[@from='{lower_nick_one}%{irc_server_one}'][@type='get'][@to]",
1018 1019 1020 1021
                             after = (partial(save_value, "to", partial(extract_attribute, "/iq", "to")),
                                      partial(save_value, "id", partial(extract_attribute, "/iq", "id")))),
                     # Respond to the request, using the extracted 'to' value as our 'from'
                     partial(send_stanza,
1022
                             "<iq type='result' to='{lower_nick_one}%{irc_server_one}' id='{id}' from='{to}'><query xmlns='jabber:iq:version'><name>e2e test</name><version>1.0</version><os>Fedora</os></query></iq>"),
1023 1024 1025
                     partial(expect_stanza,
                             "/iq[@from='#foo%{irc_server_one}/{nick_one}'][@type='result'][@to='{jid_one}/{resource_one}'][@id='second_version']"),
                 ]),
1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055
                Scenario("version_on_global_nick",
                [
                    partial(log_message, "Joining the channel"),
                    handshake_sequence(),
                    partial(send_stanza,
                            "<presence from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}/{nick_one}' />"),
                    connection_sequence("irc.localhost", '{jid_one}/{resource_one}'),
                    partial(expect_stanza,
                            "/message/body[text()='Mode #foo [+nt] by {irc_host_one}']"),
                    partial(expect_stanza,
                            ("/presence[@to='{jid_one}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_one}']/muc_user:x/muc_user:item[@affiliation='admin'][@role='moderator']",
                             "/presence/muc_user:x/muc_user:status[@code='110']")
                            ),
                    partial(expect_stanza, "/message[@from='#foo%{irc_server_one}'][@type='groupchat']/subject[not(text())]"),

                    partial(log_message, "Send a version request to ourself"),
                     partial(send_stanza,
                             "<iq type='get' from='{jid_one}/{resource_one}' id='first_version' to='{lower_nick_one}%{irc_server_one}'><query xmlns='jabber:iq:version' /></iq>"),

                     partial(log_message, "Receive our own request"),
                     partial(expect_stanza,
                             "/iq[@from='{lower_nick_one}%{irc_server_one}'][@type='get'][@to='{jid_one}/{resource_one}']",
                             after = partial(save_value, "id", partial(extract_attribute, "/iq", 'id'))),
                     partial(log_message, "Respond to the request"),
                     partial(send_stanza,
                             "<iq type='result' to='{lower_nick_one}%{irc_server_one}' id='{id}' from='{jid_one}/{resource_one}'><query xmlns='jabber:iq:version'><name>e2e test</name><version>1.0</version><os>Fedora</os></query></iq>"),
                     partial(expect_stanza,
                             "/iq[@from='{lower_nick_one}%{irc_server_one}'][@type='result'][@to='{jid_one}/{resource_one}'][@id='first_version']/version:query/version:name[text()='e2e test (through the biboumi gateway) 1.0 Fedora']"),

                ]),
louiz’'s avatar
louiz’ committed
1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072
                Scenario("self_invite",
                [
                    handshake_sequence(),
                    partial(send_stanza,
                            "<presence from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}/{nick_one}' />"),
                    connection_sequence("irc.localhost", '{jid_one}/{resource_one}'),
                    partial(expect_stanza,
                            "/message/body[text()='Mode #foo [+nt] by {irc_host_one}']"),
                    partial(expect_stanza,
                            ("/presence[@to='{jid_one}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_one}']/muc_user:x/muc_user:item[@affiliation='admin'][@role='moderator']",
                             "/presence/muc_user:x/muc_user:status[@code='110']")
                            ),
                    partial(expect_stanza, "/message[@from='#foo%{irc_server_one}'][@type='groupchat']/subject[not(text())]"),
                    partial(send_stanza,
                            "<message from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}'><x xmlns='http://jabber.org/protocol/muc#user'><invite to='{nick_one}'/></x></message>"),
                    partial(expect_stanza,
                            "/message/body[text()='{nick_one} is already on channel #foo']")
1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105
                ]),
                Scenario("client_error",
                [
                    handshake_sequence(),
                    # First resource
                    partial(send_stanza,
                            "<presence from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}/{nick_one}' />"),
                    connection_sequence("irc.localhost", '{jid_one}/{resource_one}'),
                    partial(expect_stanza,
                            "/message/body[text()='Mode #foo [+nt] by {irc_host_one}']"),
                    partial(expect_stanza,
                            ("/presence[@to='{jid_one}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_one}']/muc_user:x/muc_user:item[@affiliation='admin'][@role='moderator']",
                             "/presence/muc_user:x/muc_user:status[@code='110']")
                            ),
                    partial(expect_stanza, "/message[@from='#foo%{irc_server_one}'][@type='groupchat']/subject[not(text())]"),

                    # Second resource, same channel
                    partial(send_stanza,
                            "<presence from='{jid_one}/{resource_two}' to='#foo%{irc_server_one}/{nick_one}' />"),
                    partial(expect_stanza,
                            ("/presence[@to='{jid_one}/{resource_two}'][@from='#foo%{irc_server_one}/{nick_one}']/muc_user:x/muc_user:item[@affiliation='admin'][@role='moderator']",
                             "/presence/muc_user:x/muc_user:status[@code='110']")
                            ),
                    partial(expect_stanza, "/message[@from='#foo%{irc_server_one}'][@type='groupchat'][@to='{jid_one}/{resource_two}']/subject[not(text())]"),

                    # Now the first resource has an error
                    partial(send_stanza,
                            "<message from='{jid_one}/{resource_one}' to='#foo%%{irc_server_one}/{nick_one}' type='error'><error type='cancel'><recipient-unavailable xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/></error></message>"),
                    # Receive a leave only to the leaving resource
                    partial(expect_stanza,
                            ("/presence[@type='unavailable'][@from='#foo%{irc_server_one}/{nick_one}'][@to='{jid_one}/{resource_one}']/muc_user:x/muc_user:status[@code='110']",
                             "/presence/status[text()='Biboumi note: 1 resources are still in this channel.']")
                            ),
1106 1107 1108 1109 1110 1111 1112 1113 1114 1115
                ]),
                Scenario("simple_mam",
                [
                    handshake_sequence(),
                    partial(send_stanza,
                            "<presence from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}/{nick_one}' />"),
                    connection_sequence("irc.localhost", '{jid_one}/{resource_one}'),
                    partial(expect_stanza,
                            "/message/body[text()='Mode #foo [+nt] by {irc_host_one}']"),
                    partial(expect_stanza,
1116
                            ("/presence[@to='{jid_one}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_one}']/muc_user:x/muc_user:item[@affiliation='admin'][@role='moderator']",
1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140
                             "/presence/muc_user:x/muc_user:status[@code='110']")
                            ),
                    partial(expect_stanza, "/message[@from='#foo%{irc_server_one}'][@type='groupchat']/subject[not(text())]"),

                    # Send two channel messages
                    partial(send_stanza, "<message from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}' type='groupchat'><body>coucou</body></message>"),
                    partial(expect_stanza, "/message[@from='#foo%{irc_server_one}/{nick_one}'][@to='{jid_one}/{resource_one}'][@type='groupchat']/body[text()='coucou']"),

                    partial(send_stanza, "<message from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}' type='groupchat'><body>coucou 2</body></message>"),
                    partial(expect_stanza, "/message[@from='#foo%{irc_server_one}/{nick_one}'][@to='{jid_one}/{resource_one}'][@type='groupchat']/body[text()='coucou 2']"),

                    # Retrieve the complete archive
                    partial(send_stanza, "<iq to='#foo%{irc_server_one}' from='{jid_one}/{resource_one}' type='set' id='id1'><query xmlns='urn:xmpp:mam:1' queryid='qid1' /></iq>"),

                    partial(expect_stanza,
                            ("/message/mam:result[@queryid='qid1']/forward:forwarded/delay:delay",
                            "/message/mam:result[@queryid='qid1']/forward:forwarded/client:message[@from='#foo%{irc_server_one}/{nick_one}'][@type='groupchat']/client:body[text()='coucou']")
                            ),
                    partial(expect_stanza,
                            ("/message/mam:result[@queryid='qid1']/forward:forwarded/delay:delay",
                            "/message/mam:result[@queryid='qid1']/forward:forwarded/client:message[@from='#foo%{irc_server_one}/{nick_one}'][@type='groupchat']/client:body[text()='coucou 2']")
                            ),

                    partial(expect_stanza,
1141
                            "/iq[@type='result'][@id='id1'][@from='#foo%{irc_server_one}'][@to='{jid_one}/{resource_one}']"),
1142

1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166
                    # Retrieve an empty archive by specifying an early “end” date
                    partial(send_stanza, """<iq to='#foo%{irc_server_one}' from='{jid_one}/{resource_one}' type='set' id='id2'>
                    <query xmlns='urn:xmpp:mam:1' queryid='qid2'>
                    <x xmlns='jabber:x:data' type='submit'>
                    <field var='FORM_TYPE' type='hidden'> <value>urn:xmpp:mam:1</value></field>
                    <field var='end'><value>2000-06-07T00:00:00Z</value></field>
                    </x>
                    </query></iq>"""),

                    partial(expect_stanza,
                            "/iq[@type='result'][@id='id2'][@from='#foo%{irc_server_one}'][@to='{jid_one}/{resource_one}']"),

                    # Retrieve an empty archive by specifying a late “start” date
                    # (note that this test will break in ~1000 years)
                    partial(send_stanza, """<iq to='#foo%{irc_server_one}' from='{jid_one}/{resource_one}' type='set' id='id3'>
                    <query xmlns='urn:xmpp:mam:1' queryid='qid3'>
                    <x xmlns='jabber:x:data' type='submit'>
                    <field var='FORM_TYPE' type='hidden'> <value>urn:xmpp:mam:1</value></field>
                    <field var='start'><value>3016-06-07T00:00:00Z</value></field>
                    </x>
                    </query></iq>"""),

                    partial(expect_stanza,
                            "/iq[@type='result'][@id='id3'][@from='#foo%{irc_server_one}'][@to='{jid_one}/{resource_one}']"),
1167
                ]),
1168 1169 1170 1171 1172 1173 1174 1175 1176 1177
        Scenario("mam_on_fixed_server",
                 [
                     handshake_sequence(),

                     partial(send_stanza,
                             "<presence from='{jid_one}/{resource_one}' to='#foo@{biboumi_host}/{nick_one}' />"),
                     connection_sequence("irc.localhost", '{jid_one}/{resource_one}'),
                     partial(expect_stanza,
                             "/message/body[text()='Mode #foo [+nt] by {irc_host_one}']"),
                     partial(expect_stanza,
1178
                             ("/presence[@to='{jid_one}/{resource_one}'][@from='#foo@{biboumi_host}/{nick_one}']/muc_user:x/muc_user:item[@affiliation='admin'][@role='moderator']",
1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269
                              "/presence/muc_user:x/muc_user:status[@code='110']")
                             ),
                     partial(expect_stanza, "/message[@from='#foo@{biboumi_host}'][@type='groupchat']/subject[not(text())]"),

                     partial(send_stanza, "<message from='{jid_one}/{resource_one}' to='#foo@{biboumi_host}' type='groupchat'><body>coucou</body></message>"),
                     partial(expect_stanza, "/message[@from='#foo@{biboumi_host}/{nick_one}'][@to='{jid_one}/{resource_one}'][@type='groupchat']/body[text()='coucou']"),

                     partial(send_stanza, "<message from='{jid_one}/{resource_one}' to='#foo@{biboumi_host}' type='groupchat'><body>coucou 2</body></message>"),
                     partial(expect_stanza, "/message[@from='#foo@{biboumi_host}/{nick_one}'][@to='{jid_one}/{resource_one}'][@type='groupchat']/body[text()='coucou 2']"),

                     # Retrieve the complete archive
                     partial(send_stanza, "<iq to='#foo@{biboumi_host}' from='{jid_one}/{resource_one}' type='set' id='id1'><query xmlns='urn:xmpp:mam:1' queryid='qid1' /></iq>"),

                     partial(expect_stanza,
                             ("/message/mam:result[@queryid='qid1']/forward:forwarded/delay:delay",
                              "/message/mam:result[@queryid='qid1']/forward:forwarded/client:message[@from='#foo@{biboumi_host}/{nick_one}'][@type='groupchat']/client:body[text()='coucou']")
                             ),
                     partial(expect_stanza,
                             ("/message/mam:result[@queryid='qid1']/forward:forwarded/delay:delay",
                              "/message/mam:result[@queryid='qid1']/forward:forwarded/client:message[@from='#foo@{biboumi_host}/{nick_one}'][@type='groupchat']/client:body[text()='coucou 2']")
                             ),
                 ], conf="fixed_server"),
        Scenario("channel_history_on_fixed_server",
                 [
                     handshake_sequence(),
                     # First user join
                     partial(send_stanza,
                             "<presence from='{jid_one}/{resource_one}' to='#foo@{biboumi_host}/{nick_one}' />"),
                     connection_sequence("irc.localhost", '{jid_one}/{resource_one}'),
                     partial(expect_stanza,
                             "/message/body[text()='Mode #foo [+nt] by {irc_host_one}']"),
                     partial(expect_stanza,
                             ("/presence[@to='{jid_one}/{resource_one}'][@from='#foo@{biboumi_host}/{nick_one}']/muc_user:x/muc_user:item[@affiliation='admin'][@jid='~nick@localhost'][@role='moderator']",
                              "/presence/muc_user:x/muc_user:status[@code='110']")
                             ),
                     partial(expect_stanza, "/message[@from='#foo@{biboumi_host}'][@type='groupchat']/subject[not(text())]"),

                     # Send one channel message
                     partial(send_stanza, "<message from='{jid_one}/{resource_one}' to='#foo@{biboumi_host}' type='groupchat'><body>coucou</body></message>"),
                     partial(expect_stanza, "/message[@from='#foo@{biboumi_host}/{nick_one}'][@to='{jid_one}/{resource_one}'][@type='groupchat']/body[text()='coucou']"),

                     # Second user joins
                     partial(send_stanza,
                             "<presence from='{jid_one}/{resource_two}' to='#foo@{biboumi_host}/{nick_one}' />"),
                     # connection_sequence("irc.localhost", '{jid_one}/{resource_two}'),
                     # partial(expect_stanza,
                     #         "/message/body[text()='Mode #foo [+nt] by {irc_host_one}']"),
                     partial(expect_stanza,