Initial commit.

This commit is contained in:
2021-05-24 22:18:33 +03:00
commit e2954d55f4
3701 changed files with 330017 additions and 0 deletions

View File

@@ -0,0 +1,32 @@
"""A suite of unit tests that verifies the correct behaviour of various
functions/methods in the pyzord code.
Note these tests the source of pyzor, not the version currently installed.
"""
import unittest
import test_gdbm
import test_mysql
import test_redis
import test_client
import test_digest
import test_server
import test_account
def suite():
"""Gather all the tests from this package in a test suite."""
test_suite = unittest.TestSuite()
test_suite.addTest(test_gdbm.suite())
test_suite.addTest(test_mysql.suite())
test_suite.addTest(test_redis.suite())
test_suite.addTest(test_client.suite())
test_suite.addTest(test_digest.suite())
test_suite.addTest(test_server.suite())
test_suite.addTest(test_account.suite())
return test_suite
if __name__ == '__main__':
unittest.main(defaultTest='suite')

View File

@@ -0,0 +1,139 @@
"""Test the pyzor.account module
"""
import os
import sys
import time
import email
import hashlib
import unittest
import StringIO
import pyzor
import pyzor.config
import pyzor.account
class AccountTest(unittest.TestCase):
def setUp(self):
unittest.TestCase.setUp(self)
self.timestamp = 1381219396
self.msg = email.message_from_string("")
self.msg["Op"] = "ping"
self.msg["Thread"] = "14941"
self.msg["PV"] = "2.1"
self.msg["User"] = "anonymous"
self.msg["Time"] = str(self.timestamp)
def tearDown(self):
unittest.TestCase.tearDown(self)
def test_sign_msg(self):
"""Test the sign message function"""
hashed_key = hashlib.sha1(b"test_key").hexdigest()
expected = "2ab1bad2aae6fd80c656a896c82eef0ec1ec38a0"
result = pyzor.account.sign_msg(hashed_key, self.timestamp, self.msg)
self.assertEqual(result, expected)
def test_hash_key(self):
"""Test the hash key function"""
user = "testuser"
key = "testkey"
expected = "0957bd79b58263657127a39762879098286d8477"
result = pyzor.account.hash_key(key, user)
self.assertEqual(result, expected)
def test_verify_signature(self):
"""Test the verify signature function"""
def mock_sm(h, t, m):
return "testsig"
real_sm = pyzor.account.sign_msg
pyzor.account.sign_msg = mock_sm
try:
self.msg["Sig"] = "testsig"
del self.msg["Time"]
self.msg["Time"] = str(int(time.time()))
pyzor.account.verify_signature(self.msg, "testkey")
finally:
pyzor.account.sign_msg = real_sm
def test_verify_signature_old_timestamp(self):
"""Test the verify signature with old timestamp"""
def mock_sm(h, t, m):
return "testsig"
real_sm = pyzor.account.sign_msg
pyzor.account.sign_msg = mock_sm
try:
self.msg["Sig"] = "testsig"
self.assertRaises(pyzor.SignatureError, pyzor.account.verify_signature, self.msg, "testkey")
finally:
pyzor.account.sign_msg = real_sm
def test_verify_signature_bad_signature(self):
"""Test the verify signature with invalid signature"""
def mock_sm(h, t, m):
return "testsig"
real_sm = pyzor.account.sign_msg
pyzor.account.sign_msg = mock_sm
try:
self.msg["Sig"] = "testsig-bad"
del self.msg["Time"]
self.msg["Time"] = str(int(time.time()))
self.assertRaises(pyzor.SignatureError, pyzor.account.verify_signature, self.msg, "testkey")
finally:
pyzor.account.sign_msg = real_sm
class LoadAccountTest(unittest.TestCase):
"""Tests for the load_accounts function"""
def setUp(self):
unittest.TestCase.setUp(self)
self.real_exists = os.path.exists
os.path.exists = lambda p: True
self.mock_file = StringIO.StringIO()
self.real_open = pyzor.account.__builtins__["open"]
def mock_open(path, mode="r", buffering=-1):
if path == "test_file":
self.mock_file.seek(0)
return self.mock_file
else:
return self.real_open(path, mode, buffering)
pyzor.account.__builtins__["open"] = mock_open
def tearDown(self):
unittest.TestCase.tearDown(self)
os.path.exists = self.real_exists
pyzor.account.__builtins__["open"] = self.real_open
def test_load_accounts(self):
"""Test loading the account file"""
self.mock_file.write("public.pyzor.org : 24441 : test : 123abc,cba321\n"
"public2.pyzor.org : 24441 : test2 : 123abc,cba321")
result = pyzor.config.load_accounts("test_file")
self.assertIn(("public.pyzor.org", 24441), result)
self.assertIn(("public2.pyzor.org", 24441), result)
account = result[("public.pyzor.org", 24441)]
self.assertEqual((account.username, account.salt, account.key),
("test", "123abc", "cba321"))
account = result[("public2.pyzor.org", 24441)]
self.assertEqual((account.username, account.salt, account.key),
("test2", "123abc", "cba321"))
def test_load_accounts_comment(self):
"""Test skipping commented lines"""
self.mock_file.write("#public1.pyzor.org : 24441 : test : 123abc,cba321")
result = pyzor.config.load_accounts("test_file")
self.assertNotIn(("public.pyzor.org", 24441), result)
self.assertFalse(result)
def suite():
"""Gather all the tests from this module in a test suite."""
test_suite = unittest.TestSuite()
test_suite.addTest(unittest.makeSuite(AccountTest))
test_suite.addTest(unittest.makeSuite(LoadAccountTest))
return test_suite
if __name__ == '__main__':
unittest.main()

