def test_worker_context_gets_stack(container_factory): class FooService(object): name = 'baz' container = container_factory(FooService, {}) service = FooService() context = WorkerContext(container, service, DummyProvider("bar")) assert context.call_id == 'baz.bar.0' assert context.call_id_stack == ['baz.bar.0'] # Build stack context = WorkerContext(container, service, DummyProvider("foo"), data={'call_id_stack': context.call_id_stack}) assert context.call_id == 'baz.foo.1' assert context.call_id_stack == ['baz.bar.0', 'baz.foo.1'] # Long stack many_ids = [str(i) for i in range(10)] context = WorkerContext(container, service, DummyProvider("long"), data={'call_id_stack': many_ids}) expected = many_ids + ['baz.long.2'] assert context.call_id_stack == expected
def test_config_adapters(default_info, custom_info, mock_container): mock_container.service_name = 'dummy' mock_container.config = { constants.CONFIG_KEY: { constants.ADAPTERS_CONFIG_KEY: { 'nameko.web.handlers.HttpRequestHandler': 'test_dependency.CustomAdapter', } } } tracer = Tracer().bind(mock_container, 'logger') tracer.setup() default_worker_ctx = WorkerContext(mock_container, None, DummyProvider()) http_worker_ctx = WorkerContext(mock_container, None, HttpRequestHandler('GET', 'http://yo')) calls = [ tracer.worker_setup, tracer.worker_result, tracer.worker_setup, tracer.worker_result ] for call_ in calls: call_(default_worker_ctx) call_(http_worker_ctx) assert default_info.call_count == 4 assert custom_info.call_count == 4
def test_dispatch_to_rabbit(rabbit_manager, rabbit_config, mock_container): vhost = rabbit_config['vhost'] container = mock_container container.shared_extensions = {} container.service_name = "srcservice" container.config = rabbit_config service = Mock() worker_ctx = WorkerContext(container, service, DummyProvider()) dispatcher = EventDispatcher().bind(container, 'dispatch') dispatcher.setup() dispatcher.start() # we should have an exchange but no queues exchanges = rabbit_manager.get_exchanges(vhost) queues = rabbit_manager.get_queues(vhost) assert "srcservice.events" in [exchange['name'] for exchange in exchanges] assert queues == [] # manually add a queue to capture the events rabbit_manager.create_queue(vhost, "event-sink", auto_delete=True) rabbit_manager.create_queue_binding(vhost, "srcservice.events", "event-sink", routing_key="eventtype") service.dispatch = dispatcher.get_dependency(worker_ctx) service.dispatch("eventtype", "msg") # test event receieved on manually added queue messages = rabbit_manager.get_messages(vhost, "event-sink") assert ['"msg"'] == [msg['payload'] for msg in messages]
def test_get_builtin_providers(provider_name, context_key, container): provider = get_dependency( container, ContextDataProvider, name=provider_name) worker_ctx = WorkerContext( container, "service", provider, data={context_key: 'value'}) assert provider.acquire_injection(worker_ctx) == "value"
def test_get_unset_value(container): provider = get_dependency( container, ContextDataProvider, name="custom_value") worker_ctx = WorkerContext( container, "service", provider, data={}) assert provider.acquire_injection(worker_ctx) is None
def test_get_custom_context_value(container): provider = get_dependency( container, ContextDataProvider, name="custom_value") worker_ctx = WorkerContext( container, "service", provider, data={CUSTOM_CONTEXT_KEY: "hello"}) assert provider.acquire_injection(worker_ctx) == "hello"
def test_publish_to_exchange(empty_config, maybe_declare, patch_publisher): container = Mock(spec=ServiceContainer) container.service_name = "srcservice" container.config = empty_config service = Mock() worker_ctx = WorkerContext(container, service, DummyProvider("publish")) publisher = PublishProvider(exchange=foobar_ex) publisher.bind("publish", container) producer = Mock() connection = Mock() get_connection, get_producer = patch_publisher(publisher) get_connection.return_value = as_context_manager(connection) get_producer.return_value = as_context_manager(producer) # test declarations publisher.prepare() maybe_declare.assert_called_once_with(foobar_ex, connection) # test publish msg = "msg" publisher.inject(worker_ctx) service.publish(msg, publish_kwarg="value") headers = {'nameko.call_id_stack': ['srcservice.publish.0']} producer.publish.assert_called_once_with(msg, headers=headers, exchange=foobar_ex, retry=True, retry_policy=DEFAULT_RETRY_POLICY, publish_kwarg="value")
def test_get_unset_value(container): dependency = get_extension(container, ContextDataProvider, attr_name="custom_value") worker_ctx = WorkerContext(container, "service", Mock(), data={}) assert dependency.get_dependency(worker_ctx) is None
def test_get_builtin_dependencies(attr_name, context_key, container): dependency = get_extension( container, ContextDataProvider, attr_name=attr_name) worker_ctx = WorkerContext( container, "service", Mock(), data={context_key: 'value'}) assert dependency.get_dependency(worker_ctx) == "value"
def test_consume_provider(empty_config): container = Mock(spec=ServiceContainer) container.worker_ctx_cls = WorkerContext container.service_name = "service" container.config = empty_config worker_ctx = WorkerContext(container, None, DummyProvider()) spawn_worker = container.spawn_worker spawn_worker.return_value = worker_ctx queue_consumer = Mock() consume_provider = ConsumeProvider(queue=foobar_queue, requeue_on_error=False) consume_provider.queue_consumer = queue_consumer consume_provider.bind("name", container) message = Mock(headers={}) # test lifecycle consume_provider.prepare() queue_consumer.register_provider.assert_called_once_with(consume_provider) consume_provider.stop() queue_consumer.unregister_provider.assert_called_once_with( consume_provider) # test handling successful call queue_consumer.reset_mock() consume_provider.handle_message("body", message) handle_result = spawn_worker.call_args[1]['handle_result'] handle_result(worker_ctx, 'result') queue_consumer.ack_message.assert_called_once_with(message) # test handling failed call without requeue queue_consumer.reset_mock() consume_provider.requeue_on_error = False consume_provider.handle_message("body", message) handle_result = spawn_worker.call_args[1]['handle_result'] handle_result(worker_ctx, None, (Exception, Exception('Error'), "tb")) queue_consumer.ack_message.assert_called_once_with(message) # test handling failed call with requeue queue_consumer.reset_mock() consume_provider.requeue_on_error = True consume_provider.handle_message("body", message) handle_result = spawn_worker.call_args[1]['handle_result'] handle_result(worker_ctx, None, (Exception, Exception('Error'), "tb")) assert not queue_consumer.ack_message.called queue_consumer.requeue_message.assert_called_once_with(message) # test requeueing on ContainerBeingKilled (even without requeue_on_error) queue_consumer.reset_mock() consume_provider.requeue_on_error = False spawn_worker.side_effect = ContainerBeingKilled() consume_provider.handle_message("body", message) assert not queue_consumer.ack_message.called queue_consumer.requeue_message.assert_called_once_with(message)
def test_get_custom_context_value(container): dependency = get_extension( container, ContextDataProvider, attr_name="custom_value") worker_ctx = WorkerContext( container, "service", Mock(), data={CUSTOM_CONTEXT_KEY: "hello"}) assert dependency.get_dependency(worker_ctx) == "hello"
def test_unserialisable_headers(rabbit_manager, rabbit_config, mock_container): vhost = rabbit_config['vhost'] container = mock_container container.service_name = "service" container.config = rabbit_config container.spawn_managed_thread = eventlet.spawn ctx_data = {'language': 'en', 'customheader': None} service = Mock() worker_ctx = WorkerContext( container, service, DummyProvider('method'), data=ctx_data ) publisher = Publisher( exchange=foobar_ex, queue=foobar_queue).bind(container, "publish") publisher.setup() publisher.start() service.publish = publisher.get_dependency(worker_ctx) service.publish("msg") messages = rabbit_manager.get_messages(vhost, foobar_queue.name) assert messages[0]['properties']['headers'] == { 'nameko.language': 'en', 'nameko.call_id_stack': ['service.method.0'], # no `customheader` }
def test_publish_custom_headers( mock_container, maybe_declare, mock_producer, mock_connection, rabbit_config ): container = mock_container container.config = rabbit_config container.service_name = "srcservice" ctx_data = {'language': 'en', 'customheader': 'customvalue'} service = Mock() worker_ctx = WorkerContext( container, service, DummyProvider('method'), data=ctx_data ) publisher = Publisher(queue=foobar_queue).bind(container, "publish") # test declarations publisher.setup() maybe_declare.assert_called_once_with(foobar_queue, mock_connection) # test publish msg = "msg" headers = {'nameko.language': 'en', 'nameko.customheader': 'customvalue', 'nameko.call_id_stack': ['srcservice.method.0']} service.publish = publisher.get_dependency(worker_ctx) service.publish(msg, publish_kwarg="value") mock_producer.publish.assert_called_once_with( msg, headers=headers, exchange=foobar_ex, retry=True, serializer=container.serializer, mandatory=False, retry_policy=DEFAULT_RETRY_POLICY, publish_kwarg="value")
def test_event_dispatcher(mock_container, mock_producer, rabbit_config): container = mock_container container.config = rabbit_config container.service_name = "srcservice" service = Mock() worker_ctx = WorkerContext(container, service, DummyProvider("dispatch")) event_dispatcher = EventDispatcher(retry_policy={ 'max_retries': 5 }).bind(container, attr_name="dispatch") event_dispatcher.setup() service.dispatch = event_dispatcher.get_dependency(worker_ctx) service.dispatch('eventtype', 'msg') headers = event_dispatcher.get_message_headers(worker_ctx) mock_producer.publish.assert_called_once_with( 'msg', exchange=ANY, headers=headers, serializer=container.serializer, routing_key='eventtype', retry=True, mandatory=False, retry_policy={'max_retries': 5}) _, call_kwargs = mock_producer.publish.call_args exchange = call_kwargs['exchange'] assert exchange.name == 'srcservice.events'
def test_publish_to_exchange(maybe_declare, patch_publisher, mock_container): container = mock_container container.service_name = "srcservice" service = Mock() worker_ctx = WorkerContext(container, service, DummyProvider("publish")) publisher = Publisher(exchange=foobar_ex).bind(container, "publish") producer = Mock() connection = Mock() get_connection, get_producer = patch_publisher(publisher) get_connection.return_value = as_context_manager(connection) get_producer.return_value = as_context_manager(producer) # test declarations publisher.setup() maybe_declare.assert_called_once_with(foobar_ex, connection) # test publish msg = "msg" service.publish = publisher.get_dependency(worker_ctx) service.publish(msg, publish_kwarg="value") headers = { 'nameko.call_id_stack': ['srcservice.publish.0'] } producer.publish.assert_called_once_with( msg, headers=headers, exchange=foobar_ex, retry=True, serializer=container.serializer, retry_policy=DEFAULT_RETRY_POLICY, publish_kwarg="value")
def test_event_dispatcher(mock_container): container = mock_container container.service_name = "srcservice" service = Mock() worker_ctx = WorkerContext(container, service, DummyProvider("dispatch")) event_dispatcher = EventDispatcher(retry_policy={ 'max_retries': 5 }).bind(container, attr_name="dispatch") event_dispatcher.setup() service.dispatch = event_dispatcher.get_dependency(worker_ctx) from mock import ANY with patch('nameko.standalone.events.producers') as mock_producers: with mock_producers[ANY].acquire() as mock_producer: service.dispatch('eventtype', 'msg') headers = event_dispatcher.get_message_headers(worker_ctx) mock_producer.publish.assert_called_once_with( 'msg', exchange=ANY, headers=headers, serializer=container.serializer, routing_key='eventtype', retry=True, retry_policy={'max_retries': 5}) _, call_kwargs = mock_producer.publish.call_args exchange = call_kwargs['exchange'] assert exchange.name == 'srcservice.events'
def test_dispatch_to_rabbit(rabbit_manager, rabbit_config): vhost = rabbit_config['vhost'] container = Mock(spec=ServiceContainer) container.service_name = "srcservice" container.config = rabbit_config service = Mock() worker_ctx = WorkerContext(container, service, DummyProvider()) dispatcher = EventDispatcher() dispatcher.bind("dispatch", container) dispatcher.prepare() dispatcher.start() # we should have an exchange but no queues exchanges = rabbit_manager.get_exchanges(vhost) queues = rabbit_manager.get_queues(vhost) assert "srcservice.events" in [exchange['name'] for exchange in exchanges] assert queues == [] # manually add a queue to capture the events rabbit_manager.create_queue(vhost, "event-sink", auto_delete=True) rabbit_manager.create_binding(vhost, "srcservice.events", "event-sink", rt_key=ExampleEvent.type) dispatcher.inject(worker_ctx) service.dispatch(ExampleEvent("msg")) # test event receieved on manually added queue messages = rabbit_manager.get_messages(vhost, "event-sink") assert ['msg'] == [msg['payload'] for msg in messages]
def worker_ctx(self, container, service_class): entrypoint = get_extension(container, Entrypoint, method_name='some_method') return WorkerContext(container, service_class, entrypoint, args=('some-arg', ))
def __init__( self, config, context_data=None, timeout=None, reply_listener_cls=SingleThreadedReplyListener ): container = self.ServiceContainer(config) self._worker_ctx = WorkerContext( container, service=None, entrypoint=self.Dummy, data=context_data) self._reply_listener = reply_listener_cls( timeout=timeout).bind(container)
def test_default_adapters(default_info, http_info, mock_container): mock_container.service_name = 'dummy' mock_container.config = {} tracer = Tracer().bind(mock_container, 'logger') tracer.setup() default_worker_ctx = WorkerContext(mock_container, None, DummyProvider()) http_worker_ctx = WorkerContext(mock_container, None, HttpRequestHandler('GET', 'http://yo')) calls = [ tracer.worker_setup, tracer.worker_result, tracer.worker_setup, tracer.worker_result ] for call_ in calls: call_(default_worker_ctx) call_(http_worker_ctx) assert default_info.call_count == 4 assert http_info.call_count == 4
def test_short_call_stack(container_factory): class FooService(object): name = 'baz' container = container_factory(FooService, {PARENT_CALLS_CONFIG_KEY: 1}) service = FooService() # Trim stack many_ids = [str(i) for i in range(100)] context = WorkerContext(container, service, DummyProvider("long"), data={'call_id_stack': many_ids}) assert context.call_id_stack == ['99', 'baz.long.0']
def test_with_parent(self, mock_container): mock_container.service_name = "foo" service = Mock() entrypoint = DummyProvider("bar") context_data = {'call_id_stack': ['parent.method.1']} worker_ctx = WorkerContext(mock_container, service, entrypoint, data=context_data) assert worker_ctx.immediate_parent_call_id == "parent.method.1"
def test_without_origin(self, mock_container): mock_container.service_name = "foo" service = Mock() entrypoint = DummyProvider("bar") context_data = {} worker_ctx = WorkerContext(mock_container, service, entrypoint, data=context_data) assert worker_ctx.origin_call_id is None
def test_publish_to_queue(patch_maybe_declare, mock_producer, mock_connection, mock_container): container = mock_container container.config = {'AMQP_URI': 'memory://'} container.shared_extensions = {} container.service_name = "srcservice" ctx_data = {'language': 'en'} service = Mock() worker_ctx = WorkerContext(container, service, DummyProvider("publish"), data=ctx_data) publisher = Publisher(queue=foobar_queue).bind(container, "publish") # test declarations publisher.setup() assert patch_maybe_declare.call_args_list == [ call(foobar_queue, mock_connection) ] # test publish msg = "msg" headers = { 'nameko.language': 'en', 'nameko.call_id_stack': ['srcservice.publish.0'], } service.publish = publisher.get_dependency(worker_ctx) service.publish(msg, publish_kwarg="value") expected_args = ('msg', ) expected_kwargs = { 'publish_kwarg': "value", 'exchange': foobar_ex, 'headers': headers, 'declare': publisher.declare, 'retry': publisher.publisher_cls.retry, 'retry_policy': publisher.publisher_cls.retry_policy, 'compression': publisher.publisher_cls.compression, 'mandatory': publisher.publisher_cls.mandatory, 'expiration': publisher.publisher_cls.expiration, 'delivery_mode': publisher.publisher_cls.delivery_mode, 'priority': publisher.publisher_cls.priority, 'serializer': publisher.serializer } assert mock_producer.publish.call_args_list == [ call(*expected_args, **expected_kwargs) ]
def worker_ctx(self, container, service_class): environ = create_environ('/spam/1?test=123', 'http://localhost:8080/', data=json.dumps({'foo': 'bar'}), content_type='application/json') request = Request(environ) entrypoint = get_extension(container, HttpRequestHandler, method_name='some_method') return WorkerContext(container, service_class, entrypoint, args=(request, 1))
def test_headers(self, mock_container, producer): """ Headers provided at publish time are merged with any provided at instantiation time. Nameko headers are always present. """ mock_container.config = { 'AMQP_URI': 'memory://localhost' } mock_container.service_name = "service" # use a real worker context so nameko headers are generated service = Mock() entrypoint = Mock(method_name="method") worker_ctx = WorkerContext( mock_container, service, entrypoint, data={'context': 'data'} ) nameko_headers = { 'nameko.context': 'data', 'nameko.call_id_stack': ['service.method.0'], } instantiation_value = {'foo': Mock()} publish_value = {'bar': Mock()} publisher = Publisher( **{'headers': instantiation_value} ).bind(mock_container, "publish") publisher.setup() publish = publisher.get_dependency(worker_ctx) def merge_dicts(base, *updates): merged = base.copy() [merged.update(update) for update in updates] return merged publish("payload") assert producer.publish.call_args[1]['headers'] == merge_dicts( nameko_headers, instantiation_value ) publish("payload", headers=publish_value) assert producer.publish.call_args[1]['headers'] == merge_dicts( nameko_headers, instantiation_value, publish_value )
def test_publish_custom_headers(mock_container, mock_producer, mock_connection, rabbit_config): container = mock_container container.config = rabbit_config container.service_name = "srcservice" ctx_data = {'language': 'en', 'customheader': 'customvalue'} service = Mock() worker_ctx = WorkerContext(container, service, DummyProvider('method'), data=ctx_data) publisher = Publisher(queue=foobar_queue).bind(container, "publish") publisher.setup() # test publish msg = "msg" headers = { 'nameko.language': 'en', 'nameko.customheader': 'customvalue', 'nameko.call_id_stack': ['srcservice.method.0'] } service.publish = publisher.get_dependency(worker_ctx) service.publish(msg, publish_kwarg="value") expected_args = ('msg', ) expected_kwargs = { 'publish_kwarg': "value", 'exchange': foobar_ex, 'headers': headers, 'declare': publisher.declare, 'retry': publisher.publisher_cls.retry, 'retry_policy': publisher.publisher_cls.retry_policy, 'compression': publisher.publisher_cls.compression, 'mandatory': publisher.publisher_cls.mandatory, 'expiration': publisher.publisher_cls.expiration, 'delivery_mode': publisher.publisher_cls.delivery_mode, 'priority': publisher.publisher_cls.priority, 'serializer': publisher.serializer } assert mock_producer.publish.call_args_list == [ call(*expected_args, **expected_kwargs) ]
def entrypoint(self, request, container_factory, rabbit_config): EXCHANGE_NAME = "some-exchange" ROUTING_KEY = "some.routing.key" class Service(object): name = "some-service" exchange = Exchange(EXCHANGE_NAME) @rpc def rpc(self, payload): pass @event_handler("publisher", "property_updated") def event_handler(self, payload): pass @consume(queue=Queue('service', exchange=exchange, routing_key=ROUTING_KEY)) def consume(self, payload): pass container = container_factory(Service, rabbit_config) extension_class = request.param methods = { Rpc: 'rpc', EventHandler: 'event_handler', Consumer: 'consume' } entrypoint = get_extension(container, extension_class, method_name=methods[extension_class]) worker_context = WorkerContext(container, Service, entrypoint, args=('spam', )) return entrypoint, worker_context
def test_publish_to_rabbit(rabbit_manager, rabbit_config, mock_container): vhost = rabbit_config['vhost'] container = mock_container container.service_name = "service" container.config = rabbit_config ctx_data = {'language': 'en', 'customheader': 'customvalue'} service = Mock() worker_ctx = WorkerContext( container, service, DummyProvider('method'), data=ctx_data ) publisher = Publisher( exchange=foobar_ex, queue=foobar_queue ).bind(container, "publish") publisher.setup() publisher.start() # test queue, exchange and binding created in rabbit exchanges = rabbit_manager.get_exchanges(vhost) queues = rabbit_manager.get_queues(vhost) bindings = rabbit_manager.get_queue_bindings(vhost, foobar_queue.name) assert "foobar_ex" in [exchange['name'] for exchange in exchanges] assert "foobar_queue" in [queue['name'] for queue in queues] assert "foobar_ex" in [binding['source'] for binding in bindings] service.publish = publisher.get_dependency(worker_ctx) service.publish("msg") # test message published to queue messages = rabbit_manager.get_messages(vhost, foobar_queue.name) assert ['"msg"'] == [msg['payload'] for msg in messages] # test message headers assert messages[0]['properties']['headers'] == { 'nameko.language': 'en', 'nameko.customheader': 'customvalue', 'nameko.call_id_stack': ['service.method.0'], }
def test_event_dispatcher(mock_container, mock_producer, rabbit_config): container = mock_container container.config = rabbit_config container.service_name = "srcservice" service = Mock() worker_ctx = WorkerContext(container, service, DummyProvider("dispatch")) custom_retry_policy = {'max_retries': 5} event_dispatcher = EventDispatcher(retry_policy=custom_retry_policy).bind( container, attr_name="dispatch") event_dispatcher.setup() service.dispatch = event_dispatcher.get_dependency(worker_ctx) service.dispatch('eventtype', 'msg') headers = event_dispatcher.get_message_headers(worker_ctx) expected_args = ('msg',) expected_kwargs = { 'exchange': ANY, 'routing_key': 'eventtype', 'headers': headers, 'declare': event_dispatcher.declare, 'retry': event_dispatcher.publisher_cls.retry, 'retry_policy': custom_retry_policy, 'compression': event_dispatcher.publisher_cls.compression, 'mandatory': event_dispatcher.publisher_cls.mandatory, 'expiration': event_dispatcher.publisher_cls.expiration, 'delivery_mode': event_dispatcher.publisher_cls.delivery_mode, 'priority': event_dispatcher.publisher_cls.priority, 'serializer': event_dispatcher.serializer, } assert mock_producer.publish.call_count == 1 args, kwargs = mock_producer.publish.call_args assert args == expected_args assert kwargs == expected_kwargs assert kwargs['exchange'].name == 'srcservice.events'