def test_transition_events(storage): """It should call the appropriate functions on every state transition.""" class Listener(CircuitBreakerListener): def __init__(self): self.out = [] def state_change(self, breaker, old, new): assert breaker self.out.append((old.state, new.state)) listener = Listener() breaker = CircuitBreaker(listeners=(listener, ), state_storage=storage) assert CircuitBreakerState.CLOSED == breaker.current_state breaker.open() assert CircuitBreakerState.OPEN == breaker.current_state breaker.half_open() assert CircuitBreakerState.HALF_OPEN == breaker.current_state breaker.close() assert CircuitBreakerState.CLOSED == breaker.current_state assert [(CircuitBreakerState.CLOSED, CircuitBreakerState.OPEN), (CircuitBreakerState.OPEN, CircuitBreakerState.HALF_OPEN), (CircuitBreakerState.HALF_OPEN, CircuitBreakerState.CLOSED) ] == listener.out
def test_close(storage): """It should allow the circuit to be closed manually.""" breaker = CircuitBreaker(fail_max=3, state_storage=storage) breaker.open() assert 0 == breaker.fail_counter assert CircuitBreakerState.OPEN == breaker.current_state breaker.close() assert 0 == breaker.fail_counter assert CircuitBreakerState.CLOSED == breaker.current_state
def test_half_open_thread_safety(self): """ it should allow only one trial call when the circuit is half-open. """ breaker = CircuitBreaker(fail_max=1, timeout_duration=timedelta(seconds=0.01)) breaker.open() sleep(0.01) @breaker def err(): raise DummyException() def trigger_failure(): try: err() except DummyException: pass except CircuitBreakerError: pass class StateListener(CircuitBreakerListener): def __init__(self): self._count = 0 def before_call(self, breaker, func, *args, **kwargs): sleep(0.00005) def state_change(self, breaker, old, new): if new.name == STATE_HALF_OPEN: self._count += 1 state_listener = StateListener() breaker.add_listener(state_listener) self._start_threads(trigger_failure, 5) self.assertEqual(1, state_listener._count)