3
0
mirror of https://github.com/jlu5/PyLink.git synced 2024-11-01 01:09:22 +01:00

Tests for FMODE/FJOIN, and bugfixes

This commit is contained in:
James Lu 2015-07-05 19:19:49 -07:00
parent 87781abc82
commit 2cbd6fd851
4 changed files with 106 additions and 45 deletions

33
main.py
View File

@ -8,11 +8,11 @@ import sys
from collections import defaultdict
from log import log
from conf import conf
import conf
import classes
class Irc():
def __init__(self, proto):
def __init__(self, proto, conf):
# Initialize some variables
self.connected = False
self.name = conf['server']['netname']
@ -26,21 +26,30 @@ class Irc():
self.cmodes = {'op': 'o', 'secret': 's', 'private': 'p',
'noextmsg': 'n', 'moderated': 'm', 'inviteonly': 'i',
'topiclock': 't', 'limit': 'l', 'ban': 'b',
'voice': 'v', 'key': 'k'}
'voice': 'v', 'key': 'k',
# Type A, B, and C modes
'*A': 'b',
'*B': 'k',
'*C': 'l',
'*D': 'imnpstr'}
self.umodes = {'invisible': 'i', 'snomask': 's', 'wallops': 'w',
'oper': 'o'}
'oper': 'o',
'*A': '', '*B': '', '*C': 's', '*D': 'iow'}
self.maxnicklen = 30
self.prefixmodes = 'ov'
self.serverdata = conf['server']
self.sid = self.serverdata["sid"]
self.proto = proto
self.connect()
def connect(self):
ip = self.serverdata["ip"]
port = self.serverdata["port"]
self.sid = self.serverdata["sid"]
log.info("Connecting to network %r on %s:%s", self.name, ip, port)
self.socket = socket.socket()
self.socket.connect((ip, port))
self.proto = proto
proto.connect(self)
self.proto.connect(self)
self.loaded = []
self.load_plugins()
self.connected = True
@ -70,7 +79,7 @@ class Irc():
self.socket.send(data)
def load_plugins(self):
to_load = conf['plugins']
to_load = conf.conf['plugins']
plugins_folder = [os.path.join(os.getcwd(), 'plugins')]
# Here, we override the module lookup and import the plugins
# dynamically depending on which were configured.
@ -87,11 +96,11 @@ class Irc():
if __name__ == '__main__':
log.info('PyLink starting...')
if conf['login']['password'] == 'changeme':
if conf.conf['login']['password'] == 'changeme':
log.critical("You have not set the login details correctly! Exiting...")
sys.exit(2)
protoname = conf['server']['protocol']
protoname = conf.conf['server']['protocol']
protocols_folder = [os.path.join(os.getcwd(), 'protocols')]
try:
moduleinfo = imp.find_module(protoname, protocols_folder)
@ -103,4 +112,4 @@ if __name__ == '__main__':
log.critical('Failed to load protocol module: import error %s', protoname, str(e))
sys.exit(2)
else:
irc_obj = Irc(proto)
irc_obj = Irc(proto, conf.conf)

View File

@ -8,32 +8,25 @@ import classes
from collections import defaultdict
import unittest
class FakeIRC(main.Irc):
def __init__(self, proto):
self.connected = False
self.users = {}
self.channels = defaultdict(classes.IrcChannel)
self.name = 'fakeirc'
self.servers = {}
self.proto = proto
global testconf
testconf = {'server':
{'netname': 'fakeirc',
'ip': '0.0.0.0',
'port': 7000,
'recvpass': "abcd",
'sendpass': "abcd",
'protocol': "null",
'hostname': "pylink.unittest",
'sid': "9PY",
'channels': ["#pylink"],
}
}
self.serverdata = {'netname': 'fakeirc',
'ip': '0.0.0.0',
'port': 7000,
'recvpass': "abcd",
'sendpass': "abcd",
'protocol': "testingonly",
'hostname': "pylink.unittest",
'sid': "9PY",
'channels': ["#pylink"],
}
self.conf = {'server': self.serverdata}
ip = self.serverdata["ip"]
port = self.serverdata["port"]
self.sid = self.serverdata["sid"]
self.socket = None
class FakeIRC(main.Irc):
def connect(self):
self.messages = []
self.socket = None
def run(self, data):
"""Queues a message to the fake IRC server."""
log.debug('-> ' + data)

