예제 #1
0
 def test_retry_times(self):
     """
     `retry_times` returns function that will retry given number of times
     """
     can_retry = retry_times(3)
     for exception in (DummyException(), NotImplementedError(),
                       ValueError()):
         self.assertTrue(can_retry(Failure(exception)))
     self.assertFalse(can_retry(Failure(DummyException())))
예제 #2
0
 def test_transient_errors_except_terminates_on_provided_exceptions(self):
     """
     If the failure is of a type provided to :func:`transient_errors_except`,
     the function it returns will treat it as terminal (returns False)
     """
     can_retry = transient_errors_except(DummyException)
     self.assertFalse(can_retry(Failure(DummyException())))
예제 #3
0
    def test_ignores_transient_failures_and_retries(self):
        """
        Retries after interval if the ``do_work`` function errbacks with an
        error that is ignored by the ``can_retry`` function.  The error is
        not propagated.
        """
        wrapped_retry = mock.MagicMock(wraps=self.retry_function, spec=[])
        d = retry(self.work_function, wrapped_retry, self.interval_function,
                  self.clock)

        self.assertNoResult(d)
        self.assertEqual(len(self.retries), 1)

        # no result on errback
        self.retries[-1].errback(DummyException('hey!'))
        self.assertIsNone(self.successResultOf(self.retries[-1]))
        self.assertNoResult(d)
        wrapped_retry.assert_called_once_with(CheckFailure(DummyException))

        self.clock.advance(self.interval)

        # since it was an errback, loop retries the function again
        self.assertNoResult(d)
        self.assertEqual(len(self.retries), 2)

        # stop loop
        self.retries[-1].callback('result!')
        self.assertEqual(self.successResultOf(d), 'result!')
예제 #4
0
 def test_repeating_interval_always_returns_interval(self):
     """
     ``repeating_interval`` returns the same interval no matter what the
     failure
     """
     next_interval = repeating_interval(3)
     for exception in (DummyException(), NotImplementedError()):
         self.assertEqual(next_interval(Failure(exception)), 3)
예제 #5
0
 def test_raise_error_on_code_does_not_match_code(self):
     """
     ``raise_error_on_code`` expects an APIError, and raises a particular
     error given a specific code.  Otherwise, it just wraps it in a
     :class:`RequestError`
     """
     failure = Failure(APIError(404, '', {}))
     self.assertRaises(RequestError, raise_error_on_code,
                       failure, 500, DummyException(), 'url')
예제 #6
0
 def test_pooled_deferred_errbbacks_not_obscured(self):
     """
     The errbacks of pooled deferreds are not obscured by removing them
     from the pool.
     """
     holdup = Deferred()
     self.pool.add(holdup)
     holdup.errback(DummyException('hey'))
     self.failureResultOf(holdup, DummyException)
예제 #7
0
    def test_terminal_errors_except_defaults_to_all_errors_bad(self):
        """
        If no args are provided to :func:`fail_unless`, the
        function it returns treats all errors as terminal (returns False)
        """
        can_retry = terminal_errors_except()

        for exception in (DummyException(), NotImplementedError()):
            self.assertFalse(can_retry(Failure(exception)))
예제 #8
0
    def test_transient_errors_except_defaults_to_all_transient(self):
        """
        If no args are provided to :func:`transient_errors_except`, the
        function it returns treats all errors as transient (returns True)
        """
        can_retry = transient_errors_except()

        for exception in (DummyException(), NotImplementedError()):
            self.assertTrue(can_retry(Failure(exception)))
예제 #9
0
 def test_exp_backoff_interval(self):
     """
     ``exponential_backoff_interval`` returns previous interval * 2 every
     time it is called
     """
     err = DummyException()
     next_interval = exponential_backoff_interval(3)
     self.assertEqual(next_interval(err), 3)
     self.assertEqual(next_interval(err), 6)
     self.assertEqual(next_interval(err), 12)
예제 #10
0
    def test_preserves_cancellation_function_errback(self):
        """
        If a cancellation function that errbacks (with a non-CancelledError) is
        provided to the deferred being cancelled, this other error will not be
        converted to a TimedOutError.
        """
        d = Deferred(lambda c: c.errback(DummyException('what!')))
        timeout_deferred(d, 10, self.clock)
        self.assertNoResult(d)

        self.clock.advance(15)

        self.failureResultOf(d, DummyException)
예제 #11
0
    def test_retries_at_intervals_specified_by_interval_function(self):
        """
        ``do_work``, if it experiences transient failures, will be retried at
        intervals returned by the ``next_interval`` function
        """
        changing_interval = mock.MagicMock(spec=[])
        d = retry(self.work_function, self.retry_function, changing_interval,
                  self.clock)

        changing_interval.return_value = 1
        self.assertEqual(len(self.retries), 1)
        self.retries[-1].errback(DummyException('hey!'))
        self.assertNoResult(d)
        changing_interval.assert_called_once_with(CheckFailure(DummyException))

        self.clock.advance(1)
        changing_interval.return_value = 2
        self.assertEqual(len(self.retries), 2)
        self.retries[-1].errback(DummyException('hey!'))
        self.assertNoResult(d)
        changing_interval.assert_has_calls(
            [mock.call(CheckFailure(DummyException))] * 2)

        # the next interval has changed - after 1 second, it is still not
        # retried
        self.clock.advance(1)
        self.assertEqual(len(self.retries), 2)
        self.assertNoResult(d)
        changing_interval.assert_has_calls(
            [mock.call(CheckFailure(DummyException))] * 2)

        # after 2 seconds, the function is retried
        self.clock.advance(1)
        self.assertEqual(len(self.retries), 3)

        # stop retrying
        self.retries[-1].callback('hey')
