Skip to content

Commit

Permalink
Merge pull request #7 from NextThought/tests
Browse files Browse the repository at this point in the history
Tests
  • Loading branch information
jamadden committed Jul 28, 2016
2 parents 46ff270 + dbf58b7 commit 9e0c7bf
Show file tree
Hide file tree
Showing 5 changed files with 302 additions and 80 deletions.
3 changes: 3 additions & 0 deletions CHANGES.rst
Expand Up @@ -9,3 +9,6 @@ Changes
``ZODB.POSException.StorageError`` for unexpected ``TypeErrors``
during commit, the new class
``nti.transactions.interfaces.CommitFailedError`` is raised.
- Introduce a new subclass of ``TransactionError``,
``AbortFailedError`` that is raised when an abort fails due to a
system error.
9 changes: 9 additions & 0 deletions src/nti/transactions/interfaces.py
Expand Up @@ -17,3 +17,12 @@ class CommitFailedError(TransactionError):
This is raised instead of raising very generic system exceptions such as
TypeError.
"""

class AbortFailedError(TransactionError):
"""
Aborting the active transaction failed for an unknown and unexpected
reason.
This is raised instead of raising very generic system exceptions
such as ValueError and AttributeError.
"""
148 changes: 148 additions & 0 deletions src/nti/transactions/tests/test_transaction.py
Expand Up @@ -20,7 +20,13 @@
import fudge

from ..transactions import _do_commit
from ..transactions import TransactionLoop
from ..interfaces import CommitFailedError
from ..interfaces import AbortFailedError

import transaction
from transaction.interfaces import TransientError


class TestCommit(unittest.TestCase):
class RaisingCommit(object):
Expand Down Expand Up @@ -70,3 +76,145 @@ class MyException(Exception):
def test_commit_clean_but_long(self, fake_logger):
fake_logger.expects_call()
_do_commit(self.RaisingCommit(None), '', 0)

class TestLoop(unittest.TestCase):

def test_trivial(self):
result = TransactionLoop(lambda a: a, retries=1, long_commit_duration=1, sleep=1)(1)
assert_that(result, is_(1))

def test_retriable(self, loop_class=TransactionLoop, exc_type=TransientError):

calls = []
def handler():
if not calls:
calls.append(1)
raise exc_type()
return "hi"

loop = loop_class(handler)
result = loop()
assert_that(result, is_("hi"))
assert_that(calls, is_([1]))

def test_custom_retriable(self):
class Loop(TransactionLoop):
_retryable_errors = ((Exception, None),)

self.test_retriable(Loop, AssertionError)

def test_retriable_gives_up(self):
def handler():
raise TransientError()
loop = TransactionLoop(handler, sleep=0.01, retries=1)
assert_that(calling(loop), raises(TransientError))

@fudge.patch('transaction.begin', 'transaction.abort')
def test_note(self, fake_begin, fake_abort):
(fake_begin.expects_call()
.returns_fake()
.expects('note').with_args("Hi")
.provides("isDoomed").returns(True))
fake_abort.expects_call()

class Loop(TransactionLoop):
def describe_transaction(self):
return "Hi"

def _TransactionLoop__free(self, tx):
pass

result = Loop(lambda: 42)()
assert_that(result, is_(42))


@fudge.patch('transaction.begin', 'transaction.abort')
def test_abort_no_side_effect(self, fake_begin, fake_abort):
fake_begin.expects_call().returns_fake()
fake_abort.expects_call()

class Loop(TransactionLoop):
side_effect_free = True

def _TransactionLoop__free(self, tx):
pass

result = Loop(lambda: 42)()
assert_that(result, is_(42))

@fudge.patch('transaction.abort')
def test_abort_doomed(self, fake_abort):
fake_abort.expects_call()

class Loop(TransactionLoop):

def _TransactionLoop__free(self, tx):
pass

def handler():
transaction.get().doom()
return 42

result = Loop(handler)()
assert_that(result, is_(42))


@fudge.patch('transaction.abort')
def test_abort_veto(self, fake_abort):
fake_abort.expects_call()

class Loop(TransactionLoop):
def should_veto_commit(self, result):
assert_that(result, is_(42))
return True

def _TransactionLoop__free(self, tx):
pass

result = Loop(lambda: 42)()
assert_that(result, is_(42))

@fudge.patch('transaction.begin', 'transaction.abort')
def test_abort_systemexit(self, fake_begin, fake_abort):
fake_begin.expects_call().returns_fake()
fake_abort.expects_call().raises(ValueError)

class Loop(TransactionLoop):

def _TransactionLoop__free(self, tx):
pass

def handler():
raise SystemExit()

loop = Loop(handler)
try:
loop()
self.fail("Should raise SystemExit")
except SystemExit:
pass

@fudge.patch('transaction.begin', 'transaction.abort',
'nti.transactions.transactions.logger.exception',
'nti.transactions.transactions.logger.warning')
def test_abort_exception_raises(self, fake_begin, fake_abort,
fake_logger, fake_format):
fake_begin.expects_call().returns_fake()
# aborting itself raises a ValueError, which
# gets transformed
fake_abort.expects_call().raises(ValueError)

# Likewise for the things we try to do to log it
fake_logger.expects_call().raises(ValueError)
fake_format.expects_call().raises(ValueError)

class Loop(TransactionLoop):

def _TransactionLoop__free(self, tx):
pass

def handler():
raise Exception()

loop = Loop(handler)
assert_that(calling(loop), raises(AbortFailedError))
25 changes: 24 additions & 1 deletion src/nti/transactions/tests/test_transaction_queue.py
Expand Up @@ -10,6 +10,8 @@
from hamcrest import is_
from hamcrest import has_length
from hamcrest import assert_that
from hamcrest import calling
from hamcrest import raises

import transaction
import six
Expand All @@ -25,7 +27,8 @@
from queue import Full
from queue import Queue

from nti.transactions.transactions import put_nowait
from ..transactions import put_nowait
from ..transactions import do

from nti.testing.base import AbstractTestBase

Expand Down Expand Up @@ -109,3 +112,23 @@ def test_put_failure( self ):

assert_that( cm.exception, is_( Full ) )
assert_that( queue.get(block=False), is_( object ) )

class TestObjectDataManager(AbstractTestBase):

def test_vote(self):
class Exc(Exception):
pass
def vote():
raise Exc()

odm = do(call=lambda: 1, vote=vote)
assert_that(calling(odm.tpc_vote).with_args(None), raises(Exc))

def test_callable_name(self):
class X(object):
def thing(self):
pass

x = X()
odm = do(target=x, method_name='thing')
assert_that(odm.callable, is_(x.thing))

0 comments on commit 9e0c7bf

Please sign in to comment.