More tests

dave/xdcc
dave 6 years ago
parent 585efb2c18
commit e8652c37c8

@ -31,6 +31,8 @@ class IRCCore(object):
self.rate_max = float(rate_max)
self.rate_int = float(rate_int)
self.reconnect_delay = 3.0
self.connected = False
"""If we're connected or not"""
@ -60,7 +62,7 @@ class IRCCore(object):
self.initHooks()
self.outputq = asyncio.Queue()
self._loop.call_soon(asyncio.ensure_future, self.outputqueue())
self._loop.call_soon_threadsafe(asyncio.ensure_future, self.outputqueue())
async def loop(self, loop):
while self.alive:
@ -94,8 +96,8 @@ class IRCCore(object):
self.writer.close()
if self.alive:
# TODO ramp down reconnect attempts
logging.info("Reconnecting in 3s...")
await asyncio.sleep(3)
logging.info("Reconnecting in {}s...".format(self.reconnect_delay))
await asyncio.sleep(self.reconnect_delay)
async def outputqueue(self):
bucket = burstbucket(self.rate_max, self.rate_int)

@ -13,7 +13,7 @@ from .common import load as pload
from .common import messageHasCommand
class ModuleBase:
class ModuleBase(object):
"""All modules will extend this class
:param bot: A reference to the main bot passed when this module is created

@ -198,7 +198,7 @@ class Remind(ModuleBase):
else:
return zonestr
@info("after <duration> have the bot remind after", cmds=["after", "in"])
@info("after <duration> <reminder> have the bot remind after", cmds=["after", "in"])
@command("after", "in", allow_private=True)
def remindin(self, msg, cmd):
replyTo = msg.args[0]

@ -64,13 +64,7 @@ class Connection:
c = self.connection.cursor()
return c
def escape(self, s):
raise NotImplementedError
def ondisable(self):
self.connection.close()
# Connects to the database server, and selects a database (Or attempts to create it if it doesn't exist yet)
# Opens the sqlite database / attempts to create it if it doesn't exist yet
def _connect(self):
self.log.info("Sqlite: opening database %s" % self.master.getFilePath(self.dbname))
self.connection = sqlite3.connect(self.master.getFilePath(self.dbname), check_same_thread=False)

@ -1,3 +1,4 @@
apipkg==1.4
appdirs==1.4.3
certifi==2017.4.17
chardet==3.0.4
@ -5,6 +6,7 @@ cheroot==5.9.1
CherryPy==12.0.1
coverage==4.4.2
decorator==4.0.11
execnet==1.5.0
idna==2.5
ipdb==0.10.3
ipython==6.0.0
@ -30,6 +32,8 @@ pyparsing==2.2.0
PySocks==1.6.7
pytest==3.2.5
pytest-cov==2.5.1
pytest-forked==0.2
pytest-xdist==1.20.1
pytz==2017.3
pyzmq==16.0.2
requests==2.18.1

@ -3,4 +3,4 @@
export PYTHONUNBUFFERED=1
export PYTHONPATH=.
py.test --cov=pyircbot --cov-report html tests/
py.test --cov=pyircbot --cov-report html -n 4 tests/

@ -1,8 +1,9 @@
import os
import sys
import pytest
from random import randint
from threading import Thread
from random import randint
from pyircbot import PyIRCBot
from pyircbot.pyircbot import PrimitiveBot
from pyircbot.irccore import IRCEvent, UserPrefix
from unittest.mock import MagicMock
@ -20,6 +21,7 @@ class FakeBaseBot(PrimitiveBot):
def __init__(self, config):
super().__init__(config)
self.act_PRIVMSG = MagicMock()
self._modules = []
def feed_line(self, trailing, cmd="PRIVMSG", args=["#test"], sender=("chatter", "root", "cia.gov")):
"""
@ -36,13 +38,27 @@ class FakeBaseBot(PrimitiveBot):
if validation:
hook.method(msg, validation)
def closeAllModules(self):
for modname in self._modules:
self.unloadmodule(modname)
def loadmodule(self, module_name):
super().loadmodule(module_name)
self._modules.append(module_name)
def unloadmodule(self, module_name):
super().unloadmodule(module_name)
self._modules.remove(module_name)
@pytest.fixture
def fakebot():
def fakebot(tmpdir):
# TODO copy data tree to isolated place so each fakebot() is isolated
bot = FakeBaseBot({"bot": {"datadir": "./examples/data/"},
os.mkdir(os.path.join(tmpdir, "data"))
bot = FakeBaseBot({"bot": {"datadir": tmpdir},
"module_configs": {}})
yield bot
bot.closeAllModules()
@pytest.fixture
@ -79,3 +95,70 @@ def ircserver():
server_t.start()
yield port, server
server.stop()
@pytest.fixture
def livebot(ircserver, tmpdir):
port, server = ircserver
channel = "#test" + str(randint(100000, 1000000))
nick = "testbot" + str(randint(100000, 1000000))
config = {
"bot": {
"datadir": tmpdir,
"rpcbind": "0.0.0.0",
"rpcport": -1,
"usermodules": []
},
"connection": {
"servers": [
["localhost", port]
],
"force_ipv6": False,
"rate_limit": {
"rate_max": 5.0,
"rate_int": 1.1
}
},
"modules": [
"PingResponder",
"Services"
],
"module_configs": {
"Services": {
"user": {
"nick": [
nick,
nick + "_",
nick + "__"
],
"password": "nickservpassword",
"username": "pyircbot3",
"hostname": "pyircbot3.domain.com",
"realname": "pyircbot3"
},
"ident": {
"enable": "no",
"to": "nickserv",
"command": "identify %(password)s",
"ghost": "no",
"ghost_to": "nickserv",
"ghost_cmd": "ghost %(nick)s %(password)s"
},
"channels": [
channel
],
"privatechannels": {
"to": "chanserv",
"command": "invite %(channel)s",
"list": []
}
}
}
}
bot = PyIRCBot(config)
bot_t = Thread(target=bot.run, daemon=True)
# bot_t.start()
yield port, server, bot, bot_t, channel, nick
bot.kill(message="bye", forever=True)

@ -1,9 +1,20 @@
import os
import pytest
from tests.lib import * # NOQA - fixtures
@pytest.fixture
def bot(fakebot):
fakebot.botconfig["module_configs"]["ASCII"] = {
"line_delay": 1.1,
"allow_parallel": False,
"allow_hilight": True,
"list_max": 15
}
adir = os.path.join(fakebot.botconfig["bot"]["datadir"], "data", "ASCII")
os.makedirs(adir, exist_ok=True)
with open(os.path.join(adir, "test.txt"), "w") as f:
f.write("hello world!")
fakebot.loadmodule("ASCII")
return fakebot
@ -11,3 +22,8 @@ def bot(fakebot):
def test_ascii(bot):
bot.feed_line(".ascii test")
bot.act_PRIVMSG.assert_called_once_with('#test', 'hello world!')
def test_listascii(bot):
bot.feed_line(".listascii")
bot.act_PRIVMSG.assert_called_once_with('#test', 'Avalable asciis: test')

@ -15,13 +15,13 @@ def calcbot(fakebot):
"delayCalcSpecific": 0,
"delayMatch": 0}
fakebot.loadmodule("SQLite")
tables = ["calc_addedby", "calc_channels", "calc_definitions", "calc_words"]
with closing(fakebot.moduleInstances["SQLite"].opendb("calc.db")) as db:
for q in ["DROP TABLE calc_addedby;",
"DROP TABLE calc_channels;",
"DROP TABLE calc_definitions;",
"DROP TABLE calc_words;"]:
db.query(q)
for t in tables:
db.query("DROP TABLE IF EXISTS `{}`;".format(t))
fakebot.loadmodule("Calc")
for t in tables:
assert fakebot.moduleInstances["Calc"].sql.tableExists("calc_addedby")
return fakebot

@ -19,10 +19,8 @@ def nickbot(fakebot):
"delayMatch": 0}
fakebot.loadmodule("SQLite")
with closing(fakebot.moduleInstances["SQLite"].opendb("attributes.db")) as db:
for q in ["DROP TABLE attribute;",
"DROP TABLE items;",
"DROP TABLE `values`;"]:
db.query(q)
for table in ["attribute", "items", "values"]:
db.query("DROP TABLE IF EXISTS `{}`;".format(table))
fakebot.loadmodule("AttributeStorageLite")
fakebot.loadmodule("NickUser")
return fakebot
@ -37,7 +35,15 @@ def test_blind_login(nickbot):
nickbot.act_PRIVMSG.assert_called_once_with('chatter', '.login: You must first set a password with .setpass')
def test_no_pms(nickbot):
nickbot.feed_line(".login foobar")
nickbot.act_PRIVMSG.assert_not_called()
def test_register(nickbot):
pm(nickbot, ".setpass")
nickbot.act_PRIVMSG.assert_called_once_with('chatter', '.setpass: usage: ".setpass newpass" or ".setpass oldpass newpass"')
nickbot.act_PRIVMSG.reset_mock()
pm(nickbot, ".setpass foobar")
nickbot.act_PRIVMSG.assert_called_once_with('chatter', '.setpass: Your password has been set to "foobar".')
nickbot.act_PRIVMSG.reset_mock()
@ -45,6 +51,9 @@ def test_register(nickbot):
def test_register_login(nickbot):
test_register(nickbot)
pm(nickbot, ".login")
nickbot.act_PRIVMSG.assert_called_once_with('chatter', '.login: usage: ".login password"')
nickbot.act_PRIVMSG.reset_mock()
pm(nickbot, ".login foobar")
nickbot.act_PRIVMSG.assert_called_once_with('chatter', '.login: You have been logged in from: cia.gov')
nickbot.act_PRIVMSG.reset_mock()

