Files
cachier/tests/test_mongo_core.py

236 lines
6.3 KiB
Python

"""Testing the MongoDB core of cachier."""
from __future__ import print_function
import sys
import datetime
from datetime import timedelta
from random import random
from time import sleep
import threading
try:
import queue
except ImportError: # python 2
import Queue as queue
import pytest
import pymongo
from pymongo.mongo_client import MongoClient
from pymongo.errors import OperationFailure
from cachier import cachier
from cachier.mongo_core import _MongoCore, RecalculationNeeded
_TEST_HOST = 'ds119508.mlab.com'
_TEST_PORT = 19508
_TEST_USERNAME = 'cachier_test'
_TEST_PWD = 'ZGhjO5CQESYJ69U4z65G79YG'
def _get_cachier_db_mongo_client():
client = MongoClient(host=_TEST_HOST, port=_TEST_PORT, retryWrites=False)
client.cachier_test.authenticate(
name=_TEST_USERNAME,
password=_TEST_PWD,
mechanism='SCRAM-SHA-1'
)
return client
_COLLECTION_NAME = 'cachier_test{}.{}.{}'.format(
sys.version_info[0], sys.version_info[1], sys.version_info[2])
def _test_mongetter():
if not hasattr(_test_mongetter, 'client'):
_test_mongetter.client = _get_cachier_db_mongo_client()
db_obj = _test_mongetter.client['cachier_test']
if _COLLECTION_NAME not in db_obj.list_collection_names():
db_obj.create_collection(_COLLECTION_NAME)
return db_obj[_COLLECTION_NAME]
# === Mongo core tests ===
def test_information():
print("\npymongo version: ", end="")
print(pymongo.__version__)
@cachier(mongetter=_test_mongetter)
def _test_mongo_caching(arg_1, arg_2):
"""Some function."""
return random() + arg_1 + arg_2
def test_mongo_index_creation():
"""Basic Mongo core functionality."""
collection = _test_mongetter()
_test_mongo_caching.clear_cache()
val1 = _test_mongo_caching(1, 2)
val2 = _test_mongo_caching(1, 2)
assert val1 == val2
assert _MongoCore._INDEX_NAME in collection.index_information()
def test_mongo_core():
"""Basic Mongo core functionality."""
_test_mongo_caching.clear_cache()
val1 = _test_mongo_caching(1, 2)
val2 = _test_mongo_caching(1, 2)
assert val1 == val2
val3 = _test_mongo_caching(1, 2, ignore_cache=True)
assert val3 != val1
val4 = _test_mongo_caching(1, 2)
assert val4 == val1
val5 = _test_mongo_caching(1, 2, overwrite_cache=True)
assert val5 != val1
val6 = _test_mongo_caching(1, 2)
assert val6 == val5
MONGO_DELTA = timedelta(seconds=3)
@cachier(mongetter=_test_mongetter, stale_after=MONGO_DELTA, next_time=False)
def _stale_after_mongo(arg_1, arg_2):
"""Some function."""
return random() + arg_1 + arg_2
def test_mongo_stale_after():
"""Testing MongoDB core stale_after functionality."""
_stale_after_mongo.clear_cache()
val1 = _stale_after_mongo(1, 2)
val2 = _stale_after_mongo(1, 2)
assert val1 == val2
sleep(3)
val3 = _stale_after_mongo(1, 2)
assert val3 != val1
@cachier(mongetter=_test_mongetter)
def _takes_time(arg_1, arg_2):
"""Some function."""
sleep(3)
return random() + arg_1 + arg_2
def _calls_takes_time(res_queue):
res = _takes_time(34, 82.3)
res_queue.put(res)
def test_mongo_being_calculated():
"""Testing MongoDB core handling of being calculated scenarios."""
_takes_time.clear_cache()
res_queue = queue.Queue()
thread1 = threading.Thread(
target=_calls_takes_time, kwargs={'res_queue': res_queue})
thread2 = threading.Thread(
target=_calls_takes_time, kwargs={'res_queue': res_queue})
thread1.start()
sleep(1)
thread2.start()
thread1.join()
thread2.join()
assert res_queue.qsize() == 2
res1 = res_queue.get()
res2 = res_queue.get()
assert res1 == res2
class _BadMongoCollection:
def __init__(self, mongetter):
self.collection = mongetter()
self.index_information = self.collection.index_information
self.create_indexes = self.collection.create_indexes
self.find_one = self.collection.find_one
def delete_many(self, *args, **kwargs): # skipcq: PYL-R0201, PYL-W0613
pass
def update_many(self, *args, **kwargs): # skipcq: PYL-R0201, PYL-W0613
pass
def update_one(self, *args, **kwargs): # skipcq: PYL-R0201, PYL-W0613
raise OperationFailure(Exception())
def _bad_mongetter():
return _BadMongoCollection(_test_mongetter)
@cachier(mongetter=_bad_mongetter)
def _func_w_bad_mongo(arg_1, arg_2):
"""Some function."""
return random() + arg_1 + arg_2
def test_mongo_write_failure():
"""Testing MongoDB core handling of writing failure scenarios."""
with pytest.raises(OperationFailure):
val1 = _func_w_bad_mongo(1, 2)
val2 = _func_w_bad_mongo(1, 2)
assert val1 == val2
def test_mongo_clear_being_calculated():
"""Testing MongoDB core clear_being_calculated."""
_func_w_bad_mongo.clear_being_calculated()
def test_stalled_mongo_db_cache():
@cachier(mongetter=_test_mongetter)
def _stalled_func():
return 1
core = _MongoCore(_test_mongetter, None, False)
core.set_func(_stalled_func)
core.clear_cache()
with pytest.raises(RecalculationNeeded):
core.wait_on_entry_calc(key=None)
def test_stalled_mong_db_core(monkeypatch):
def mock_get_entry(self, args, kwargs): # skipcq: PYL-R0201, PYL-W0613
return "key", {'being_calculated': True}
def mock_get_entry_by_key(self, key): # skipcq: PYL-R0201, PYL-W0613
return "key", None
monkeypatch.setattr(
"cachier.mongo_core._MongoCore.get_entry", mock_get_entry)
monkeypatch.setattr(
"cachier.mongo_core._MongoCore.get_entry_by_key", mock_get_entry_by_key
)
@cachier(mongetter=_test_mongetter)
def _stalled_func():
return 1
res = _stalled_func()
assert res == 1
def mock_get_entry_2(self, args, kwargs): # skipcq: PYL-W0613
entry = {
'being_calculated': True,
"value": 1,
"time": datetime.datetime.now() - datetime.timedelta(seconds=10)
}
return "key", entry
monkeypatch.setattr(
"cachier.mongo_core._MongoCore.get_entry", mock_get_entry_2)
stale_after = datetime.timedelta(seconds=1)
@cachier(mongetter=_test_mongetter, stale_after=stale_after)
def _stalled_func_2():
"""Testing stalled function"""
return 2
res = _stalled_func_2()
assert res == 2