def test_handle_circuit_breaker_on_command(self):
        """ given that we have a circuit breaker policy for a command, when we raise an exception, then we should break the circuit after n retries"""
        self._handler = MyHandlerBreakingCircuitAfterThreeFailures()
        self._request = MyCommand()
        self._subscriber_registry.register(MyCommand, lambda: self._handler)

        exception_raised = False
        try:
            self._commandProcessor.send(self._request)
        except RuntimeError:
            exception_raised = True

        # Now see if the circuit is broken following the failed call
        circuit_broken = False
        try:
            self._commandProcessor.send(self._request)
        except CircuitBrokenError:
            circuit_broken = True

        self.assertTrue(
            exception_raised,
            "Exepcted an exception to be raised, when we run out of retries")
        self.assertTrue(circuit_broken,
                        "Expected the circuit to be broken to further calls")
        self.assertFalse(
            self._handler.called,
            "Did not expect the handle method on the handler to be called with the message"
        )
        self.assertTrue(self._handler.call_count == 3,
                        "Expected two retries of the pipeline")
Exemple #2
0
    def test_the_pump_should_fail_on_a_missing_message_mapper(self):
        """
            Given that I have a message pump for a channel
             When there is no message mapper for that channel
             Then we shhould throw an exception to indicate a configuration error
        """
        handler = MyCommandHandler()
        request = MyCommand()
        channel = Mock(spec=Channel)
        command_processor = Mock(spec=CommandProcessor)

        message_pump = MessagePump(command_processor, channel, None)

        header = BrightsideMessageHeader(uuid4(), request.__class__.__name__, BrightsideMessageType.MT_COMMAND)
        body = BrightsideMessageBody(JsonRequestSerializer(request=request).serialize_to_json(),
                                     BrightsideMessageBodyType.application_json)
        message = BrightsideMessage(header, body)

        quit_message = create_quit_message()

        # add messages to that when channel is called it returns first message then qui tmessage
        response_queue = [message, quit_message]
        channel_spec = {"receive.side_effect": response_queue}
        channel.configure_mock(**channel_spec)

        excepton_caught = False
        try:
            message_pump.run()
        except ConfigurationException:
            excepton_caught = True

        self.assertTrue(excepton_caught)
Exemple #3
0
    def test_the_pump_should_acknowledge_and_discard_an_unacceptable_message(self):
        """
            Given that I have a message pump for a channel
             When I cannot read the message received from that channel
             Then I should acknowledge the message to dicard it
        """
        handler = MyCommandHandler()
        request = MyCommand()
        channel = Mock(spec=Channel)
        command_processor = Mock(spec=CommandProcessor)

        message_pump = MessagePump(command_processor, channel, map_my_command_to_request)

        header = BrightsideMessageHeader(uuid4(), request.__class__.__name__, BrightsideMessageType.MT_UNACCEPTABLE)
        body = BrightsideMessageBody(JsonRequestSerializer(request=request).serialize_to_json(),
                                     BrightsideMessageBodyType.application_json)
        message = BrightsideMessage(header, body)

        quit_message = create_quit_message()

        # add messages to that when channel is called it returns first message then qui tmessage
        response_queue = [message, quit_message]
        channel_spec = {"receive.side_effect": response_queue}
        channel.configure_mock(**channel_spec)

        message_pump.run()

        channel.receive.assert_called_with(0.5)
        self.assertEqual(channel.receive.call_count, 2)
        # We acknowledge so that a 'poison pill' message cannot block our queue
        self.assertEqual(channel.acknowledge.call_count, 1)
        # Does not send the message, just discards it
        self.assertEqual(command_processor.send.call_count, 0)