@ -0,0 +1,41 @@
import pytest
from contextlib import closing
from tests.lib import * # NOQA - fixtures
from time import sleep
import datetime
@pytest.fixture
def rbot(fakebot):
"""
Provide a bot loaded with the Calc module. Clear the database.
"""
fakebot.botconfig["module_configs"]["Remind"] = {"mytimezone": "US/Pacific", "precision": 0.2}
fakebot.loadmodule("SQLite")
with closing(fakebot.moduleInstances["SQLite"].opendb("remind.db")) as db:
db.query("DROP TABLE IF EXISTS `reminders`;")
fakebot.loadmodule("Remind")
return fakebot
@pytest.mark.slow
def test_remind_in(rbot):
rbot.feed_line(".in 3s frig off")
rbot.act_PRIVMSG.assert_called_once_with('#test', 'chatter: Ok, talk to you in approx 0h0m')
rbot.act_PRIVMSG.reset_mock()
sleep(2.5)
rbot.act_PRIVMSG.assert_not_called()
sleep(1)
rbot.act_PRIVMSG.assert_called_once_with('#test', 'chatter: Reminder: frig off')
@pytest.mark.slow
def test_remind_at(rbot):
then = datetime.datetime.now() + datetime.timedelta(seconds=3)
rbot.feed_line(".at {} frig off".format(then.strftime("%H:%M:%SPDT")))
rbot.act_PRIVMSG.assert_called_once_with('#test', 'chatter: Ok, will do. Approx 0h0m to go.')
rbot.act_PRIVMSG.reset_mock()
sleep(2)
rbot.act_PRIVMSG.assert_not_called()
sleep(2)
rbot.act_PRIVMSG.assert_called_once_with('#test', 'chatter: Reminder: frig off')

