diff --git a/irctest/server_tests/test_resume.py b/irctest/server_tests/test_resume.py index 907b1b4..99bf9e6 100644 --- a/irctest/server_tests/test_resume.py +++ b/irctest/server_tests/test_resume.py @@ -4,6 +4,9 @@ from irctest import cases +from irctest.numerics import RPL_AWAY + +ANCIENT_TIMESTAMP = '2006-01-02T15:04:05.999Z' class ResumeTestCase(cases.BaseServerTestCase): @@ -19,7 +22,7 @@ class ResumeTestCase(cases.BaseServerTestCase): self.connectClient('bar', capabilities=['batch', 'draft/labeled-response', 'server-time']) ms = self.getMessages(1) - welcome = self.connectClient('baz', capabilities=['batch', 'draft/labeled-response', 'server-time', 'draft/resume-0.3']) + welcome = self.connectClient('baz', capabilities=['batch', 'draft/labeled-response', 'server-time', 'draft/resume-0.4']) resume_messages = [m for m in welcome if m.command == 'RESUME'] self.assertEqual(len(resume_messages), 1) self.assertEqual(resume_messages[0].params[0], 'TOKEN') @@ -43,14 +46,14 @@ class ResumeTestCase(cases.BaseServerTestCase): bad_token = 'a' * len(token) self.addClient() self.sendLine(3, 'CAP LS') - self.sendLine(3, 'CAP REQ :batch draft/labeled-response server-time draft/resume-0.3') + self.sendLine(3, 'CAP REQ :batch draft/labeled-response server-time draft/resume-0.4') self.sendLine(3, 'NICK tempnick') self.sendLine(3, 'USER tempuser 0 * tempuser') - self.sendLine(3, 'RESUME ' + bad_token + ' 2006-01-02T15:04:05.999Z') + self.sendLine(3, ' '.join(('RESUME', bad_token, ANCIENT_TIMESTAMP))) # resume with a bad token MUST fail ms = self.getMessages(3) - resume_err_messages = [m for m in ms if m.command == 'RESUME' and m.params[0] == 'ERR'] + resume_err_messages = [m for m in ms if m.command == 'FAIL' and m.params[:2] == ['RESUME', 'INVALID_TOKEN']] self.assertEqual(len(resume_err_messages), 1) # however, registration should proceed with the alternative nick self.sendLine(3, 'CAP END') @@ -59,20 +62,22 @@ class ResumeTestCase(cases.BaseServerTestCase): self.addClient() self.sendLine(4, 'CAP LS') - self.sendLine(4, 'CAP REQ :batch draft/labeled-response server-time draft/resume-0.3') + self.sendLine(4, 'CAP REQ :batch draft/labeled-response server-time draft/resume-0.4') self.sendLine(4, 'NICK tempnick_') self.sendLine(4, 'USER tempuser 0 * tempuser') # resume with a timestamp in the distant past - self.sendLine(4, 'RESUME ' + token + ' 2006-01-02T15:04:05.999Z') + self.sendLine(4, ' '.join(('RESUME', token, ANCIENT_TIMESTAMP))) # successful resume does not require CAP END: # https://github.com/ircv3/ircv3-specifications/pull/306/files#r255318883 ms = self.getMessages(4) + # now, do a valid resume with the correct token resume_messages = [m for m in ms if m.command == 'RESUME'] self.assertEqual(len(resume_messages), 2) self.assertEqual(resume_messages[0].params[0], 'TOKEN') new_token = resume_messages[0].params[1] self.assertNotEqual(token, new_token, 'should receive a new, strong resume token; instead got ' + new_token) + # success message self.assertMessageEqual(resume_messages[1], command='RESUME', params=['SUCCESS', 'baz']) # test replay of messages @@ -86,9 +91,77 @@ class ResumeTestCase(cases.BaseServerTestCase): # hence will typically match by accident self.assertEqual(privmsgs[0].tags.get('time'), channelMsgTime) + # legacy client should receive a QUIT and a JOIN + quit, join = [m for m in self.getMessages(1) if m.command in ('QUIT', 'JOIN')] + self.assertEqual(quit.command, 'QUIT') + self.assertTrue(quit.prefix.startswith('baz')) + self.assertMessageEqual(join, command='JOIN', params=['#xyz']) + self.assertTrue(join.prefix.startswith('baz')) + # original client should have been disconnected self.assertDisconnected(2) # new client should be receiving PRIVMSG sent to baz self.sendLine(1, 'PRIVMSG baz :hello again') self.getMessages(1) self.assertMessageEqual(self.getMessage(4), command='PRIVMSG', params=['baz', 'hello again']) + + # test chain-resuming (resuming the resumed connection, using the new token) + self.addClient() + self.sendLine(5, 'CAP LS') + self.sendLine(5, 'CAP REQ :batch draft/labeled-response server-time draft/resume-0.4') + self.sendLine(5, 'NICK tempnick_') + self.sendLine(5, 'USER tempuser 0 * tempuser') + self.sendLine(5, 'RESUME ' + new_token) + ms = self.getMessages(5) + + resume_messages = [m for m in ms if m.command == 'RESUME'] + self.assertEqual(len(resume_messages), 2) + self.assertEqual(resume_messages[0].params[0], 'TOKEN') + new_new_token = resume_messages[0].params[1] + self.assertNotEqual(token, new_new_token, 'should receive a new, strong resume token; instead got ' + new_new_token) + self.assertNotEqual(new_token, new_new_token, 'should receive a new, strong resume token; instead got ' + new_new_token) + # success message + self.assertMessageEqual(resume_messages[1], command='RESUME', params=['SUCCESS', 'baz']) + + + @cases.SpecificationSelector.requiredBySpecification('Oragono') + def testBRB(self): + self.connectClient('bar', capabilities=['batch', 'draft/labeled-response', 'message-tags', 'server-time']) + ms = self.getMessages(1) + self.joinChannel(1, '#xyz') + + welcome = self.connectClient('baz', capabilities=['batch', 'draft/labeled-response', 'server-time', 'draft/resume-0.4']) + resume_messages = [m for m in welcome if m.command == 'RESUME'] + self.assertEqual(len(resume_messages), 1) + self.assertEqual(resume_messages[0].params[0], 'TOKEN') + token = resume_messages[0].params[1] + self.joinChannel(2, '#xyz') + + self.getMessages(1) + self.sendLine(2, 'BRB :software upgrade') + # should receive, e.g., `BRB 210` (number of seconds) + ms = [m for m in self.getMessages(2) if m.command == 'BRB'] + self.assertEqual(len(ms), 1) + self.assertGreater(int(ms[0].params[0]), 1) + # BRB disconnects you + self.assertDisconnected(2) + # without sending a QUIT line to friends + self.assertEqual(self.getMessages(1), []) + + self.sendLine(1, 'PRIVMSG baz :hey there') + # BRB message should be sent as an away message + self.assertMessageEqual(self.getMessage(1), command=RPL_AWAY, params=['bar', 'baz', 'software upgrade']) + + self.addClient(3) + self.sendLine(3, 'CAP REQ :batch account-tag message-tags draft/resume-0.4') + self.sendLine(3, ' '.join(('RESUME', token, ANCIENT_TIMESTAMP))) + ms = self.getMessages(3) + + resume_messages = [m for m in ms if m.command == 'RESUME'] + self.assertEqual(len(resume_messages), 2) + self.assertEqual(resume_messages[0].params[0], 'TOKEN') + self.assertMessageEqual(resume_messages[1], command='RESUME', params=['SUCCESS', 'baz']) + + privmsgs = [m for m in ms if m.command == 'PRIVMSG' and m.prefix.startswith('bar')] + self.assertEqual(len(privmsgs), 1) + self.assertMessageEqual(privmsgs[0], params=['baz', 'hey there'])