Exemple #4
0
    def test_the_pump_should_dispatch_a_command_processor(self):
        """
            Given that I have a message pump for a channel
             When I read a message from that channel
             Then the message should be dispatched to a handler
        """
        handler = MyCommandHandler()
        request = MyCommand()
        channel = Mock(spec=Channel)
        command_processor = Mock(spec=CommandProcessor)

        message_pump = MessagePump(command_processor, channel, map_my_command_to_request)

        header = BrightsideMessageHeader(uuid4(), request.__class__.__name__, BrightsideMessageType.MT_COMMAND)
        body = BrightsideMessageBody(JsonRequestSerializer(request=request).serialize_to_json(),
                                     BrightsideMessageBodyType.application_json)
        message = BrightsideMessage(header, body)

        quit_message = create_quit_message()

        # add messages to that when channel is called it returns first message then quit message
        response_queue = [message, quit_message]
        channel_spec = {"receive.side_effect": response_queue}
        channel.configure_mock(**channel_spec)

        message_pump.run()

        channel.receive.assert_called_with(0.5)
        self.assertEqual(channel.receive.call_count, 2)
        self.assertTrue(command_processor.send.call_count, 1)
        self.assertEqual(channel.acknowledge.call_count, 1)
Exemple #5
0
    def test_the_pump_should_ack_failed_messages(self):
        """
            Given that I have a message pump for a channel
             When the handler raises an application exception for that message
             Then ack the message to prevent 'poison pill' message
        """
        handler = MyCommandHandler()
        request = MyCommand()
        channel = Mock(spec=Channel)
        command_processor = Mock(spec=CommandProcessor)

        message_pump = MessagePump(command_processor, channel, map_my_command_to_request)

        header = BrightsideMessageHeader(uuid4(), request.__class__.__name__, BrightsideMessageType.MT_COMMAND)
        body = BrightsideMessageBody(JsonRequestSerializer(request=request).serialize_to_json(),
                                     BrightsideMessageBodyType.application_json)
        message = BrightsideMessage(header, body)

        quit_message = create_quit_message()

        # add messages to that when channel is called it returns first message then qui tmessage
        response_queue = [message, quit_message]
        channel_spec = {"receive.side_effect": response_queue}
        channel.configure_mock(**channel_spec)

        app_error_spec = {"send.side_effect": ZeroDivisionError()}
        command_processor.configure_mock(**app_error_spec)

        message_pump.run()

        channel.receive.assert_called_with(0.5)
        self.assertEqual(channel.receive.call_count, 2)
        self.assertTrue(command_processor.send.call_count, 1)
        self.assertEqual(channel.acknowledge.call_count, 1)
Exemple #6
0
    def test_handle_command(self):
        """ given that we have a handler registered for a command, when we send a command, it should call the handler"""
        self._handler = MyCommandHandler()
        self._request = MyCommand()
        self._subscriber_registry.register(MyCommand, lambda: self._handler)
        self._commandProcessor.send(self._request)

        self.assertTrue(self._handler.called, "Expected the handle method on the handler to be called with the message")
    def test_handle_command(self):
        """ given that we have a message mapper and producer registered for a commnd, when we post a command, it should send via the producer"""
        self._request = MyCommand()
        self._commandProcessor.post(self._request)

        self.assertTrue(self._message_store.message_was_added, "Expected a message to be added")
        self.assertTrue(self._message_store.get_message(self._request.id), "Expected the command to be converted into a message")
        self.assertTrue(self._producer.was_sent_message, "Expected a message to be sent via the producer")
    def test_handle_retry_on_command(self):
        """ given that we have a retry plocy for a command, when we raise an exception, then we should retry n times or until succeeds """
        self._handler = MyHandlerSupportingRetry()
        self._request = MyCommand()
        self._subscriber_registry.register(MyCommand, lambda: self._handler)
        self._commandProcessor.send(self._request)

        self.assertTrue(self._handler.called, "Expected the handle method on the handler to be called with the message")
        self.assertTrue(self._handler.call_count == 3, "Expected two retries of the pipeline")
