Пример #1
0
def test_shop_checkout_integration(
    rabbit_config, runner_factory, rpc_proxy_factory
):
    """ Simulate a checkout flow as an integration test.

    Requires instances of AcmeShopService, StockService and InvoiceService
    to be running. Explicitly replaces the rpc proxy to PaymentService so
    that service doesn't need to be hosted.

    Also replaces the event dispatcher dependency on AcmeShopService and
    disables the timer entrypoint on StockService. Limiting the interactions
    of services in this way reduces the scope of the integration test and
    eliminates undesirable side-effects (e.g. processing events unnecessarily).
    """
    context_data = {'user_id': 'wile_e_coyote'}
    shop = rpc_proxy_factory('acmeshopservice', context_data=context_data)

    runner = runner_factory(
        rabbit_config, AcmeShopService, StockService, InvoiceService)

    # replace ``event_dispatcher`` and ``payment_rpc``  dependencies on
    # AcmeShopService with ``MockDependencyProvider``\s
    shop_container = get_container(runner, AcmeShopService)
    fire_event, payment_rpc = replace_dependencies(
        shop_container, "fire_event", "payment_rpc")

    # restrict entrypoints on StockService
    stock_container = get_container(runner, StockService)
    restrict_entrypoints(stock_container, "check_price", "check_stock")

    runner.start()

    # add some items to the basket
    assert shop.add_to_basket("anvil") == "anvil"
    assert shop.add_to_basket("invisible_paint") == "invisible_paint"

    # try to buy something that's out of stock
    with pytest.raises(RemoteError) as exc_info:
        shop.add_to_basket("toothpicks")
    assert exc_info.value.exc_type == "ItemOutOfStockError"

    # provide a mock response from the payment service
    payment_rpc.take_payment.return_value = "Payment complete."

    # checkout
    res = shop.checkout()

    total_amount = 100 + 10
    assert res == total_amount

    # verify integration with mocked out payment service
    payment_rpc.take_payment.assert_called_once_with({
        'customer': "wile_e_coyote",
        'address': "12 Long Road, High Cliffs, Utah",
        'amount': total_amount,
        'message': "Dear Wile E Coyote. Please pay $110 to ACME Corp."
    })

    # verify events fired as expected
    assert fire_event.call_count == 3
Пример #2
0
def test_restrict_nonexistent_entrypoint(container_factory, rabbit_config):
    class Service(object):
        @rpc
        def method(self, arg):
            pass

    container = container_factory(Service, rabbit_config)

    with pytest.raises(DependencyNotFound):
        restrict_entrypoints(container, "nonexist")
Пример #3
0
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(MethodNotFound) as exc_info:
            service_proxy.handler_one("msg")
        assert exc_info.value.message == "handler_one"

    # dispatch an event to handler_two
    msg = "msg"
    with event_dispatcher('srcservice', rabbit_config) as dispatch:

        with entrypoint_waiter(container, 'handler_two'):
            dispatch(ExampleEvent(msg))

    # method_called should have exactly one call, derived from the event
    # handler and not from the disabled @once entrypoint
    method_called.assert_called_once_with(msg)
Пример #4
0
def test_restrict_nonexistent_entrypoint(container_factory, rabbit_config):

    class Service(object):
        @rpc
        def method(self, arg):
            pass

    container = container_factory(Service, rabbit_config)

    with pytest.raises(DependencyNotFound):
        restrict_entrypoints(container, "nonexist")
Пример #5
0
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(MethodNotFound) as exc_info:
            service_proxy.handler_one("msg")
        assert exc_info.value.message == "handler_one"

    # dispatch an event to handler_two
    msg = "msg"
    with event_dispatcher('srcservice', rabbit_config) as dispatch:

        with entrypoint_waiter(container, 'handler_two'):
            dispatch(ExampleEvent(msg))

    # method_called should have exactly one call, derived from the event
    # handler and not from the disabled @once entrypoint
    method_called.assert_called_once_with(msg)