예제 #12
0
    def test_propagates_failure_if_failed_before_timeout(self):
        """
        The deferred errbacks with the failure if it fails before the
        timeout (e.g. timing out the deferred does not obscure the errback
        failure).
        """
        clock = Clock()
        d = Deferred()
        timeout_deferred(d, 10, clock)
        d.errback(DummyException("fail"))
        self.failureResultOf(d, DummyException)

        # the timeout never happens - no further errback occurs
        clock.advance(15)
        self.assertIsNone(self.successResultOf(d))
예제 #13
0
    def test_verified_delete_does_not_propagate_verification_failure(self):
        """
        verified_delete propagates deletions from server deletion
        """
        clock = Clock()
        self.treq.delete.return_value = succeed(
            mock.Mock(spec=['code'], code=204))
        self.treq.head.return_value = fail(DummyException('failure'))
        self.treq.content.side_effect = lambda *args: succeed("")

        d = verified_delete(self.log,
                            'http://url/',
                            'my-auth-token',
                            'serverId',
                            clock=clock)
        self.assertIsNone(self.successResultOf(d))
예제 #14
0
    def test_notify_does_not_notify_until_pooled_deferreds_errbacks(self):
        """
        If there are one or more deferreds in the pool, ``notify_when_empty``
        does not notify until they are fired - works with errbacks too.
        """
        holdup = Deferred()
        self.pool.add(holdup)

        d = self.pool.notify_when_empty()
        self.assertNoResult(d)

        holdup.errback(DummyException('hey'))
        self.successResultOf(d)

        # don't leave unhandled Deferred lying around
        self.failureResultOf(holdup)
예제 #15
0
    def test_request_failure(self):
        """
        On failed call to request, failure is returned and request logged
        """
        d = logging_treq.request('patch', self.url, data='',
                                 log=self.log, clock=self.clock)
        self.treq.request.assert_called_once_with(
            method='patch', url=self.url,
            headers={'x-otter-request-id': ['uuid']}, data='')
        self.assertNoResult(d)

        self.clock.advance(5)
        self.treq.request.return_value.errback(Failure(DummyException('e')))

        self.failureResultOf(d, DummyException)
        self._assert_failure_logging('patch', DummyException, 5)
예제 #16
0
    def test_default_can_retry_function(self):
        """
        If no ``can_retry`` function is provided, a default function treats
        any failure as transient
        """
        d = retry(self.work_function, None, self.interval_function, self.clock)

        self.assertEqual(len(self.retries), 1)
        self.retries[-1].errback(DummyException('temp'))

        self.clock.advance(self.interval)

        self.assertEqual(len(self.retries), 2)
        self.retries[-1].errback(NotImplementedError())

        self.assertNoResult(d)
예제 #17
0
    def test_stops_on_non_transient_error(self):
        """
        If ``do_work`` errbacks with something the ``can_retry`` function does
        not ignore, the error is propagated up.  ``do_work`` is not retried.
        """
        d = retry(self.work_function, lambda *args: False,
                  self.interval_function, self.clock)

        self.assertNoResult(d)
        self.assertEqual(len(self.retries), 1)

        self.retries[-1].errback(DummyException('fail!'))
        self.failureResultOf(d, DummyException)

        # work_function not called again
        self.clock.advance(self.interval)
        self.assertEqual(len(self.retries), 1)
예제 #18
0
 def test_random_interval(self):
     """
     ``random_interval`` returns the different random interval each time it
     is called
     """
     next_interval = random_interval(5, 10)
     intervals = set()
     for exception in [
             DummyException(),
             NotImplementedError(),
             ValueError(),
             FloatingPointError(),
             IOError()
     ]:
         interval = next_interval(exception)
         self.assertTrue(5 <= interval <= 10)
         self.assertNotIn(interval, intervals)
         intervals.add(interval)
예제 #19
0
    def _test_method_failure(self, method):
        """
        On failed call to ``method``, failure is returned and request logged
        """
        request_function = getattr(logging_treq, method)
        d = request_function(url=self.url, headers={}, data='', log=self.log,
                             clock=self.clock)

        treq_function = getattr(self.treq, method)
        treq_function.assert_called_once_with(
            url=self.url, headers={'x-otter-request-id': ['uuid']}, data='')
        self.assertNoResult(d)

        self.clock.advance(5)
        treq_function.return_value.errback(Failure(DummyException('e')))

        self.failureResultOf(d, DummyException)
        self._assert_failure_logging(method, DummyException, 5)
예제 #20
0
    def test_cancelling_deferred_does_not_cancel_completed_work(self):
        """
        Cancelling the deferred does not attempt to cancel previously
        callbacked results from ``do_work``
        """
        d = retry(self.work_function, self.retry_function,
                  self.interval_function, self.clock)

        self.assertEqual(len(self.retries), 1)
        self.retries[-1].errback(DummyException('temp'))

        # cancel main deferred
        d.cancel()
        self.failureResultOf(d, CancelledError)

        # work_function's deferred is not cancelled
        self.assertEqual(self.retries[-1].cancel.call_count, 0)
        self.assertIsNone(self.successResultOf(self.retries[-1]))
예제 #21
0
    def test_default_next_interval_function(self):
        """
        If no ``next_interval`` function is provided, a default function returns
        5 no matter what the failure.
        """
        d = retry(self.work_function, self.retry_function, None, self.clock)

        self.assertEqual(len(self.retries), 1)
        self.retries[-1].errback(DummyException('temp'))

        self.clock.advance(5)

        self.assertEqual(len(self.retries), 2)
        self.retries[-1].errback(NotImplementedError())

        self.clock.advance(5)

        self.assertEqual(len(self.retries), 3)
        self.assertNoResult(d)