Exemple #9
0
    def test_missing_command_handler_registration(self):
        """Given that we are missing a handler for a command, when we send a command, it should throw an exception"""

        self._handler = MyCommandHandler()
        self._request = MyCommand()

        exception_thrown = False
        try:
            self._commandProcessor.send(self._request)
        except:
            exception_thrown = True

        self.assertTrue(exception_thrown, "Expected an exception to be thrown when no handler is registered for a command")
    def test_exceed_retry_on_command(self):
        """ given that we have a retry policy for a command, when we raise an exception, then we should bubble the exception out after n retries"""
        self._handler = MyHandlerBreakingAfterRetry()
        self._request = MyCommand()
        self._subscriber_registry.register(MyCommand, lambda: self._handler)

        exception_raised = False
        try:
            self._commandProcessor.send(self._request)
        except RuntimeError:
            exception_raised = True

        self.assertTrue(exception_raised, "Exepcted the exception to bubble out, when we run out of retries")
        self.assertFalse(self._handler.called, "Did not expect the handle method on the handler to be called with the message")
        self.assertTrue(self._handler.call_count == 3, "Expected two retries of the pipeline")
    def test_missing_message_producer(self):
        """given that we have no me message producer configured for the commandprocessor
            when we post a command
            it should raise a confiugration error
        """
        self._commandProcessor = CommandProcessor(
            message_mapper_registry=self._messageMapperRegistry,
            message_store=self._message_store,
            producer=None)

        was_exception_thrown = False
        try:
            self._request = MyCommand()
            self._commandProcessor.post(self._request)
        except ConfigurationException:
            was_exception_thrown = True

        self.assertTrue(was_exception_thrown)
Exemple #12
0
    def test_logging_a_handler(self):
        """s
        Given that I have a handler decorated for logging
        When I call that handler
        Then I should receive logs indicating the call and return of the handler
        * N.b. This is an example of using decorators to extend the Brightside pipeline
        """
        handler = MyCommandHandler()
        request = MyCommand()
        self._subscriber_registry.register(MyCommand, lambda: handler)
        logger = logging.getLogger("tests.handlers_testdoubles")
        with patch.object(logger, 'log') as mock_log:
            self._commandProcessor.send(request)

        mock_log.assert_has_calls([
            call(logging.DEBUG, "Entering handle " + str(request)),
            call(logging.DEBUG, "Exiting handle " + str(request))
        ])
Exemple #13
0
    def test_handle_requeue_has_upper_bound(self):
        """
        Given that I have a channel
        When I receive a requeue on that channel
        I should ask the consumer to requeue, up to a retry limit
        So that poison messages do not fill our queues
        """
        handler = MyCommandHandler()
        request = MyCommand()
        channel = FakeChannel(name="MyCommand")
        command_processor = Mock(spec=CommandProcessor)

        message_pump = MessagePump(command_processor, channel, map_my_command_to_request, requeue_count=3)

        header = BrightsideMessageHeader(uuid4(), request.__class__.__name__, BrightsideMessageType.MT_COMMAND)
        body = BrightsideMessageBody(JsonRequestSerializer(request=request).serialize_to_json(),
                                     BrightsideMessageBodyType.application_json)
        message = BrightsideMessage(header, body)

        channel.add(message)

        requeue_spec = {"send.side_effect": DeferMessageException()}

        command_processor.configure_mock(**requeue_spec)

        started_event = Event()

        t = Thread(target=message_pump.run, args=(started_event,))

        t.start()

        started_event.wait()

        time.sleep(1)

        channel.stop()

        t.join()

        self.assertTrue(command_processor.send.call_count, 3)
Exemple #14
0
    def test_stop_performer(self):
        """
        Given that I have started a performer
        When I stop the performer
        Then it should terminate the pump
        :return:
        """
        request = MyCommand()
        pipeline = Queue()
        connection = Connection(config.broker_uri,
                                "examples.perfomer.exchange")
        configuration = BrightsideConsumerConfiguration(
            pipeline, "performer.test.queue", "examples.tests.mycommand")
        performer = Performer("test_channel", connection, configuration,
                              mock_consumer_factory,
                              mock_command_processor_factory,
                              map_my_command_to_request)

        header = BrightsideMessageHeader(uuid4(), request.__class__.__name__,
                                         BrightsideMessageType.MT_COMMAND)
        body = BrightsideMessageBody(
            JsonRequestSerializer(request=request).serialize_to_json(),
            BrightsideMessageBodyType.application_json)
        message = BrightsideMessage(header, body)

        pipeline.put(message)

        started_event = Event()
        p = performer.run(started_event)

        started_event.wait()

        time.sleep(1)

        performer.stop()

        p.join()

        self.assertTrue(True)
