This repository has been archived by the owner on Jun 12, 2018. It is now read-only.
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
4 changed files
with
203 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
# -*- test-case-name: go.apps.jsbox.tests.test_kv -*- | ||
# -*- coding: utf-8 -*- | ||
|
||
import json | ||
|
||
from twisted.internet.defer import returnValue | ||
|
||
from vumi.persist.redis_base import Manager | ||
|
||
|
||
class KeyValueManager(object): | ||
""" | ||
Retrieves key value data for a jsbox application. | ||
""" | ||
|
||
# this uses Manager.calls_manager so that it can be used from | ||
# Django. | ||
|
||
def __init__(self, redis): | ||
self.redis = self.manager = redis | ||
|
||
def _sandboxed_key(self, sandbox_id, key): | ||
# TODO: refactor vumi.application.sandbox.RedisResource | ||
# to make this a static method (or something else | ||
# that allows sharing this logic). | ||
return "#".join(["sandboxes", sandbox_id, key]) | ||
|
||
def _sub_manager_for_user_store(self, campaign_key, user_store): | ||
if user_store: | ||
user_key_prefix = "users.%s" % (user_store,) | ||
else: | ||
user_key_prefix = "users" | ||
sub_store_key = self._sandboxed_key(campaign_key, user_key_prefix) | ||
sub_redis = self.redis.sub_manager(sub_store_key) | ||
# TODO: make key_separator an option on sub_manager | ||
sub_redis._key_separator = "." | ||
return sub_redis | ||
|
||
@Manager.calls_manager | ||
def answers(self, campaign_key, user_store=None): | ||
sub_redis = self._sub_manager_for_user_store(campaign_key, user_store) | ||
keys = yield sub_redis.keys() | ||
items = {} | ||
for key in keys: | ||
raw_value = yield sub_redis.get(key) | ||
try: | ||
value = json.loads(raw_value) | ||
except: | ||
continue | ||
items[key] = value | ||
returnValue(items) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
# -*- coding: utf-8 -*- | ||
|
||
import json | ||
import logging | ||
import datetime | ||
import re | ||
|
||
from mock import Mock | ||
from twisted.trial.unittest import TestCase | ||
from twisted.internet.defer import inlineCallbacks | ||
|
||
from vumi.tests.utils import LogCatcher | ||
|
||
from go.apps.jsbox.kv import KeyValueManager | ||
from go.vumitools.tests.utils import GoPersistenceMixin | ||
|
||
|
||
class LogCheckerMixin(object): | ||
"""Mixing for test cases that want to check logs.""" | ||
def parse_iso_format(self, iso_string): | ||
dt_string, _sep, micro_string = iso_string.partition(".") | ||
dt = datetime.datetime.strptime(dt_string, "%Y-%m-%dT%H:%M:%S") | ||
microsecond = int(micro_string or '0') | ||
return dt.replace(microsecond=microsecond) | ||
|
||
def check_logs(self, actual, expected, epsilon_dt=None): | ||
if epsilon_dt is None: | ||
epsilon_dt = datetime.timedelta(seconds=5) | ||
zero_dt = datetime.timedelta(seconds=0) | ||
now = datetime.datetime.utcnow() | ||
log_re = re.compile(r"^\[(?P<dt>.*?), (?P<lvl>.*?)\] (?P<msg>.*)$") | ||
actual = list(reversed(actual)) | ||
for msg, (expected_level, expected_msg) in zip(actual, expected): | ||
match = log_re.match(msg) | ||
self.assertTrue(match is not None, | ||
"Expected formatted log message but got %r" | ||
% (msg,)) | ||
self.assertEqual(match.group('msg'), expected_msg) | ||
self.assertEqual(match.group('lvl'), expected_level) | ||
dt = self.parse_iso_format(match.group('dt')) | ||
self.assertTrue(zero_dt <= now - dt < epsilon_dt) | ||
self.assertEqual(len(actual), len(expected)) | ||
|
||
|
||
class TestTxKeyValueManager(TestCase, GoPersistenceMixin, LogCheckerMixin): | ||
@inlineCallbacks | ||
def setUp(self): | ||
super(TestTxKeyValueManager, self).setUp() | ||
yield self._persist_setUp() | ||
self.parent_redis = yield self.get_redis_manager() | ||
self.redis = self.parent_redis.sub_manager( | ||
KeyValueManager.DEFAULT_SUB_STORE) | ||
|
||
@inlineCallbacks | ||
def tearDown(self): | ||
yield super(TestTxKeyValueManager, self).tearDown() | ||
yield self._persist_tearDown() | ||
|
||
def kv_manager(self): | ||
return KeyValueManager(self.parent_redis) | ||
|
||
@inlineCallbacks | ||
def test_add_log(self): | ||
lm = self.log_manager() | ||
yield lm.add_log("campaign-1", "conv-1", "Hello info!", logging.INFO) | ||
logs = yield self.redis.lrange("campaign-1:conv-1", 0, -1) | ||
self.check_logs(logs, [("INFO", "Hello info!")]) | ||
|
||
@inlineCallbacks | ||
def test_add_log_trims(self): | ||
lm = self.log_manager(max_logs=10) | ||
for i in range(10): | ||
yield lm.add_log("campaign-1", "conv-1", "%d" % i, logging.INFO) | ||
logs = yield self.redis.lrange("campaign-1:conv-1", 0, -1) | ||
self.check_logs(logs, [ | ||
("INFO", "%d" % i) for i in range(10) | ||
]) | ||
|
||
yield lm.add_log("campaign-1", "conv-1", "10", logging.INFO) | ||
logs = yield self.redis.lrange("campaign-1:conv-1", 0, -1) | ||
self.check_logs(logs, [ | ||
("INFO", "%d" % i) for i in range(1, 11) | ||
]) | ||
|
||
@inlineCallbacks | ||
def test_get_logs(self): | ||
lm = self.log_manager() | ||
for i in range(3): | ||
yield self.redis.lpush("campaign-1:conv-1", str(i)) | ||
logs = yield lm.get_logs("campaign-1", "conv-1") | ||
self.assertEqual(logs, ['2', '1', '0']) | ||
|
||
|
||
class TestKeyValueManager(TestTxKeyValueManager): | ||
sync_persistence = True |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters