Merge pull request #21 from slingamn/wip

more tests
This commit is contained in:
Daniel Oaks 2019-02-15 13:32:56 +10:00 committed by GitHub
commit 2825454dfe
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 184 additions and 24 deletions

View File

@ -1,5 +1,4 @@
import os import os
import time
import subprocess import subprocess
from irctest.basecontrollers import NotImplementedByController from irctest.basecontrollers import NotImplementedByController
@ -52,6 +51,7 @@ accounts:
enabled-callbacks: enabled-callbacks:
- none # no verification needed, will instantly register successfully - none # no verification needed, will instantly register successfully
allow-multiple-per-connection: true allow-multiple-per-connection: true
bcrypt-cost: 4
authentication-enabled: true authentication-enabled: true

View File

@ -3,8 +3,6 @@
""" """
from irctest import cases from irctest import cases
from irctest.client_mock import NoMessageException
from irctest.basecontrollers import NotImplementedByController
class AccountTagTestCase(cases.BaseServerTestCase, cases.OptionalityHelper): class AccountTagTestCase(cases.BaseServerTestCase, cases.OptionalityHelper):
def connectRegisteredClient(self, nick): def connectRegisteredClient(self, nick):

View File

@ -1,5 +1,4 @@
from irctest import cases from irctest import cases
from irctest.irc_utils.message_parser import Message
class CapTestCase(cases.BaseServerTestCase): class CapTestCase(cases.BaseServerTestCase):
@cases.SpecificationSelector.requiredBySpecification('IRCv3.1') @cases.SpecificationSelector.requiredBySpecification('IRCv3.1')
@ -96,3 +95,33 @@ class CapTestCase(cases.BaseServerTestCase):
subcommand='ACK', subparams=['multi-prefix'], subcommand='ACK', subparams=['multi-prefix'],
fail_msg='Expected “CAP ACK :multi-prefix” after ' fail_msg='Expected “CAP ACK :multi-prefix” after '
'sending “CAP REQ :multi-prefix”, but got {msg}.') 'sending “CAP REQ :multi-prefix”, but got {msg}.')
@cases.SpecificationSelector.requiredBySpecification('Oragono')
def testCapRemovalByClient(self):
"""Test CAP LIST and removal of caps via CAP REQ :-tagname."""
self.addClient(1)
self.sendLine(1, 'CAP LS 302')
self.assertIn('multi-prefix', self.getCapLs(1))
self.sendLine(1, 'CAP REQ :echo-message server-time')
self.sendLine(1, 'nick bar')
self.sendLine(1, 'user user 0 * realname')
self.sendLine(1, 'CAP END')
self.skipToWelcome(1)
self.getMessages(1)
self.sendLine(1, 'CAP LIST')
messages = self.getMessages(1)
cap_list = [m for m in messages if m.command == 'CAP'][0]
self.assertEqual(set(cap_list.params[2].split()), {'echo-message', 'server-time'})
self.assertIn('time', cap_list.tags)
# remove the server-time cap
self.sendLine(1, 'CAP REQ :-server-time')
self.getMessages(1)
# server-time should be disabled
self.sendLine(1, 'CAP LIST')
messages = self.getMessages(1)
cap_list = [m for m in messages if m.command == 'CAP'][0]
self.assertEqual(set(cap_list.params[2].split()), {'echo-message'})
self.assertNotIn('time', cap_list.tags)

View File