View File

@@ -0,0 +1,168 @@
import sys
import time
import socket
import unittest
import pyzor
import pyzor.client
import pyzor.account
import pyzor.message
def make_MockSocket(response, request):
"""Create a MockSocket class that will append requests to
the specified `request` list and return the specified `response`
"""
class MockSocket():
def __init__(self, *args, **kwargs):
pass
def settimeout(self, timeout):
pass
def recvfrom(self, packetsize):
return response, ("127.0.0.1", 24441)
def sendto(self, data, flag, address):
request.append(data)
return MockSocket
def make_MockThreadId(thread):
"""Creates a MockThreadId class that will generate
the specified thread number.
"""
class MockThreadId(int):
def __new__(cls, i):
return int.__new__(cls, i)
@classmethod
def generate(cls):
return thread
def in_ok_range(self):
return True
return MockThreadId
def mock_sign_msg(hash_key, timestamp, msg):
return "TestSig"
def mock_hash_key(user_key, user):
return None
class ClientTest(unittest.TestCase):
def setUp(self):
unittest.TestCase.setUp(self)
self.real_sg = pyzor.account.sign_msg
pyzor.account.sign_msg = mock_sign_msg
self.real_hk = pyzor.account.hash_key
pyzor.account.hash_key = mock_hash_key
self.thread = 33715
# the response the mock socket will send
self.response = "Code: 200\nDiag: OK\nPV: 2.1\nThread: 33715\n\n"
# the requests send by the client will be stored here
self.request = []
# the expected request that the client should send
self.expected = {"Thread": str(self.thread),
"PV": str(pyzor.proto_version),
"User": "anonymous",
"Time": str(int(time.time())),
"Sig": "TestSig"}
def tearDown(self):
unittest.TestCase.tearDown(self)
pyzor.account.sign_msg = self.real_sg
pyzor.account.hash_key = self.real_hk
def check_request(self, request):
"""Check if the request sent by the client is equal
to the expected one.
"""
req = {}
request = request.decode("utf8").replace("\n\n", "\n")
for line in request.splitlines():
key = line.split(":")[0].strip()
value = line.split(":")[1].strip()
req[key] = value
self.assertEqual(req, self.expected)
def check_client(self, accounts, method, *args, **kwargs):
"""Tests if the request and response are sent
and read correctly by the client.
"""
real_socket = socket.socket
socket.socket = make_MockSocket(self.response.encode("utf8"),
self.request)
real_ThreadId = pyzor.message.ThreadId
pyzor.message.ThreadId = make_MockThreadId(self.thread)
client = pyzor.client.Client(accounts)
try:
response = getattr(client, method)(*args, **kwargs)
self.assertEqual(str(response), self.response)
self.check_request(self.request[0])
finally:
socket.socket = real_socket
pyzor.message.ThreadId = real_ThreadId
return client
def test_ping(self):
"""Test the client ping request"""
self.expected["Op"] = "ping"
self.check_client(None, "ping")
def test_pong(self):
"""Test the client pong request"""
digest = "2aedaac999d71421c9ee49b9d81f627a7bc570aa"
self.expected["Op"] = "pong"
self.expected["Op-Digest"] = "2aedaac999d71421c9ee49b9d81f627a7bc570aa"
self.check_client(None, "pong", digest)
def test_check(self):
"""Test the client check request"""
digest = "2aedaac999d71421c9ee49b9d81f627a7bc570aa"
self.expected["Op"] = "check"
self.expected["Op-Digest"] = "2aedaac999d71421c9ee49b9d81f627a7bc570aa"
self.check_client(None, "check", digest)
def test_info(self):
"""Test the client info request"""
digest = "2aedaac999d71421c9ee49b9d81f627a7bc570aa"
self.expected["Op"] = "info"
self.expected["Op-Digest"] = "2aedaac999d71421c9ee49b9d81f627a7bc570aa"
self.check_client(None, "info", digest)
def test_report(self):
"""Test the client report request"""
digest = "2aedaac999d71421c9ee49b9d81f627a7bc570aa"
self.expected["Op"] = "report"
self.expected["Op-Digest"] = "2aedaac999d71421c9ee49b9d81f627a7bc570aa"
self.expected["Op-Spec"] = "20,3,60,3"
self.check_client(None, "report", digest)
def test_whitelist(self):
"""Test the client whitelist request"""
digest = "2aedaac999d71421c9ee49b9d81f627a7bc570aa"
self.expected["Op"] = "whitelist"
self.expected["Op-Digest"] = "2aedaac999d71421c9ee49b9d81f627a7bc570aa"
self.expected["Op-Spec"] = "20,3,60,3"
self.check_client(None, "whitelist", digest)
def test_handle_account(self):
"""Test client handling accounts"""
test_account = pyzor.account.Account("TestUser", "TestKey", "TestSalt")
self.expected["Op"] = "ping"
self.expected["User"] = "TestUser"
self.check_client({("public.pyzor.org", 24441): test_account}, "ping")
def test_handle_invalid_thread(self):
"""Test invalid thread id"""
self.thread += 20
self.expected["Op"] = "ping"
self.assertRaises(pyzor.ProtocolError, self.check_client, None, "ping")
def suite():
"""Gather all the tests from this module in a test suite."""
test_suite = unittest.TestSuite()
test_suite.addTest(unittest.makeSuite(ClientTest))
return test_suite
if __name__ == '__main__':
unittest.main()

View File