View File

@ -11,7 +11,7 @@ import utils
class TestInspIRCdProtocol(unittest.TestCase):
def setUp(self):
self.irc = test_proto_common.FakeIRC(inspircd)
self.irc = test_proto_common.FakeIRC(inspircd, test_proto_common.testconf)
self.proto = self.irc.proto
self.sdata = self.irc.serverdata
# This is to initialize ourself as an internal PseudoServer, so we can spawn clients
@ -153,20 +153,70 @@ class TestInspIRCdProtocol(unittest.TestCase):
def testHandleKill(self):
self.irc.takeMsgs() # Ignore the initial connect messages
self.irc.run(':9PYAAAAAA KILL 9PYAAAAAA :killed')
olduid = self.irc.pseudoclient.uid
self.irc.run(':{u} KILL {u} :killed'.format(u=olduid))
msgs = self.irc.takeMsgs()
commands = self.irc.takeCommands(msgs)
# Make sure we're respawning our PseudoClient when its killed
self.assertIn('UID', commands)
self.assertIn('FJOIN', commands)
# Also make sure that we're updating the irc.pseudoclient field
self.assertNotEqual(self.irc.pseudoclient.uid, olduid)
def testHandleKick(self):
self.irc.takeMsgs() # Ignore the initial connect messages
self.irc.run(':9PYAAAAAA KICK #pylink 9PYAAAAAA :kicked')
self.irc.run(':{u} KICK #pylink {u} :kicked'.format(u=self.irc.pseudoclient.uid))
# Ditto above
msgs = self.irc.takeMsgs()
commands = self.irc.takeCommands(msgs)
self.assertIn('FJOIN', commands)
def testHandleFjoinUsers(self):
self.irc.run(':70M FJOIN #Chat 1423790411 + :,10XAAAAAA ,10XAAAAAB')
self.assertEqual({'10XAAAAAA', '10XAAAAAB'}, self.irc.channels['#chat'].users)
# self.assertIn('10XAAAAAB', self.irc.channels['#chat'].users)
# Sequential FJOINs must NOT remove existing users
self.irc.run(':70M FJOIN #Chat 1423790412 + :,10XAAAAAC')
# Join list can be empty too, in the case of permanent channels with 0 users.
self.irc.run(':70M FJOIN #Chat 1423790413 +nt :')
def testHandleFjoinModes(self):
self.irc.run(':70M FJOIN #Chat 1423790411 +nt :,10XAAAAAA ,10XAAAAAB')
self.assertEqual({('+n', None), ('+t', None)}, self.irc.channels['#chat'].modes)
# Sequential FJOINs must NOT remove existing modes
self.irc.run(':70M FJOIN #Chat 1423790412 + :,10XAAAAAC')
self.assertEqual({('+n', None), ('+t', None)}, self.irc.channels['#chat'].modes)
def testHandleFjoinModesWithArgs(self):
self.irc.run(':70M FJOIN #Chat 1423790414 +nlks 10 t0psekrit :,10XAAAAAA ,10XAAAAAB')
self.assertEqual({('+n', None), ('+s', None), ('+l', '10'), ('+k', 't0psekrit')},
self.irc.channels['#chat'].modes)
def testHandleFjoinPrefixes(self):
self.irc.run(':70M FJOIN #Chat 1423790418 +nt :ov,10XAAAAAA v,10XAAAAAB ,10XAAAAAC')
self.assertEqual({('+n', None), ('+t', None)}, self.irc.channels['#chat'].modes)
self.assertEqual({'10XAAAAAA', '10XAAAAAB', '10XAAAAAC'}, self.irc.channels['#chat'].users)
self.assertIn('10XAAAAAA', self.irc.channels['#chat'].prefixmodes['ops'])
self.assertEqual({'10XAAAAAA', '10XAAAAAB'}, self.irc.channels['#chat'].prefixmodes['voices'])
def testHandleFmode(self):
self.irc.run(':70M FJOIN #pylink 1423790411 +n :o,10XAAAAAA ,10XAAAAAB')
self.irc.run(':70M FMODE #pylink 1423790412 +ikl herebedragons 100')
self.assertEqual({('+i', None), ('+k', 'herebedragons'), ('+l', '100'), ('+n', None)}, self.irc.channels['#pylink'].modes)
self.irc.run(':70M FMODE #pylink 1423790413 -ilk+m herebedragons')
self.assertEqual({('+m', None), ('+n', None)}, self.irc.channels['#pylink'].modes)
def testHandleFmodeWithPrefixes(self):
self.irc.run(':70M FJOIN #pylink 1423790411 +n :o,10XAAAAAA ,10XAAAAAB')
# Prefix modes are stored separately, so they should never show up in .modes
self.assertNotIn(('+o', '10XAAAAAA'), self.irc.channels['#pylink'].modes)
self.assertEqual({'10XAAAAAA'}, self.irc.channels['#pylink'].prefixmodes['ops'])
self.irc.run(':70M FMODE #pylink 1423790412 +lot 50 %s' % self.u)
self.assertIn(self.u, self.irc.channels['#pylink'].prefixmodes['ops'])
modes = {('+l', '50'), ('+n', None), ('+t', None)}
self.assertEqual(modes, self.irc.channels['#pylink'].modes)
self.irc.run(':70M FMODE #pylink 1423790413 -o %s' % self.u)
self.assertEqual(modes, self.irc.channels['#pylink'].modes)
self.assertNotIn(self.u, self.irc.channels['#pylink'].prefixmodes['ops'])
if __name__ == '__main__':
unittest.main()