@ -11,7 +11,7 @@ def tellbot(fakebot):
fakebot.botconfig["module_configs"]["Tell"] = {"max": 10, "maxage": 2678400}
fakebot.loadmodule("SQLite")
with closing(fakebot.moduleInstances["SQLite"].opendb("tell.db")) as db:
db.query("DROP TABLE tells;")
db.query("DROP TABLE IF EXISTS tells;")
fakebot.loadmodule("Tell")
return fakebot

@ -0,0 +1,87 @@
from tests.lib import * # NOQA - fixtures
from unittest.mock import MagicMock, call
from pyircbot.rpc import BotRPC
from pyircbot.rpcclient import connect
from random import randint
from time import sleep
def test_rpc(monkeypatch):
port = randint(40000, 65000)
m = MagicMock()
m.botconfig = {"bot": {"rpcbind": "127.0.0.1", "rpcport": port}}
server = BotRPC(m)
sleep(0.05)
calltrack = MagicMock()
def fake(*args):
calltrack(*args)
return args
for k, v in server.server.funcs.items():
server.server.funcs[k] = fake
methods = [["importModule", "foo"],
["deportModule", "foo"],
["loadModule", "foo"],
["unloadModule", "foo"],
["reloadModule", "foo"],
["redoModule", "foo"],
["getLoadedModules"],
["pluginCommand", "foo", "foo", "foo"],
["setPluginVar", "foo", "foo"],
["getPluginVar", "foo", "foo", "foo"],
["eval", "foo"],
["exec", "foo"],
["quit", "foo"]]
client = connect("127.0.0.1", port)
for test in methods:
method = test[0]
args = test[1:]
server.server.funcs[method] = fake
print("Calling {} with: {}".format(method, args))
getattr(client, method)(*args)
calltrack.assert_called_once_with(*args)
calltrack.reset_mock()
def test_rpc_internal(monkeypatch):
port = randint(40000, 65000)
m = MagicMock()
m.botconfig = {"bot": {"rpcbind": "127.0.0.1", "rpcport": port}}
server = BotRPC(m)
methods = [["importModule", "foo"],
["deportModule", "foo"],
["loadModule", "foo"],
["unloadModule", "foo"],
["redoModule", "foo"],]
for test in methods:
method = test[0]
args = test[1:]
getattr(server, method)(*args)
getattr(m, method.lower()).assert_called_once_with(*args)
getattr(m, method.lower()).reset_mock()
m.moduleInstances = {"Foo": None, "Bar": None}
assert server.getLoadedModules() == ["Foo", "Bar"]
m.reset_mock()
server.reloadModule("Foo")
m.unloadmodule.assert_called_once_with("Foo")
m.loadmodule.assert_called_once_with("Foo")
m.reset_mock()
# ["pluginCommand", "foo", "foo", "foo"],
# ["setPluginVar", "foo", "foo"],
# ["getPluginVar", "foo", "foo", "foo"]
# ["eval", "foo"],
# ["exec", "foo"],
# ["quit", "foo"]]
Loading…
Cancel
Save