Пример #6
0
def test_restrict_nonexistent_entrypoint(container_factory, rabbit_config):
    class Service(object):
        name = "service"

        @rpc
        def method(self, arg):
            pass  # pragma: no cover

    container = container_factory(Service, rabbit_config)

    with pytest.raises(ExtensionNotFound):
        restrict_entrypoints(container, "nonexist")
Пример #7
0
def test_restrict_entrypoint_container_already_started(container_factory,
                                                       rabbit_config):
    class Service(object):
        @rpc
        def method(self, arg):
            pass

    container = container_factory(Service, rabbit_config)
    container.start()

    with pytest.raises(RuntimeError):
        restrict_entrypoints(container, "method")
Пример #8
0
def test_restrict_entrypoint_container_already_started(container_factory, rabbit_config):
    class Service(object):
        name = "service"

        @rpc
        def method(self, arg):
            pass

    container = container_factory(Service, rabbit_config)
    container.start()

    with pytest.raises(RuntimeError):
        restrict_entrypoints(container, "method")
Пример #9
0
def test_restrict_nonexistent_entrypoint(container_factory, rabbit_config):

    class Service(object):
        name = "service"

        @rpc
        def method(self, arg):
            pass  # pragma: no cover

    container = container_factory(Service, rabbit_config)

    with pytest.raises(ExtensionNotFound):
        restrict_entrypoints(container, "nonexist")
Пример #10
0
def test_rpc_consumer_unregisters_if_no_providers(container_factory,
                                                  rabbit_config):
    class Service(object):
        name = "service"

        @rpc
        def method(self):
            pass  # pragma: no cover

    container = container_factory(Service, rabbit_config)
    restrict_entrypoints(container)  # disable 'method' entrypoint

    rpc_consumer = get_extension(container, RpcConsumer)
    with patch.object(rpc_consumer, 'queue_consumer') as queue_consumer:
        rpc_consumer.stop()

    assert queue_consumer.unregister_provider.called
    assert rpc_consumer._unregistered_from_queue_consumer.ready()
Пример #11
0
def test_shop_checkout_integration(rabbit_config, runner_factory,
                                   rpc_proxy_factory):
    context_data = {"user_id": "wile_e_coyote"}

    shop = rpc_proxy_factory("acmesshopservice", context_data=context_data)

    runner = runner_factory(rabbit_config, AcmeShopService, StockService,
                            InvoiceService)

    shop_container = get_container(runner, AcmeShopService)
    fire_event, payment_rpc = replace_dependencies(shop_container,
                                                   "fire_event", "payment_rpc")

    stock_container = get_container(runner, StockService)
    restrict_entrypoints(stock_container, "check_price", "check_stock")

    runner.start()

    assert shop.add_to_basket("anvil") == "anvil"
    assert shop.add_to_basket("invisible_paint") == "invisible_paint"

    with pytest.raises(RemoteError) as exc_info:
        shop.add_to_basket("toothpicks")
    assert exc_info.value.exc_type == "ItemOutOfStockError"

    payment_rpc.take_payment.return_value = "Payment complete."

    res = shop.checkout()

    total_amount = 100 + 10
    assert res == total_amount

    payment_rpc.take_payment.assert_called_once_with({
        "customer":
        "wile_e_coyote",
        "address":
        "12 Long Road, High Cliffs, Utah",
        "amount":
        total_amount,
        "message":
        "Dear Wile E Coyote, Please pay $110 to ACME Corp.",
    })

    assert fire_event.call_count == 3
Пример #12
0
def test_rpc_consumer_unregisters_if_no_providers(
    container_factory, rabbit_config
):
    class Service(object):
        name = "service"

        @rpc
        def method(self):
            pass

    container = container_factory(Service, rabbit_config)
    restrict_entrypoints(container)  # disable 'method' entrypoint

    rpc_consumer = get_extension(container, RpcConsumer)
    with patch.object(rpc_consumer, 'queue_consumer') as queue_consumer:
        rpc_consumer.stop()

    assert queue_consumer.unregister_provider.called
    assert rpc_consumer._unregistered_from_queue_consumer.ready()
