diff options
-rw-r--r-- | docs/api/testing.rst | 2 | ||||
-rw-r--r-- | docs/testing.rst | 32 | ||||
-rw-r--r-- | kazoo/testing/__init__.py | 151 | ||||
-rw-r--r-- | kazoo/tests/test_client.py | 2 | ||||
-rw-r--r-- | kazoo/tests/test_lock.py | 13 | ||||
-rw-r--r-- | kazoo/tests/test_security.py | 12 |
6 files changed, 10 insertions, 202 deletions
diff --git a/docs/api/testing.rst b/docs/api/testing.rst index cca345f..b24c84a 100644 --- a/docs/api/testing.rst +++ b/docs/api/testing.rst @@ -10,5 +10,3 @@ Public API .. autoclass:: KazooTestHarness .. autoclass:: KazooTestCase - .. autoclass:: ZookeeperErrors - .. autoclass:: ZooError diff --git a/docs/testing.rst b/docs/testing.rst index cf3d52e..640aae1 100644 --- a/docs/testing.rst +++ b/docs/testing.rst @@ -59,35 +59,3 @@ equivalent test to the one above: self.client.ensure_path('/test/path') result = self.client.get('/test/path') ... - -Faking Zookeeper Results -======================== - -It can be useful to simulate errors or a connection loss when running test code -to ensure that your program functions in a robust manner. Kazoo provides a -:meth:`~kazoo.testing.KazooTestHarness.add_errors` method that can be passed -an error structure composed of :class:`~kazoo.testing.ZooError` that will be -used for the underlying Python `Zookeeper` library calls. - -Example: - -.. code-block:: python - - from kazoo.testing import KazooTestCase - from kazoo.testing import ZooError - - class MyTest(KazooTestCase): - def testmycode(self): - errors = dict( - acreate=[ - ZooError('completion', zookeeper.CONNECTIONLOSS, False), - True, - ZooError('call', SystemError(), False) - ] - ) - - self.client.add_errors(errors) - - # ensure_path internally calls acreate - self.client.ensure_path('/test/path') - result = self.client.get('/test/path') diff --git a/kazoo/testing/__init__.py b/kazoo/testing/__init__.py index 5eb0881..dcc987c 100644 --- a/kazoo/testing/__init__.py +++ b/kazoo/testing/__init__.py @@ -3,22 +3,17 @@ import atexit import logging import os import uuid -from collections import namedtuple import threading import unittest -import zookeeper from kazoo.client import KazooClient from kazoo.protocol.states import ( - Callback, KazooState ) from kazoo.testing.common import ZookeeperCluster log = logging.getLogger(__name__) -zookeeper.deterministic_conn_order(True) - CLUSTER = None @@ -35,152 +30,6 @@ def get_global_cluster(): return CLUSTER -class ZooError(namedtuple('ZooError', ('when', 'exception', 'allow'))): - """A Zookeeper Error to throw instead of or in addition to executing the - Zookeeper command - - Since the :class:`KazooClient` implements most of the zookeeper commands - using the async calls, the exception could occur during the call itself - (which is rare), or on the completion. - - .. attribute:: when - - When the exception should be tossed, can be set to 'call' or - 'completion'. - - .. attribute:: exception - - The exception instance or Zookeeper exception code (an int). - - .. attribute:: allow - - Boolean indicating if the Zookeeper function should be called - even though an exception is being returned. This is useful for - reproducing rare events in Zookeeper where a connection is lost - before a response is returned but the command ran successfully. - - """ - - -class ZookeeperErrors(object): - """A Zookeeper proxy with errors""" - def __init__(self, errors, handler): - """Create a :class:`ZookeeperErrors` object that throws the - desired errors upon calls - - :param errors: A dict structure keyed by the Zookeeper function - to intercept, with the value being a list of - :class:`ZooError`s to throw. When the list is empty - calls will proceed as normal, include ``True`` in a - place to allow a call to pass if desired as well. - :param handler: The handler instance used by the KazooClient. - - Example:: - - errors = dict( - acreate=[ - ZooError('completion', zookeeper.CONNECTIONLOSS, False), - True, - ZooError('call', SystemError(), False) - ] - ) - - self.client.add_errors(errors) - - This example replaces the first call to `zookeeper.acreate` with a - CONNECTIONLOSS, and doesn't run the call in the background. The second - call will run fine, the third will have a SystemError, and any more - calls will proceed as usual. - - .. warning:: - - Care should be taken when simulating errors that your test code - executes the Zookeeper commands in a reliably deterministic manner. - - """ - for k, v in errors.items(): - v.reverse() - self.errors = errors - self.handler = handler - - def _intercept_completion(self): - # Completion callback is always at the end - asy = self.handler.async_result() - - def new_completion(handle, code, *args): - log.info("Intercept completion, handle: %s, " - "code: %s, args: %s", handle, code, args) - asy.set() - return asy, new_completion - - def _intercept_watcher(self): - def new_watcher(*args): - log.info("Intercept watcher, args: %s", args) - return new_watcher - - def _call_exception(self, err, name, func, *args, **kwargs): - if err.allow: - # Strip completion/watch callbacks, replace with dummies - asy = False - if callable(args[-1]): - asy, args[-1] = self._intercept_completion() - if callable(args[-2]): - args[-2] = self._intercept_watcher() - log.debug("Calling actual function: %s", name) - func(*args, **kwargs) - if asy: - asy.wait() - log.debug("Raising desired exception: %s", err.exception) - raise err.exception - else: - log.debug("Raising desired exception on call: %s", - err.exception) - raise err.exception - - def _completion_exception(self, err, name, func, *args, **kwargs): - # Ensure args is a list - if isinstance(args, tuple): - args = list(args) - - if callable(args[-1]): - # Replace completion callback with one that returns the - # exception - old_complete = args[-1] - args[-1] = lambda handle, code, *new_args: old_complete( - handle, err.exception, *new_args) - if callable(args[-2]): - # Strip the watch callback - args[-2] = lambda *args: 1 - # Call it if we're supposed to - if err.allow: - log.debug("Calling actual function: %s", name) - func(*args, **kwargs) - else: - # Make sure the completion callback is called - log.debug("Returning desired exception on call: %s", - err.exception) - self.handler.dispatch_callback( - Callback('completion', func, (None, err.exception)) - ) - return zookeeper.OK - - def __getattr__(self, name): - func = getattr(zookeeper, name) - err = self.errors.get(name) - if err: - err = err.pop() - if not isinstance(err, ZooError): - return func - - def func_wrapper(*args, **kwargs): - if err.when == 'call': - return self._call_exception(err, name, func, *args, **kwargs) - else: - return self._completion_exception(err, name, func, *args, - **kwargs) - return func_wrapper - - class KazooTestHarness(object): """Harness for testing code that uses Kazoo diff --git a/kazoo/tests/test_client.py b/kazoo/tests/test_client.py index a7a239f..7eb9e05 100644 --- a/kazoo/tests/test_client.py +++ b/kazoo/tests/test_client.py @@ -5,11 +5,9 @@ import unittest from nose.tools import eq_ from kazoo.testing import KazooTestCase -from kazoo.testing import ZooError from kazoo.exceptions import BadArgumentsError from kazoo.exceptions import NoNodeError from kazoo.exceptions import NoAuthError -from kazoo.exceptions import ZookeeperError from kazoo.exceptions import ConnectionLoss diff --git a/kazoo/tests/test_lock.py b/kazoo/tests/test_lock.py index 7d332d6..94813a3 100644 --- a/kazoo/tests/test_lock.py +++ b/kazoo/tests/test_lock.py @@ -1,12 +1,13 @@ import uuid import threading -import zookeeper from nose.tools import eq_ -from kazoo.exceptions import CancelledError +from kazoo.exceptions import ( + CancelledError, + ConnectionLoss +) from kazoo.testing import KazooTestCase -from kazoo.testing import ZooError from kazoo.tests.util import wait @@ -120,12 +121,6 @@ class KazooLockTests(KazooTestCase): thread.join() def test_lock_fail_first_call(self): - self.add_errors(dict( - acreate=[True, # This is our lock node create - ZooError('completion', zookeeper.CONNECTIONLOSS, True) - ] - )) - event1 = threading.Event() lock1 = self.client.Lock(self.lockpath, "one") thread1 = threading.Thread(target=self._thread_lock_acquire_til_event, diff --git a/kazoo/tests/test_security.py b/kazoo/tests/test_security.py index 29bebdd..4a7e670 100644 --- a/kazoo/tests/test_security.py +++ b/kazoo/tests/test_security.py @@ -1,7 +1,7 @@ import unittest from nose.tools import eq_ -import zookeeper +from kazoo.security import Permissions class TestACL(unittest.TestCase): @@ -11,13 +11,13 @@ class TestACL(unittest.TestCase): def test_read_acl(self): acl = self._makeOne("digest", ":", read=True) - eq_(acl.perms & zookeeper.PERM_READ, zookeeper.PERM_READ) + eq_(acl.perms & Permissions.READ, Permissions.READ) def test_all_perms(self): - acl = self._makeOne("digest", ":", write=True, create=True, - delete=True, admin=True) - for perm in [zookeeper.PERM_WRITE, zookeeper.PERM_CREATE, - zookeeper.PERM_DELETE, zookeeper.PERM_ADMIN]: + acl = self._makeOne("digest", ":", read=True, write=True, + create=True, delete=True, admin=True) + for perm in [Permissions.READ, Permissions.CREATE, Permissions.WRITE, + Permissions.DELETE, Permissions.ADMIN]: eq_(acl.perms & perm, perm) def test_perm_listing(self): |