@@ -0,0 +1,200 @@
"""The the pyzor.digest module
"""
import sys
import hashlib
import unittest
from pyzor.digest import *
HTML_TEXT = """<html><head><title>Email spam</title></head><body>
<p><b>Email spam</b>, also known as <b>junk email</b>
or <b>unsolicited bulk email</b> (<i>UBE</i>), is a subset of
<a href="/wiki/Spam_(electronic)" title="Spam (electronic)">electronic spam</a>
involving nearly identical messages sent to numerous recipients by <a href="/wiki/Email" title="Email">
email</a>. Clicking on <a href="/wiki/Html_email#Security_vulnerabilities" title="Html email" class="mw-redirect">
links in spam email</a> may send users to <a href="/wiki/Phishing" title="Phishing">phishing</a>
web sites or sites that are hosting <a href="/wiki/Malware" title="Malware">malware</a>.</body></html>"""
HTML_TEXT_STRIPED = 'Email spam Email spam , also known as junk email or unsolicited bulk email ( UBE ),'\
' is a subset of electronic spam involving nearly identical messages sent to numerous recipients by email'\
' . Clicking on links in spam email may send users to phishing web sites or sites that are hosting malware .'
class HTMLStripperTests(unittest.TestCase):
def setUp(self):
unittest.TestCase.setUp(self)
self.data = []
def tearDown(self):
unittest.TestCase.tearDown(self)
def test_HTMLStripper(self):
stripper = HTMLStripper(self.data)
stripper.feed(HTML_TEXT)
res = " ".join(self.data)
self.assertEqual(res, HTML_TEXT_STRIPED)
class PreDigestTests(unittest.TestCase):
def setUp(self):
unittest.TestCase.setUp(self)
self.lines = []
def mock_digest_paylods(c, message):
yield message.decode("utf8")
def mock_handle_line(s, line):
self.lines.append(line.decode("utf8"))
self.real_digest_payloads = DataDigester.digest_payloads
self.real_handle_line = DataDigester.handle_line
DataDigester.digest_payloads = mock_digest_paylods
DataDigester.handle_line = mock_handle_line
def tearDown(self):
unittest.TestCase.tearDown(self)
DataDigester.digest_payloads = self.real_digest_payloads
DataDigester.handle_line = self.real_handle_line
def test_predigest_emails(self):
"""Test email removal in the predigest process"""
real_longstr = DataDigester.longstr_ptrn
DataDigester.longstr_ptrn = re.compile(r'\S{100,}')
emails = ["test@example.com",
"test123@example.com",
"test+abc@example.com",
"test.test2@example.com",
"test.test2+abc@example.com", ]
message = "Test %s Test2"
expected = "TestTest2"
try:
for email in emails:
self.lines = []
DataDigester((message % email).encode("utf8"))
self.assertEqual(self.lines[0], expected)
finally:
DataDigester.longstr_ptrn = real_longstr
# XXX This fails
# def test_predigest_emails_whitespace(self):
# real_longstr = DataDigester.longstr_ptrn
# DataDigester.longstr_ptrn = re.compile(r'\S{100,}')
# emails = ["chirila@example. com",
# "chirila@example . com",
# "chirila @example. com",
# "chirila@ example. com",
# "chirila @example . com",
# "chirila @ example. com",
# "chirila @ example . com",]
# message = "Test %s Test2"
# expected = "TestTest2"
# try:
# for email in emails:
# self.lines = []
# DataDigester(message % email)
# self.assertEqual(self.lines[0], expected)
# finally:
# DataDigester.longstr_ptrn = real_longstr
def test_predigest_urls(self):
"""Test url removal in the predigest process"""
real_longstr = DataDigester.longstr_ptrn
DataDigester.longstr_ptrn = re.compile(r'\S{100,}')
urls = ["http://www.example.com",
# "www.example.com", # XXX This also fail
"http://example.com",
# "example.com", # XXX This also fails
"http://www.example.com/test/"
"http://www.example.com/test/test2", ]
message = "Test %s Test2"
expected = "TestTest2"
try:
for url in urls:
self.lines = []
DataDigester((message % url).encode("utf8"))
self.assertEqual(self.lines[0], expected)
finally:
DataDigester.longstr_ptrn = real_longstr
def test_predigest_long(self):
"""Test long "words" removal in the predigest process"""
strings = ["0A2D3f%a#S",
"3sddkf9jdkd9",
"@@#@@@@@@@@@"]
message = "Test %s Test2"
expected = "TestTest2"
for string in strings:
self.lines = []
DataDigester((message % string).encode("utf8"))
self.assertEqual(self.lines[0], expected)
def test_predigest_min_line_lenght(self):
"""Test small lines removal in the predigest process"""
message = "This line is included\n"\
"not this\n"\
"This also"
expected = ["Thislineisincluded", "Thisalso"]
DataDigester(message.encode("utf8"))
self.assertEqual(self.lines, expected)
def test_predigest_atomic(self):
"""Test atomic messages (lines <= 4) in the predigest process"""
message = "All this message\nShould be included\nIn the predigest"
expected = ["Allthismessage", "Shouldbeincluded", "Inthepredigest"]
DataDigester(message.encode("utf8"))
self.assertEqual(self.lines, expected)
def test_predigest_pieced(self):
"""Test pieced messages (lines > 4) in the predigest process"""
message = ""
for i in range(100):
message += "Line%d test test test\n" % i
expected = []
for i in [20, 21, 22, 60, 61, 62]:
expected.append("Line%dtesttesttest" % i)
DataDigester(message.encode("utf8"))
self.assertEqual(self.lines, expected)
class DigestTests(unittest.TestCase):
def setUp(self):
unittest.TestCase.setUp(self)
self.lines = []
def mock_digest_paylods(c, message):
yield message.decode("utf8")
self.real_digest_payloads = DataDigester.digest_payloads
DataDigester.digest_payloads = mock_digest_paylods
def tearDown(self):
unittest.TestCase.tearDown(self)
DataDigester.digest_payloads = self.real_digest_payloads
def test_digest(self):
message = b"That's some good ham right there"
predigested = b"That'ssomegoodhamrightthere"
digest = hashlib.sha1()
digest.update(predigested)
expected = digest.hexdigest()
result = DataDigester(message).value
self.assertEqual(result, expected)
def suite():
"""Gather all the tests from this module in a test suite."""
test_suite = unittest.TestSuite()
test_suite.addTest(unittest.makeSuite(HTMLStripperTests))
test_suite.addTest(unittest.makeSuite(PreDigestTests))
test_suite.addTest(unittest.makeSuite(DigestTests))
return test_suite
if __name__ == '__main__':
unittest.main()

View File

@@ -0,0 +1,153 @@
"""Test the pyzor.engines.gdbm_ module."""
import gdbm
import unittest
import threading
from datetime import datetime, timedelta
import pyzor.engines
import pyzor.engines.gdbm_
import pyzor.engines.common
class MockTimer():
def __init__(self, *args, **kwargs):
pass
def start(self):
pass
def setDaemon(self, daemon):
pass
class MockGdbm(dict):
"""Mock a gdbm database"""
def firstkey(self):
if not self.keys():
return None
self.key_index = 1
return self.keys()[0]
def nextkey(self, key):
if len(self.keys()) <= self.key_index:
return None
else:
self.key_index += 1
return self.keys()[self.key_index]
def sync(self):
pass
def reorganize(self):
pass
class GdbmTest(unittest.TestCase):
"""Test the GdbmDBHandle class"""
max_age = 60 * 60 * 24 * 30 * 4
r_count = 24
wl_count = 42
entered = datetime.now() - timedelta(days=10)
updated = datetime.now() - timedelta(days=2)
wl_entered = datetime.now() - timedelta(days=20)
wl_updated = datetime.now() - timedelta(days=3)
def setUp(self):
unittest.TestCase.setUp(self)
self.real_timer = threading.Timer
threading.Timer = MockTimer
self.db = MockGdbm()
def mock_open(fn, mode):
return self.db
self.real_open = gdbm.open
gdbm.open = mock_open
self.record = pyzor.engines.common.Record(self.r_count, self.wl_count,
self.entered, self.updated,
self.wl_entered, self.wl_updated)
def tearDown(self):
unittest.TestCase.tearDown(self)
threading.Timer = self.real_timer
gdbm.open = self.real_open
def record_as_str(self, record=None):
if not record:
record = self.record
return ("1,%s,%s,%s,%s,%s,%s" % (record.r_count, record.r_entered,
record.r_updated, record.wl_count,
record.wl_entered, record.wl_updated)).encode("utf8")
def test_set_item(self):
"""Test GdbmDBHandle.__setitem__"""
digest = "2aedaac999d71421c9ee49b9d81f627a7bc570aa"
handle = pyzor.engines.gdbm_.GdbmDBHandle(None, None,
max_age=self.max_age)
handle[digest] = self.record
self.assertEqual(self.db[digest], self.record_as_str().decode("utf8"))
def test_get_item(self):
"""Test GdbmDBHandle.__getitem__"""
digest = "2aedaac999d71421c9ee49b9d81f627a7bc570aa"
handle = pyzor.engines.gdbm_.GdbmDBHandle(None, None,
max_age=self.max_age)
self.db[digest] = self.record_as_str()
result = handle[digest]
self.assertEqual(self.record_as_str(result), self.record_as_str())
def test_del_item(self):
"""Test GdbmDBHandle.__delitem__"""
digest = "2aedaac999d71421c9ee49b9d81f627a7bc570aa"
handle = pyzor.engines.gdbm_.GdbmDBHandle(None, None,
max_age=self.max_age)
self.db[digest] = self.record_as_str()
del handle[digest]
self.assertFalse(self.db.get(digest))
def test_reorganize_older(self):
"""Test GdbmDBHandle.start_reorganizing with older records"""
digest = "2aedaac999d71421c9ee49b9d81f627a7bc570aa"
self.db[digest] = self.record_as_str()
handle = pyzor.engines.gdbm_.GdbmDBHandle(None, None,
max_age=3600 * 24)
self.assertFalse(self.db.get(digest))
def test_reorganize_older_no_max_age(self):
"""Test GdbmDBHandle.start_reorganizing with older records, but no
max_age set.
"""
digest = "2aedaac999d71421c9ee49b9d81f627a7bc570aa"
self.db[digest] = self.record_as_str()
handle = pyzor.engines.gdbm_.GdbmDBHandle(None, None,
max_age=None)
self.assertEqual(self.db[digest], self.record_as_str())
def test_reorganize_fresh(self):
"""Test GdbmDBHandle.start_reorganizing with newer records"""
digest = "2aedaac999d71421c9ee49b9d81f627a7bc570aa"
self.db[digest] = self.record_as_str()
handle = pyzor.engines.gdbm_.GdbmDBHandle(None, None,
max_age=3600 * 24 * 3)
self.assertEqual(self.db[digest], self.record_as_str())
def suite():
"""Gather all the tests from this module in a test suite."""
test_suite = unittest.TestSuite()
test_suite.addTest(unittest.makeSuite(GdbmTest))
return test_suite
if __name__ == '__main__':
unittest.main()

View File

