More tests

This commit is contained in:
dave 2017-12-03 20:54:39 -08:00
parent 585efb2c18
commit e8652c37c8
13 changed files with 262 additions and 26 deletions

View File

@ -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)

View File

@ -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

View File

@ -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]

View File

@ -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)

View File

@ -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

View File

@ -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/

View File

@ -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)

View File

@ -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')

View File

@ -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

View File

@ -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()

View File

@ -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')

View File

@ -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

87
tests/test_rpcclient.py Normal file
View File

@ -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"]]