def setUp(self): # patching some global objects and preparing mockups of them self._stderr_patcher = rlocked_patch('sys.stderr') self.stderr_mock = self._stderr_patcher.start() self.addCleanup(self._stderr_patcher.stop) # patching some imported modules and preparing mockups of them self._time_patcher = rlocked_patch('n6lib.amqp_getters_pushers.time') self.time_mock = self._time_patcher.start() self.addCleanup(self._time_patcher.stop) self._traceback_patcher = rlocked_patch( 'n6lib.amqp_getters_pushers.traceback') self.traceback_mock = self._traceback_patcher.start() self.addCleanup(self._traceback_patcher.stop) self._pika_patcher = rlocked_patch('n6lib.amqp_getters_pushers.pika') self.pika_mock = self._pika_patcher.start() self.addCleanup(self._pika_patcher.stop) # preparing sentinel exceptions class AMQPConnectionError_sentinel_exc(Exception): pass class ConnectionClosed_sentinel_exc(Exception): pass class generic_sentinel_exc(Exception): pass self.AMQPConnectionError_sentinel_exc = AMQPConnectionError_sentinel_exc self.ConnectionClosed_sentinel_exc = ConnectionClosed_sentinel_exc self.generic_sentinel_exc = generic_sentinel_exc # preparing mockups of different objects self.conn_mock = RLockedMagicMock() self.channel_mock = RLockedMagicMock() self.optional_setup_communication_mock = RLockedMagicMock() self.serialize = RLockedMagicMock() self.error_callback = RLockedMagicMock() # configuring the mockups self.pika_mock.exceptions.AMQPConnectionError = AMQPConnectionError_sentinel_exc self.pika_mock.exceptions.ConnectionClosed = ConnectionClosed_sentinel_exc self.pika_mock.ConnectionParameters.return_value = sen.conn_parameters self.pika_mock.BlockingConnection.side_effect = [ AMQPConnectionError_sentinel_exc, self.conn_mock, ] self.pika_mock.BasicProperties.return_value = sen.props self.conn_mock.channel.return_value = self.channel_mock self.serialize.side_effect = (lambda data: data)
def test_publishing_with_fatal_error(self): with rlocked_patch( 'n6lib.amqp_getters_pushers.AMQPThreadedPusher._publish', # not an Exception subclass: side_effect=BaseException, ) as _publish_mock: self._make_obj() try: self.obj.push(sen.data, sen.rk) # we must wait to let the pub. thread operate and crash while _publish_mock.call_count < 1: time.sleep(0.01) self.obj._publishing_thread.join(15.0) self.assertFalse(self.obj._publishing_thread.is_alive()) self.assertFalse(self.obj._publishing) self.assertTrue(self.stderr_mock.mock_calls) finally: self.obj._publishing_thread.join(15.0) if self.obj._publishing_thread.is_alive(): raise RuntimeError('unexpected problem: the publishing ' 'thread did not terminate :-/') self.assertFalse(self.error_callback.mock_calls) self.assertFalse(self.traceback_mock.print_exc.mock_calls)
def test_publishing_with_error_callback_raising_exception(self): self.error_callback.side_effect = TypeError exceptions_from_publish = [ self.ConnectionClosed_sentinel_exc, TypeError, ] expected_publish_call_count = 2 with rlocked_patch( 'n6lib.amqp_getters_pushers.AMQPThreadedPusher._publish', side_effect=self._side_effect_for_publish( exceptions_from_publish)) as _publish_mock: self._error_case_commons(_publish_mock, expected_publish_call_count) self.assertEqual(_publish_mock.mock_calls, [ call(sen.data, sen.rk, None), call(sen.data, sen.rk, None), ]) self.assertEqual(self.optional_setup_communication_mock.mock_calls, [call()]) self.assertEqual(self.error_callback.mock_calls, [call(ANY)]) self.assertEqual(self.traceback_mock.print_exc.mock_calls, [call()]) self.assertTrue(self.stderr_mock.mock_calls ) # `print(..., file=sys.stderr)` used... # the message has not been published self.assertFalse(self.pika_mock.BasicProperties.mock_calls) self.assertFalse(self.channel_mock.basic_publish.mock_calls)
def test_publishing_with_exceptions_and_no_error_callback(self): self.error_callback = None exceptions_from_publish = [ self.ConnectionClosed_sentinel_exc, TypeError, ] expected_publish_call_count = 2 with rlocked_patch( 'n6lib.amqp_getters_pushers.AMQPThreadedPusher._publish', side_effect=self._side_effect_for_publish( exceptions_from_publish)) as _publish_mock: self._error_case_commons(_publish_mock, expected_publish_call_count) assert self.error_callback is None, "bug in test case" self.assertIsNone(self.obj._error_callback) self.assertEqual(_publish_mock.mock_calls, [ call(sen.data, sen.rk, None), call(sen.data, sen.rk, None), ]) self.assertEqual(self.optional_setup_communication_mock.mock_calls, [call()]) self.assertEqual(self.traceback_mock.print_exc.mock_calls, [call()]) # the message has not been published self.assertFalse(self.pika_mock.BasicProperties.mock_calls) self.assertFalse(self.channel_mock.basic_publish.mock_calls)
def test_publishing_flag_is_False(self): # do not try to reconnect on pika.exceptions.ConnectionClosed # but continue publishing until the output fifo is empty def basic_publish_side_effect(*args, **kwargs): time.sleep(0.02) self.channel_mock.basic_publish.side_effect = basic_publish_side_effect with rlocked_patch( 'n6lib.amqp_getters_pushers.AMQPThreadedPusher._publish', side_effect=self._side_effect_for_publish(exception_seq=[ None, self.ConnectionClosed_sentinel_exc, None, ]), ) as _publish_mock: with self._testing_normal_push(error_callback_call_count=1) as obj: obj._publishing = False obj._output_fifo.put_nowait((sen.data1, sen.rk1, None)) obj._output_fifo.put_nowait((sen.data_err, sen.rk_err, None)) obj._output_fifo.put_nowait((sen.data2, sen.rk2, { 'custom': sen.custom_value })) self.assertEqual(_publish_mock.mock_calls, [ call(sen.data1, sen.rk1, None), call(sen.data_err, sen.rk_err, None), call(sen.data2, sen.rk2, {'custom': sen.custom_value}), ]) # no reconnections self.assertFalse(self.optional_setup_communication_mock.mock_calls) # one error callback call self.assertEqual(self.error_callback.mock_calls, [call(ANY)])
def test_serialization_error(self): self.serialize.side_effect = TypeError expected_serialize_call_count = 1 with rlocked_patch( 'n6lib.amqp_getters_pushers.AMQPThreadedPusher._publish' ) as _publish_mock: self._error_case_commons(self.serialize, expected_serialize_call_count) self.assertEqual(self.serialize.mock_calls, [call(sen.data)]) self.assertEqual(self.error_callback.mock_calls, [call(ANY)]) self.assertFalse(self.traceback_mock.print_exc.mock_calls) self.assertFalse(self.stderr_mock.mock_calls) self.assertFalse(self.optional_setup_communication_mock.mock_calls) # the message has not been published self.assertFalse(_publish_mock.mock_calls) self.assertFalse(self.pika_mock.BasicProperties.mock_calls) self.assertFalse(self.channel_mock.basic_publish.mock_calls)
def test_publishing_with_one_ConnectionClosed(self): exceptions_from_publish = [ self.ConnectionClosed_sentinel_exc, None, ] expected_publish_call_count = 2 with rlocked_patch( 'n6lib.amqp_getters_pushers.AMQPThreadedPusher._publish', side_effect=self._side_effect_for_publish( exceptions_from_publish)) as _publish_mock: self._error_case_commons(_publish_mock, expected_publish_call_count) self.assertEqual(self.optional_setup_communication_mock.mock_calls, [call()]) self.assertEqual(_publish_mock.mock_calls, [ call(sen.data, sen.rk, None), call(sen.data, sen.rk, None), ]) self.assertFalse(self.error_callback.mock_calls) self.assertFalse(self.traceback_mock.print_exc.mock_calls) self.assertFalse(self.stderr_mock.mock_calls) # properties of the published message have been created properly... self.assertEqual(self.pika_mock.BasicProperties.mock_calls, [ call(prop_kwarg=sen.prop_value), ]) # ...and the message has been published properly self.assertEqual(self.channel_mock.basic_publish.mock_calls, [ call( exchange=sen.exchange, routing_key=sen.rk, body=sen.data, properties=sen.props, mandatory=sen.mandatory, ), ])
def test__init__specifying_all_and_obtaining_global_conn_params__with_ssl( self): connection_params_dict_mock = RLockedMagicMock( name='connection_params_dict') connection_params_dict_mock.get.return_value = True connection_params_dict_mock.__contains__.return_value = True with rlocked_patch( 'n6lib.amqp_getters_pushers.get_amqp_connection_params_dict', **{'return_value': connection_params_dict_mock}) as get_amqp_conn_params_mock: self.meth.__init__(connection_params_dict=None, exchange={ 'exchange': sen.exchange, 'bar': sen.bar }, queues_to_declare=[ sen.queue1, { 'blabla': sen.blabla }, { 'blabla': sen.blabla, 'callback': sen.callback }, ], serialize=sen.serialize, prop_kwargs=sen.prop_kwargs, mandatory=sen.mandatory, output_fifo_max_size=54321, error_callback=sen.error_callback) # attrs self.assertIs(self.mock._connection_params_dict, connection_params_dict_mock) self.assertEqual(self.mock._exchange, { 'exchange': sen.exchange, 'bar': sen.bar }) self.assertEqual(self.mock._exchange_name, sen.exchange) self.assertEqual(self.mock._queues_to_declare, [ { 'queue': sen.queue1, 'callback': ANY }, { 'blabla': sen.blabla, 'callback': ANY }, { 'blabla': sen.blabla, 'callback': sen.callback }, ]) self.assertEqual(self.mock._serialize, sen.serialize) self.assertEqual(self.mock._prop_kwargs, sen.prop_kwargs) self.assertEqual(self.mock._mandatory, sen.mandatory) self.assertIs(self.mock._output_fifo.__class__, queue.Queue) self.assertEqual(self.mock._output_fifo.maxsize, 54321) self.assertEqual(self.mock._error_callback, sen.error_callback) # calls self.assertEqual(self.mock.mock_calls, [ call._setup_communication(), call._start_publishing(), ]) self.assertEqual(get_amqp_conn_params_mock.mock_calls, [ call(), ]) self.assertEqual( connection_params_dict_mock.mock_calls, [ call.get('ssl'), call.setdefault('credentials', ANY), ('__contains__', ('client_properties', ), {}), # because cannot use `call.__contains__` ]) self.assertIsInstance( connection_params_dict_mock.setdefault.mock_calls[0][-2] [1], # 2nd arg for setdefault pika.credentials.ExternalCredentials)