@@ -0,0 +1,156 @@
"""Test the pyzor.engines.mysql module."""
import unittest
import threading
from datetime import datetime, timedelta
import pyzor.engines
import pyzor.engines.mysql
import pyzor.engines.common
class MockTimer():
def __init__(self, *args, **kwargs):
pass
def start(self):
pass
def setDaemon(self, daemon):
pass
def make_MockMySQL(result, queries):
class MockCursor():
def __init__(self):
pass
def fetchone(self):
return result
def fetchall(self):
return [result]
def execute(self, query, args=None):
queries.append((query, args))
def close(self):
pass
class MockDB():
def cursor(self):
return MockCursor()
def close(self):
pass
def commit(self):
pass
def autocommit(self, value):
pass
class MockMysql():
@staticmethod
def connect(*args, **kwargs):
return MockDB()
class Error(Exception):
pass
return MockMysql
class MySQLTest(unittest.TestCase):
"""Test the GdbmDBHandle class"""
max_age = 60 * 60 * 24 * 30 * 4
r_count = 24
wl_count = 42
entered = datetime.now() - timedelta(days=10)
updated = datetime.now() - timedelta(days=2)
wl_entered = datetime.now() - timedelta(days=20)
wl_updated = datetime.now() - timedelta(days=3)
def setUp(self):
unittest.TestCase.setUp(self)
self.real_timer = threading.Timer
threading.Timer = MockTimer
self.record = pyzor.engines.common.Record(self.r_count, self.wl_count,
self.entered, self.updated,
self.wl_entered, self.wl_updated)
self.response = self.record_unpack()
self.queries = []
mock_MySQL = make_MockMySQL(self.response, self.queries)
self.real_mysql = pyzor.engines.mysql.MySQLdb
pyzor.engines.mysql.MySQLdb = mock_MySQL
def tearDown(self):
unittest.TestCase.tearDown(self)
threading.Timer = self.real_timer
pyzor.engines.mysql.MySQLdb = self.real_mysql
def record_unpack(self, record=None):
if not record:
record = self.record
return (record.r_count, record.wl_count,
record.r_entered, record.r_updated,
record.wl_entered, record.wl_updated)
def test_reconnect(self):
"""Test MySQLDBHandle.__init__"""
expected = "DELETE FROM testtable WHERE r_updated<%s"
pyzor.engines.mysql.MySQLDBHandle("testhost,testuser,testpass,testdb,testtable",
None, max_age=self.max_age)
self.assertEqual(self.queries[0][0], expected)
def test_no_reorganize(self):
pyzor.engines.mysql.MySQLDBHandle("testhost,testuser,testpass,testdb,testtable",
None, max_age=None)
self.assertFalse(self.queries)
def test_set_item(self):
"""Test MySQLDBHandle.__setitem__"""
digest = "2aedaac999d71421c9ee49b9d81f627a7bc570aa"
expected = ("INSERT INTO testtable (digest, r_count, wl_count, "
"r_entered, r_updated, wl_entered, wl_updated) "
"VALUES (%s, %s, %s, %s, %s, %s, %s) ON "
"DUPLICATE KEY UPDATE r_count=%s, wl_count=%s, "
"r_entered=%s, r_updated=%s, wl_entered=%s, "
"wl_updated=%s",
(digest, self.r_count, self.wl_count, self.entered,
self.updated, self.wl_entered, self.wl_updated,
self.r_count, self.wl_count, self.entered,
self.updated, self.wl_entered, self.wl_updated))
handle = pyzor.engines.mysql.MySQLDBHandle("testhost,testuser,testpass,testdb,testtable",
None, max_age=self.max_age)
handle[digest] = self.record
self.assertEqual(self.queries[1], expected)
def test_get_item(self):
"""Test MySQLDBHandle.__getitem__"""
digest = "2aedaac999d71421c9ee49b9d81f627a7bc570aa"
expected = ("SELECT r_count, wl_count, r_entered, r_updated, "
"wl_entered, wl_updated FROM testtable WHERE digest=%s",
(digest,))
handle = pyzor.engines.mysql.MySQLDBHandle("testhost,testuser,testpass,testdb,testtable",
None, max_age=self.max_age)
result = handle[digest]
self.assertEqual(self.queries[1], expected)
self.assertEqual(self.record_unpack(result), self.record_unpack())
def test_del_item(self):
"""Test MySQLDBHandle.__detitem__"""
digest = "2aedaac999d71421c9ee49b9d81f627a7bc570aa"
expected = ("DELETE FROM testtable WHERE digest=%s", (digest,))
handle = pyzor.engines.mysql.MySQLDBHandle("testhost,testuser,testpass,testdb,testtable",
None, max_age=self.max_age)
del handle[digest]
self.assertEqual(self.queries[1], expected)
def suite():
"""Gather all the tests from this module in a test suite."""
test_suite = unittest.TestSuite()
test_suite.addTest(unittest.makeSuite(MySQLTest))
return test_suite
if __name__ == '__main__':
unittest.main()

View File

