diff --git a/.github/workflows/examples.yml b/.github/workflows/examples.yml index 2dc9cd66..29683438 100644 --- a/.github/workflows/examples.yml +++ b/.github/workflows/examples.yml @@ -266,3 +266,36 @@ jobs: timeout-minutes: 10 run: | pytest -xsv tests + + vhost: + runs-on: ${{ matrix.os }} + + strategy: + fail-fast: false + matrix: + python-version: ["3.12"] + os: ["ubuntu-latest"] + + steps: + - name: Install apt packages + if: startsWith(matrix.os, 'ubuntu-') + run: | + sudo apt update + - uses: actions/checkout@v4 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + cache: 'pip' + cache-dependency-path: '**/setup.py' + - name: Install dependencies + working-directory: examples/vhost + run: | + python -m pip install --upgrade pip + pip install -r requirements.txt + + - name: Run tests + working-directory: examples/vhost + timeout-minutes: 10 + run: | + pytest -xsv tests diff --git a/docs/userguide/examples/index.rst b/docs/userguide/examples/index.rst index 2bf781c2..4d235441 100644 --- a/docs/userguide/examples/index.rst +++ b/docs/userguide/examples/index.rst @@ -21,5 +21,6 @@ Every example is an independent project and is tested via the rabbitmq_management range myutils + vhost django hybrid_setup diff --git a/docs/userguide/examples/vhost.rst b/docs/userguide/examples/vhost.rst new file mode 100644 index 00000000..d7f71437 --- /dev/null +++ b/docs/userguide/examples/vhost.rst @@ -0,0 +1,105 @@ +.. _examples_vhost: + +======= + vhost +======= + +:Release: |version| +:Date: |today| + +.. contents:: + :local: + :depth: 2 + +Description +=========== + +This example project demonstrates how to use a single Redis container as both a broker and a result backend, +using different vhosts for each purpose. + +Breakdown +========= + +File Structure +~~~~~~~~~~~~~~ + +The following diagram lists the relevant files in the project. + +.. code-block:: text + + rabbitmq_management/ + ├── tests/ + │ ├── __init__.py + │ ├── conftest.py + │ └── test_vhost.py + └── requirements.txt + +conftest.py +~~~~~~~~~~~ + +We create our own Redis container, and then use it as both a broker and a result backend. + +.. code-block:: python + + redis_image = fetch(repository=REDIS_IMAGE) + redis_test_container: RedisContainer = container( + image="{redis_image.id}", + ports=REDIS_PORTS, + environment=REDIS_ENV, + network="{default_pytest_celery_network.name}", + wrapper_class=RedisContainer, + timeout=REDIS_CONTAINER_TIMEOUT, + ) + +As the default vhost is "0", we can use it as the broker, and create a new vhost for the result backend. + +.. code-block:: python + + @pytest.fixture + def redis_broker(redis_test_container: RedisContainer) -> RedisTestBroker: + broker = RedisTestBroker(redis_test_container) + yield broker + broker.teardown() + + + @pytest.fixture + def celery_broker_cluster(redis_broker: RedisTestBroker) -> CeleryBrokerCluster: + cluster = CeleryBrokerCluster(redis_broker) + yield cluster + cluster.teardown() + +For the backend, we need to change some settings to use a different vhost, so we create our own +Redis backend and work it out there. + +.. code-block:: python + + class MyRedisTestBackend(RedisTestBackend): + def config(self, *args: tuple, **kwargs: dict) -> dict: + return super().config(vhost=1, *args, **kwargs) + +Lastly, we add our backend that uses the same Redis container as the broker and our +``MyRedisTestBackend`` class. + +.. code-block:: python + + @pytest.fixture + def redis_backend(redis_test_container: RedisContainer) -> MyRedisTestBackend: + backend = MyRedisTestBackend(redis_test_container) + yield backend + backend.teardown() + + + @pytest.fixture + def celery_backend_cluster(redis_backend: MyRedisTestBackend) -> CeleryBackendCluster: + cluster = CeleryBackendCluster(redis_backend) + yield cluster + cluster.teardown() + +test_vhost.py +~~~~~~~~~~~~~ + +We can now run tests that will share the same Redis container for both the broker and the result backend components. + +.. literalinclude:: ../../../examples/vhost/tests/test_vhost.py + :language: python + :caption: examples.vhost.tests.test_vhost.py diff --git a/examples/vhost/pytest.ini b/examples/vhost/pytest.ini new file mode 100644 index 00000000..af1295e9 --- /dev/null +++ b/examples/vhost/pytest.ini @@ -0,0 +1,5 @@ +[pytest] +log_cli = true +log_cli_level = INFO +log_cli_format = %(asctime)s [%(levelname)8s] %(message)s (%(filename)s:%(lineno)s) +log_cli_date_format = %Y-%m-%d %H:%M:%S diff --git a/examples/vhost/requirements.txt b/examples/vhost/requirements.txt new file mode 100644 index 00000000..08d00b8f --- /dev/null +++ b/examples/vhost/requirements.txt @@ -0,0 +1,3 @@ +pytest>=7.4.4 +pytest-xdist>=3.5.0 +pytest-celery[all]@git+https://github.com/celery/pytest-celery.git diff --git a/examples/vhost/tests/__init__.py b/examples/vhost/tests/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/examples/vhost/tests/conftest.py b/examples/vhost/tests/conftest.py new file mode 100644 index 00000000..87a5bf9d --- /dev/null +++ b/examples/vhost/tests/conftest.py @@ -0,0 +1,56 @@ +import pytest +from pytest_docker_tools import container +from pytest_docker_tools import fetch + +from pytest_celery import REDIS_CONTAINER_TIMEOUT +from pytest_celery import REDIS_ENV +from pytest_celery import REDIS_IMAGE +from pytest_celery import REDIS_PORTS +from pytest_celery import CeleryBackendCluster +from pytest_celery import CeleryBrokerCluster +from pytest_celery import RedisContainer +from pytest_celery import RedisTestBackend +from pytest_celery import RedisTestBroker + +redis_image = fetch(repository=REDIS_IMAGE) +redis_test_container: RedisContainer = container( + image="{redis_image.id}", + ports=REDIS_PORTS, + environment=REDIS_ENV, + network="{default_pytest_celery_network.name}", + wrapper_class=RedisContainer, + timeout=REDIS_CONTAINER_TIMEOUT, +) + + +@pytest.fixture +def redis_broker(redis_test_container: RedisContainer) -> RedisTestBroker: + broker = RedisTestBroker(redis_test_container) + yield broker + broker.teardown() + + +@pytest.fixture +def celery_broker_cluster(redis_broker: RedisTestBroker) -> CeleryBrokerCluster: + cluster = CeleryBrokerCluster(redis_broker) + yield cluster + cluster.teardown() + + +class MyRedisTestBackend(RedisTestBackend): + def config(self, *args: tuple, **kwargs: dict) -> dict: + return super().config(vhost=1, *args, **kwargs) + + +@pytest.fixture +def redis_backend(redis_test_container: RedisContainer) -> MyRedisTestBackend: + backend = MyRedisTestBackend(redis_test_container) + yield backend + backend.teardown() + + +@pytest.fixture +def celery_backend_cluster(redis_backend: MyRedisTestBackend) -> CeleryBackendCluster: + cluster = CeleryBackendCluster(redis_backend) + yield cluster + cluster.teardown() diff --git a/examples/vhost/tests/test_vhost.py b/examples/vhost/tests/test_vhost.py new file mode 100644 index 00000000..daa761f7 --- /dev/null +++ b/examples/vhost/tests/test_vhost.py @@ -0,0 +1,16 @@ +from pytest_celery import RESULT_TIMEOUT +from pytest_celery import CeleryTestSetup +from pytest_celery import ping + + +class TestVhost: + def test_ping(self, celery_setup: CeleryTestSetup): + assert ping.s().delay().get(timeout=RESULT_TIMEOUT) == "pong" + + def test_vhost(self, celery_setup: CeleryTestSetup): + assert celery_setup.app.conf.broker_url[:-1] == celery_setup.app.conf.result_backend[:-1] + assert celery_setup.app.conf.broker_url.endswith("/0") + assert celery_setup.app.conf.result_backend.endswith("/1") + + def test_single_redis(self, celery_setup: CeleryTestSetup): + assert celery_setup.broker.container.id == celery_setup.backend.container.id