@ -7,7 +7,13 @@ from irctest import cases
from irctest import client_mock from irctest import client_mock
from irctest import runner from irctest import runner
from irctest.irc_utils import ambiguities from irctest.irc_utils import ambiguities
from irctest.irc_utils.message_parser import Message
RPL_NOTOPIC = '331'
RPL_NAMREPLY = '353'
ERR_NOSUCHCHANNEL = '403'
ERR_NOTONCHANNEL = '442'
ERR_CHANOPRIVSNEEDED = '482'
class JoinTestCase(cases.BaseServerTestCase): class JoinTestCase(cases.BaseServerTestCase):
@cases.SpecificationSelector.requiredBySpecification('RFC1459', 'RFC2812', @cases.SpecificationSelector.requiredBySpecification('RFC1459', 'RFC2812',
@ -228,6 +234,42 @@ class JoinTestCase(cases.BaseServerTestCase):
# either 403 ERR_NOSUCHCHANNEL or 443 ERR_NOTONCHANNEL # either 403 ERR_NOSUCHCHANNEL or 443 ERR_NOTONCHANNEL
self.assertIn(m.command, ('403', '443')) self.assertIn(m.command, ('403', '443'))
@cases.SpecificationSelector.requiredBySpecification('RFC2812')
def testUnsetTopicResponses(self):
"""Test various cases related to RPL_NOTOPIC with set and unset topics."""
self.connectClient('bar')
self.sendLine(1, 'JOIN #test')
messages = self.getMessages(1)
# shouldn't send RPL_NOTOPIC for a new channel
self.assertNotIn(RPL_NOTOPIC, [m.command for m in messages])
self.connectClient('baz')
self.sendLine(2, 'JOIN #test')
messages = self.getMessages(2)
# topic is still unset, shouldn't send RPL_NOTOPIC on initial join
self.assertNotIn(RPL_NOTOPIC, [m.command for m in messages])
self.sendLine(2, 'TOPIC #test')
messages = self.getMessages(2)
# explicit TOPIC should receive RPL_NOTOPIC
self.assertIn(RPL_NOTOPIC, [m.command for m in messages])
self.sendLine(1, 'TOPIC #test :new topic')
self.getMessages(1)
# client 2 should get the new TOPIC line
messages = [message for message in self.getMessages(2) if message.command == 'TOPIC']
self.assertEqual(len(messages), 1)
self.assertEqual(messages[0].params, ['#test', 'new topic'])
# unset the topic:
self.sendLine(1, 'TOPIC #test :')
self.getMessages(1)
self.connectClient('qux')
self.sendLine(3, 'join #test')
messages = self.getMessages(3)
# topic is once again unset, shouldn't send RPL_NOTOPIC on initial join
self.assertNotIn(RPL_NOTOPIC, [m.command for m in messages])
@cases.SpecificationSelector.requiredBySpecification('RFC1459', 'RFC2812') @cases.SpecificationSelector.requiredBySpecification('RFC1459', 'RFC2812')
def testListEmpty(self): def testListEmpty(self):
"""<https://tools.ietf.org/html/rfc1459#section-4.2.6> """<https://tools.ietf.org/html/rfc1459#section-4.2.6>
@ -296,8 +338,6 @@ class JoinTestCase(cases.BaseServerTestCase):
# TODO: check foo is an operator # TODO: check foo is an operator
import time
time.sleep(0.1)
self.getMessages(1) self.getMessages(1)
self.getMessages(2) self.getMessages(2)
self.getMessages(3) self.getMessages(3)
@ -318,6 +358,52 @@ class JoinTestCase(cases.BaseServerTestCase):
self.assertMessageEqual(m, command='KICK', self.assertMessageEqual(m, command='KICK',
params=['#chan', 'bar', 'bye']) params=['#chan', 'bar', 'bye'])
@cases.SpecificationSelector.requiredBySpecification('RFC2812')
def testKickPrivileges(self):
"""Test who has the ability to kick / what error codes are sent for invalid kicks."""
self.connectClient('foo')
self.sendLine(1, 'JOIN #chan')
self.getMessages(1)
self.connectClient('bar')
self.sendLine(2, 'JOIN #chan')
messages = self.getMessages(2)
names = set()
for message in messages:
if message.command == RPL_NAMREPLY:
names.update(set(message.params[-1].split()))
# assert foo is opped
self.assertIn('@foo', names, f'unexpected names: {names}')
self.connectClient('baz')
self.sendLine(3, 'KICK #chan bar')
replies = set(m.command for m in self.getMessages(3))
self.assertTrue(
ERR_NOTONCHANNEL in replies or ERR_CHANOPRIVSNEEDED in replies or ERR_NOSUCHCHANNEL in replies,
f'did not receive acceptable error code for kick from outside channel: {replies}')
self.joinChannel(3, '#chan')
self.getMessages(3)
self.sendLine(3, 'KICK #chan bar')
replies = set(m.command for m in self.getMessages(3))
# now we're a channel member so we should receive ERR_CHANOPRIVSNEEDED
self.assertIn(ERR_CHANOPRIVSNEEDED, replies)
self.sendLine(1, 'MODE #chan +o baz')
self.getMessages(1)
# should be able to kick an unprivileged user:
self.sendLine(3, 'KICK #chan bar')
# should be able to kick an operator:
self.sendLine(3, 'KICK #chan foo')
baz_replies = set(m.command for m in self.getMessages(3))
self.assertNotIn(ERR_CHANOPRIVSNEEDED, baz_replies)
kick_targets = [m.params[1] for m in self.getMessages(1) if m.command == 'KICK']
# foo should see bar and foo being kicked
self.assertTrue(any(target.startswith('foo') for target in kick_targets), f'unexpected kick targets: {kick_targets}')
self.assertTrue(any(target.startswith('bar') for target in kick_targets), f'unexpected kick targets: {kick_targets}')
@cases.SpecificationSelector.requiredBySpecification('RFC2812') @cases.SpecificationSelector.requiredBySpecification('RFC2812')
def testKickNonexistentChannel(self): def testKickNonexistentChannel(self):
"""“Kick command [...] Numeric replies: [...] ERR_NOSUCHCHANNEL.""" """“Kick command [...] Numeric replies: [...] ERR_NOSUCHCHANNEL."""
@ -519,3 +605,26 @@ class InviteTestCase(cases.BaseServerTestCase):
# we were invited, so join should succeed now # we were invited, so join should succeed now
self.joinChannel(2, '#chan') self.joinChannel(2, '#chan')
class ChannelQuitTestCase(cases.BaseServerTestCase):
@cases.SpecificationSelector.requiredBySpecification('RFC2812')
def testQuit(self):
"""“Once a user has joined a channel, he receives information about
all commands his server receives affecting the channel. This
includes [...] QUIT
<https://tools.ietf.org/html/rfc2812#section-3.2.1>
"""
self.connectClient('bar')
self.joinChannel(1, '#chan')
self.connectClient('qux')
self.sendLine(2, 'JOIN #chan')
self.getMessages(1)
self.sendLine(2, 'QUIT :qux out')
self.getMessages(2)
m = self.getMessage(1)
self.assertEqual(m.command, 'QUIT')
self.assertTrue(m.prefix.startswith('qux')) # nickmask of quitter
self.assertIn('qux out', m.params[0])

View File

@ -4,9 +4,6 @@ Tests section 4.1 of RFC 1459.
""" """
from irctest import cases from irctest import cases
from irctest import authentication
from irctest.irc_utils.message_parser import Message
from irctest.basecontrollers import NotImplementedByController
from irctest.client_mock import ConnectionClosed from irctest.client_mock import ConnectionClosed
class PasswordedConnectionRegistrationTestCase(cases.BaseServerTestCase): class PasswordedConnectionRegistrationTestCase(cases.BaseServerTestCase):
@ -31,6 +28,16 @@ class PasswordedConnectionRegistrationTestCase(cases.BaseServerTestCase):
self.assertNotEqual(m.command, '001', self.assertNotEqual(m.command, '001',
msg='Got 001 after NICK+USER but missing PASS') msg='Got 001 after NICK+USER but missing PASS')
@cases.SpecificationSelector.requiredBySpecification('RFC1459', 'RFC2812')
def testWrongPassword(self):
self.addClient()
self.sendLine(1, 'PASS {}'.format(self.password + "garbage"))
self.sendLine(1, 'NICK foo')
self.sendLine(1, 'USER username * * :Realname')
m = self.getRegistrationMessage(1)
self.assertNotEqual(m.command, '001',
msg='Got 001 after NICK+USER but incorrect PASS')
@cases.SpecificationSelector.requiredBySpecification('RFC1459', 'RFC2812') @cases.SpecificationSelector.requiredBySpecification('RFC1459', 'RFC2812')
def testPassAfterNickuser(self): def testPassAfterNickuser(self):
"""“The password can and must be set before any attempt to register """“The password can and must be set before any attempt to register

View File

@ -22,7 +22,7 @@ class EchoMessageTestCase(cases.BaseServerTestCase):
# TODO: check also without this # TODO: check also without this
self.sendLine(1, 'CAP REQ :echo-message{}'.format( self.sendLine(1, 'CAP REQ :echo-message{}'.format(
' server-time' if server_time else '')) ' server-time' if server_time else ''))
m = self.getRegistrationMessage(1) self.getRegistrationMessage(1)
# TODO: Remove this one the trailing space issue is fixed in Charybdis # TODO: Remove this one the trailing space issue is fixed in Charybdis
# and Mammon: # and Mammon:
#self.assertMessageEqual(m, command='CAP', #self.assertMessageEqual(m, command='CAP',

View File

@ -3,7 +3,6 @@
""" """
from irctest import cases from irctest import cases
from irctest.irc_utils.message_parser import Message
class MetadataTestCase(cases.BaseServerTestCase, cases.OptionalityHelper): class MetadataTestCase(cases.BaseServerTestCase, cases.OptionalityHelper):
def connectRegisteredClient(self, nick): def connectRegisteredClient(self, nick):
@ -41,7 +40,7 @@ class MetadataTestCase(cases.BaseServerTestCase, cases.OptionalityHelper):
@cases.SpecificationSelector.requiredBySpecification('IRCv3.1') @cases.SpecificationSelector.requiredBySpecification('IRCv3.1')
@cases.OptionalityHelper.skipUnlessHasMechanism('PLAIN') @cases.OptionalityHelper.skipUnlessHasMechanism('PLAIN')
def testNotLoggedIn(self): def testLoggedIn(self):
self.connectClient('foo', capabilities=['extended-join'], self.connectClient('foo', capabilities=['extended-join'],
skip_if_cap_nak=True) skip_if_cap_nak=True)
self.joinChannel(1, '#chan') self.joinChannel(1, '#chan')

View File

@ -4,10 +4,6 @@ Section 3.2 of RFC 2812
""" """
from irctest import cases from irctest import cases
from irctest import client_mock
from irctest import runner
from irctest.irc_utils import ambiguities
from irctest.irc_utils.message_parser import Message
class PrivmsgTestCase(cases.BaseServerTestCase): class PrivmsgTestCase(cases.BaseServerTestCase):
@cases.SpecificationSelector.requiredBySpecification('RFC1459', 'RFC2812') @cases.SpecificationSelector.requiredBySpecification('RFC1459', 'RFC2812')

View File

@ -4,7 +4,6 @@ Tests METADATA features.
""" """
from irctest import cases from irctest import cases
from irctest.irc_utils.message_parser import Message
class MetadataTestCase(cases.BaseServerTestCase): class MetadataTestCase(cases.BaseServerTestCase):
valid_metadata_keys = {'valid_key1', 'valid_key2'} valid_metadata_keys = {'valid_key1', 'valid_key2'}

View File

@ -88,7 +88,7 @@ class EchoMessageTestCase(cases.BaseServerTestCase):
self.assertMonoffline(1, 'bar') self.assertMonoffline(1, 'bar')
@cases.SpecificationSelector.requiredBySpecification('IRCv3.2') @cases.SpecificationSelector.requiredBySpecification('IRCv3.2')
def testMonitorOneConnection(self): def testMonitorOneConnectionWithQuit(self):
self.connectClient('foo') self.connectClient('foo')
self.check_server_support() self.check_server_support()
self.connectClient('bar') self.connectClient('bar')

View File

@ -4,10 +4,6 @@ Tests multi-prefix.
""" """
from irctest import cases from irctest import cases
from irctest import client_mock
from irctest import runner
from irctest.irc_utils import ambiguities
from irctest.irc_utils.message_parser import Message
class MultiPrefixTestCase(cases.BaseServerTestCase): class MultiPrefixTestCase(cases.BaseServerTestCase):
@cases.SpecificationSelector.requiredBySpecification('IRCv3.1') @cases.SpecificationSelector.requiredBySpecification('IRCv3.1')

View File

@ -1,7 +1,6 @@
import base64 import base64
from irctest import cases from irctest import cases
from irctest.irc_utils.message_parser import Message
class RegistrationTestCase(cases.BaseServerTestCase): class RegistrationTestCase(cases.BaseServerTestCase):
def testRegistration(self): def testRegistration(self):

View File

@ -5,8 +5,36 @@ User commands as specified in Section 3.6 of RFC 2812:
from irctest import cases from irctest import cases
RPL_WHOISUSER = '311'
RPL_WHOISCHANNELS = '319' RPL_WHOISCHANNELS = '319'
class WhoisTestCase(cases.BaseServerTestCase):
@cases.SpecificationSelector.requiredBySpecification('RFC2812')
def testWhoisUser(self):
"""Test basic WHOIS behavior"""
nick = 'myCoolNickname'
username = 'myCoolUsername'
realname = 'My Real Name'
self.addClient()
self.sendLine(1, f'NICK {nick}')
self.sendLine(1, f'USER {username} 0 * :{realname}')
self.skipToWelcome(1)
self.connectClient('otherNickname')
self.getMessages(2)
self.sendLine(2, 'WHOIS mycoolnickname')
messages = self.getMessages(2)
whois_user = messages[0]
self.assertEqual(whois_user.command, RPL_WHOISUSER)
# "<client> <nick> <username> <host> * :<realname>"
self.assertEqual(whois_user.params[1], nick)
self.assertEqual(whois_user.params[2], '~' + username)
# dumb regression test for oragono/oragono#355:
self.assertNotIn(whois_user.params[3], [nick, username, '~' + username, realname])
self.assertEqual(whois_user.params[5], realname)
class InvisibleTestCase(cases.BaseServerTestCase): class InvisibleTestCase(cases.BaseServerTestCase):
@cases.SpecificationSelector.requiredBySpecification('Oragono') @cases.SpecificationSelector.requiredBySpecification('Oragono')