@@ -0,0 +1,188 @@
"""Test the pyzor.engines.gdbm_ module."""
import unittest
from datetime import datetime, timedelta
import pyzor.engines
import pyzor.engines.redis_
import pyzor.engines.common
class EncodingRedisTest(unittest.TestCase):
"""Test the RedisDBHandle class"""
r_count = 24
wl_count = 42
entered = datetime(2014, 4, 23, 15, 41, 30)
updated = datetime(2014, 4, 25, 17, 22, 25)
wl_entered = datetime(2014, 2, 12, 11, 10, 55)
wl_updated = datetime(2014, 3, 25, 5, 1, 50)
def setUp(self):
unittest.TestCase.setUp(self)
self.record = pyzor.engines.common.Record(self.r_count, self.wl_count,
self.entered, self.updated,
self.wl_entered, self.wl_updated)
def compare_records(self, r1, r2):
attrs = ("r_count", "r_entered", "r_updated",
"wl_count", "wl_entered", "wl_updated")
self.assertTrue(all(getattr(r1, attr) == getattr(r2, attr)
for attr in attrs))
def tearDown(self):
unittest.TestCase.tearDown(self)
def test_encode_record(self):
expected = ("24,2014-04-23 15:41:30,2014-04-25 17:22:25,"
"42,2014-02-12 11:10:55,2014-03-25 05:01:50").encode()
result = pyzor.engines.redis_.RedisDBHandle._encode_record(self.record)
self.assertEqual(result, expected)
def test_encode_record_no_date(self):
expected = ("24,2014-04-23 15:41:30,,"
"42,2014-02-12 11:10:55,2014-03-25 05:01:50").encode()
self.record.r_updated = None
result = pyzor.engines.redis_.RedisDBHandle._encode_record(self.record)
self.assertEqual(result, expected)
def test_encode_record_no_white(self):
expected = ("24,2014-04-23 15:41:30,2014-04-25 17:22:25,"
"0,,").encode()
self.record.wl_count = 0
self.record.wl_entered = None
self.record.wl_updated = None
result = pyzor.engines.redis_.RedisDBHandle._encode_record(self.record)
self.assertEqual(result, expected)
def test_decode_record(self):
encoded = ("24,2014-04-23 15:41:30,2014-04-25 17:22:25,"
"42,2014-02-12 11:10:55,2014-03-25 05:01:50").encode()
result = pyzor.engines.redis_.RedisDBHandle._decode_record(encoded)
self.compare_records(result, self.record)
def test_decode_record_no_date(self):
encoded = ("24,2014-04-23 15:41:30,,"
"42,2014-02-12 11:10:55,2014-03-25 05:01:50").encode()
result = pyzor.engines.redis_.RedisDBHandle._decode_record(encoded)
self.record.r_updated = None
self.compare_records(result, self.record)
def test_decode_record_no_white(self):
encoded = ("24,2014-04-23 15:41:30,2014-04-25 17:22:25,"
"0,,").encode()
result = pyzor.engines.redis_.RedisDBHandle._decode_record(encoded)
self.record.wl_count = 0
self.record.wl_entered = None
self.record.wl_updated = None
self.compare_records(result, self.record)
def make_MockRedis(commands):
class MockRedis():
def __init__(self, *args, **kwargs):
commands.append(("init", args, kwargs))
def set(self, *args, **kwargs):
commands.append(("set", args, kwargs))
def setex(self, *args, **kwargs):
commands.append(("setex", args, kwargs))
def get(self, *args, **kwargs):
commands.append(("get", args, kwargs))
def delete(self, *args, **kwargs):
commands.append(("delete", args, kwargs))
return MockRedis
mock_encode_record = lambda s, x: x
mock_decode_record = lambda s, x: x
class RedisTest(unittest.TestCase):
max_age = 60 * 60
def setUp(self):
unittest.TestCase.setUp(self)
self.commands = []
self.real_redis = pyzor.engines.redis_.redis.StrictRedis
self.real_encode = pyzor.engines.redis_.RedisDBHandle._encode_record
self.real_decode = pyzor.engines.redis_.RedisDBHandle._decode_record
pyzor.engines.redis_.redis.StrictRedis = make_MockRedis(self.commands)
pyzor.engines.redis_.RedisDBHandle._encode_record = mock_encode_record
pyzor.engines.redis_.RedisDBHandle._decode_record = mock_decode_record
def tearDown(self):
unittest.TestCase.tearDown(self)
pyzor.engines.redis_.redis.StrictRedis = self.real_redis
pyzor.engines.redis_.RedisDBHandle._encode_record = self.real_encode
pyzor.engines.redis_.RedisDBHandle._decode_record = self.real_decode
def test_init(self):
expected = {"host": "example.com",
"port": 6387,
"password": "passwd",
"db": 5,
}
db = pyzor.engines.redis_.RedisDBHandle("example.com,6387,passwd,5",
None)
self.assertEqual(self.commands[0], ("init", (), expected))
def test_init_defaults(self):
expected = {"host": "localhost",
"port": 6379,
"password": None,
"db": 0,
}
db = pyzor.engines.redis_.RedisDBHandle(",,,", None)
self.assertEqual(self.commands[0], ("init", (), expected))
def test_set(self):
digest = "2aedaac999d71421c9ee49b9d81f627a7bc570aa"
value = "record test"
db = pyzor.engines.redis_.RedisDBHandle(",,,", None)
db[digest] = value
expected = ("pyzord.digest.%s" % digest, value)
self.assertEqual(self.commands[1], ("set", expected, {}))
def test_set_max_age(self):
digest = "2aedaac999d71421c9ee49b9d81f627a7bc570aa"
value = "record test"
db = pyzor.engines.redis_.RedisDBHandle(",,,", None,
max_age=self.max_age)
db[digest] = value
expected = ("pyzord.digest.%s" % digest, self.max_age, value)
self.assertEqual(self.commands[1], ("setex", expected, {}))
def test_get(self):
digest = "2aedaac999d71421c9ee49b9d81f627a7bc570aa"
db = pyzor.engines.redis_.RedisDBHandle(",,,", None)
result = db[digest]
expected = ("pyzord.digest.%s" % digest,)
self.assertEqual(self.commands[1], ("get", expected, {}))
def test_delete(self):
digest = "2aedaac999d71421c9ee49b9d81f627a7bc570aa"
db = pyzor.engines.redis_.RedisDBHandle(",,,", None)
del db[digest]
expected = ("pyzord.digest.%s" % digest,)
self.assertEqual(self.commands[1], ("delete", expected, {}))
def suite():
"""Gather all the tests from this module in a test suite."""
test_suite = unittest.TestSuite()
test_suite.addTest(unittest.makeSuite(EncodingRedisTest))
test_suite.addTest(unittest.makeSuite(RedisTest))
return test_suite
if __name__ == '__main__':
unittest.main()