View File

@ -90,8 +90,8 @@ def parseModes(irc, target, args):
['+mitl-o', '3', 'person'] => [('+m', None), ('+i', None), ('+t', None), ('+l', '3'), ('-o', 'person')]
"""
# http://www.irc.org/tech_docs/005.html
# A = Mode that adds or removes a nick or address to a list. Always has a parameter.
# B = Mode that changes a setting and always has a parameter.
# A = Mode that adds or removes a nick or address to a list. Always has a parameter.
# B = Mode that changes a setting and always has a parameter.
# C = Mode that changes a setting and only has a parameter when set.
# D = Mode that changes a setting and never has a parameter.
usermodes = not isChannel(target)
@ -101,7 +101,7 @@ def parseModes(irc, target, args):
args = args[1:]
if usermodes:
log.debug('(%s) Using irc.umodes for this query: %s', irc.name, irc.umodes)
supported_modes = irc.umodes
supported_modes = irc.umodes
else:
log.debug('(%s) Using irc.cmodes for this query: %s', irc.name, irc.cmodes)
supported_modes = irc.cmodes
@ -111,6 +111,7 @@ def parseModes(irc, target, args):
prefix = mode
else:
arg = None
log.debug('Current mode: %s%s; args left: %s', prefix, mode, args)
if mode in (supported_modes['*A'] + supported_modes['*B']):
# Must have parameter.
log.debug('Mode %s: This mode must have parameter.', mode)
@ -150,16 +151,24 @@ def applyModes(irc, target, changedmodes):
log.debug('(%s) Final prefixmodes list: %s', irc.name, irc.channels[target].prefixmodes)
if mode[0][1] in irc.prefixmodes:
# Ignore other prefix modes such as InspIRCd's +Yy
log.debug('(%s) Not adding mode %s to IrcChannel.modes because it\'s a prefix mode', irc.name, str(mode))
continue
if mode[0][0] == '+':
# We're adding a mode
modelist.add(mode)
log.debug('(%s) Adding mode %r on %s', irc.name, mode, target)
else:
# We're removing a mode
mode[0] = mode[0].replace('-', '+')
modelist.discard(mode)
log.debug('(%s) Removing mode %r on %s', irc.name, mode, target)
# We're removing a mode
if mode[1] is None:
# We're removing a mode that only takes arguments when setting.
for oldmode in modelist.copy():
if oldmode[0][1] == mode[0][1]:
modelist.discard(oldmode)
else:
# Swap the - for a + and then remove it from the list.
mode = ('+' + mode[0][1], mode[1])
modelist.discard(mode)
log.debug('(%s) Final modelist: %s', irc.name, modelist)
def joinModes(modes):