Exemple #15
0
    def test_stop_consumer(self):
        """Given that I have a dispatcher
            When I stop a consumer
            Then the performer should terminate
        """
        request = MyCommand()
        pipeline = Queue()
        connection = Connection(config.broker_uri,
                                "examples.perfomer.exchange")
        configuration = BrightsideConsumerConfiguration(
            pipeline, "dispatcher.test.queue", "examples.tests.mycommand")
        consumer = ConsumerConfiguration(connection, configuration,
                                         mock_consumer_factory,
                                         mock_command_processor_factory,
                                         map_my_command_to_request)
        dispatcher = Dispatcher({"MyCommand": consumer})

        header = BrightsideMessageHeader(uuid4(), request.__class__.__name__,
                                         BrightsideMessageType.MT_COMMAND)
        body = BrightsideMessageBody(
            JsonRequestSerializer(request=request).serialize_to_json(),
            BrightsideMessageBodyType.application_json)
        message = BrightsideMessage(header, body)

        pipeline.put(message)

        self.assertEqual(dispatcher.state, DispatcherState.ds_awaiting)

        dispatcher.receive()

        time.sleep(1)

        dispatcher.end()

        self.assertEqual(dispatcher.state, DispatcherState.ds_stopped)
        self.assertTrue(pipeline.empty())
Exemple #16
0
    def test_restart_consumer(self):
        """Given that I have a dispatcher with all consumers stopped
            When I restart a consumer
            Then the dispatcher should have one running consumer
        """
        connection = Connection(config.broker_uri,
                                "examples.perfomer.exchange")

        # First consumer
        request = MyCommand()
        pipeline_one = Queue()
        configuration_one = BrightsideConsumerConfiguration(
            pipeline_one, "restart_command.test.queue",
            "examples.tests.mycommand")
        consumer_one = ConsumerConfiguration(connection, configuration_one,
                                             mock_consumer_factory,
                                             mock_command_processor_factory,
                                             map_my_command_to_request)

        header_one = BrightsideMessageHeader(uuid4(),
                                             request.__class__.__name__,
                                             BrightsideMessageType.MT_COMMAND)
        body_one = BrightsideMessageBody(
            JsonRequestSerializer(request=request).serialize_to_json(),
            BrightsideMessageBodyType.application_json)
        message_one = BrightsideMessage(header_one, body_one)

        pipeline_one.put(message_one)

        # Second consumer
        event = MyEvent()
        pipeline_two = Queue()
        configuration_two = BrightsideConsumerConfiguration(
            pipeline_two, "restart_event.test.queue", "examples.tests.myevent")
        consumer_two = ConsumerConfiguration(connection, configuration_two,
                                             mock_consumer_factory,
                                             mock_command_processor_factory,
                                             map_my_event_to_request)

        header_two = BrightsideMessageHeader(uuid4(), event.__class__.__name__,
                                             BrightsideMessageType.MT_EVENT)
        body_two = BrightsideMessageBody(
            JsonRequestSerializer(request=event).serialize_to_json(),
            BrightsideMessageBodyType.application_json)
        message_two = BrightsideMessage(header_two, body_two)

        pipeline_two.put_nowait(message_two)

        # Dispatcher
        dispatcher = Dispatcher({
            "consumer_one": consumer_one,
            "consumer_two": consumer_two
        })

        # Consume the messages and stop
        self.assertEqual(dispatcher.state, DispatcherState.ds_awaiting)

        dispatcher.receive()

        time.sleep(1)

        dispatcher.end()

        self.assertEqual(dispatcher.state, DispatcherState.ds_stopped)
        self.assertTrue(pipeline_one.empty())

        #Now add a new message, restart a consumer, and eat
        event_three = MyEvent()
        header_three = BrightsideMessageHeader(uuid4(),
                                               event.__class__.__name__,
                                               BrightsideMessageType.MT_EVENT)
        body_three = BrightsideMessageBody(
            JsonRequestSerializer(request=event_three).serialize_to_json(),
            BrightsideMessageBodyType.application_json)
        message_three = BrightsideMessage(header_three, body_three)

        pipeline_two.put_nowait(message_three)

        dispatcher.open("consumer_two")

        time.sleep(1)

        dispatcher.end()

        self.assertEqual(dispatcher.state, DispatcherState.ds_stopped)
        self.assertTrue(pipeline_two.empty())