View File

@@ -0,0 +1,341 @@
"""Test the pyzor.server module
"""
import io
import sys
import time
import logging
import hashlib
import unittest
import SocketServer
from datetime import datetime, timedelta
import pyzor
import pyzor.server
import pyzor.engines.common
class MockServer():
"""Mocks the pyzor.server.Server class"""
def __init__(self):
self.log = logging.getLogger("pyzord")
self.usage_log = logging.getLogger("pyzord-usage")
self.log.addHandler(logging.NullHandler())
self.usage_log.addHandler(logging.NullHandler())
class MockDatagramRequestHandler():
""" Mock the SocketServer.DatagramRequestHand."""
def __init__(self, headers, database=None, acl=None, accounts=None):
"""Initiates an request handler and set's the data in `headers` as
the request. Also set's the database, acl and accounts for the
MockServer.
This will be set as base class for RequestHandler.
"""
self.rfile = io.BytesIO()
self.wfile = io.BytesIO()
for i, j in headers.iteritems():
self.rfile.write(("%s: %s\n" % (i, j)).encode("utf8"))
self.rfile.seek(0)
self.packet = None
self.client_address = ["127.0.0.1"]
# Setup MockServer data
self.server = MockServer()
self.server.database = database
if acl:
self.server.acl = acl
else:
self.server.acl = {pyzor.anonymous_user: ("check", "report", "ping", "info", "whitelist",)}
self.server.accounts = accounts
self.handle()
def handle(self):
pass
class RequestHandlerTest(unittest.TestCase):
def setUp(self):
unittest.TestCase.setUp(self)
self.real_drh = SocketServer.DatagramRequestHandler
SocketServer.DatagramRequestHandler = MockDatagramRequestHandler
pyzor.server.RequestHandler.__bases__ = (MockDatagramRequestHandler,)
# setup the basic values for request and response
self.request = {"User": pyzor.anonymous_user,
"Time": str(int(time.time())),
"PV": str(pyzor.proto_version),
"Thread": "3597"}
self.expected_response = {"Code": "200",
"Diag": "OK",
"PV": str(pyzor.proto_version),
"Thread": "3597"}
def tearDown(self):
unittest.TestCase.tearDown(self)
SocketServer.DatagramRequestHandler = self.real_drh
pyzor.server.RequestHandler.__bases__ = (self.real_drh,)
def check_response(self, handler):
"""Checks if the response from the handler is equal to
the expected response.
"""
handler.wfile.seek(0)
response = handler.wfile.read()
response = response.decode("utf8").replace("\n\n", "\n")
result = {}
for line in response.splitlines():
key = line.split(":", 1)[0].strip()
value = line.split(":")[1].strip()
result[key] = value
self.assertEqual(result, self.expected_response)
def timestamp(self, time_obj):
if not time_obj:
return 0
else:
return str(int(time.mktime(time_obj.timetuple())))
def test_ping(self):
"""Tests the ping command handler"""
self.request["Op"] = "ping"
handler = pyzor.server.RequestHandler(self.request)
self.check_response(handler)
def test_pong(self):
"""Tests the pong command handler"""
digest = "2aedaac999d71421c9ee49b9d81f627a7bc570aa"
database = {digest: pyzor.engines.common.Record(24, 42)}
self.request["Op"] = "pong"
self.request["Op-Digest"] = digest
handler = pyzor.server.RequestHandler(self.request, database)
self.expected_response["Count"] = str(sys.maxint)
self.expected_response["WL-Count"] = "0"
def test_check(self):
"""Tests the check command handler"""
digest = "2aedaac999d71421c9ee49b9d81f627a7bc570aa"
database = {digest: pyzor.engines.common.Record(24, 42)}
self.request["Op"] = "check"
self.request["Op-Digest"] = digest
handler = pyzor.server.RequestHandler(self.request, database)
self.expected_response["Count"] = "24"
self.expected_response["WL-Count"] = "42"
self.check_response(handler)
def test_check_new(self):
"""Tests the check command handler with a new record"""
digest = "2aedaac999d71421c9ee49b9d81f627a7bc570aa"
database = {}
self.request["Op"] = "check"
self.request["Op-Digest"] = digest
handler = pyzor.server.RequestHandler(self.request, database)
self.expected_response["Count"] = "0"
self.expected_response["WL-Count"] = "0"
self.check_response(handler)
def test_info(self):
"""Tests the info command handler"""
entered = datetime.now() - timedelta(days=10)
updated = datetime.now()
wl_entered = datetime.now() - timedelta(days=20)
wl_updated = datetime.now() - timedelta(days=2)
digest = "2aedaac999d71421c9ee49b9d81f627a7bc570aa"
database = {digest: pyzor.engines.common.Record(24, 42, entered, updated,
wl_entered, wl_updated)}
self.request["Op"] = "info"
self.request["Op-Digest"] = digest
handler = pyzor.server.RequestHandler(self.request, database)
self.expected_response["Count"] = "24"
self.expected_response["WL-Count"] = "42"
self.expected_response["Entered"] = self.timestamp(entered)
self.expected_response["Updated"] = self.timestamp(updated)
self.expected_response["WL-Entered"] = self.timestamp(wl_entered)
self.expected_response["WL-Updated"] = self.timestamp(wl_updated)
self.check_response(handler)
def test_info_new(self):
"""Tests the info command handler with a new record"""
digest = "2aedaac999d71421c9ee49b9d81f627a7bc570aa"
database = {}
self.request["Op"] = "info"
self.request["Op-Digest"] = digest
handler = pyzor.server.RequestHandler(self.request, database)
self.expected_response["Count"] = "0"
self.expected_response["WL-Count"] = "0"
self.expected_response["Entered"] = "0"
self.expected_response["Updated"] = "0"
self.expected_response["WL-Entered"] = "0"
self.expected_response["WL-Updated"] = "0"
self.check_response(handler)
def test_report(self):
"""Tests the report command handler"""
digest = "2aedaac999d71421c9ee49b9d81f627a7bc570aa"
database = {digest: pyzor.engines.common.Record(24, 42)}
self.request["Op"] = "report"
self.request["Op-Digest"] = digest
handler = pyzor.server.RequestHandler(self.request, database)
self.check_response(handler)
self.assertEqual(database[digest].r_count, 25)
def test_report_new(self):
"""Tests the report command handler with a new record"""
digest = "2aedaac999d71421c9ee49b9d81f627a7bc570aa"
database = {}
self.request["Op"] = "report"
self.request["Op-Digest"] = digest
handler = pyzor.server.RequestHandler(self.request, database)
self.check_response(handler)
self.assertEqual(database[digest].r_count, 1)
def test_whitelist(self):
"""Tests the whitelist command handler"""
digest = "2aedaac999d71421c9ee49b9d81f627a7bc570aa"
database = {digest: pyzor.engines.common.Record(24, 42)}
self.request["Op"] = "whitelist"
self.request["Op-Digest"] = digest
handler = pyzor.server.RequestHandler(self.request, database)
self.check_response(handler)
self.assertEqual(database[digest].wl_count, 43)
def test_whitelist_new(self):
"""Tests the whitelist command handler with a new record"""
digest = "2aedaac999d71421c9ee49b9d81f627a7bc570aa"
database = {}
self.request["Op"] = "whitelist"
self.request["Op-Digest"] = digest
handler = pyzor.server.RequestHandler(self.request, database)
self.check_response(handler)
self.assertEqual(database[digest].wl_count, 1)
def test_handle_no_version(self):
"""Tests handling an request with no version specified"""
self.request["Op"] = "ping"
del self.request["PV"]
handler = pyzor.server.RequestHandler(self.request)
self.expected_response["Code"] = "400"
self.expected_response["Diag"] = "Bad request"
self.check_response(handler)
def test_handle_unsupported_version(self):
"""Tests handling an request with an unsupported version specified"""
self.request["Op"] = "ping"
self.request["PV"] = str(pyzor.proto_version + 2)
handler = pyzor.server.RequestHandler(self.request)
self.expected_response["Code"] = "505"
self.expected_response["Diag"] = "Version Not Supported"
self.check_response(handler)
def test_handle_not_implemented(self):
"""Tests handling an request with an unimplemented command"""
self.request["Op"] = "notimplemented"
acl = {pyzor.anonymous_user: "notimplemented"}
handler = pyzor.server.RequestHandler(self.request, acl=acl)
self.expected_response["Code"] = "501"
self.expected_response["Diag"] = "Not implemented"
self.check_response(handler)
def test_handle_unauthorized(self):
"""Tests handling an request with an unauthorized command"""
self.request["Op"] = "report"
acl = {pyzor.anonymous_user: ("ping", "check")}
handler = pyzor.server.RequestHandler(self.request, acl=acl)
self.expected_response["Code"] = "403"
self.expected_response["Diag"] = "Forbidden"
self.check_response(handler)
def test_handle_account(self):
"""Tests handling an request where user is not anonymous"""
self.request["Op"] = "ping"
self.request["User"] = "testuser"
acl = {"testuser": ("ping", "check")}
accounts = {"testuser": "testkey"}
mock_vs = lambda x, y: None
real_vs = pyzor.account.verify_signature
pyzor.account.verify_signature = mock_vs
try:
handler = pyzor.server.RequestHandler(self.request, acl=acl,
accounts=accounts)
self.check_response(handler)
finally:
pyzor.account.verify_signature = real_vs
def test_handle_unknown_account(self):
"""Tests handling an request where user is unkwown"""
self.request["Op"] = "ping"
self.request["User"] = "testuser"
acl = {"testuser": ("ping", "check")}
accounts = {}
self.expected_response["Code"] = "401"
self.expected_response["Diag"] = "Unauthorized"
def mock_vs(x, y):
pass
real_vs = pyzor.account.verify_signature
pyzor.account.verify_signature = mock_vs
try:
handler = pyzor.server.RequestHandler(self.request, acl=acl,
accounts=accounts)
self.check_response(handler)
finally:
pyzor.account.verify_signature = real_vs
def test_handle_invalid_signature(self):
"""Tests handling an request where user key is invalid"""
self.request["Op"] = "ping"
self.request["User"] = "testuser"
acl = {"testuser": ("ping", "check")}
accounts = {"testuser": ("ping", "check")}
self.expected_response["Code"] = "401"
self.expected_response["Diag"] = "Unauthorized"
def mock_vs(x, y):
raise pyzor.SignatureError("Invalid signature.")
real_vs = pyzor.account.verify_signature
pyzor.account.verify_signature = mock_vs
try:
handler = pyzor.server.RequestHandler(self.request, acl=acl,
accounts=accounts)
self.check_response(handler)
finally:
pyzor.account.verify_signature = real_vs
def suite():
"""Gather all the tests from this module in a test suite."""
test_suite = unittest.TestSuite()
test_suite.addTest(unittest.makeSuite(RequestHandlerTest))
return test_suite
if __name__ == '__main__':
unittest.main()