From 4a8c96c8838a7e0cb5a608595004594015e76783 Mon Sep 17 00:00:00 2001 From: James Lu Date: Sun, 18 Aug 2019 16:36:02 -0700 Subject: [PATCH] And now, a test fixture for protocols/ --- conf.py | 2 +- test/protocol_test_fixture.py | 232 +++++++++++++++++++++++++++++++++ test/test_protocol_inspircd.py | 13 ++ test/test_protocol_unreal.py | 13 ++ 4 files changed, 259 insertions(+), 1 deletion(-) create mode 100644 test/protocol_test_fixture.py create mode 100644 test/test_protocol_inspircd.py create mode 100644 test/test_protocol_unreal.py diff --git a/conf.py b/conf.py index 28bed83..a57998a 100644 --- a/conf.py +++ b/conf.py @@ -30,7 +30,7 @@ conf = {'bot': }, 'logging': { - 'stdout': 'INFO' + 'console': 'INFO' }, 'servers': # Wildcard defaultdict! This means that diff --git a/test/protocol_test_fixture.py b/test/protocol_test_fixture.py new file mode 100644 index 0000000..f1534c2 --- /dev/null +++ b/test/protocol_test_fixture.py @@ -0,0 +1,232 @@ +""" +A test fixture for PyLink protocol modules. +""" +import time +import unittest +import collections +from unittest.mock import patch + +from pylinkirc import conf, world +from pylinkirc.classes import User, Server, Channel + +class DummySocket(): + def __init__(self): + #self.recv_messages = collections.deque() + self.sent_messages = collections.deque() + + @staticmethod + def connect(address): + return + ''' + def recv(bufsize, *args): + if self.recv_messages: + data = self.recv_messages.popleft() + print('<-', data) + return data + else: + return None + ''' + + def recv(bufsize, *args): + raise NotImplementedError + + def send(self, data): + print('->', data) + self.sent_messages.append(data) + +class BaseProtocolTest(unittest.TestCase): + proto_class = None + netname = 'test' + serverdata = conf.conf['servers'][netname] + + def setUp(self): + if not self.proto_class: + raise RuntimeError("Must set target protocol module in proto_class") + self.p = self.proto_class(self.netname) + + # Stub connect() and the socket for now... + self.p.connect = lambda self: None + self.p.socket = DummySocket() + + if self.serverdata: + self.p.serverdata = self.serverdata + + def _make_user(self, nick, uid, ts=None, sid=None, **kwargs): + """ + Creates a user for testing. + """ + if ts is None: + ts = int(time.time()) + userobj = User(self.p, nick, ts, uid, sid, **kwargs) + self.p.users[uid] = userobj + return userobj + + ### STATEKEEPING FUNCTIONS + + def test_nick_to_uid(self): + self.assertEqual(self.p.nick_to_uid('TestUser'), None) + + self._make_user('TestUser', 'testuid1') + + self.assertEqual(self.p.nick_to_uid('TestUser'), 'testuid1') + self.assertEqual(self.p.nick_to_uid('TestUser', multi=True), ['testuid1']) + self.assertEqual(self.p.nick_to_uid('BestUser'), None) + self.assertEqual(self.p.nick_to_uid('RestUser', multi=True), []) + + self._make_user('TestUser', 'testuid2') + self.assertEqual(self.p.nick_to_uid('TestUser', multi=True), ['testuid1', 'testuid2']) + + def test_is_internal(self): + self.p.servers['internalserver'] = Server(self.p, None, 'internal.server', internal=True) + self.p.sid = 'internalserver' + self.p.servers['externalserver'] = Server(self.p, None, 'external.server', internal=False) + + iuser = self._make_user('someone', 'uid1', sid='internalserver') + euser = self._make_user('sometwo', 'uid2', sid='externalserver') + + self.assertTrue(self.p.is_internal_server('internalserver')) + self.assertFalse(self.p.is_internal_server('externalserver')) + self.assertTrue(self.p.is_internal_client('uid1')) + self.assertFalse(self.p.is_internal_client('uid2')) + + def test_is_manipulatable(self): + self.p.servers['serv1'] = Server(self.p, None, 'myserv.local', internal=True) + iuser = self._make_user('yes', 'uid1', sid='serv1', manipulatable=True) + euser = self._make_user('no', 'uid2', manipulatable=False) + + self.assertTrue(self.p.is_manipulatable_client('uid1')) + self.assertFalse(self.p.is_manipulatable_client('uid2')) + + def test_get_service_bot(self): + self.assertFalse(self.p.get_service_bot('nonexistent')) + + regular_user = self._make_user('Guest12345', 'Guest12345@1') + service_user = self._make_user('MyServ', 'MyServ@2') + service_user.service = 'myserv' + + self.assertFalse(self.p.get_service_bot('Guest12345@1')) + + with patch.dict(world.services, {'myserv': 'myserv instance'}, clear=True): + self.assertEqual(self.p.get_service_bot('MyServ@2'), 'myserv instance') + + def test_to_lower(self): + check = lambda inp, expected: self.assertEqual(self.p.to_lower(inp), expected) + check_unchanged = lambda inp: self.assertEqual(self.p.to_lower(inp), inp) + + check('BLAH!', 'blah!') + check('BLAH!', 'blah!') # since we memoize + check_unchanged('zabcdefghijklmnopqrstuvwxy') + check('123Xyz !@#$%^&*()-=+', '123xyz !@#$%^&*()-=+') + + if self.p.casemapping == 'rfc1459': + check('hello [] {} |\\ ~^', 'hello [] [] \\\\ ^^') + check('{Test Case}', '[test case]') + else: + check_unchanged('hello [] {} |\\ ~^') + check('{Test Case}', '{test case}') + + def test_is_nick(self): + assertT = lambda inp: self.assertTrue(self.p.is_nick(inp)) + assertF = lambda inp: self.assertFalse(self.p.is_nick(inp)) + + assertT('test') + assertT('PyLink') + assertT('[bracketman]') + assertT('{RACKETman}') + assertT('bar|tender') + assertT('\\bar|bender\\') + assertT('GL|ovd') + assertT('B') + assertT('`') + assertT('Hello123') + assertT('test-') + assertT('test-test') + assertT('_jkl9') + assertT('_-jkl9') + + assertF('') + assertF('?') + assertF('nick@network') + assertF('Space flight') + assertF(' ') + assertF(' Space blight') + assertF('0') + assertF('-test') + assertF('#lounge') + assertF('\\bar/bender\\') + assertF('GL/ovd') # Technically not valid, but some IRCds don't care ;) + assertF('100AAAAAC') # TS6 UID + + self.assertFalse(self.p.is_nick('longnicklongnicklongnicklongnicklongnicklongnick', nicklen=20)) + self.assertTrue(self.p.is_nick('ninechars', nicklen=9)) + self.assertTrue(self.p.is_nick('ChanServ', nicklen=20)) + self.assertTrue(self.p.is_nick('leneight', nicklen=9)) + self.assertFalse(self.p.is_nick('bitmonster', nicklen=9)) + self.assertFalse(self.p.is_nick('ninecharsplus', nicklen=12)) + + def test_is_channel(self): + assertT = lambda inp: self.assertTrue(self.p.is_channel(inp)) + assertF = lambda inp: self.assertFalse(self.p.is_channel(inp)) + + assertT('#test') + assertT('#') + assertT('#a#b#c') + + assertF('nick!user@host') + assertF('&channel') # we don't support these yet + assertF('lorem ipsum') + + def test_is_ascii(self): + assertT = lambda inp: self.assertTrue(self.p._isASCII(inp)) + assertF = lambda inp: self.assertFalse(self.p._isASCII(inp)) + + assertT('iotgjw@sy9!4py645ujg990rEYghiwaK0r4{SEFIre') + assertT('touche') + assertF('touché') + assertF('测试1') + + def test_is_server_name(self): + self.assertTrue(self.p.is_server_name('test.local')) + self.assertTrue(self.p.is_server_name('IRC.example.com')) + self.assertTrue(self.p.is_server_name('services.')) + self.assertFalse(self.p.is_server_name('.org')) + self.assertFalse(self.p.is_server_name('bacon')) + + def test_is_hostmask(self): + assertT = lambda inp: self.assertTrue(self.p.is_hostmask(inp)) + assertF = lambda inp: self.assertFalse(self.p.is_hostmask(inp)) + + assertT('nick!user@host') + assertT('abc123!~ident@ip1-2-3-4.example.net') + + assertF('brick!user') + assertF('user@host') + assertF('!@') + assertF('!') + assertF('@abcd') + assertF('#channel') + assertF('test.host') + assertF('nick ! user @ host') + assertF('alpha!beta@example.net#otherchan') # Janus workaround + + def test_get_SID(self): + self.p.servers['serv1'] = Server(self.p, None, 'myserv.local', internal=True) + + check = lambda inp, expected: self.assertEqual(self.p._get_SID(inp), expected) + check('myserv.local', 'serv1') + check('MYSERV.local', 'serv1') + check('serv1', 'serv1') + check('other.server', 'other.server') + + def test_get_UID(self): + u = self._make_user('you', uid='100') + check = lambda inp, expected: self.assertEqual(self.p._get_UID(inp), expected) + + check('you', '100') # nick to UID + check('YOu', '100') + check('100', '100') # already a UID + check('Test', 'Test') # non-existent + + # TODO: _squit wrapper + + # TODO: parse_modes and later diff --git a/test/test_protocol_inspircd.py b/test/test_protocol_inspircd.py new file mode 100644 index 0000000..14dff6c --- /dev/null +++ b/test/test_protocol_inspircd.py @@ -0,0 +1,13 @@ +import unittest + +from pylinkirc.protocols import inspircd + +import protocol_test_fixture as ptf + +class InspIRCdProtocolTest(ptf.BaseProtocolTest): + proto_class = inspircd.InspIRCdProtocol + + # In the future we will have protocol specific test cases here + +if __name__ == '__main__': + unittest.main() diff --git a/test/test_protocol_unreal.py b/test/test_protocol_unreal.py new file mode 100644 index 0000000..e8f3b58 --- /dev/null +++ b/test/test_protocol_unreal.py @@ -0,0 +1,13 @@ +import unittest + +from pylinkirc.protocols import unreal + +import protocol_test_fixture as ptf + +class UnrealProtocolTest(ptf.BaseProtocolTest): + proto_class = unreal.UnrealProtocol + + # In the future we will have protocol specific test cases here + +if __name__ == '__main__': + unittest.main()