def test_example_service(container_factory, rabbit_config): db_uri = 'sqlite:///{}'.format(tempfile.NamedTemporaryFile().name) engine = create_engine(db_uri) FooModel.metadata.create_all(engine) config = { ORM_DB_URIS_KEY: { 'foo-service:foo_base': db_uri } } config.update(rabbit_config) container = container_factory(FooService, config) container.start() with wait_for_call(5, handle_spam_called) as handle_spam: handle_spam.assert_called_with('ham & eggs') with wait_for_call(5, handle_foo_called) as handle_foo: handle_foo.assert_called_with('message') entries = list(engine.execute('SELECT data FROM spam LIMIT 1')) assert entries == [('ham',)] container.stop()
def test_rpc_container_being_killed_retries(container_factory, rabbit_config): container = container_factory(ExampleService, rabbit_config) container.start() def wait_for_result(): with RpcProxy("exampleservice", rabbit_config) as proxy: return proxy.task_a() container._being_killed = True rpc_provider = get_dependency(container, RpcProvider, name='task_a') with patch.object( rpc_provider, 'rpc_consumer', wraps=rpc_provider.rpc_consumer, ) as wrapped_consumer: waiter = eventlet.spawn(wait_for_result) with wait_for_call(1, wrapped_consumer.requeue_message): pass # wait until at least one message has been requeued assert not waiter.dead container._being_killed = False assert waiter.wait() == 'result_a' # now completed
def test_consume_from_rabbit(rabbit_manager, rabbit_config): vhost = rabbit_config['vhost'] container = Mock(spec=ServiceContainer) container.worker_ctx_cls = CustomWorkerContext container.service_name = "service" container.config = rabbit_config container.max_workers = 10 def spawn_thread(method, protected): return eventlet.spawn(method) container.spawn_managed_thread = spawn_thread worker_ctx = CustomWorkerContext(container, None, None) factory = DependencyFactory( ConsumeProvider, queue=foobar_queue, requeue_on_error=False) consumer = factory.create_and_bind_instance("injection_name", container) # prepare and start dependencies consumer.prepare() consumer.queue_consumer.prepare() consumer.start() consumer.queue_consumer.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] # test message consumed from queue container.spawn_worker.return_value = worker_ctx headers = {'nameko.language': 'en', 'nameko.customheader': 'customvalue'} rabbit_manager.publish( vhost, foobar_ex.name, '', 'msg', properties=dict(headers=headers)) ctx_data = { 'language': 'en', 'customheader': 'customvalue', } with wait_for_call(CONSUME_TIMEOUT, container.spawn_worker) as method: method.assert_called_once_with( consumer, ('msg', ), {}, context_data=ctx_data, handle_result=ANY_PARTIAL) handle_result = method.call_args[1]['handle_result'] # ack message handle_result(worker_ctx, 'result') # stop will hang if the consumer hasn't acked or requeued messages with eventlet.timeout.Timeout(CONSUME_TIMEOUT): consumer.stop()
def test_rpc_container_being_killed_retries( container_factory, rabbit_config): container = container_factory(ExampleService, rabbit_config) container.start() def wait_for_result(): with ServiceRpcProxy("exampleservice", rabbit_config) as proxy: return proxy.task_a() container._being_killed = True rpc_provider = get_extension(container, Rpc, method_name='task_a') with patch.object( rpc_provider, 'rpc_consumer', wraps=rpc_provider.rpc_consumer, ) as wrapped_consumer: waiter = eventlet.spawn(wait_for_result) with wait_for_call(1, wrapped_consumer.requeue_message): pass # wait until at least one message has been requeued assert not waiter.dead container._being_killed = False assert waiter.wait() == 'result_a' # now completed
def test_call_id_over_events(rabbit_config, predictable_call_ids, runner_factory): one_called = Mock() two_called = Mock() stack_request = Mock() LoggingWorkerContext = get_logging_worker_context(stack_request) class HelloEvent(NamekoEvent): type = "hello" class EventListeningServiceOne(object): @event_handler('event_raiser', 'hello') def hello(self, name): one_called() class EventListeningServiceTwo(object): @event_handler('event_raiser', 'hello') def hello(self, name): two_called() class EventRaisingService(object): name = "event_raiser" dispatch = event_dispatcher() @rpc def say_hello(self): self.dispatch(HelloEvent(self.name)) runner = runner_factory(rabbit_config) runner.add_service(EventListeningServiceOne, LoggingWorkerContext) runner.add_service(EventListeningServiceTwo, LoggingWorkerContext) runner.add_service(EventRaisingService, LoggingWorkerContext) runner.start() container = get_container(runner, EventRaisingService) with entrypoint_hook(container, "say_hello") as say_hello: say_hello() with wait_for_call(5, one_called), wait_for_call(5, two_called): assert predictable_call_ids.call_count == 3 stack_request.assert_has_calls([ call(None), call(['event_raiser.say_hello.0']), call(['event_raiser.say_hello.0']), ])
def test_wait_for_call(): mock = Mock() def call_after(seconds): eventlet.sleep(seconds) mock.method() # should not raise eventlet.spawn(call_after, 0) with wait_for_call(1, mock.method): pass mock.reset_mock() with pytest.raises(eventlet.Timeout): eventlet.spawn(call_after, 1) with wait_for_call(0, mock.method): pass
def test_consume_from_rabbit(container_factory, rabbit_manager, rabbit_config): vhost = rabbit_config["vhost"] container = Mock(spec=ServiceContainer) container.shared_extensions = {} container.worker_ctx_cls = CustomWorkerContext container.service_name = "service" container.config = rabbit_config container.max_workers = 10 def spawn_thread(method, protected): return eventlet.spawn(method) container.spawn_managed_thread = spawn_thread worker_ctx = CustomWorkerContext(container, None, DummyProvider()) consumer = Consumer(queue=foobar_queue, requeue_on_error=False).bind(container, "publish") # prepare and start extensions consumer.setup() consumer.queue_consumer.setup() consumer.start() consumer.queue_consumer.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] # test message consumed from queue container.spawn_worker.return_value = worker_ctx headers = {"nameko.language": "en", "nameko.customheader": "customvalue"} rabbit_manager.publish(vhost, foobar_ex.name, "", "msg", properties=dict(headers=headers)) ctx_data = {"language": "en", "customheader": "customvalue"} with wait_for_call(CONSUME_TIMEOUT, container.spawn_worker) as method: method.assert_called_once_with(consumer, ("msg",), {}, context_data=ctx_data, handle_result=ANY_PARTIAL) handle_result = method.call_args[1]["handle_result"] # ack message handle_result(worker_ctx, "result") # stop will hang if the consumer hasn't acked or requeued messages with eventlet.timeout.Timeout(CONSUME_TIMEOUT): consumer.stop() consumer.queue_consumer.kill()
def test_restrict_entrypoints(container_factory, rabbit_config): method_called = Mock() class OnceProvider(EntrypointProvider): """ Entrypoint that spawns a worker exactly once, as soon as the service container started. """ def __init__(self, *args, **kwargs): self.args = args self.kwargs = kwargs def start(self): self.container.spawn_worker(self, self.args, self.kwargs) @entrypoint def once(*args, **kwargs): return DependencyFactory(OnceProvider, args, kwargs) class ExampleEvent(Event): type = "eventtype" class Service(object): @rpc @once("assert not seen") def handler_one(self, arg): method_called(arg) @event_handler('srcservice', 'eventtype') def handler_two(self, msg): method_called(msg) container = container_factory(Service, rabbit_config) # disable the entrypoints on handler_one restrict_entrypoints(container, "handler_two") container.start() # verify the rpc entrypoint on handler_one is disabled with RpcProxy("service", rabbit_config) as service_proxy: with pytest.raises(RemoteError) as exc_info: service_proxy.handler_one("msg") assert exc_info.value.exc_type == "MethodNotFound" # dispatch an event to handler_two msg = "msg" with event_dispatcher('srcservice', rabbit_config) as dispatch: dispatch(ExampleEvent(msg)) # method_called should have exactly one call, derived from the event # handler and not from the disabled @once entrypoint with wait_for_call(1, method_called): method_called.assert_called_once_with(msg)
def test_dispatch(container_factory, rabbit_config): config = rabbit_config container = container_factory(Service, config) container.start() msg = "msg" with event_dispatcher('srcservice', config) as dispatch: dispatch(TestEvent(msg)) with wait_for_call(1, handler_called): handler_called.assert_called_once_with(msg)
def test_entrypoint_hook(runner_factory, rabbit_config): service_classes = (Service, ServiceA, ServiceB, ServiceC) runner = runner_factory(rabbit_config, *service_classes) runner.start() service_container = get_container(runner, Service) event_payload = "msg" with entrypoint_hook(service_container, 'handle') as handle: handle(event_payload) with wait_for_call(1, handle_event): handle_event.assert_called_once_with(event_payload)
def test_kill_stops_cron(container_factory, tracker): class Service(object): name = "service" @cron('* * * * * *') def tick(self): tracker() container = container_factory(Service, {}) container.start() with wait_for_call(2.0, tracker): container.kill() eventlet.sleep(2.0) assert tracker.call_count == 1
def test_busy_check_on_teardown(): # max_workers needs to be provided, as it's used in a semaphore count config = {'max_workers': 4} kill_called = Mock() class MockedContainer(ServiceContainer): def kill(self): kill_called() super(MockedContainer, self).kill() sr = ServiceRunner(config, container_cls=MockedContainer) sr.add_service(ExampleService) sr.start() sr.kill() with wait_for_call(5, kill_called) as kill_called_waited: assert kill_called_waited.call_count == 1
def test_busy_check_on_teardown(): # max_workers needs to be provided, as it's used in a semaphore count config = MagicMock({'max_workers': 4}) kill_called = Mock() class MockedContainer(ServiceContainer): def kill(self, exc): kill_called(type(exc)) super(MockedContainer, self).kill(exc) sr = ServiceRunner(config, container_cls=MockedContainer) sr.add_service(ExampleService) sr.start() sr.kill(Exception()) with wait_for_call(5, kill_called) as kill_called_waited: kill_called_waited.assert_called_with(Exception)
def test_kill_stops_timer(): container = create_autospec(ServiceContainer) container.service_name = "service" container.spawn_managed_thread = spawn_managed_thread timer = Timer(interval=0).bind(container, "method") timer.setup() timer.start() with wait_for_call(1, container.spawn_worker): timer.kill() # unless the timer is dead, the following nap would cause a timer # to trigger eventlet.sleep(0.1) assert container.spawn_worker.call_count == 1
def test_kill_stops_timer(): container = Mock(spec=ServiceContainer) container.service_name = "service" container.spawn_managed_thread = eventlet.spawn timer = TimerProvider(interval=0, config_key=None) timer.bind('foobar', container) timer.prepare() timer.start() with wait_for_call(1, container.spawn_worker): timer.kill(Exception('time')) # unless the timer is dead, the following nap would cause a timer # to trigger eventlet.sleep(0.1) assert container.spawn_worker.call_count == 1
def test_kill_stops_timer(): container = Mock(spec=ServiceContainer) container.service_name = "service" container.spawn_managed_thread = eventlet.spawn timer = TimerProvider(interval=0, config_key=None) timer.bind('foobar', container) timer.prepare() timer.start() with wait_for_call(1, container.spawn_worker): timer.kill() # unless the timer is dead, the following nap would cause a timer # to trigger eventlet.sleep(0.1) assert container.spawn_worker.call_count == 1
def test_kill_stops_timer(container_factory, tracker): class Service(object): name = "service" @timer(0) def tick(self): tracker() container = container_factory(Service, {}) container.start() with wait_for_call(1, tracker): container.kill() # unless the timer is dead, the following nap would cause a timer # to trigger eventlet.sleep(0.1) assert tracker.call_count == 1
def test_provider(): container = create_autospec(ServiceContainer) container.service_name = "service" container.spawn_managed_thread = spawn_managed_thread timer = Timer(interval=0.1).bind(container, "method") timer.setup() timer.start() assert timer.interval == 0.1 with wait_for_call(1, container.spawn_worker) as spawn_worker: with Timeout(1): timer.stop() # the timer should have stopped and should only have spawned # a single worker spawn_worker.assert_called_once_with(timer, (), {}) assert timer.gt.dead
def test_provider(): container = Mock(spec=ServiceContainer) container.service_name = "service" container.config = Mock() container.spawn_managed_thread = eventlet.spawn timer = TimerProvider(interval=0, config_key=None) timer.bind('foobar', container) timer.prepare() assert timer.interval == 0 timer.start() with wait_for_call(1, container.spawn_worker) as spawn_worker: with Timeout(1): timer.stop() # the timer should have stopped and should only have spawned # a single worker spawn_worker.assert_called_once_with(timer, (), {}) assert timer.gt.dead
def test_consume_from_rabbit(rabbit_manager, rabbit_config, mock_container): vhost = rabbit_config['vhost'] container = mock_container container.shared_extensions = {} container.worker_ctx_cls = WorkerContext container.service_name = "service" container.config = rabbit_config container.max_workers = 10 content_type = 'application/data' container.accept = [content_type] def spawn_managed_thread(method, identifier=None): return eventlet.spawn(method) container.spawn_managed_thread = spawn_managed_thread worker_ctx = WorkerContext(container, None, DummyProvider()) consumer = Consumer( queue=foobar_queue, requeue_on_error=False).bind(container, "publish") # prepare and start extensions consumer.setup() consumer.queue_consumer.setup() consumer.start() consumer.queue_consumer.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] # test message consumed from queue container.spawn_worker.return_value = worker_ctx headers = {'nameko.language': 'en', 'nameko.customheader': 'customvalue'} rabbit_manager.publish( vhost, foobar_ex.name, '', 'msg', properties=dict(headers=headers, content_type=content_type)) ctx_data = { 'language': 'en', 'customheader': 'customvalue', } with wait_for_call(CONSUME_TIMEOUT, container.spawn_worker) as method: method.assert_called_once_with(consumer, ('msg',), {}, context_data=ctx_data, handle_result=ANY_PARTIAL) handle_result = method.call_args[1]['handle_result'] # ack message handle_result(worker_ctx, 'result') # stop will hang if the consumer hasn't acked or requeued messages with eventlet.timeout.Timeout(CONSUME_TIMEOUT): consumer.stop() consumer.queue_consumer.kill()
def test_parallel_executor_submit_makes_call(container): to_call = Mock(return_value=99) future = ParallelExecutor(container).submit(to_call, 1) with wait_for_call(5, to_call) as to_call_waited: to_call_waited.assert_called_with(1) assert future.result() == 99
def test_consume_from_rabbit(rabbit_manager, rabbit_config): vhost = rabbit_config['vhost'] container = Mock(spec=ServiceContainer) container.worker_ctx_cls = CustomWorkerContext container.service_name = "service" container.config = rabbit_config container.max_workers = 10 def spawn_thread(method, protected): return eventlet.spawn(method) container.spawn_managed_thread = spawn_thread worker_ctx = CustomWorkerContext(container, None, DummyProvider()) factory = DependencyFactory(ConsumeProvider, queue=foobar_queue, requeue_on_error=False) consumer = factory.create_and_bind_instance("injection_name", container) # prepare and start dependencies consumer.prepare() consumer.queue_consumer.prepare() consumer.start() consumer.queue_consumer.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] # test message consumed from queue container.spawn_worker.return_value = worker_ctx headers = {'nameko.language': 'en', 'nameko.customheader': 'customvalue'} rabbit_manager.publish(vhost, foobar_ex.name, '', 'msg', properties=dict(headers=headers)) ctx_data = { 'language': 'en', 'customheader': 'customvalue', } with wait_for_call(CONSUME_TIMEOUT, container.spawn_worker) as method: method.assert_called_once_with(consumer, ('msg', ), {}, context_data=ctx_data, handle_result=ANY_PARTIAL) handle_result = method.call_args[1]['handle_result'] # ack message handle_result(worker_ctx, 'result') # stop will hang if the consumer hasn't acked or requeued messages with eventlet.timeout.Timeout(CONSUME_TIMEOUT): consumer.stop() consumer.queue_consumer.kill()