Пример #13
0
def test_restrict_entrypoints(container_factory, rabbit_config):

    method_called = Mock()

    class Service(object):
        name = "service"

        @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 ServiceRpcProxy("service", rabbit_config) as service_proxy:
        with pytest.raises(MethodNotFound) as exc_info:
            service_proxy.handler_one("msg")
        assert str(exc_info.value) == "handler_one"

    # dispatch an event to handler_two
    msg = "msg"
    dispatch = event_dispatcher(rabbit_config)

    with entrypoint_waiter(container, "handler_two"):
        dispatch("srcservice", "eventtype", msg)

    # method_called should have exactly one call, derived from the event
    # handler and not from the disabled @once entrypoint
    method_called.assert_called_once_with(msg)
Пример #14
0
def test_restrict_entrypoints(container_factory, rabbit_config):

    method_called = Mock()

    class Service(object):
        name = "service"

        @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 ServiceRpcProxy("service", rabbit_config) as service_proxy:
        with pytest.raises(MethodNotFound) as exc_info:
            service_proxy.handler_one("msg")
        assert str(exc_info.value) == "handler_one"

    # dispatch an event to handler_two
    msg = "msg"
    dispatch = event_dispatcher(rabbit_config)

    with entrypoint_waiter(container, 'handler_two'):
        dispatch('srcservice', 'eventtype', msg)

    # method_called should have exactly one call, derived from the event
    # handler and not from the disabled @once entrypoint
    method_called.assert_called_once_with(msg)
Пример #15
0
def test_shop_checkout_integration(rabbit_config, runner_factory,
                                   rpc_proxy_factory):
    """ Simulate a checkout flow as an integration test.

    Requires instances of AcmeShopService, StockService and InvoiceService
    to be running. Explicitly replaces the rpc proxy to PaymentService so
    that service doesn't need to be hosted.

    Also replaces the event dispatcher dependency on AcmeShopService and
    disables the timer entrypoint on StockService. Limiting the interactions
    of services in this way reduces the scope of the integration test and
    eliminates undesirable side-effects (e.g. processing events unnecessarily).
    """
    context_data = {'user_id': 'wile_e_coyote'}
    shop = rpc_proxy_factory('acmeshopservice', context_data=context_data)

    runner = runner_factory(rabbit_config, AcmeShopService, StockService,
                            InvoiceService)

    # replace ``event_dispatcher`` and ``payment_rpc``  dependencies on
    # AcmeShopService with ``MockDependencyProvider``\s
    shop_container = get_container(runner, AcmeShopService)
    fire_event, payment_rpc = replace_dependencies(shop_container,
                                                   "fire_event", "payment_rpc")

    # restrict entrypoints on StockService
    stock_container = get_container(runner, StockService)
    restrict_entrypoints(stock_container, "check_price", "check_stock")

    runner.start()

    # add some items to the basket
    assert shop.add_to_basket("anvil") == "anvil"
    assert shop.add_to_basket("invisible_paint") == "invisible_paint"

    # try to buy something that's out of stock
    with pytest.raises(RemoteError) as exc_info:
        shop.add_to_basket("toothpicks")
    assert exc_info.value.exc_type == "ItemOutOfStockError"

    # provide a mock response from the payment service
    payment_rpc.take_payment.return_value = "Payment complete."

    # checkout
    res = shop.checkout()

    total_amount = 100 + 10
    assert res == total_amount

    # verify integration with mocked out payment service
    payment_rpc.take_payment.assert_called_once_with({
        'customer':
        "wile_e_coyote",
        'address':
        "12 Long Road, High Cliffs, Utah",
        'amount':
        total_amount,
        'message':
        "Dear Wile E Coyote. Please pay $110 to ACME Corp."
    })

    # verify events fired as expected
    assert fire_event.call_count == 3