├── .flake8
├── .github
├── CODEOWNERS
├── FUNDING.yml
├── ISSUE_TEMPLATE
│ ├── Bug-Report.md
│ ├── Documentation-Bug-Report.md
│ ├── Feature-Request.md
│ └── config.yml
├── dependabot.yml
└── workflows
│ ├── codeql-analysis.yml
│ ├── deploy.yml
│ ├── docker.yml
│ ├── examples.yml
│ ├── linter.yml
│ ├── parallel-support.yml
│ ├── python-package.yml
│ └── semgrep.yml
├── .gitignore
├── .pre-commit-config.yaml
├── .readthedocs.yaml
├── CONTRIBUTING.rst
├── CONTRIBUTORS.txt
├── Changelog.rst
├── LICENSE
├── README.rst
├── docs
├── Makefile
├── _static
│ └── .keep
├── _templates
│ └── sidebardonations.html
├── build.sh
├── changelog.rst
├── conf.py
├── contributing.rst
├── copyright.rst
├── devguide
│ ├── index.rst
│ ├── local-development-environment.rst
│ ├── release.rst
│ ├── sphinx.rst
│ └── tox.rst
├── faq.rst
├── getting-started
│ ├── first-steps.rst
│ ├── help.rst
│ ├── index.rst
│ ├── introduction.rst
│ ├── next-steps.rst
│ └── vendors.rst
├── glossary.rst
├── images
│ ├── celery_512.png
│ └── favicon.ico
├── includes
│ ├── installation.txt
│ └── worker-breakdown.txt
├── index.rst
├── reference
│ ├── index.rst
│ ├── pytest_celery.api.rst
│ ├── pytest_celery.fixtures.rst
│ ├── pytest_celery.rst
│ ├── pytest_celery.vendors.localstack.rst
│ ├── pytest_celery.vendors.memcached.rst
│ ├── pytest_celery.vendors.rabbitmq.rst
│ ├── pytest_celery.vendors.redis.backend.rst
│ ├── pytest_celery.vendors.redis.broker.rst
│ ├── pytest_celery.vendors.redis.rst
│ ├── pytest_celery.vendors.rst
│ ├── pytest_celery.vendors.worker.content.rst
│ └── pytest_celery.vendors.worker.rst
└── userguide
│ ├── advanced-installation.rst
│ ├── app-conf.rst
│ ├── celery-bug-report.rst
│ ├── default-tasks.rst
│ ├── examples
│ ├── django.rst
│ ├── hybrid_setup.rst
│ ├── index.rst
│ ├── myutils.rst
│ ├── myworker.rst
│ ├── rabbitmq_management.rst
│ ├── range.rst
│ ├── vhost.rst
│ └── worker_pool.rst
│ ├── index.rst
│ ├── resources
│ └── index.rst
│ ├── setup-matrix.rst
│ ├── signals.rst
│ ├── tasks.rst
│ └── utils-module.rst
├── examples
├── celery_bug_report.py
├── django
│ ├── demoapp
│ │ ├── __init__.py
│ │ ├── migrations
│ │ │ ├── 0001_initial.py
│ │ │ └── __init__.py
│ │ ├── models.py
│ │ ├── tasks.py
│ │ └── views.py
│ ├── manage.py
│ ├── proj
│ │ ├── __init__.py
│ │ ├── celery.py
│ │ ├── settings.py
│ │ ├── urls.py
│ │ └── wsgi.py
│ ├── pytest.ini
│ ├── requirements.txt
│ └── tests
│ │ ├── DjangoWorker.Dockerfile
│ │ ├── __init__.py
│ │ ├── conftest.py
│ │ └── test_tasks.py
├── hybrid_setup
│ ├── pytest.ini
│ ├── requirements.txt
│ └── tests
│ │ ├── __init__.py
│ │ ├── conftest.py
│ │ ├── test_hybrid_setup.py
│ │ └── vendors
│ │ ├── __init__.py
│ │ ├── memcached.py
│ │ ├── rabbitmq.py
│ │ └── workers
│ │ ├── __init__.py
│ │ ├── gevent.Dockerfile
│ │ ├── gevent.py
│ │ ├── legacy.Dockerfile
│ │ ├── legacy.py
│ │ ├── signals.py
│ │ └── tasks.py
├── myutils
│ ├── pytest.ini
│ ├── requirements.txt
│ └── tests
│ │ ├── __init__.py
│ │ ├── conftest.py
│ │ ├── myutils.py
│ │ └── test_myutils.py
├── myworker
│ ├── pytest.ini
│ ├── requirements.txt
│ └── tests
│ │ ├── __init__.py
│ │ ├── conftest.py
│ │ ├── myworker
│ │ ├── Dockerfile
│ │ ├── __init__.py
│ │ └── myworker.py
│ │ └── test_myworker.py
├── rabbitmq_management
│ ├── pytest.ini
│ ├── requirements.txt
│ └── tests
│ │ ├── __init__.py
│ │ ├── conftest.py
│ │ └── test_management_broker.py
├── range
│ ├── pytest.ini
│ ├── requirements.txt
│ └── tests
│ │ ├── __init__.py
│ │ ├── conftest.py
│ │ ├── test_range.py
│ │ └── test_range_cluster.py
├── vhost
│ ├── pytest.ini
│ ├── requirements.txt
│ └── tests
│ │ ├── __init__.py
│ │ ├── conftest.py
│ │ └── test_vhost.py
└── worker_pool
│ ├── Dockerfile
│ ├── pytest.ini
│ ├── requirements.txt
│ ├── tasks.py
│ └── tests
│ ├── __init__.py
│ ├── test_gevent_pool.py
│ └── test_solo_pool.py
├── poetry.lock
├── pyproject.toml
├── src
└── pytest_celery
│ ├── __init__.py
│ ├── api
│ ├── __init__.py
│ ├── backend.py
│ ├── base.py
│ ├── broker.py
│ ├── container.py
│ ├── setup.py
│ └── worker.py
│ ├── defaults.py
│ ├── fixtures
│ ├── __init__.py
│ ├── backend.py
│ ├── broker.py
│ ├── setup.py
│ └── worker.py
│ ├── plugin.py
│ └── vendors
│ ├── __init__.py
│ ├── localstack
│ ├── __init__.py
│ ├── api.py
│ ├── container.py
│ ├── defaults.py
│ └── fixtures.py
│ ├── memcached
│ ├── __init__.py
│ ├── api.py
│ ├── container.py
│ ├── defaults.py
│ └── fixtures.py
│ ├── rabbitmq
│ ├── __init__.py
│ ├── api.py
│ ├── container.py
│ ├── defaults.py
│ └── fixtures.py
│ ├── redis
│ ├── __init__.py
│ ├── backend
│ │ ├── __init__.py
│ │ ├── api.py
│ │ ├── defaults.py
│ │ └── fixtures.py
│ ├── broker
│ │ ├── __init__.py
│ │ ├── api.py
│ │ ├── defaults.py
│ │ └── fixtures.py
│ ├── container.py
│ └── defaults.py
│ └── worker
│ ├── Dockerfile
│ ├── __init__.py
│ ├── container.py
│ ├── content
│ ├── __init__.py
│ ├── app.py
│ └── utils.py
│ ├── defaults.py
│ ├── fixtures.py
│ ├── tasks.py
│ └── volume.py
├── tests
├── __init__.py
├── conftest.py
├── defaults.py
├── integration
│ ├── __init__.py
│ ├── api
│ │ ├── __init__.py
│ │ ├── custom_setup
│ │ │ ├── __init__.py
│ │ │ ├── conftest.py
│ │ │ └── test_custom_setup.py
│ │ ├── test_backend.py
│ │ ├── test_base.py
│ │ ├── test_broker.py
│ │ ├── test_container.py
│ │ ├── test_setup.py
│ │ └── test_worker.py
│ ├── conftest.py
│ └── vendors
│ │ ├── __init__.py
│ │ ├── test_default_tasks.py
│ │ ├── test_localstack.py
│ │ ├── test_memcached.py
│ │ ├── test_rabbitmq.py
│ │ ├── test_redis.py
│ │ └── test_worker.py
├── smoke
│ ├── __init__.py
│ ├── conftest.py
│ ├── signals.py
│ ├── test_canvas.py
│ ├── test_control.py
│ ├── test_failover.py
│ ├── test_signals.py
│ ├── test_task.py
│ └── test_worker.py
├── tasks.py
├── test_pytest_celery.py
└── unit
│ ├── __init__.py
│ ├── api
│ ├── __init__.py
│ ├── test_backend.py
│ ├── test_base.py
│ ├── test_broker.py
│ ├── test_container.py
│ ├── test_setup.py
│ └── test_worker.py
│ ├── conftest.py
│ └── vendors
│ ├── __init__.py
│ ├── test_localstack.py
│ ├── test_memcached.py
│ ├── test_rabbitmq.py
│ ├── test_redis.py
│ └── test_worker
│ ├── __init__.py
│ ├── test_default_tasks.py
│ ├── test_volume.py
│ └── test_worker.py
└── tox.ini
/.flake8:
--------------------------------------------------------------------------------
1 | [flake8]
2 | max-line-length = 120
3 |
--------------------------------------------------------------------------------
/.github/CODEOWNERS:
--------------------------------------------------------------------------------
1 | # GitHub automatically requests reviews from the specified code owners when a pull request changes any owned files.
2 |
3 | * @Nusnus
4 |
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | github: nusnus
4 | open_collective: celery
5 | patreon: # Replace with a single Patreon username
6 | ko_fi: # Replace with a single Ko-fi username
7 | custom: # Replace with a single custom sponsorship URL
8 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/Documentation-Bug-Report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Documentation Bug Report
3 | about: Is something wrong with our documentation?
4 | title: ''
5 | labels: 'documentation'
6 | assignees: ''
7 |
8 | ---
9 |
10 |
15 | # Checklist
16 |
19 |
20 | - [ ] I have checked the [issues list](https://github.com/celery/pytest-celery/issues?utf8=%E2%9C%93&q=is%3Aissue+label%3A%22documentation%22+)
21 | for similar or identical bug reports.
22 | - [ ] I have checked the [pull requests list](https://github.com/celery/pytest-celery/pulls?q=is%3Apr+label%3A%22documentation%22)
23 | for existing proposed fixes.
24 | - [ ] I have checked the [commit log](https://github.com/celery/pytest-celery/commits/main)
25 | to find out if the bug was already fixed in the main branch.
26 | - [ ] I have included all related issues and possible duplicate issues in this issue
27 | (If there are none, check this box anyway).
28 |
29 | ## Related Issues and Possible Duplicates
30 |
40 |
41 | #### Related Issues
42 |
43 | - None
44 |
45 | #### Possible Duplicates
46 |
47 | - None
48 |
49 | # Description
50 |
54 |
55 | # Suggestions
56 |
57 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/Feature-Request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature Request
3 | about: Do you need a new feature?
4 | title: ''
5 | labels: 'feature'
6 | assignees: ''
7 |
8 | ---
9 |
10 |
15 | # Checklist
16 |
19 |
20 | - [ ] I have checked the [issues list](https://github.com/celery/pytest-celery/issues?utf8=%E2%9C%93&q=is%3Aissue+label%3A%22feature%22+)
21 | for similar or identical feature requests.
22 | - [ ] I have checked the [pull requests list](https://github.com/celery/pytest-celery/pulls?utf8=%E2%9C%93&q=is%3Apr+label%3A%22feature%22+)
23 | for existing proposed implementations of this feature.
24 | - [ ] I have checked the [commit log](https://github.com/celery/pytest-celery/commits/main)
25 | to find out if the same feature was already implemented in the
26 | main branch.
27 | - [ ] I have included all related issues and possible duplicate issues in this issue
28 | (If there are none, check this box anyway).
29 |
30 | ## Related Issues and Possible Duplicates
31 |
41 |
42 | #### Related Issues
43 |
44 | - None
45 |
46 | #### Possible Duplicates
47 |
48 | - None
49 |
50 | # Brief Summary
51 |
55 |
56 | # Design
57 |
58 | ## Proposed Behavior
59 |
63 |
64 | ## Proposed UI/UX
65 |
69 |
70 | ## Diagrams
71 |
79 | N/A
80 |
81 | ## Alternatives
82 |
86 | None
87 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/config.yml:
--------------------------------------------------------------------------------
1 | blank_issues_enabled: false
2 | contact_links:
3 | - name: Celery Issue Tracker
4 | url: https://github.com/celery/celery/issues/
5 | about: If this issue only involves Celery, please open a new issue there.
6 |
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 | updates:
3 | - package-ecosystem: "github-actions"
4 | directory: "/"
5 | schedule:
6 | interval: "daily"
7 | - package-ecosystem: "pip"
8 | directory: "/"
9 | schedule:
10 | interval: "daily"
11 |
--------------------------------------------------------------------------------
/.github/workflows/codeql-analysis.yml:
--------------------------------------------------------------------------------
1 | # For most projects, this workflow file will not need changing; you simply need
2 | # to commit it to your repository.
3 | #
4 | # You may wish to alter this file to override the set of languages analyzed,
5 | # or to provide custom queries or build logic.
6 | #
7 | # ******** NOTE ********
8 | # We have attempted to detect the languages in your repository. Please check
9 | # the `language` matrix defined below to confirm you have the correct set of
10 | # supported CodeQL languages.
11 | #
12 | name: "CodeQL"
13 |
14 | on:
15 | push:
16 | branches: [main]
17 | pull_request:
18 | # The branches below must be a subset of the branches above
19 | # branches: [ main ]
20 |
21 | jobs:
22 | analyze:
23 | name: Analyze
24 | runs-on: blacksmith-4vcpu-ubuntu-2204
25 | permissions:
26 | actions: read
27 | contents: read
28 | security-events: write
29 |
30 | strategy:
31 | fail-fast: false
32 | matrix:
33 | language: ["python"]
34 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
35 | # Learn more about CodeQL language support at https://git.io/codeql-language-support
36 |
37 | steps:
38 | - name: Checkout repository
39 | uses: actions/checkout@v4
40 |
41 | # Initializes the CodeQL tools for scanning.
42 | - name: Initialize CodeQL
43 | uses: github/codeql-action/init@v3
44 | with:
45 | languages: ${{ matrix.language }}
46 | # If you wish to specify custom queries, you can do so here or in a config file.
47 | # By default, queries listed here will override any specified in a config file.
48 | # Prefix the list here with "+" to use these queries and those in the config file.
49 | # queries: ./path/to/local/query, your-org/your-repo/queries@main
50 |
51 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
52 | # If this step fails, then you should remove it and run the build manually (see below)
53 | - name: Autobuild
54 | uses: github/codeql-action/autobuild@v3
55 |
56 | # ℹ️ Command-line programs to run using the OS shell.
57 | # 📚 https://git.io/JvXDl
58 |
59 | # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
60 | # and modify them (or add more) to build your code if your project
61 | # uses a compiled language
62 |
63 | #- run: |
64 | # make bootstrap
65 | # make release
66 |
67 | - name: Perform CodeQL Analysis
68 | uses: github/codeql-action/analyze@v3
69 |
--------------------------------------------------------------------------------
/.github/workflows/deploy.yml:
--------------------------------------------------------------------------------
1 | name: Deploy to PyPI
2 |
3 | on:
4 | release:
5 | types: [published]
6 |
7 | jobs:
8 | deploy:
9 | runs-on: blacksmith-4vcpu-ubuntu-2204
10 | env:
11 | POETRY_VIRTUALENVS_CREATE: "false"
12 | steps:
13 | - uses: actions/checkout@v4
14 |
15 | - name: Install poetry
16 | run: |
17 | pipx install poetry
18 | pipx inject poetry poetry-bumpversion
19 |
20 | - name: Build
21 | run: |
22 | poetry version ${{ github.ref_name }}
23 | poetry build
24 |
25 | - name: Publish
26 | run: |
27 | poetry config pypi-token.pypi ${{ secrets.PYPI_API_TOKEN }}
28 | poetry publish
29 |
--------------------------------------------------------------------------------
/.github/workflows/docker.yml:
--------------------------------------------------------------------------------
1 | name: Docker
2 |
3 | on:
4 | pull_request:
5 | branches: [ 'main']
6 | paths:
7 | - '.github/workflows/docker.yml'
8 | - 'src/pytest_celery/vendors/worker/**'
9 | - "**.py"
10 | - "**.txt"
11 | - "**.toml"
12 | - "tox.ini"
13 | - 'Dockerfile'
14 | - "poetry.lock"
15 | push:
16 | branches: [ 'main']
17 | paths:
18 | - '.github/workflows/docker.yml'
19 | - 'src/pytest_celery/vendors/worker/**'
20 | - "**.py"
21 | - "**.txt"
22 | - "**.toml"
23 | - "tox.ini"
24 | - 'Dockerfile'
25 | - "poetry.lock"
26 |
27 |
28 | jobs:
29 | build-worker:
30 | runs-on: blacksmith-4vcpu-ubuntu-2204
31 | timeout-minutes: 5
32 | steps:
33 | - uses: actions/checkout@v4
34 | - name: Build Celery Worker
35 | run: cd src/pytest_celery/vendors/worker && docker build -t pytest-celery-worker .
36 |
--------------------------------------------------------------------------------
/.github/workflows/linter.yml:
--------------------------------------------------------------------------------
1 | name: Linter
2 |
3 | on: [pull_request]
4 |
5 | jobs:
6 | check:
7 | name: ${{ matrix.check }} check
8 | runs-on: blacksmith-4vcpu-ubuntu-2204
9 | strategy:
10 | matrix:
11 | check: [lint, mypy]
12 | steps:
13 | - name: Checkout branch
14 | uses: actions/checkout@v4
15 |
16 | - name: Install apt packages
17 | run: |
18 | sudo apt update
19 |
20 | - name: Set up Python 3.13
21 | uses: useblacksmith/setup-python@v6
22 | with:
23 | python-version: '3.13'
24 | cache: 'pip'
25 | cache-dependency-path: '**/setup.py'
26 |
27 | - name: Install Poetry
28 | uses: snok/install-poetry@v1.4.1
29 | with:
30 | version: 1.8.4
31 |
32 | - name: Install CI dependencies
33 | run: |
34 | poetry config virtualenvs.create false
35 | poetry install --only ci
36 |
37 | - name: Run check
38 | run: tox -e ${{ matrix.check }}
39 |
--------------------------------------------------------------------------------
/.github/workflows/semgrep.yml:
--------------------------------------------------------------------------------
1 | on:
2 | workflow_dispatch: {}
3 | pull_request: {}
4 | push:
5 | branches:
6 | - main
7 | - master
8 | paths:
9 | - .github/workflows/semgrep.yml
10 | schedule:
11 | # random HH:MM to avoid a load spike on GitHub Actions at 00:00
12 | - cron: 36 4 * * *
13 | name: Semgrep
14 | jobs:
15 | semgrep:
16 | name: Scan
17 | runs-on: blacksmith-4vcpu-ubuntu-2204
18 | env:
19 | SEMGREP_APP_TOKEN: ${{ secrets.SEMGREP_APP_TOKEN }}
20 | container:
21 | image: returntocorp/semgrep
22 | steps:
23 | - uses: actions/checkout@v4
24 | - run: semgrep ci
25 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # IDE stuff
2 |
3 | .idea/*
4 |
5 | # Byte-compiled / optimized / DLL files
6 | __pycache__/
7 | *.py[cod]
8 | *$py.class
9 |
10 | # C extensions
11 | *.so
12 |
13 | # Distribution / packaging
14 | .Python
15 | build/
16 | develop-eggs/
17 | dist/
18 | downloads/
19 | eggs/
20 | .eggs/
21 | lib/
22 | lib64/
23 | parts/
24 | sdist/
25 | var/
26 | wheels/
27 | share/python-wheels/
28 | *.egg-info/
29 | .installed.cfg
30 | *.egg
31 | MANIFEST
32 |
33 | # PyInstaller
34 | # Usually these files are written by a python script from a template
35 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
36 | *.manifest
37 | *.spec
38 |
39 | # Installer logs
40 | pip-log.txt
41 | pip-delete-this-directory.txt
42 |
43 | # Unit test / coverage reports
44 | htmlcov/
45 | .tox/
46 | .nox/
47 | .coverage
48 | .coverage.*
49 | .cache
50 | nosetests.xml
51 | coverage.xml
52 | *.cover
53 | *.py,cover
54 | .hypothesis/
55 | .pytest_cache/
56 | cover/
57 |
58 | # Translations
59 | *.mo
60 | *.pot
61 |
62 | # Django stuff:
63 | *.log
64 | local_settings.py
65 | db.sqlite3
66 | db.sqlite3-journal
67 |
68 | # Flask stuff:
69 | instance/
70 | .webassets-cache
71 |
72 | # Scrapy stuff:
73 | .scrapy
74 |
75 | # Sphinx documentation
76 | docs/_build/
77 | docs/_draft.rst
78 |
79 | # PyBuilder
80 | .pybuilder/
81 | target/
82 |
83 | # Jupyter Notebook
84 | .ipynb_checkpoints
85 |
86 | # IPython
87 | profile_default/
88 | ipython_config.py
89 |
90 | # pyenv
91 | # For a library or package, you might want to ignore these files since the code is
92 | # intended to run in multiple environments; otherwise, check them in:
93 | .python-version
94 |
95 | # pipenv
96 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
97 | # However, in case of collaboration, if having platform-specific dependencies or dependencies
98 | # having no cross-platform support, pipenv may install dependencies that don't work, or not
99 | # install all needed dependencies.
100 | #Pipfile.lock
101 |
102 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow
103 | __pypackages__/
104 |
105 | # Celery stuff
106 | celerybeat-schedule
107 | celerybeat.pid
108 |
109 | # SageMath parsed files
110 | *.sage.py
111 |
112 | # Environments
113 | .env
114 | .venv
115 | env/
116 | venv/
117 | ENV/
118 | env.bak/
119 | venv.bak/
120 |
121 | # Spyder project settings
122 | .spyderproject
123 | .spyproject
124 |
125 | # Rope project settings
126 | .ropeproject
127 |
128 | # mkdocs documentation
129 | /site
130 |
131 | # mypy
132 | .mypy_cache/
133 | .dmypy.json
134 | dmypy.json
135 |
136 | # Pyre type checker
137 | .pyre/
138 |
139 | # pytype static type analyzer
140 | .pytype/
141 |
142 | # Cython debug symbols
143 | cython_debug/
144 |
145 | # PyCharm
146 | .idea/
147 |
148 | # VSCode
149 | .devcontainer
150 | .vscode/
151 |
152 | # YAPF
153 | .style.yapf
154 |
--------------------------------------------------------------------------------
/.pre-commit-config.yaml:
--------------------------------------------------------------------------------
1 | repos:
2 | - repo: https://github.com/python-poetry/poetry
3 | rev: 1.8.0
4 | hooks:
5 | - id: poetry-check
6 |
7 | - repo: https://github.com/asottile/yesqa
8 | rev: v1.5.0
9 | hooks:
10 | - id: yesqa
11 |
12 | - repo: https://github.com/asottile/pyupgrade
13 | rev: v3.19.1
14 | hooks:
15 | - id: pyupgrade
16 | args: ["--py38-plus"]
17 |
18 | - repo: https://github.com/PyCQA/autoflake
19 | rev: v2.3.1
20 | hooks:
21 | - id: autoflake
22 | args: ["--in-place", "--remove-unused-variables"]
23 |
24 | - repo: https://github.com/pycqa/isort
25 | rev: 5.13.2
26 | hooks:
27 | - id: isort
28 | args:
29 | [
30 | "--multi-line",
31 | "7",
32 | "--force-single-line-imports",
33 | "--profile",
34 | "black",
35 | ]
36 |
37 | - repo: https://github.com/codespell-project/codespell
38 | rev: v2.3.0
39 | hooks:
40 | - id: codespell
41 | args: [--write-changes]
42 | additional_dependencies:
43 | - tomli
44 |
45 | # Disabled until fixed support for Pre-commit v4.x.x
46 | # - repo: https://github.com/PyCQA/docformatter
47 | # rev: v1.7.5
48 | # hooks:
49 | # - id: docformatter
50 | # args: [--in-place]
51 | # language: python
52 |
53 | - repo: https://github.com/pre-commit/pre-commit-hooks
54 | rev: v5.0.0
55 | hooks:
56 | - id: check-ast
57 | - id: check-case-conflict
58 | - id: check-docstring-first
59 | exclude: "examples/django/proj/settings.py"
60 | - id: check-json
61 | - id: check-merge-conflict
62 | - id: check-shebang-scripts-are-executable
63 | - id: check-symlinks
64 | - id: check-toml
65 | - id: check-yaml
66 | - id: debug-statements
67 | - id: destroyed-symlinks
68 | - id: detect-private-key
69 | - id: end-of-file-fixer
70 | - id: forbid-submodules
71 | - id: mixed-line-ending
72 | - id: pretty-format-json
73 | args: ["--autofix"]
74 | - id: trailing-whitespace
75 |
76 | - repo: https://github.com/psf/black
77 | rev: 24.10.0
78 | hooks:
79 | - id: black
80 | args: ["--line-length", "120"]
81 |
82 | - repo: https://github.com/PyCQA/flake8
83 | rev: 7.1.1
84 | hooks:
85 | - id: flake8
86 | args: ["--max-line-length", "120"]
87 |
--------------------------------------------------------------------------------
/.readthedocs.yaml:
--------------------------------------------------------------------------------
1 | # Read the Docs configuration file for Sphinx projects
2 | # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
3 |
4 | # Required
5 | version: 2
6 |
7 | # Set the OS, Python version and other tools you might need
8 | build:
9 | os: ubuntu-22.04
10 | tools:
11 | python: "3.12"
12 | jobs:
13 | post_create_environment:
14 | # Install poetry
15 | # https://python-poetry.org/docs/#installing-manually
16 | - pip install poetry==1.8.4
17 | # Tell poetry to not use a virtual environment
18 | - poetry config virtualenvs.create false
19 | - poetry export --with docs -E all -f requirements.txt --output docs/requirements.txt
20 |
21 | # Build documentation in the "docs/" directory with Sphinx
22 | sphinx:
23 | configuration: docs/conf.py
24 | # You can configure Sphinx to use a different builder, for instance use the dirhtml builder for simpler URLs
25 | # builder: "dirhtml"
26 | # Fail on all warnings to avoid broken references
27 | # fail_on_warning: true
28 | # Optionally build your docs in additional formats such as PDF and ePub
29 | # formats:
30 | # - pdf
31 | # - epub
32 |
33 | # Optional but recommended, declare the Python requirements required
34 | # to build your documentation
35 | # See https://docs.readthedocs.io/en/stable/guides/reproducible-builds.html
36 | python:
37 | install:
38 | - method: pip
39 | path: .
40 | - requirements: docs/requirements.txt
41 |
--------------------------------------------------------------------------------
/CONTRIBUTORS.txt:
--------------------------------------------------------------------------------
1 | The contributor proposes to grant a license for specific software (referred to as a "Contribution" or multiple "Contributions") to pytest-celery. In turn, pytest-celery agrees to accept these Contributions under the conditions outlined in the BSD open source license.
2 | The contributor acknowledges and consents to pytest-celery having the irreversible and enduring right to produce and share copies of any Contribution.
3 | Additionally, pytest-celery has the authority to generate and distribute collective works and derivative works based on any Contribution, all within the framework of the BSD License.
4 |
5 | Contributors
6 | ------------
7 |
8 | Tomer Nosrati, 2022/17/07
9 | Aviv Rahamim, 2025/02/23
10 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2025 Tomer Nosrati. All rights reserved.
2 |
3 | pytest-celery is licensed under The BSD License (3 Clause, also known as
4 | the new BSD license). The license is an OSI approved Open Source
5 | license and is GPL-compatible(1).
6 |
7 | The license text can also be found here:
8 | https://www.opensource.org/license/BSD-3-Clause
9 |
10 |
11 | License
12 | =======
13 |
14 | Redistribution and use in source and binary forms, with or without
15 | modification, are permitted provided that the following conditions are met:
16 | * Redistributions of source code must retain the above copyright
17 | notice, this list of conditions and the following disclaimer.
18 | * Redistributions in binary form must reproduce the above copyright
19 | notice, this list of conditions and the following disclaimer in the
20 | documentation and/or other materials provided with the distribution.
21 | * Neither the name of Tomer Nosrati, nor the
22 | names of its contributors may be used to endorse or promote products
23 | derived from this software without specific prior written permission.
24 |
25 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
26 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
27 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
28 | PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Tomer Nosrati OR CONTRIBUTORS
29 | BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
30 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
31 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
32 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
33 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
34 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
35 | POSSIBILITY OF SUCH DAMAGE.
36 |
--------------------------------------------------------------------------------
/docs/_static/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/celery/pytest-celery/4d1a1dbcf340abab43d773b928f824a5a4ce7f08/docs/_static/.keep
--------------------------------------------------------------------------------
/docs/_templates/sidebardonations.html:
--------------------------------------------------------------------------------
1 |
3 |
4 |
Donations
5 |
Please help support this community project with a donation.
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/docs/build.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # Used to build the documentation locally for testing/debug purposes.
4 | # Nothing uses this script automatically!
5 |
6 | python -m sphinx -T -E -b html -d _build/doctrees -D language=en . _build/html
7 |
--------------------------------------------------------------------------------
/docs/changelog.rst:
--------------------------------------------------------------------------------
1 | .. include:: ../Changelog.rst
2 |
--------------------------------------------------------------------------------
/docs/conf.py:
--------------------------------------------------------------------------------
1 | from sphinx_celery import conf
2 |
3 | config = conf.build_config(
4 | "pytest_celery",
5 | __file__,
6 | project="pytest_celery",
7 | version_dev="1.2",
8 | version_stable="1.1",
9 | canonical_url="https://pytest-celery.readthedocs.io/",
10 | webdomain="pytest-celery.readthedocs.io",
11 | github_project="celery/pytest-celery",
12 | author="Tomer Nosrati",
13 | author_name="Tomer Nosrati",
14 | copyright="2025",
15 | publisher="Celery Project",
16 | html_logo="images/celery_512.png",
17 | html_favicon="images/favicon.ico",
18 | html_prepend_sidebars=["sidebardonations.html"],
19 | extra_extensions=[
20 | "sphinx_click",
21 | "sphinx.ext.napoleon",
22 | "celery.contrib.sphinx",
23 | "sphinxcontrib.mermaid",
24 | ],
25 | apicheck_ignore_modules=[
26 | r"celery.contrib.*",
27 | ],
28 | linkcheck_ignore=[
29 | r"^http://localhost",
30 | r"^http://0.0.0.0",
31 | r"https://github\.com/Jc2k/pytest-docker-tools\?tab=readme-ov-file#images",
32 | r"https://github\.com/Jc2k/pytest-docker-tools\?tab=readme-ov-file#containers",
33 | r"https://github\.com/Jc2k/pytest-docker-tools\?tab=readme-ov-file#fixture-wrappers",
34 | r"https://github\.com/celery/celery/blob/main/requirements/test\.txt#L2",
35 | r"https://github\.com/celery/celery/blob/main/tox\.ini#L30",
36 | r"https://www\.opensource\.org/license/BSD-3-Clause",
37 | r"https://pypi\.org/project/pytest-celery/#history",
38 | ],
39 | autodoc_mock_imports=[],
40 | )
41 |
42 | del config["intersphinx_mapping"]["eventlet"]
43 |
44 | globals().update(config)
45 |
46 | settings = {}
47 | ignored_settings = {}
48 |
49 |
50 | def configcheck_project_settings():
51 | return set(settings)
52 |
53 |
54 | def configcheck_should_ignore(setting):
55 | return setting in ignored_settings
56 |
--------------------------------------------------------------------------------
/docs/contributing.rst:
--------------------------------------------------------------------------------
1 | .. include:: ../CONTRIBUTING.rst
2 |
--------------------------------------------------------------------------------
/docs/copyright.rst:
--------------------------------------------------------------------------------
1 | .. _copyright:
2 |
3 | Copyright
4 | =========
5 |
6 | *pytest-celery User Manual*
7 |
8 | by Tomer Nosrati
9 |
10 | .. |copy| unicode:: U+000A9 .. COPYRIGHT SIGN
11 |
12 | Copyright |copy| 2025, Tomer Nosrati.
13 |
14 | All rights reserved. This material may be copied or distributed only
15 | subject to the terms and conditions set forth in the `Creative Commons
16 | Attribution-ShareAlike 4.0 International
17 | `_ license.
18 |
19 | You may share and adapt the material, even for commercial purposes, but
20 | you must give the original author credit. If you alter, transform, or build upon this
21 | work, you may distribute the resulting work only under the same or a
22 | compatible license to this one.
23 |
24 | .. note::
25 |
26 | While the *pytest-celery* documentation is offered under the
27 | Creative Commons *Attribution-ShareAlike 4.0 International* license,
28 | the pytest-celery *software* is offered under the
29 | `BSD License (3 Clause) `_.
30 |
--------------------------------------------------------------------------------
/docs/devguide/index.rst:
--------------------------------------------------------------------------------
1 | .. _devguide:
2 |
3 | ==========================
4 | Plugin Development Guide
5 | ==========================
6 |
7 | :Release: |version|
8 | :Date: |today|
9 |
10 | The purpose of this guide is to explain the plugin's software development lifecycle (SDLC),
11 | and to provide a set of guidelines and best practices for developing the plugin itself using all of
12 | the available tools and resources.
13 |
14 | This guide is intended for developers who want to contribute to the plugin. It explains which tools
15 | are used and how to set up a local development environment.
16 |
17 | .. toctree::
18 | :maxdepth: 1
19 |
20 | local-development-environment
21 | tox
22 | sphinx
23 | release
24 |
--------------------------------------------------------------------------------
/docs/devguide/sphinx.rst:
--------------------------------------------------------------------------------
1 | .. _sphinx:
2 |
3 | ======================
4 | Shpinx Documentation
5 | ======================
6 |
7 | :Release: |version|
8 | :Date: |today|
9 |
10 | The plugin uses the :pypi:`sphinx_celery ` engine to generate the documentation.
11 | The documentation is written in reStructuredText format and is located in the ``docs`` directory of the repository.
12 |
13 | .. contents::
14 | :local:
15 | :depth: 2
16 |
17 | Building the documentation
18 | ==========================
19 |
20 | To build the documentation, use the :ref:`tox_docs` tox environment.
21 |
22 | Live Documentation
23 | ==================
24 |
25 | To serve the documentation locally, use the :ref:`tox_docs-livehtml` tox environment.
26 |
27 | Generate API Documentation
28 | ==========================
29 |
30 | To generate the API documentation, use the :ref:`tox_docs-apidoc` tox environment.
31 |
32 | Linting the documentation
33 | =========================
34 |
35 | To lint the documentation, use the :ref:`tox_lint` tox environment, or these Makefile commands::
36 |
37 | make -C ./docs apicheck
38 | make -C ./docs linkcheck
39 | make -C ./docs configcheck
40 |
41 | Makefile
42 | ========
43 |
44 | The docs are managed using the following Makefile:
45 |
46 | .. literalinclude:: ../../docs/Makefile
47 | :language: make
48 | :caption: docs.Makefile
49 | :start-after: .PHONY: help
50 | :end-before: .PHONY: clean
51 |
--------------------------------------------------------------------------------
/docs/getting-started/help.rst:
--------------------------------------------------------------------------------
1 | .. _help:
2 |
3 | ======
4 | Help
5 | ======
6 |
7 | :Release: |version|
8 | :Date: |today|
9 |
10 | .. contents::
11 | :local:
12 | :depth: 2
13 |
14 | Bug tracker
15 | ===========
16 |
17 | If you have any suggestions, bug reports, or annoyances please report them
18 | to our `issue tracker `_.
19 |
20 | Community support
21 | =================
22 |
23 | Please use the GitHub discussion forum for general questions, discussions and
24 | community support at https://github.com/celery/pytest-celery/discussions
25 |
26 | Contributing
27 | ============
28 |
29 | Development of pytest-celery happens at GitHub: https://github.com/celery/pytest-celery
30 |
31 | You're highly encouraged to participate in the development of pytest-celery.
32 | Be sure to also read the :ref:`contributing` section in the documentation.
33 |
34 | For development setup instructions, see :ref:`devguide`.
35 |
36 | License
37 | =======
38 |
39 | .. literalinclude:: ../../LICENSE
40 | :language: text
41 | :caption: pytest-celery/LICENSE
42 |
--------------------------------------------------------------------------------
/docs/getting-started/index.rst:
--------------------------------------------------------------------------------
1 | .. _getting-started:
2 |
3 | =================
4 | Getting Started
5 | =================
6 |
7 | :Release: |version|
8 | :Date: |today|
9 |
10 | .. toctree::
11 | :maxdepth: 2
12 |
13 | introduction
14 | first-steps
15 | next-steps
16 | vendors
17 | help
18 |
--------------------------------------------------------------------------------
/docs/glossary.rst:
--------------------------------------------------------------------------------
1 | .. _glossary:
2 |
3 | Glossary
4 | ========
5 |
6 | .. glossary::
7 | :sorted:
8 |
9 | tbd
10 | To be defined.
11 |
12 | container
13 | A container in the context of pytest-celery is a low level implementation of a Celery architecture
14 | component.
15 |
16 | node
17 | A node in the context of pytest-celery is a high level implementation of a Celery architecture
18 | component.
19 |
20 | component
21 | Celery architecture component that is defined using a container, a node, APIs & pytest fixtures.
22 | It is a collective name for all of the parts that compose a component, according to the pytest-celery
23 | design.
24 |
25 | vendor
26 | Independent built-in components provided by the plugin. Vendors can be used as-is,
27 | reconfigured, extended or overridden completely by the user.
28 |
--------------------------------------------------------------------------------
/docs/images/celery_512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/celery/pytest-celery/4d1a1dbcf340abab43d773b928f824a5a4ce7f08/docs/images/celery_512.png
--------------------------------------------------------------------------------
/docs/images/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/celery/pytest-celery/4d1a1dbcf340abab43d773b928f824a5a4ce7f08/docs/images/favicon.ico
--------------------------------------------------------------------------------
/docs/includes/installation.txt:
--------------------------------------------------------------------------------
1 | Installation
2 | ============
3 |
4 | The **pytest-celery** plugin can be easily installed via the Python Package Index (PyPI) using :command:`pip`.
5 |
6 | Installing the pytest-celery package
7 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
8 |
9 | To install the latest version of **pytest-celery**, run the following command:
10 |
11 | .. code-block:: console
12 |
13 | pip install -U pytest-celery
14 |
15 | This command installs **pytest-celery** along with its required dependencies.
16 |
17 | This will include:
18 |
19 | - Latest version of :pypi:`celery `.
20 | - RabbitMQ broker via :pypi:`kombu `, installed as a dependency of Celery.
21 |
22 | Installing pytest-celery vendors
23 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
24 |
25 | The plugin detects which vendor dependencies are installed in the test environment to configure
26 | the default configurations automatically. This means that by just installing the matching dependencies,
27 | the plugin will allow extending the default configurations, up to the supported built-in :ref:`vendors`.
28 |
29 | .. warning::
30 |
31 | If you don't install any vendor (e.g. no extras and no manual installation), the plugin will result in an
32 | empty :ref:`setup-matrix` and might not be fully functional.
33 |
34 | To install the vendors, you may either install all of the dependencies manually, or use the following extras:
35 |
36 | - ``all``: Installs all vendors.
37 | - ``redis``: Installs Redis vendor, providing **broker** and **result backend** components.
38 | - ``memcached``: Installs Memcached vendor, providing a **result backend** component.
39 | - ``sqs``: Installs Localstack vendor, providing an **SQS broker** component.
40 |
41 | The following extra is installed by default:
42 |
43 | - ``rabbitmq``: Installs RabbitMQ vendor, providing a **broker** component.
44 |
45 | To install **pytest-celery** with the built-in :ref:`vendors`, replace ```` with the name of the vendor.
46 |
47 | .. code-block:: console
48 |
49 | pip install -U "pytest-celery[]"
50 |
51 | RabbitMQ & Redis combo
52 | ----------------------
53 |
54 | .. code-block:: console
55 |
56 | pip install -U "pytest-celery[redis]"
57 |
58 | This will configure the plugin to generate all possible setups using only RabbitMQ and Redis vendors.
59 |
60 | SQS & Redis combo
61 | -----------------
62 |
63 | .. code-block:: console
64 |
65 | pip install -U "pytest-celery[redis,sqs]"
66 |
67 | This will configure the plugin to generate all possible setups using only Localstack and Redis vendors.
68 |
69 | All vendors
70 | -----------
71 |
72 | .. code-block:: console
73 |
74 | pip install -U "pytest-celery[all]"
75 |
76 | This will configure the plugin to generate all possible setups.
77 |
78 | This approach allows you to tailor the installation to your project's specific needs by including only the necessary optional vendors.
79 |
--------------------------------------------------------------------------------
/docs/index.rst:
--------------------------------------------------------------------------------
1 | ===================================
2 | Official pytest plugin for Celery
3 | ===================================
4 |
5 | Welcome to :pypi:`pytest-celery `, the official pytest plugin for Celery.
6 |
7 | The pytest-celery plugin introduces significant enhancements with the introduction of
8 | version >= 1.0.0, shifting towards a Docker-based approach for smoke and production-like testing.
9 | While the `celery.contrib.pytest` API continues to support detailed integration
10 | and unit testing, the new Docker-based methodology is tailored for testing in
11 | environments that closely mirror production settings.
12 |
13 | Adopting version >= 1.0.0 enriches your testing suite with these new capabilities
14 | without affecting your existing tests, allowing for a smooth upgrade path.
15 | The documentation here will navigate you through utilizing the Docker-based approach.
16 | For information on the `celery.contrib.pytest` API for integration and unit testing,
17 | please refer to the `official documentation`_.
18 |
19 | .. _`official documentation`: https://docs.celeryproject.org/en/latest/userguide/testing.html
20 |
21 | The pytest-celery plugin is Open Source and licensed under the `BSD License`_.
22 |
23 | .. _`BSD License`: https://www.opensource.org/license/BSD-3-Clause
24 |
25 | .. image:: https://opencollective.com/static/images/opencollectivelogo-footer-n.svg
26 | :target: https://opencollective.com/celery
27 | :alt: Open Collective logo
28 | :width: 240px
29 |
30 | `Open Collective `_ is our community-powered funding platform that fuels Celery's
31 | ongoing development. Your sponsorship directly supports improvements, maintenance, and innovative features that keep
32 | Celery robust and reliable.
33 |
34 | Getting Started
35 | ===============
36 |
37 | - If you're new to pytest-celery you can get started by following the :ref:`getting-started` tutorial.
38 | - You can also check out the :ref:`FAQ `.
39 |
40 | Contents
41 | ========
42 |
43 | .. toctree::
44 | :maxdepth: 1
45 |
46 | copyright
47 |
48 | .. toctree::
49 | :maxdepth: 2
50 |
51 | getting-started/index
52 | userguide/index
53 | devguide/index
54 |
55 | .. toctree::
56 | :maxdepth: 1
57 |
58 | reference/index
59 | contributing
60 | faq
61 | changelog
62 | glossary
63 |
64 | Indices and tables
65 | ==================
66 |
67 | * :ref:`genindex`
68 | * :ref:`modindex`
69 | * :ref:`search`
70 |
--------------------------------------------------------------------------------
/docs/reference/index.rst:
--------------------------------------------------------------------------------
1 | .. _apiref:
2 |
3 | API Documentation
4 | =================
5 |
6 | .. toctree::
7 | :maxdepth: 4
8 |
9 | pytest_celery
10 |
--------------------------------------------------------------------------------
/docs/reference/pytest_celery.api.rst:
--------------------------------------------------------------------------------
1 | pytest\_celery.api package
2 | ==========================
3 |
4 | Submodules
5 | ----------
6 |
7 | pytest\_celery.api.backend module
8 | ---------------------------------
9 |
10 | .. automodule:: pytest_celery.api.backend
11 | :members:
12 | :undoc-members:
13 | :show-inheritance:
14 |
15 | pytest\_celery.api.base module
16 | ------------------------------
17 |
18 | .. automodule:: pytest_celery.api.base
19 | :members:
20 | :undoc-members:
21 | :show-inheritance:
22 |
23 | pytest\_celery.api.broker module
24 | --------------------------------
25 |
26 | .. automodule:: pytest_celery.api.broker
27 | :members:
28 | :undoc-members:
29 | :show-inheritance:
30 |
31 | pytest\_celery.api.container module
32 | -----------------------------------
33 |
34 | .. automodule:: pytest_celery.api.container
35 | :members:
36 | :undoc-members:
37 | :show-inheritance:
38 |
39 | pytest\_celery.api.setup module
40 | -------------------------------
41 |
42 | .. automodule:: pytest_celery.api.setup
43 | :members:
44 | :undoc-members:
45 | :show-inheritance:
46 |
47 | pytest\_celery.api.worker module
48 | --------------------------------
49 |
50 | .. automodule:: pytest_celery.api.worker
51 | :members:
52 | :undoc-members:
53 | :show-inheritance:
54 |
55 | Module contents
56 | ---------------
57 |
58 | .. automodule:: pytest_celery.api
59 | :members:
60 | :undoc-members:
61 | :show-inheritance:
62 |
--------------------------------------------------------------------------------
/docs/reference/pytest_celery.fixtures.rst:
--------------------------------------------------------------------------------
1 | pytest\_celery.fixtures package
2 | ===============================
3 |
4 | Submodules
5 | ----------
6 |
7 | pytest\_celery.fixtures.backend module
8 | --------------------------------------
9 |
10 | .. automodule:: pytest_celery.fixtures.backend
11 | :members:
12 | :undoc-members:
13 | :show-inheritance:
14 |
15 | pytest\_celery.fixtures.broker module
16 | -------------------------------------
17 |
18 | .. automodule:: pytest_celery.fixtures.broker
19 | :members:
20 | :undoc-members:
21 | :show-inheritance:
22 |
23 | pytest\_celery.fixtures.setup module
24 | ------------------------------------
25 |
26 | .. automodule:: pytest_celery.fixtures.setup
27 | :members:
28 | :undoc-members:
29 | :show-inheritance:
30 |
31 | pytest\_celery.fixtures.worker module
32 | -------------------------------------
33 |
34 | .. automodule:: pytest_celery.fixtures.worker
35 | :members:
36 | :undoc-members:
37 | :show-inheritance:
38 |
39 | Module contents
40 | ---------------
41 |
42 | .. automodule:: pytest_celery.fixtures
43 | :members:
44 | :undoc-members:
45 | :show-inheritance:
46 |
--------------------------------------------------------------------------------
/docs/reference/pytest_celery.rst:
--------------------------------------------------------------------------------
1 | pytest\_celery package
2 | ======================
3 |
4 | Subpackages
5 | -----------
6 |
7 | .. toctree::
8 | :maxdepth: 4
9 |
10 | pytest_celery.api
11 | pytest_celery.fixtures
12 | pytest_celery.vendors
13 |
14 | Submodules
15 | ----------
16 |
17 | pytest\_celery.defaults module
18 | ------------------------------
19 |
20 | .. automodule:: pytest_celery.defaults
21 | :members:
22 | :undoc-members:
23 | :show-inheritance:
24 |
25 | pytest\_celery.plugin module
26 | ----------------------------
27 |
28 | .. automodule:: pytest_celery.plugin
29 | :members:
30 | :undoc-members:
31 | :show-inheritance:
32 |
33 | Module contents
34 | ---------------
35 |
36 | .. automodule:: pytest_celery
37 | :members:
38 | :undoc-members:
39 | :show-inheritance:
40 |
--------------------------------------------------------------------------------
/docs/reference/pytest_celery.vendors.localstack.rst:
--------------------------------------------------------------------------------
1 | pytest\_celery.vendors.localstack package
2 | =========================================
3 |
4 | Submodules
5 | ----------
6 |
7 | pytest\_celery.vendors.localstack.api module
8 | --------------------------------------------
9 |
10 | .. automodule:: pytest_celery.vendors.localstack.api
11 | :members:
12 | :undoc-members:
13 | :show-inheritance:
14 |
15 | pytest\_celery.vendors.localstack.container module
16 | --------------------------------------------------
17 |
18 | .. automodule:: pytest_celery.vendors.localstack.container
19 | :members:
20 | :undoc-members:
21 | :show-inheritance:
22 |
23 | pytest\_celery.vendors.localstack.defaults module
24 | -------------------------------------------------
25 |
26 | .. automodule:: pytest_celery.vendors.localstack.defaults
27 | :members:
28 | :undoc-members:
29 | :show-inheritance:
30 |
31 | pytest\_celery.vendors.localstack.fixtures module
32 | -------------------------------------------------
33 |
34 | .. automodule:: pytest_celery.vendors.localstack.fixtures
35 | :members:
36 | :undoc-members:
37 | :show-inheritance:
38 |
39 | Module contents
40 | ---------------
41 |
42 | .. automodule:: pytest_celery.vendors.localstack
43 | :members:
44 | :undoc-members:
45 | :show-inheritance:
46 |
--------------------------------------------------------------------------------
/docs/reference/pytest_celery.vendors.memcached.rst:
--------------------------------------------------------------------------------
1 | pytest\_celery.vendors.memcached package
2 | ========================================
3 |
4 | Submodules
5 | ----------
6 |
7 | pytest\_celery.vendors.memcached.api module
8 | -------------------------------------------
9 |
10 | .. automodule:: pytest_celery.vendors.memcached.api
11 | :members:
12 | :undoc-members:
13 | :show-inheritance:
14 |
15 | pytest\_celery.vendors.memcached.container module
16 | -------------------------------------------------
17 |
18 | .. automodule:: pytest_celery.vendors.memcached.container
19 | :members:
20 | :undoc-members:
21 | :show-inheritance:
22 |
23 | pytest\_celery.vendors.memcached.defaults module
24 | ------------------------------------------------
25 |
26 | .. automodule:: pytest_celery.vendors.memcached.defaults
27 | :members:
28 | :undoc-members:
29 | :show-inheritance:
30 |
31 | pytest\_celery.vendors.memcached.fixtures module
32 | ------------------------------------------------
33 |
34 | .. automodule:: pytest_celery.vendors.memcached.fixtures
35 | :members:
36 | :undoc-members:
37 | :show-inheritance:
38 |
39 | Module contents
40 | ---------------
41 |
42 | .. automodule:: pytest_celery.vendors.memcached
43 | :members:
44 | :undoc-members:
45 | :show-inheritance:
46 |
--------------------------------------------------------------------------------
/docs/reference/pytest_celery.vendors.rabbitmq.rst:
--------------------------------------------------------------------------------
1 | pytest\_celery.vendors.rabbitmq package
2 | =======================================
3 |
4 | Submodules
5 | ----------
6 |
7 | pytest\_celery.vendors.rabbitmq.api module
8 | ------------------------------------------
9 |
10 | .. automodule:: pytest_celery.vendors.rabbitmq.api
11 | :members:
12 | :undoc-members:
13 | :show-inheritance:
14 |
15 | pytest\_celery.vendors.rabbitmq.container module
16 | ------------------------------------------------
17 |
18 | .. automodule:: pytest_celery.vendors.rabbitmq.container
19 | :members:
20 | :undoc-members:
21 | :show-inheritance:
22 |
23 | pytest\_celery.vendors.rabbitmq.defaults module
24 | -----------------------------------------------
25 |
26 | .. automodule:: pytest_celery.vendors.rabbitmq.defaults
27 | :members:
28 | :undoc-members:
29 | :show-inheritance:
30 |
31 | pytest\_celery.vendors.rabbitmq.fixtures module
32 | -----------------------------------------------
33 |
34 | .. automodule:: pytest_celery.vendors.rabbitmq.fixtures
35 | :members:
36 | :undoc-members:
37 | :show-inheritance:
38 |
39 | Module contents
40 | ---------------
41 |
42 | .. automodule:: pytest_celery.vendors.rabbitmq
43 | :members:
44 | :undoc-members:
45 | :show-inheritance:
46 |
--------------------------------------------------------------------------------
/docs/reference/pytest_celery.vendors.redis.backend.rst:
--------------------------------------------------------------------------------
1 | pytest\_celery.vendors.redis.backend package
2 | ============================================
3 |
4 | Submodules
5 | ----------
6 |
7 | pytest\_celery.vendors.redis.backend.api module
8 | -----------------------------------------------
9 |
10 | .. automodule:: pytest_celery.vendors.redis.backend.api
11 | :members:
12 | :undoc-members:
13 | :show-inheritance:
14 |
15 | pytest\_celery.vendors.redis.backend.defaults module
16 | ----------------------------------------------------
17 |
18 | .. automodule:: pytest_celery.vendors.redis.backend.defaults
19 | :members:
20 | :undoc-members:
21 | :show-inheritance:
22 |
23 | pytest\_celery.vendors.redis.backend.fixtures module
24 | ----------------------------------------------------
25 |
26 | .. automodule:: pytest_celery.vendors.redis.backend.fixtures
27 | :members:
28 | :undoc-members:
29 | :show-inheritance:
30 |
31 | Module contents
32 | ---------------
33 |
34 | .. automodule:: pytest_celery.vendors.redis.backend
35 | :members:
36 | :undoc-members:
37 | :show-inheritance:
38 |
--------------------------------------------------------------------------------
/docs/reference/pytest_celery.vendors.redis.broker.rst:
--------------------------------------------------------------------------------
1 | pytest\_celery.vendors.redis.broker package
2 | ===========================================
3 |
4 | Submodules
5 | ----------
6 |
7 | pytest\_celery.vendors.redis.broker.api module
8 | ----------------------------------------------
9 |
10 | .. automodule:: pytest_celery.vendors.redis.broker.api
11 | :members:
12 | :undoc-members:
13 | :show-inheritance:
14 |
15 | pytest\_celery.vendors.redis.broker.defaults module
16 | ---------------------------------------------------
17 |
18 | .. automodule:: pytest_celery.vendors.redis.broker.defaults
19 | :members:
20 | :undoc-members:
21 | :show-inheritance:
22 |
23 | pytest\_celery.vendors.redis.broker.fixtures module
24 | ---------------------------------------------------
25 |
26 | .. automodule:: pytest_celery.vendors.redis.broker.fixtures
27 | :members:
28 | :undoc-members:
29 | :show-inheritance:
30 |
31 | Module contents
32 | ---------------
33 |
34 | .. automodule:: pytest_celery.vendors.redis.broker
35 | :members:
36 | :undoc-members:
37 | :show-inheritance:
38 |
--------------------------------------------------------------------------------
/docs/reference/pytest_celery.vendors.redis.rst:
--------------------------------------------------------------------------------
1 | pytest\_celery.vendors.redis package
2 | ====================================
3 |
4 | Subpackages
5 | -----------
6 |
7 | .. toctree::
8 | :maxdepth: 4
9 |
10 | pytest_celery.vendors.redis.backend
11 | pytest_celery.vendors.redis.broker
12 |
13 | Submodules
14 | ----------
15 |
16 | pytest\_celery.vendors.redis.container module
17 | ---------------------------------------------
18 |
19 | .. automodule:: pytest_celery.vendors.redis.container
20 | :members:
21 | :undoc-members:
22 | :show-inheritance:
23 |
24 | pytest\_celery.vendors.redis.defaults module
25 | --------------------------------------------
26 |
27 | .. automodule:: pytest_celery.vendors.redis.defaults
28 | :members:
29 | :undoc-members:
30 | :show-inheritance:
31 |
32 | Module contents
33 | ---------------
34 |
35 | .. automodule:: pytest_celery.vendors.redis
36 | :members:
37 | :undoc-members:
38 | :show-inheritance:
39 |
--------------------------------------------------------------------------------
/docs/reference/pytest_celery.vendors.rst:
--------------------------------------------------------------------------------
1 | pytest\_celery.vendors package
2 | ==============================
3 |
4 | Subpackages
5 | -----------
6 |
7 | .. toctree::
8 | :maxdepth: 4
9 |
10 | pytest_celery.vendors.localstack
11 | pytest_celery.vendors.memcached
12 | pytest_celery.vendors.rabbitmq
13 | pytest_celery.vendors.redis
14 | pytest_celery.vendors.worker
15 |
16 | Module contents
17 | ---------------
18 |
19 | .. automodule:: pytest_celery.vendors
20 | :members:
21 | :undoc-members:
22 | :show-inheritance:
23 |
--------------------------------------------------------------------------------
/docs/reference/pytest_celery.vendors.worker.content.rst:
--------------------------------------------------------------------------------
1 | pytest\_celery.vendors.worker.content package
2 | =============================================
3 |
4 | Submodules
5 | ----------
6 |
7 | pytest\_celery.vendors.worker.content.app module
8 | ------------------------------------------------
9 |
10 | .. automodule:: pytest_celery.vendors.worker.content.app
11 | :members:
12 | :undoc-members:
13 | :show-inheritance:
14 |
15 | pytest\_celery.vendors.worker.content.utils module
16 | --------------------------------------------------
17 |
18 | .. automodule:: pytest_celery.vendors.worker.content.utils
19 | :members:
20 | :undoc-members:
21 | :show-inheritance:
22 |
23 | Module contents
24 | ---------------
25 |
26 | .. automodule:: pytest_celery.vendors.worker.content
27 | :members:
28 | :undoc-members:
29 | :show-inheritance:
30 |
--------------------------------------------------------------------------------
/docs/reference/pytest_celery.vendors.worker.rst:
--------------------------------------------------------------------------------
1 | pytest\_celery.vendors.worker package
2 | =====================================
3 |
4 | Subpackages
5 | -----------
6 |
7 | .. toctree::
8 | :maxdepth: 4
9 |
10 | pytest_celery.vendors.worker.content
11 |
12 | Submodules
13 | ----------
14 |
15 | pytest\_celery.vendors.worker.container module
16 | ----------------------------------------------
17 |
18 | .. automodule:: pytest_celery.vendors.worker.container
19 | :members:
20 | :undoc-members:
21 | :show-inheritance:
22 |
23 | pytest\_celery.vendors.worker.defaults module
24 | ---------------------------------------------
25 |
26 | .. automodule:: pytest_celery.vendors.worker.defaults
27 | :members:
28 | :undoc-members:
29 | :show-inheritance:
30 |
31 | pytest\_celery.vendors.worker.fixtures module
32 | ---------------------------------------------
33 |
34 | .. automodule:: pytest_celery.vendors.worker.fixtures
35 | :members:
36 | :undoc-members:
37 | :show-inheritance:
38 |
39 | pytest\_celery.vendors.worker.tasks module
40 | ------------------------------------------
41 |
42 | .. automodule:: pytest_celery.vendors.worker.tasks
43 | :members:
44 | :undoc-members:
45 | :show-inheritance:
46 |
47 | pytest\_celery.vendors.worker.volume module
48 | -------------------------------------------
49 |
50 | .. automodule:: pytest_celery.vendors.worker.volume
51 | :members:
52 | :undoc-members:
53 | :show-inheritance:
54 |
55 | Module contents
56 | ---------------
57 |
58 | .. automodule:: pytest_celery.vendors.worker
59 | :members:
60 | :undoc-members:
61 | :show-inheritance:
62 |
--------------------------------------------------------------------------------
/docs/userguide/app-conf.rst:
--------------------------------------------------------------------------------
1 | .. _app-conf:
2 |
3 | =======================================
4 | How to prepare the Celery application
5 | =======================================
6 |
7 | :Release: |version|
8 | :Date: |today|
9 |
10 | The plugin is designed to allow preparing a Celery_ app object that will be applied
11 | to the worker container for each test case. It is useful for configuring the worker
12 | using the standard Celery_ API and uses the pytest fixtures mechanism to allow controlling
13 | the worker configuration pipeline.
14 |
15 | This guide will teach you how to utilize this mechanism to control the worker configuration
16 | for your test cases.
17 |
18 | .. _Celery: https://docs.celeryq.dev/en/stable/reference/celery.html#celery.Celery
19 |
20 | .. contents::
21 | :local:
22 | :depth: 2
23 |
24 | .. note::
25 |
26 | If you already understand how the initialization pipeline works, you can skip to the
27 | :ref:`worker-app-configuration` section.
28 |
29 | .. include:: ../includes/worker-breakdown.txt
30 |
31 | .. _worker-app-configuration:
32 |
33 | Worker App Configuration
34 | ========================
35 |
36 | .. versionadded:: 1.0.0
37 |
38 | To configure the worker app, use the :func:`default_worker_app ` fixture.
39 |
40 | .. code-block:: python
41 |
42 | @pytest.fixture
43 | def default_worker_app(default_worker_app: Celery) -> Celery:
44 | app = default_worker_app
45 | # configure the app here
46 | return app
47 |
48 | Modular Configuration
49 | ~~~~~~~~~~~~~~~~~~~~~
50 |
51 | The worker app instance can be configured differently for each test case using the
52 | `Fixture availability `_ feature of pytest.
53 |
54 | For example,
55 |
56 | .. code-block:: python
57 |
58 | @pytest.fixture
59 | def default_worker_app(default_worker_app: Celery) -> Celery:
60 | app = default_worker_app
61 | app.conf.A = X
62 | return app
63 |
64 |
65 | class test_example:
66 | @pytest.fixture
67 | def default_worker_app(self, default_worker_app: Celery) -> Celery:
68 | app = default_worker_app
69 | # app.conf.A is already set to X
70 | app.conf.B = Y
71 | return app
72 |
73 | def test_worker_app(self, celery_setup: CeleryTestSetup):
74 | assert celery_setup.app.conf.A == X
75 | assert celery_setup.app.conf.B == Y
76 |
77 | .. warning::
78 |
79 | The ``default_worker_app`` fixture is called before the worker container
80 | is created so using it in a test case will not change the worker's initialization
81 | pipeline as it is already completed by the time the test case is executed.
82 |
--------------------------------------------------------------------------------
/docs/userguide/examples/index.rst:
--------------------------------------------------------------------------------
1 | .. _examples:
2 |
3 | ==========
4 | Examples
5 | ==========
6 |
7 | :Release: |version|
8 | :Date: |today|
9 |
10 | :Release: |version|
11 | :Date: |today|
12 |
13 | Every example is an independent project and is tested via the
14 | `CI system `_.
15 |
16 | .. toctree::
17 | :maxdepth: 1
18 |
19 | myworker
20 | worker_pool
21 | rabbitmq_management
22 | range
23 | myutils
24 | vhost
25 | django
26 | hybrid_setup
27 |
--------------------------------------------------------------------------------
/docs/userguide/examples/worker_pool.rst:
--------------------------------------------------------------------------------
1 | .. _examples_worker_pool:
2 |
3 | =============
4 | worker_pool
5 | =============
6 |
7 | :Release: |version|
8 | :Date: |today|
9 |
10 | .. contents::
11 | :local:
12 | :depth: 2
13 |
14 | Description
15 | ===========
16 |
17 | This example project demonstrates how to use a different `worker pool `_.
18 | The example uses two different methods to run the Celery worker with different pools.
19 |
20 | The following guide will explain each method and how they are used.
21 |
22 | .. tip::
23 |
24 | See first the :ref:`examples_myworker` example before continuing with this one.
25 |
26 | Breakdown
27 | =========
28 |
29 | File Structure
30 | ~~~~~~~~~~~~~~
31 |
32 | The following diagram lists the relevant files in the project.
33 |
34 | .. code-block:: text
35 |
36 | rabbitmq_management/
37 | ├── tests/
38 | │ ├── __init__.py
39 | │ └── test_gevent_pool.py
40 | │ └── test_solo_pool.py
41 | └── Dockerfile
42 | └── tasks.py
43 | └── requirements.txt
44 |
45 | Dockerfile
46 | ~~~~~~~~~~
47 |
48 | To use the gevent pool, we create our own image using a similar Dockerfile to the one in the :ref:`examples_myworker` example.
49 | The purpose of this worker is to ensure the gevent dependency is installed.
50 |
51 | .. literalinclude:: ../../../examples/worker_pool/Dockerfile
52 | :language: docker
53 | :caption: examples.worker_pool.Dockerfile
54 |
55 | .. literalinclude:: ../../../examples/worker_pool/requirements.txt
56 | :language: text
57 | :caption: examples.worker_pool.requirements.txt
58 |
59 | tasks.py
60 | ~~~~~~~~
61 |
62 | Our tasks module is using the example task from the `Celery gevent example `_.
63 |
64 | .. literalinclude:: ../../../examples/worker_pool/tasks.py
65 | :language: python
66 | :caption: examples.worker_pool.tasks.py
67 |
68 | .. _test_gevent_pool:
69 |
70 | test_gevent_pool.py
71 | ~~~~~~~~~~~~~~~~~~~
72 |
73 | To add a new gevent worker, we create a new :class:`CeleryWorkerContainer ` to
74 | configure the worker with the gevent pool.
75 |
76 | .. literalinclude:: ../../../examples/worker_pool/tests/test_gevent_pool.py
77 | :language: python
78 | :caption: examples.worker_pool.tests.test_gevent_pool.py
79 | :end-before: # ----------------------------
80 |
81 | And then we can just use it in our tests.
82 |
83 | .. literalinclude:: ../../../examples/worker_pool/tests/test_gevent_pool.py
84 | :language: python
85 | :caption: examples.worker_pool.tests.test_gevent_pool.py
86 | :start-after: # ----------------------------
87 |
88 | test_solo_pool.py
89 | ~~~~~~~~~~~~~~~~~
90 |
91 | The solo pool example on the other hand, reconfigures the default :ref:`built-in-worker`
92 | as it does not require any additional dependencies.
93 |
94 | .. literalinclude:: ../../../examples/worker_pool/tests/test_solo_pool.py
95 | :language: python
96 | :caption: examples.worker_pool.tests.test_solo_pool.py
97 |
--------------------------------------------------------------------------------
/docs/userguide/index.rst:
--------------------------------------------------------------------------------
1 | .. _userguide:
2 |
3 | ============
4 | User Guide
5 | ============
6 |
7 | :Release: |version|
8 | :Date: |today|
9 |
10 | The following sections will enhance your understanding of the pytest-celery plugin and help you to use it effectively.
11 | It is recommended to first review the :ref:`first-steps` and :ref:`next-steps` sections to get comfortable with the basics
12 | of the plugin before diving into the advanced features.
13 |
14 | .. toctree::
15 | :maxdepth: 1
16 |
17 | advanced-installation
18 | setup-matrix
19 | app-conf
20 | utils-module
21 | tasks
22 | default-tasks
23 | signals
24 | celery-bug-report
25 | examples/index
26 | resources/index
27 |
--------------------------------------------------------------------------------
/docs/userguide/resources/index.rst:
--------------------------------------------------------------------------------
1 | .. _resources:
2 |
3 | ==================
4 | Useful Resources
5 | ==================
6 |
7 | :Release: |version|
8 | :Date: |today|
9 |
10 | This section contains additional resources that may be useful when developing tests with :pypi:`pytest `,
11 | :pypi:`pytest-celery `, and general :pypi:`celery ` related testing articles, guides and tutorials.
12 |
13 | **You're welcome to contribute to this list!**
14 |
15 | .. contents::
16 | :local:
17 | :depth: 2
18 |
19 | Other Plugins
20 | ~~~~~~~~~~~~~
21 |
22 | - :pypi:`pytest-rerunfailures `: Do to the natural sensitivity of simulating Celery environments over Docker, it's
23 | common to have flaky tests due to failing docker resources. This plugin provide useful features to rerun failed tests, skipping tests that
24 | failed due to actual assertion errors.
25 |
--------------------------------------------------------------------------------
/docs/userguide/signals.rst:
--------------------------------------------------------------------------------
1 | .. _injecting-signals-handlers:
2 |
3 | ================================
4 | How to connect signal handlers
5 | ================================
6 |
7 | :Release: |version|
8 | :Date: |today|
9 |
10 | Signal handlers may be defined in the publisher or the consumer side or both. When done
11 | on the publisher side, they can be connected inside the scope of the test function using the
12 | `standard Celery API `_. When done on the
13 | consumer side, they can be connected using injected signal handlers modules, which we'll cover in this guide.
14 |
15 | The plugin uses its :ref:`code-generation` mechanism to inject signal handlers modules into the worker
16 | container. The available signal handlers can be configured differently for each test case using the
17 | `Fixture availability `_ feature of pytest.
18 |
19 | This guide will teach you how to utilize this mechanism to connect signal handlers to your Celery workers in your test cases.
20 |
21 | .. contents::
22 | :local:
23 | :depth: 2
24 |
25 | .. note::
26 |
27 | If you already understand how the initialization pipeline works, you can skip to the
28 | :ref:`signal-handlers-modules-injection` section.
29 |
30 | .. include:: ../includes/worker-breakdown.txt
31 |
32 | .. _signal-handlers-modules-injection:
33 |
34 | Signal handlers modules injection
35 | =================================
36 |
37 | .. versionadded:: 1.0.0
38 |
39 | To add your own signal handlers, use the :func:`default_worker_signals ` fixture.
40 |
41 | .. code-block:: python
42 |
43 | @pytest.fixture
44 | def default_worker_signals(default_worker_signals: set) -> set:
45 | from tests import signals
46 |
47 | default_worker_signals.add(signals)
48 | return default_worker_signals
49 |
50 | For example, we can review the plugin's tests to see how the signal handlers are connected.
51 |
52 | signals.py
53 | ~~~~~~~~~~
54 |
55 | This module contain our signal handlers which we want to connect on the consumer side.
56 |
57 | .. literalinclude:: ../../tests/smoke/signals.py
58 | :language: python
59 | :caption: tests.smoke.signals
60 | :start-after: from __future__ import annotations
61 |
62 | test_signals.py
63 | ~~~~~~~~~~~~~~~
64 |
65 | These tests demonstrate how to query the output of the signal handlers that were
66 | injected into the worker container alongside inline signal handlers connected on the publisher side.
67 |
68 | .. literalinclude:: ../../tests/smoke/test_signals.py
69 | :language: python
70 | :caption: tests.smoke.test_signals
71 | :start-after: class test_signals
72 |
--------------------------------------------------------------------------------
/docs/userguide/tasks.rst:
--------------------------------------------------------------------------------
1 | .. _injecting-tasks:
2 |
3 | ==================
4 | How to add tasks
5 | ==================
6 |
7 | :Release: |version|
8 | :Date: |today|
9 |
10 | The plugin uses its :ref:`code-generation` mechanism to inject tasks modules into the worker
11 | container. The available tasks can be configured differently for each test case using the
12 | `Fixture availability `_ feature of pytest.
13 |
14 | This guide will teach you how to utilize this mechanism to add tasks for your Celery workers in your test cases.
15 |
16 | .. contents::
17 | :local:
18 | :depth: 2
19 |
20 | .. note::
21 |
22 | If you already understand how the initialization pipeline works, you can skip to the
23 | :ref:`tasks-modules-injection` section.
24 |
25 | .. include:: ../includes/worker-breakdown.txt
26 |
27 | .. _tasks-modules-injection:
28 |
29 | Tasks modules injection
30 | =======================
31 |
32 | .. versionadded:: 1.0.0
33 |
34 | To add you own tasks, use the :func:`default_worker_tasks ` fixture.
35 |
36 | .. code-block:: python
37 |
38 | @pytest.fixture
39 | def default_worker_tasks(default_worker_tasks: set) -> set:
40 | from tests import tasks
41 |
42 | default_worker_tasks.add(tasks)
43 | return default_worker_tasks
44 |
--------------------------------------------------------------------------------
/docs/userguide/utils-module.rst:
--------------------------------------------------------------------------------
1 | .. _utils-module:
2 |
3 | ==========================================
4 | How to inject your own utility functions
5 | ==========================================
6 |
7 | :Release: |version|
8 | :Date: |today|
9 |
10 | The plugin injects a special ``utils.py`` module into the worker component to provide
11 | enhanced testing capabilities over the Celery worker component. The module contains API that is accessible using the
12 | :class:`CeleryTestWorker API `.
13 |
14 | This guide will teach you how to inject your own utility functions into the worker component
15 | using this mechanism.
16 |
17 | .. contents::
18 | :local:
19 | :depth: 2
20 |
21 | .. note::
22 |
23 | If you already understand how the initialization pipeline works, you can skip to the
24 | :ref:`custom-utility-functions` section.
25 |
26 | .. include:: ../includes/worker-breakdown.txt
27 |
28 | .. _custom-utility-functions:
29 |
30 | Custom Utility Functions
31 | ========================
32 |
33 | .. versionadded:: 1.0.0
34 |
35 | To configure your own module, use the :func:`default_worker_utils_module ` fixture.
36 |
37 | .. code-block:: python
38 |
39 | @pytest.fixture
40 | def default_worker_utils_module() -> ModuleType:
41 | from tests import myutils
42 |
43 | return myutils
44 |
45 | This will inject the ``myutils`` module into the worker component, **instead of the default module**,
46 | allowing you to access your own utility functions.
47 |
48 | .. warning::
49 |
50 | The module must provide all of the existing API in the ``utils.py`` module, otherwise
51 | the worker component will not function correctly (when based off of
52 | :class:`CeleryTestWorker `).
53 |
54 | For reference, the default ``utils.py`` module is defined as follows:
55 |
56 | .. literalinclude:: ../../src/pytest_celery/vendors/worker/content/utils.py
57 | :language: python
58 | :caption: pytest_celery.vendors.worker.content.utils.py
59 |
60 | .. tip::
61 |
62 | Check out the :ref:`examples_myutils` example for a demonstration of how to use this feature.
63 |
--------------------------------------------------------------------------------
/examples/django/demoapp/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/celery/pytest-celery/4d1a1dbcf340abab43d773b928f824a5a4ce7f08/examples/django/demoapp/__init__.py
--------------------------------------------------------------------------------
/examples/django/demoapp/migrations/0001_initial.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 2.2.1 on 2019-05-24 21:37
2 |
3 | from django.db import migrations
4 | from django.db import models
5 |
6 |
7 | class Migration(migrations.Migration):
8 | initial = True
9 |
10 | dependencies = []
11 |
12 | operations = [
13 | migrations.CreateModel(
14 | name="Widget",
15 | fields=[
16 | ("id", models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
17 | ("name", models.CharField(max_length=140)),
18 | ],
19 | ),
20 | ]
21 |
--------------------------------------------------------------------------------
/examples/django/demoapp/migrations/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/celery/pytest-celery/4d1a1dbcf340abab43d773b928f824a5a4ce7f08/examples/django/demoapp/migrations/__init__.py
--------------------------------------------------------------------------------
/examples/django/demoapp/models.py:
--------------------------------------------------------------------------------
1 | from django.db import models
2 |
3 |
4 | class Widget(models.Model):
5 | name = models.CharField(max_length=140)
6 |
--------------------------------------------------------------------------------
/examples/django/demoapp/tasks.py:
--------------------------------------------------------------------------------
1 | # Create your tasks here
2 |
3 | from celery import shared_task
4 |
5 | from .models import Widget
6 |
7 |
8 | @shared_task
9 | def add(x, y):
10 | return x + y
11 |
12 |
13 | @shared_task
14 | def mul(x, y):
15 | return x * y
16 |
17 |
18 | @shared_task
19 | def xsum(numbers):
20 | return sum(numbers)
21 |
22 |
23 | @shared_task
24 | def count_widgets():
25 | return Widget.objects.count()
26 |
27 |
28 | @shared_task
29 | def rename_widget(widget_id, name):
30 | w = Widget.objects.get(id=widget_id)
31 | w.name = name
32 | w.save()
33 |
--------------------------------------------------------------------------------
/examples/django/demoapp/views.py:
--------------------------------------------------------------------------------
1 | # Create your views here.
2 |
--------------------------------------------------------------------------------
/examples/django/manage.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | import os
4 | import sys
5 |
6 | if __name__ == "__main__":
7 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "proj.settings")
8 |
9 | from django.core.management import execute_from_command_line
10 |
11 | execute_from_command_line(sys.argv)
12 |
--------------------------------------------------------------------------------
/examples/django/proj/__init__.py:
--------------------------------------------------------------------------------
1 | # This will make sure the app is always imported when
2 | # Django starts so that shared_task will use this app.
3 | from .celery import app as celery_app
4 |
5 | __all__ = ("celery_app",)
6 |
--------------------------------------------------------------------------------
/examples/django/proj/celery.py:
--------------------------------------------------------------------------------
1 | import os
2 |
3 | from celery import Celery
4 |
5 | # Set the default Django settings module for the 'celery' program.
6 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "proj.settings")
7 |
8 | app = Celery("proj")
9 |
10 | # Using a string here means the worker doesn't have to serialize
11 | # the configuration object to child processes.
12 | # - namespace='CELERY' means all celery-related configuration keys
13 | # should have a `CELERY_` prefix.
14 | app.config_from_object("django.conf:settings", namespace="CELERY")
15 |
16 | # Load task modules from all registered Django apps.
17 | app.autodiscover_tasks()
18 |
19 |
20 | @app.task(bind=True, ignore_result=True)
21 | def debug_task(self):
22 | print(f"Request: {self.request!r}")
23 |
--------------------------------------------------------------------------------
/examples/django/proj/urls.py:
--------------------------------------------------------------------------------
1 | # Uncomment the next two lines to enable the admin:
2 | # from django.contrib import admin
3 | # admin.autodiscover()
4 |
5 | urlpatterns = [
6 | # Examples:
7 | # url(r'^$', 'proj.views.home', name='home'),
8 | # url(r'^proj/', include('proj.foo.urls')),
9 | # Uncomment the admin/doc line below to enable admin documentation:
10 | # url(r'^admin/doc/', include('django.contrib.admindocs.urls')),
11 | # Uncomment the next line to enable the admin:
12 | # url(r'^admin/', include(admin.site.urls)),
13 | ]
14 |
--------------------------------------------------------------------------------
/examples/django/proj/wsgi.py:
--------------------------------------------------------------------------------
1 | """WSGI config for proj project.
2 |
3 | This module contains the WSGI application used by Django's development server
4 | and any production WSGI deployments. It should expose a module-level variable
5 | named ``application``. Django's ``runserver`` and ``runfcgi`` commands discover
6 | this application via the ``WSGI_APPLICATION`` setting.
7 |
8 | Usually you will have the standard Django WSGI application here, but it also
9 | might make sense to replace the whole Django WSGI application with a custom one
10 | that later delegates to the Django one. For example, you could introduce WSGI
11 | middleware here, or combine a Django application with an application of another
12 | framework.
13 | """
14 |
15 | import os
16 |
17 | # This application object is used by any WSGI server configured to use this
18 | # file. This includes Django's development server, if the WSGI_APPLICATION
19 | # setting points here.
20 | from django.core.wsgi import get_wsgi_application
21 |
22 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "proj.settings")
23 |
24 | application = get_wsgi_application()
25 |
26 | # Apply WSGI middleware here.
27 | # from helloworld.wsgi import HelloWorldApplication
28 | # application = HelloWorldApplication(application)
29 |
--------------------------------------------------------------------------------
/examples/django/pytest.ini:
--------------------------------------------------------------------------------
1 | [pytest]
2 | DJANGO_SETTINGS_MODULE = 'proj.settings'
3 |
4 | log_cli = true
5 | log_cli_level = INFO
6 | log_cli_format = %(asctime)s [%(levelname)8s] %(message)s (%(filename)s:%(lineno)s)
7 | log_cli_date_format = %Y-%m-%d %H:%M:%S
8 |
--------------------------------------------------------------------------------
/examples/django/requirements.txt:
--------------------------------------------------------------------------------
1 | sqlalchemy>=1.2.18
2 | django>=2.2.1
3 | pytest-django>=4.7.0
4 | pytest-xdist>=3.5.0
5 | pytest-rerunfailures>=14.0
6 | pytest-celery[all]@git+https://github.com/celery/pytest-celery.git
7 |
--------------------------------------------------------------------------------
/examples/django/tests/DjangoWorker.Dockerfile:
--------------------------------------------------------------------------------
1 | FROM python:3.11-bookworm
2 |
3 | # Create a user to run the worker
4 | RUN adduser --disabled-password --gecos "" test_user
5 |
6 | # Install system dependencies
7 | RUN apt-get update && apt-get install -y build-essential git
8 |
9 | # Set arguments
10 | ARG CELERY_LOG_LEVEL=INFO
11 | ARG CELERY_WORKER_NAME=celery_dev_worker
12 | ARG CELERY_WORKER_QUEUE=celery
13 | ENV LOG_LEVEL=$CELERY_LOG_LEVEL
14 | ENV WORKER_NAME=$CELERY_WORKER_NAME
15 | ENV WORKER_QUEUE=$CELERY_WORKER_QUEUE
16 |
17 | EXPOSE 5678
18 |
19 | # Install packages
20 | WORKDIR /src
21 |
22 | COPY --chown=test_user:test_user requirements.txt .
23 | RUN pip install --no-cache-dir --upgrade pip
24 | RUN pip install -r ./requirements.txt
25 |
26 | # Switch to the test_user
27 | USER test_user
28 |
29 | # Start the celery worker
30 | CMD celery -A proj worker --loglevel=$LOG_LEVEL -n $WORKER_NAME@%h -Q $WORKER_QUEUE
31 |
--------------------------------------------------------------------------------
/examples/django/tests/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/celery/pytest-celery/4d1a1dbcf340abab43d773b928f824a5a4ce7f08/examples/django/tests/__init__.py
--------------------------------------------------------------------------------
/examples/django/tests/conftest.py:
--------------------------------------------------------------------------------
1 | from __future__ import annotations
2 |
3 | import os
4 | from typing import Any
5 |
6 | import celery
7 | import pytest
8 | from pytest_docker_tools import build
9 | from pytest_docker_tools import container
10 | from pytest_docker_tools import fxtr
11 |
12 | from pytest_celery import CeleryWorkerContainer
13 | from pytest_celery import defaults
14 |
15 |
16 | class DjangoWorkerContainer(CeleryWorkerContainer):
17 | @property
18 | def client(self) -> Any:
19 | return self
20 |
21 | @classmethod
22 | def version(cls) -> str:
23 | return celery.__version__
24 |
25 | @classmethod
26 | def log_level(cls) -> str:
27 | return "INFO"
28 |
29 | @classmethod
30 | def worker_name(cls) -> str:
31 | return "django_tests_worker"
32 |
33 | @classmethod
34 | def worker_queue(cls) -> str:
35 | return "celery"
36 |
37 |
38 | worker_image = build(
39 | path=".",
40 | dockerfile="tests/DjangoWorker.Dockerfile",
41 | tag="pytest-celery/examples/django:example",
42 | buildargs=DjangoWorkerContainer.buildargs(),
43 | )
44 |
45 |
46 | default_worker_container = container(
47 | image="{worker_image.id}",
48 | ports=fxtr("default_worker_ports"),
49 | environment=fxtr("default_worker_env"),
50 | network="{default_pytest_celery_network.name}",
51 | volumes={
52 | # Volume: Worker /app
53 | "{default_worker_volume.name}": defaults.DEFAULT_WORKER_VOLUME,
54 | # Mount: source
55 | os.path.abspath(os.getcwd()): {
56 | "bind": "/src",
57 | "mode": "rw",
58 | },
59 | },
60 | wrapper_class=DjangoWorkerContainer,
61 | timeout=defaults.DEFAULT_WORKER_CONTAINER_TIMEOUT,
62 | )
63 |
64 |
65 | @pytest.fixture
66 | def default_worker_container_cls() -> type[CeleryWorkerContainer]:
67 | return DjangoWorkerContainer
68 |
69 |
70 | @pytest.fixture(scope="session")
71 | def default_worker_container_session_cls() -> type[CeleryWorkerContainer]:
72 | return DjangoWorkerContainer
73 |
--------------------------------------------------------------------------------
/examples/django/tests/test_tasks.py:
--------------------------------------------------------------------------------
1 | from demoapp.tasks import add
2 | from demoapp.tasks import count_widgets
3 |
4 |
5 | def test_add(celery_setup):
6 | assert add.s(1, 2).delay().get() == 3
7 |
8 |
9 | def test_count_widgets(celery_setup):
10 | assert count_widgets.s().delay().get() == 0
11 |
--------------------------------------------------------------------------------
/examples/hybrid_setup/pytest.ini:
--------------------------------------------------------------------------------
1 | [pytest]
2 | log_cli = true
3 | log_cli_level = INFO
4 | log_cli_format = %(asctime)s [%(levelname)8s] %(message)s (%(filename)s:%(lineno)s)
5 | log_cli_date_format = %Y-%m-%d %H:%M:%S
6 |
--------------------------------------------------------------------------------
/examples/hybrid_setup/requirements.txt:
--------------------------------------------------------------------------------
1 | pytest>=7.4.4
2 | pytest-xdist>=3.5.0
3 | pytest-subtests>=0.11.0
4 | pytest-rerunfailures>=14.0
5 | celery[gevent]
6 | pytest-celery[all]@git+https://github.com/celery/pytest-celery.git
7 |
--------------------------------------------------------------------------------
/examples/hybrid_setup/tests/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/celery/pytest-celery/4d1a1dbcf340abab43d773b928f824a5a4ce7f08/examples/hybrid_setup/tests/__init__.py
--------------------------------------------------------------------------------
/examples/hybrid_setup/tests/conftest.py:
--------------------------------------------------------------------------------
1 | # flake8: noqa
2 |
3 | import pytest
4 | from pytest_docker_tools import network
5 |
6 | from pytest_celery import CeleryBackendCluster
7 | from pytest_celery import CeleryBrokerCluster
8 | from pytest_celery import CeleryTestWorker
9 | from pytest_celery import CeleryWorkerCluster
10 | from pytest_celery import MemcachedTestBackend
11 | from tests.vendors.memcached import *
12 | from tests.vendors.rabbitmq import *
13 | from tests.vendors.workers.gevent import *
14 | from tests.vendors.workers.legacy import *
15 |
16 | # ----------------------------
17 |
18 | hybrid_setup_example_network = network(scope="session")
19 |
20 |
21 | @pytest.fixture
22 | def celery_broker_cluster(
23 | session_rabbitmq_broker: RabbitMQTestBroker,
24 | session_failover_broker: RabbitMQTestBroker,
25 | ) -> CeleryBrokerCluster:
26 | """This is like setting broker_url to
27 | "session_rabbitmq_broker;session_failover_broker"."""
28 | cluster = CeleryBrokerCluster(
29 | session_rabbitmq_broker,
30 | session_failover_broker,
31 | )
32 | yield cluster
33 | cluster.teardown()
34 |
35 |
36 | @pytest.fixture
37 | def celery_backend_cluster(session_memcached_backend: MemcachedTestBackend) -> CeleryBackendCluster:
38 | cluster = CeleryBackendCluster(session_memcached_backend)
39 | yield cluster
40 | cluster.teardown()
41 |
42 |
43 | @pytest.fixture
44 | def celery_worker_cluster(
45 | gevent_worker: CeleryTestWorker,
46 | legacy_worker: CeleryTestWorker,
47 | ) -> CeleryWorkerCluster:
48 | cluster = CeleryWorkerCluster(gevent_worker, legacy_worker)
49 | yield cluster
50 | cluster.teardown()
51 |
52 |
53 | @pytest.fixture
54 | def default_worker_tasks(default_worker_tasks: set) -> set:
55 | from tests.vendors.workers import tasks
56 |
57 | default_worker_tasks.add(tasks)
58 | return default_worker_tasks
59 |
60 |
61 | @pytest.fixture
62 | def default_worker_signals(default_worker_signals: set) -> set:
63 | from tests.vendors.workers import signals
64 |
65 | default_worker_signals.add(signals)
66 | return default_worker_signals
67 |
--------------------------------------------------------------------------------
/examples/hybrid_setup/tests/test_hybrid_setup.py:
--------------------------------------------------------------------------------
1 | from pytest_subtests import SubTests
2 |
3 | from pytest_celery import RESULT_TIMEOUT
4 | from pytest_celery import CeleryTestSetup
5 | from pytest_celery import CeleryTestWorker
6 | from pytest_celery import RabbitMQTestBroker
7 | from pytest_celery import ping
8 | from tests.vendors.workers.tasks import job
9 |
10 |
11 | class TestHybridSetupExample:
12 | def test_ping(self, celery_setup: CeleryTestSetup):
13 | assert ping.s().delay().get(timeout=RESULT_TIMEOUT) == "pong"
14 |
15 | def test_job(self, celery_setup: CeleryTestSetup):
16 | assert job.s().delay().get(timeout=RESULT_TIMEOUT) == "Done!"
17 |
18 | def test_signal(self, celery_setup: CeleryTestSetup):
19 | celery_setup.worker.assert_log_exists("Worker init handler called!")
20 |
21 | def test_failover(
22 | self,
23 | celery_setup: CeleryTestSetup,
24 | gevent_worker: CeleryTestWorker,
25 | legacy_worker: CeleryTestWorker,
26 | session_failover_broker: RabbitMQTestBroker,
27 | subtests: SubTests,
28 | ):
29 | with subtests.test(msg="Kill the main broker"):
30 | celery_setup.broker.kill()
31 |
32 | with subtests.test(msg="Manually assert the workers"):
33 | gevent_worker.assert_log_exists("Will retry using next failover.")
34 | legacy_worker.assert_log_exists("Will retry using next failover.")
35 |
36 | with subtests.test(msg="Use the celery setup to assert the workers"):
37 | worker: CeleryTestWorker
38 | for worker in celery_setup.worker_cluster:
39 | log = f"Connected to amqp://guest:**@{session_failover_broker.hostname()}:5672//"
40 | worker.assert_log_exists(log)
41 |
42 | with subtests.test(msg="Verify that the workers are still working (publish tasks)"):
43 | assert job.s().delay().get(timeout=RESULT_TIMEOUT) == "Done!"
44 |
--------------------------------------------------------------------------------
/examples/hybrid_setup/tests/vendors/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/celery/pytest-celery/4d1a1dbcf340abab43d773b928f824a5a4ce7f08/examples/hybrid_setup/tests/vendors/__init__.py
--------------------------------------------------------------------------------
/examples/hybrid_setup/tests/vendors/memcached.py:
--------------------------------------------------------------------------------
1 | import pytest
2 | from pytest_docker_tools import container
3 | from pytest_docker_tools import fetch
4 |
5 | from pytest_celery import MEMCACHED_CONTAINER_TIMEOUT
6 | from pytest_celery import MEMCACHED_ENV
7 | from pytest_celery import MEMCACHED_IMAGE
8 | from pytest_celery import MEMCACHED_PORTS
9 | from pytest_celery import MemcachedContainer
10 | from pytest_celery import MemcachedTestBackend
11 |
12 | memcached_image = fetch(repository=MEMCACHED_IMAGE)
13 | memcached_test_container = container(
14 | # name="Memcached-Session-Backend", # Optional | Incompatible with parallel execution
15 | image="{memcached_image.id}",
16 | scope="session",
17 | ports=MEMCACHED_PORTS,
18 | environment=MEMCACHED_ENV,
19 | network="{hybrid_setup_example_network.name}",
20 | wrapper_class=MemcachedContainer,
21 | timeout=MEMCACHED_CONTAINER_TIMEOUT,
22 | )
23 |
24 |
25 | @pytest.fixture
26 | def session_memcached_backend(memcached_test_container: MemcachedContainer) -> MemcachedTestBackend:
27 | backend = MemcachedTestBackend(memcached_test_container)
28 | yield backend
29 | backend.teardown()
30 |
--------------------------------------------------------------------------------
/examples/hybrid_setup/tests/vendors/rabbitmq.py:
--------------------------------------------------------------------------------
1 | import pytest
2 | from pytest_docker_tools import container
3 | from pytest_docker_tools import fetch
4 |
5 | from pytest_celery import RABBITMQ_CONTAINER_TIMEOUT
6 | from pytest_celery import RABBITMQ_ENV
7 | from pytest_celery import RABBITMQ_IMAGE
8 | from pytest_celery import RABBITMQ_PORTS
9 | from pytest_celery import RabbitMQContainer
10 | from pytest_celery import RabbitMQTestBroker
11 |
12 | rabbitmq_image = fetch(repository=RABBITMQ_IMAGE)
13 |
14 | rabbitmq_test_container = container(
15 | # name="Main RabbitMQ Broker (session)", # Optional | Incompatible with parallel execution
16 | image="{rabbitmq_image.id}",
17 | scope="session",
18 | ports=RABBITMQ_PORTS,
19 | environment=RABBITMQ_ENV,
20 | network="{hybrid_setup_example_network.name}",
21 | wrapper_class=RabbitMQContainer,
22 | timeout=RABBITMQ_CONTAINER_TIMEOUT,
23 | )
24 |
25 |
26 | @pytest.fixture
27 | def session_rabbitmq_broker(rabbitmq_test_container: RabbitMQContainer) -> RabbitMQTestBroker:
28 | broker = RabbitMQTestBroker(rabbitmq_test_container)
29 | yield broker
30 | broker.teardown()
31 |
32 |
33 | failover_test_container = container(
34 | # name="Failover RabbitMQ Broker (session)", # Optional | Incompatible with parallel execution
35 | image="{rabbitmq_image.id}",
36 | scope="session",
37 | ports=RABBITMQ_PORTS,
38 | environment=RABBITMQ_ENV,
39 | network="{hybrid_setup_example_network.name}",
40 | wrapper_class=RabbitMQContainer,
41 | timeout=RABBITMQ_CONTAINER_TIMEOUT,
42 | )
43 |
44 |
45 | @pytest.fixture
46 | def session_failover_broker(failover_test_container: RabbitMQContainer) -> RabbitMQTestBroker:
47 | broker = RabbitMQTestBroker(failover_test_container)
48 | yield broker
49 | broker.teardown()
50 |
--------------------------------------------------------------------------------
/examples/hybrid_setup/tests/vendors/workers/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/celery/pytest-celery/4d1a1dbcf340abab43d773b928f824a5a4ce7f08/examples/hybrid_setup/tests/vendors/workers/__init__.py
--------------------------------------------------------------------------------
/examples/hybrid_setup/tests/vendors/workers/gevent.Dockerfile:
--------------------------------------------------------------------------------
1 | FROM python:3.11-bookworm
2 |
3 | # Create a user to run the worker
4 | RUN adduser --disabled-password --gecos "" test_user
5 |
6 | # Install system dependencies
7 | RUN apt-get update && apt-get install -y build-essential git libevent-dev
8 |
9 | # Set arguments
10 | ARG CELERY_LOG_LEVEL=INFO
11 | ARG CELERY_WORKER_NAME=my_worker
12 | ARG CELERY_WORKER_QUEUE=celery
13 | ENV LOG_LEVEL=$CELERY_LOG_LEVEL
14 | ENV WORKER_NAME=$CELERY_WORKER_NAME
15 | ENV WORKER_QUEUE=$CELERY_WORKER_QUEUE
16 |
17 | # Install packages
18 | RUN pip install --no-cache-dir --upgrade pip
19 | RUN pip install "celery[gevent]" "pytest-celery[all]==1.0.0"
20 |
21 | # The workdir must be /app
22 | WORKDIR /app
23 |
24 | # Switch to the test_user
25 | USER test_user
26 |
27 | # Start the celery worker
28 | CMD celery -A app worker --loglevel=$LOG_LEVEL -n $WORKER_NAME@%h -Q $WORKER_QUEUE
29 |
--------------------------------------------------------------------------------
/examples/hybrid_setup/tests/vendors/workers/gevent.py:
--------------------------------------------------------------------------------
1 | import pytest
2 | from celery import Celery
3 | from pytest_docker_tools import build
4 | from pytest_docker_tools import container
5 | from pytest_docker_tools import fxtr
6 |
7 | from pytest_celery import CeleryTestWorker
8 | from pytest_celery import CeleryWorkerContainer
9 | from pytest_celery import defaults
10 |
11 |
12 | class GeventWorkerContainer(CeleryWorkerContainer):
13 | @classmethod
14 | def command(cls, *args: str) -> list[str]:
15 | return super().command("-P", "gevent", "-c", "1000")
16 |
17 |
18 | gevent_worker_image = build(
19 | path=".",
20 | dockerfile="tests/vendors/workers/gevent.Dockerfile",
21 | tag="pytest-celery/examples/hybrid_setup:gevent",
22 | buildargs=GeventWorkerContainer.buildargs(),
23 | )
24 |
25 |
26 | gevent_worker_container = container(
27 | image="{gevent_worker_image.id}",
28 | environment=fxtr("default_worker_env"),
29 | network="{hybrid_setup_example_network.name}",
30 | volumes={"{default_worker_volume.name}": defaults.DEFAULT_WORKER_VOLUME},
31 | wrapper_class=GeventWorkerContainer,
32 | timeout=defaults.DEFAULT_WORKER_CONTAINER_TIMEOUT,
33 | command=GeventWorkerContainer.command(),
34 | )
35 |
36 |
37 | @pytest.fixture
38 | def gevent_worker(gevent_worker_container: GeventWorkerContainer, celery_setup_app: Celery) -> CeleryTestWorker:
39 | worker = CeleryTestWorker(gevent_worker_container, app=celery_setup_app)
40 | yield worker
41 | worker.teardown()
42 |
--------------------------------------------------------------------------------
/examples/hybrid_setup/tests/vendors/workers/legacy.Dockerfile:
--------------------------------------------------------------------------------
1 | # Celery 4 does not support >3.10-bookworm
2 | FROM python:3.10-bookworm
3 |
4 | # Create a user to run the worker
5 | RUN adduser --disabled-password --gecos "" test_user
6 |
7 | # Install system dependencies
8 | RUN apt-get update && apt-get install -y build-essential git
9 |
10 | # Set arguments
11 | ARG CELERY_LOG_LEVEL=INFO
12 | ARG CELERY_WORKER_NAME=my_worker
13 | ARG CELERY_WORKER_QUEUE=celery
14 | ENV LOG_LEVEL=$CELERY_LOG_LEVEL
15 | ENV WORKER_NAME=$CELERY_WORKER_NAME
16 | ENV WORKER_QUEUE=$CELERY_WORKER_QUEUE
17 |
18 | # Install packages
19 | RUN pip install --no-cache-dir --upgrade pip \
20 | celery==4.4.7 "pytest-celery[all]==1.0.0"
21 |
22 | # The workdir must be /app
23 | WORKDIR /app
24 |
25 | # Switch to the test_user
26 | USER test_user
27 |
28 | # Start the celery worker
29 | CMD celery -A app worker --loglevel=$LOG_LEVEL -n $WORKER_NAME@%h -Q $WORKER_QUEUE
30 |
--------------------------------------------------------------------------------
/examples/hybrid_setup/tests/vendors/workers/legacy.py:
--------------------------------------------------------------------------------
1 | import pytest
2 | from celery import Celery
3 | from pytest_docker_tools import build
4 | from pytest_docker_tools import container
5 | from pytest_docker_tools import fxtr
6 |
7 | from pytest_celery import CeleryTestWorker
8 | from pytest_celery import CeleryWorkerContainer
9 | from pytest_celery import defaults
10 |
11 |
12 | class LegacyWorkerContainer(CeleryWorkerContainer):
13 | @classmethod
14 | def version(cls) -> str:
15 | return "4.4.7"
16 |
17 | @classmethod
18 | def worker_queue(cls) -> str:
19 | return "legacy"
20 |
21 |
22 | legacy_worker_image = build(
23 | path=".",
24 | dockerfile="tests/vendors/workers/legacy.Dockerfile",
25 | tag="pytest-celery/examples/hybrid_setup:legacy",
26 | buildargs=LegacyWorkerContainer.buildargs(),
27 | )
28 |
29 |
30 | legacy_worker_container = container(
31 | image="{legacy_worker_image.id}",
32 | environment=fxtr("default_worker_env"),
33 | network="{hybrid_setup_example_network.name}",
34 | volumes={"{default_worker_volume.name}": defaults.DEFAULT_WORKER_VOLUME},
35 | wrapper_class=LegacyWorkerContainer,
36 | timeout=defaults.DEFAULT_WORKER_CONTAINER_TIMEOUT,
37 | command=LegacyWorkerContainer.command(),
38 | )
39 |
40 |
41 | @pytest.fixture
42 | def legacy_worker(legacy_worker_container: LegacyWorkerContainer, celery_setup_app: Celery) -> CeleryTestWorker:
43 | worker = CeleryTestWorker(legacy_worker_container, app=celery_setup_app)
44 | yield worker
45 | worker.teardown()
46 |
--------------------------------------------------------------------------------
/examples/hybrid_setup/tests/vendors/workers/signals.py:
--------------------------------------------------------------------------------
1 | from __future__ import annotations
2 |
3 | from celery.signals import worker_init
4 |
5 |
6 | @worker_init.connect
7 | def worker_init_handler(sender, **kwargs):
8 | print("Worker init handler called!")
9 |
--------------------------------------------------------------------------------
/examples/hybrid_setup/tests/vendors/workers/tasks.py:
--------------------------------------------------------------------------------
1 | import celery.utils
2 | from celery import shared_task
3 | from celery.canvas import group
4 |
5 | from pytest_celery import RESULT_TIMEOUT
6 |
7 |
8 | @shared_task
9 | def noop(*args, **kwargs) -> None:
10 | return celery.utils.noop(*args, **kwargs)
11 |
12 |
13 | @shared_task
14 | def identity(x):
15 | return x
16 |
17 |
18 | @shared_task
19 | def job() -> str:
20 | canvas = (
21 | group(
22 | identity.si("Hello, "),
23 | identity.si("world!"),
24 | )
25 | | noop.s().set(queue="legacy")
26 | | identity.si("Done!")
27 | )
28 | return canvas.delay().get(timeout=RESULT_TIMEOUT)
29 |
--------------------------------------------------------------------------------
/examples/myutils/pytest.ini:
--------------------------------------------------------------------------------
1 | [pytest]
2 | log_cli = true
3 | log_cli_level = INFO
4 | log_cli_format = %(asctime)s [%(levelname)8s] %(message)s (%(filename)s:%(lineno)s)
5 | log_cli_date_format = %Y-%m-%d %H:%M:%S
6 |
--------------------------------------------------------------------------------
/examples/myutils/requirements.txt:
--------------------------------------------------------------------------------
1 | pytest>=7.4.4
2 | pytest-xdist>=3.5.0
3 | pytest-rerunfailures>=14.0
4 | pytest-celery[all]@git+https://github.com/celery/pytest-celery.git
5 |
--------------------------------------------------------------------------------
/examples/myutils/tests/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/celery/pytest-celery/4d1a1dbcf340abab43d773b928f824a5a4ce7f08/examples/myutils/tests/__init__.py
--------------------------------------------------------------------------------
/examples/myutils/tests/conftest.py:
--------------------------------------------------------------------------------
1 | from types import ModuleType
2 |
3 | import pytest
4 |
5 | from pytest_celery import CeleryTestWorker
6 |
7 |
8 | @pytest.fixture
9 | def default_worker_utils_module() -> ModuleType:
10 | from tests import myutils
11 |
12 | return myutils
13 |
14 |
15 | class MyWorker(CeleryTestWorker):
16 |
17 | def myfunc(self) -> bool:
18 | exit_code, output = self.container.exec_run(
19 | 'python -c "from utils import myfunc; print(myfunc())"',
20 | )
21 | if exit_code != 0:
22 | raise RuntimeError(f"Error: {output}")
23 | output = output.decode("utf-8")
24 | return output.strip()
25 |
26 |
27 | @pytest.fixture
28 | def default_worker_cls() -> type[CeleryTestWorker]:
29 | return MyWorker
30 |
--------------------------------------------------------------------------------
/examples/myutils/tests/myutils.py:
--------------------------------------------------------------------------------
1 | from pytest_celery.vendors.worker.content.utils import get_running_processes_info # noqa
2 |
3 |
4 | def myfunc():
5 | return "foo"
6 |
--------------------------------------------------------------------------------
/examples/myutils/tests/test_myutils.py:
--------------------------------------------------------------------------------
1 | from pytest_celery import CeleryTestSetup
2 | from tests.conftest import MyWorker
3 | from tests.myutils import myfunc
4 |
5 |
6 | def test_myfunc():
7 | assert myfunc() == "foo"
8 |
9 |
10 | def test_myfunc_in_worker(celery_worker: MyWorker):
11 | assert celery_worker.myfunc() == "foo"
12 | assert celery_worker.get_running_processes_info()
13 |
14 |
15 | def test_myfunc_in_setup_worker(celery_setup: CeleryTestSetup):
16 | celery_worker: MyWorker = celery_setup.worker
17 | assert celery_worker.myfunc() == "foo"
18 | assert celery_worker.get_running_processes_info()
19 |
--------------------------------------------------------------------------------
/examples/myworker/pytest.ini:
--------------------------------------------------------------------------------
1 | [pytest]
2 | log_cli = true
3 | log_cli_level = INFO
4 | log_cli_format = %(asctime)s [%(levelname)8s] %(message)s (%(filename)s:%(lineno)s)
5 | log_cli_date_format = %Y-%m-%d %H:%M:%S
6 |
--------------------------------------------------------------------------------
/examples/myworker/requirements.txt:
--------------------------------------------------------------------------------
1 | pytest>=7.4.4
2 | pytest-xdist>=3.5.0
3 | pytest-rerunfailures>=14.0
4 | pytest-celery[all]@git+https://github.com/celery/pytest-celery.git
5 |
--------------------------------------------------------------------------------
/examples/myworker/tests/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/celery/pytest-celery/4d1a1dbcf340abab43d773b928f824a5a4ce7f08/examples/myworker/tests/__init__.py
--------------------------------------------------------------------------------
/examples/myworker/tests/conftest.py:
--------------------------------------------------------------------------------
1 | from tests.myworker.myworker import myworker_container # noqa
2 | from tests.myworker.myworker import myworker_image # noqa
3 | from tests.myworker.myworker import myworker_worker # noqa
4 |
--------------------------------------------------------------------------------
/examples/myworker/tests/myworker/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM python:3.11-bookworm
2 |
3 | # Create a user to run the worker
4 | RUN adduser --disabled-password --gecos "" test_user
5 |
6 | # Install system dependencies
7 | RUN apt-get update && apt-get install -y build-essential git
8 |
9 | # Set arguments
10 | ARG CELERY_LOG_LEVEL=INFO
11 | ARG CELERY_WORKER_NAME=my_worker
12 | ARG CELERY_WORKER_QUEUE=celery
13 | ENV LOG_LEVEL=$CELERY_LOG_LEVEL
14 | ENV WORKER_NAME=$CELERY_WORKER_NAME
15 | ENV WORKER_QUEUE=$CELERY_WORKER_QUEUE
16 |
17 | EXPOSE 5678
18 |
19 | # Install packages
20 | WORKDIR /src
21 |
22 | COPY --chown=test_user:test_user requirements.txt .
23 | RUN pip install --no-cache-dir --upgrade pip
24 | RUN pip install -r ./requirements.txt
25 | RUN git clone https://github.com/celery/celery.git
26 |
27 | WORKDIR /src/celery
28 |
29 | RUN pip install -e .
30 |
31 | # The workdir must be /app
32 | WORKDIR /app
33 |
34 | # Switch to the test_user
35 | USER test_user
36 |
37 | # Start the celery worker
38 | CMD celery -A app worker --loglevel=$LOG_LEVEL -n $WORKER_NAME@%h -Q $WORKER_QUEUE
39 |
--------------------------------------------------------------------------------
/examples/myworker/tests/myworker/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/celery/pytest-celery/4d1a1dbcf340abab43d773b928f824a5a4ce7f08/examples/myworker/tests/myworker/__init__.py
--------------------------------------------------------------------------------
/examples/myworker/tests/myworker/myworker.py:
--------------------------------------------------------------------------------
1 | from __future__ import annotations
2 |
3 | from typing import Any
4 |
5 | import pytest
6 | from celery import Celery
7 | from pytest_docker_tools import build
8 | from pytest_docker_tools import container
9 | from pytest_docker_tools import fxtr
10 |
11 | from pytest_celery import CeleryTestWorker
12 | from pytest_celery import CeleryWorkerContainer
13 | from pytest_celery import defaults
14 |
15 |
16 | class MyWorkerContainer(CeleryWorkerContainer):
17 | @property
18 | def client(self) -> Any:
19 | return self
20 |
21 | @classmethod
22 | def version(cls) -> str:
23 | return "Celery main branch"
24 |
25 | @classmethod
26 | def log_level(cls) -> str:
27 | return "INFO"
28 |
29 | @classmethod
30 | def worker_name(cls) -> str:
31 | return "my_worker"
32 |
33 | @classmethod
34 | def worker_queue(cls) -> str:
35 | return "myworker"
36 |
37 |
38 | myworker_image = build(
39 | path=".",
40 | dockerfile="tests/myworker/Dockerfile",
41 | tag="pytest-celery/myworker:example",
42 | buildargs=MyWorkerContainer.buildargs(),
43 | )
44 |
45 |
46 | myworker_container = container(
47 | image="{myworker_image.id}",
48 | ports=MyWorkerContainer.ports(),
49 | environment=fxtr("default_worker_env"),
50 | network="{default_pytest_celery_network.name}",
51 | volumes={"{default_worker_volume.name}": defaults.DEFAULT_WORKER_VOLUME},
52 | wrapper_class=MyWorkerContainer,
53 | timeout=defaults.DEFAULT_WORKER_CONTAINER_TIMEOUT,
54 | command=MyWorkerContainer.command(),
55 | )
56 |
57 |
58 | @pytest.fixture
59 | def myworker_worker(myworker_container: MyWorkerContainer, celery_setup_app: Celery) -> CeleryTestWorker:
60 | worker = CeleryTestWorker(myworker_container, app=celery_setup_app)
61 | yield worker
62 | worker.teardown()
63 |
--------------------------------------------------------------------------------
/examples/myworker/tests/test_myworker.py:
--------------------------------------------------------------------------------
1 | import pytest
2 | from celery.canvas import Signature
3 | from celery.result import AsyncResult
4 |
5 | from pytest_celery import RESULT_TIMEOUT
6 | from pytest_celery import CeleryTestSetup
7 | from pytest_celery import CeleryTestWorker
8 | from pytest_celery import CeleryWorkerCluster
9 | from pytest_celery import ping
10 |
11 |
12 | @pytest.fixture
13 | def celery_worker_cluster(
14 | celery_worker: CeleryTestWorker,
15 | myworker_worker: CeleryTestWorker,
16 | ) -> CeleryWorkerCluster:
17 | """Add myworker worker to the workers cluster alongside the parametrize
18 | plugin worker."""
19 | cluster = CeleryWorkerCluster(celery_worker, myworker_worker) # type: ignore
20 | yield cluster
21 | cluster.teardown()
22 |
23 |
24 | def test_ping(celery_setup: CeleryTestSetup):
25 | """Test ping task for each worker node."""
26 | worker: CeleryTestWorker
27 | for worker in celery_setup.worker_cluster:
28 | sig: Signature = ping.s()
29 | res: AsyncResult = sig.apply_async(queue=worker.worker_queue)
30 | assert res.get(timeout=RESULT_TIMEOUT) == "pong"
31 |
--------------------------------------------------------------------------------
/examples/rabbitmq_management/pytest.ini:
--------------------------------------------------------------------------------
1 | [pytest]
2 | log_cli = true
3 | log_cli_level = INFO
4 | log_cli_format = %(asctime)s [%(levelname)8s] %(message)s (%(filename)s:%(lineno)s)
5 | log_cli_date_format = %Y-%m-%d %H:%M:%S
6 |
--------------------------------------------------------------------------------
/examples/rabbitmq_management/requirements.txt:
--------------------------------------------------------------------------------
1 | pytest>=7.4.4
2 | pytest-xdist>=3.5.0
3 | pytest-rerunfailures>=14.0
4 | pytest-celery[all]@git+https://github.com/celery/pytest-celery.git
5 |
--------------------------------------------------------------------------------
/examples/rabbitmq_management/tests/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/celery/pytest-celery/4d1a1dbcf340abab43d773b928f824a5a4ce7f08/examples/rabbitmq_management/tests/__init__.py
--------------------------------------------------------------------------------
/examples/rabbitmq_management/tests/conftest.py:
--------------------------------------------------------------------------------
1 | import pytest
2 |
3 | from pytest_celery import RABBITMQ_PORTS
4 | from pytest_celery import CeleryBrokerCluster
5 | from pytest_celery import RabbitMQContainer
6 | from pytest_celery import RabbitMQTestBroker
7 |
8 |
9 | @pytest.fixture
10 | def default_rabbitmq_broker_image() -> str:
11 | return "rabbitmq:management"
12 |
13 |
14 | @pytest.fixture
15 | def default_rabbitmq_broker_ports() -> dict:
16 | # Expose the management UI port
17 | ports = RABBITMQ_PORTS.copy()
18 | ports.update({"15672/tcp": None})
19 | return ports
20 |
21 |
22 | class RabbitMQManagementTestBroker(RabbitMQTestBroker):
23 | def get_management_url(self) -> str:
24 | ip = self.container.attrs["NetworkSettings"]["Ports"]["15672/tcp"][0]["HostIp"]
25 | port = self.container.attrs["NetworkSettings"]["Ports"]["15672/tcp"][0]["HostPort"]
26 | # Opening this link during debugging allows you to see the RabbitMQ management UI
27 | # in your browser
28 | return f"http://{ip}:{port}"
29 |
30 |
31 | @pytest.fixture
32 | def celery_rabbitmq_broker(default_rabbitmq_broker: RabbitMQContainer) -> RabbitMQTestBroker:
33 | broker = RabbitMQManagementTestBroker(default_rabbitmq_broker)
34 | yield broker
35 | broker.teardown()
36 |
37 |
38 | @pytest.fixture
39 | def celery_broker_cluster(celery_rabbitmq_broker: RabbitMQTestBroker) -> CeleryBrokerCluster: # type: ignore
40 | cluster = CeleryBrokerCluster(celery_rabbitmq_broker) # type: ignore
41 | yield cluster
42 | cluster.teardown()
43 |
--------------------------------------------------------------------------------
/examples/rabbitmq_management/tests/test_management_broker.py:
--------------------------------------------------------------------------------
1 | import requests
2 | from requests.auth import HTTPBasicAuth
3 |
4 | from pytest_celery import CeleryTestSetup
5 | from tests.conftest import RabbitMQManagementTestBroker
6 |
7 |
8 | def test_login_to_broker_alone(celery_rabbitmq_broker: RabbitMQManagementTestBroker):
9 | api = celery_rabbitmq_broker.get_management_url() + "/api/whoami"
10 | response = requests.get(api, auth=HTTPBasicAuth("guest", "guest"))
11 | assert response.status_code == 200
12 | assert response.json()["name"] == "guest"
13 | assert response.json()["tags"] == ["administrator"]
14 |
15 |
16 | def test_broker_in_setup(celery_setup: CeleryTestSetup):
17 | assert isinstance(celery_setup.broker, RabbitMQManagementTestBroker)
18 | api = celery_setup.broker.get_management_url() + "/api/queues"
19 | response = requests.get(api, auth=HTTPBasicAuth("guest", "guest"))
20 | assert response.status_code == 200
21 | res = response.json()
22 | assert isinstance(res, list)
23 | assert len(list(filter(lambda queues: celery_setup.worker.hostname() in queues["name"], res))) == 1
24 |
--------------------------------------------------------------------------------
/examples/range/pytest.ini:
--------------------------------------------------------------------------------
1 | [pytest]
2 | log_cli = true
3 | log_cli_level = INFO
4 | log_cli_format = %(asctime)s [%(levelname)8s] %(message)s (%(filename)s:%(lineno)s)
5 | log_cli_date_format = %Y-%m-%d %H:%M:%S
6 |
--------------------------------------------------------------------------------
/examples/range/requirements.txt:
--------------------------------------------------------------------------------
1 | pytest>=7.4.4
2 | pytest-xdist>=3.5.0
3 | pytest-subtests>=0.11.0
4 | pytest-rerunfailures>=14.0
5 | pytest-celery[all]@git+https://github.com/celery/pytest-celery.git
6 |
--------------------------------------------------------------------------------
/examples/range/tests/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/celery/pytest-celery/4d1a1dbcf340abab43d773b928f824a5a4ce7f08/examples/range/tests/__init__.py
--------------------------------------------------------------------------------
/examples/range/tests/conftest.py:
--------------------------------------------------------------------------------
1 | from __future__ import annotations
2 |
3 | import requests
4 | from packaging.version import parse as parse_version
5 |
6 |
7 | def get_celery_versions(start_version: str, end_version: str) -> list[str]:
8 | url = "https://pypi.org/pypi/celery/json"
9 | response = requests.get(url)
10 | data = response.json()
11 | all_versions = data["releases"].keys()
12 |
13 | filtered_versions = [
14 | v
15 | for v in all_versions
16 | if (
17 | parse_version(start_version) <= parse_version(v) <= parse_version(end_version)
18 | and not parse_version(v).is_prerelease
19 | )
20 | ]
21 |
22 | return sorted(filtered_versions, key=parse_version)
23 |
--------------------------------------------------------------------------------
/examples/range/tests/test_range.py:
--------------------------------------------------------------------------------
1 | import pytest
2 | from celery.canvas import Signature
3 | from celery.result import AsyncResult
4 |
5 | from pytest_celery import RESULT_TIMEOUT
6 | from pytest_celery import CeleryTestSetup
7 | from pytest_celery import ping
8 | from tests.conftest import get_celery_versions
9 |
10 |
11 | class TestRange:
12 | @pytest.fixture(scope="session", params=get_celery_versions("v4.4.7", "v5.0.0"))
13 | def default_worker_celery_version(self, request: pytest.FixtureRequest) -> str:
14 | return request.param
15 |
16 | def test_ping(self, celery_setup: CeleryTestSetup, default_worker_celery_version: str):
17 | sig: Signature = ping.s()
18 | res: AsyncResult = sig.apply_async()
19 | assert res.get(timeout=RESULT_TIMEOUT) == "pong"
20 |
--------------------------------------------------------------------------------
/examples/vhost/pytest.ini:
--------------------------------------------------------------------------------
1 | [pytest]
2 | log_cli = true
3 | log_cli_level = INFO
4 | log_cli_format = %(asctime)s [%(levelname)8s] %(message)s (%(filename)s:%(lineno)s)
5 | log_cli_date_format = %Y-%m-%d %H:%M:%S
6 |
--------------------------------------------------------------------------------
/examples/vhost/requirements.txt:
--------------------------------------------------------------------------------
1 | pytest>=7.4.4
2 | pytest-xdist>=3.5.0
3 | pytest-rerunfailures>=14.0
4 | pytest-celery[all]@git+https://github.com/celery/pytest-celery.git
5 |
--------------------------------------------------------------------------------
/examples/vhost/tests/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/celery/pytest-celery/4d1a1dbcf340abab43d773b928f824a5a4ce7f08/examples/vhost/tests/__init__.py
--------------------------------------------------------------------------------
/examples/vhost/tests/conftest.py:
--------------------------------------------------------------------------------
1 | import pytest
2 | from pytest_docker_tools import container
3 | from pytest_docker_tools import fetch
4 |
5 | from pytest_celery import REDIS_CONTAINER_TIMEOUT
6 | from pytest_celery import REDIS_ENV
7 | from pytest_celery import REDIS_IMAGE
8 | from pytest_celery import REDIS_PORTS
9 | from pytest_celery import CeleryBackendCluster
10 | from pytest_celery import CeleryBrokerCluster
11 | from pytest_celery import RedisContainer
12 | from pytest_celery import RedisTestBackend
13 | from pytest_celery import RedisTestBroker
14 |
15 | redis_image = fetch(repository=REDIS_IMAGE)
16 | redis_test_container: RedisContainer = container(
17 | image="{redis_image.id}",
18 | ports=REDIS_PORTS,
19 | environment=REDIS_ENV,
20 | network="{default_pytest_celery_network.name}",
21 | wrapper_class=RedisContainer,
22 | timeout=REDIS_CONTAINER_TIMEOUT,
23 | )
24 |
25 |
26 | @pytest.fixture
27 | def redis_broker(redis_test_container: RedisContainer) -> RedisTestBroker:
28 | broker = RedisTestBroker(redis_test_container)
29 | yield broker
30 | broker.teardown()
31 |
32 |
33 | @pytest.fixture
34 | def celery_broker_cluster(redis_broker: RedisTestBroker) -> CeleryBrokerCluster:
35 | cluster = CeleryBrokerCluster(redis_broker)
36 | yield cluster
37 | cluster.teardown()
38 |
39 |
40 | class MyRedisTestBackend(RedisTestBackend):
41 | def config(self, *args: tuple, **kwargs: dict) -> dict:
42 | return super().config(vhost=1, *args, **kwargs)
43 |
44 |
45 | @pytest.fixture
46 | def redis_backend(redis_test_container: RedisContainer) -> MyRedisTestBackend:
47 | backend = MyRedisTestBackend(redis_test_container)
48 | yield backend
49 | backend.teardown()
50 |
51 |
52 | @pytest.fixture
53 | def celery_backend_cluster(redis_backend: MyRedisTestBackend) -> CeleryBackendCluster:
54 | cluster = CeleryBackendCluster(redis_backend)
55 | yield cluster
56 | cluster.teardown()
57 |
--------------------------------------------------------------------------------
/examples/vhost/tests/test_vhost.py:
--------------------------------------------------------------------------------
1 | from pytest_celery import RESULT_TIMEOUT
2 | from pytest_celery import CeleryTestSetup
3 | from pytest_celery import ping
4 |
5 |
6 | class TestVhost:
7 | def test_ping(self, celery_setup: CeleryTestSetup):
8 | assert ping.s().delay().get(timeout=RESULT_TIMEOUT) == "pong"
9 |
10 | def test_vhost(self, celery_setup: CeleryTestSetup):
11 | assert celery_setup.app.conf.broker_url[:-1] == celery_setup.app.conf.result_backend[:-1]
12 | assert celery_setup.app.conf.broker_url.endswith("/0")
13 | assert celery_setup.app.conf.result_backend.endswith("/1")
14 |
15 | def test_single_redis(self, celery_setup: CeleryTestSetup):
16 | assert celery_setup.broker.container.id == celery_setup.backend.container.id
17 |
--------------------------------------------------------------------------------
/examples/worker_pool/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM python:3.11-bookworm
2 |
3 | # Create a user to run the worker
4 | RUN adduser --disabled-password --gecos "" test_user
5 |
6 | # Install system dependencies
7 | RUN apt-get update && apt-get install -y build-essential git libevent-dev
8 |
9 | # Set arguments
10 | ARG CELERY_LOG_LEVEL=INFO
11 | ARG CELERY_WORKER_NAME=my_worker
12 | ARG CELERY_WORKER_QUEUE=celery
13 | ENV LOG_LEVEL=$CELERY_LOG_LEVEL
14 | ENV WORKER_NAME=$CELERY_WORKER_NAME
15 | ENV WORKER_QUEUE=$CELERY_WORKER_QUEUE
16 |
17 | EXPOSE 5678
18 |
19 | # Install packages
20 | COPY --chown=test_user:test_user requirements.txt .
21 | RUN pip install --no-cache-dir --upgrade pip
22 | RUN pip install -r ./requirements.txt
23 |
24 | # The workdir must be /app
25 | WORKDIR /app
26 |
27 | # Switch to the test_user
28 | USER test_user
29 |
30 | # Start the celery worker
31 | CMD celery -A app worker --loglevel=$LOG_LEVEL -n $WORKER_NAME@%h -Q $WORKER_QUEUE
32 |
--------------------------------------------------------------------------------
/examples/worker_pool/pytest.ini:
--------------------------------------------------------------------------------
1 | [pytest]
2 | log_cli = true
3 | log_cli_level = INFO
4 | log_cli_format = %(asctime)s [%(levelname)8s] %(message)s (%(filename)s:%(lineno)s)
5 | log_cli_date_format = %Y-%m-%d %H:%M:%S
6 |
--------------------------------------------------------------------------------
/examples/worker_pool/requirements.txt:
--------------------------------------------------------------------------------
1 | pytest>=7.4.4
2 | pytest-xdist>=3.5.0
3 | pytest-subtests>=0.11.0
4 | pytest-rerunfailures>=14.0
5 | celery[gevent]
6 | pytest-celery[all]@git+https://github.com/celery/pytest-celery.git
7 |
--------------------------------------------------------------------------------
/examples/worker_pool/tasks.py:
--------------------------------------------------------------------------------
1 | # Based on https://github.com/celery/celery/blob/main/examples/gevent/tasks.py
2 |
3 | import requests
4 | from celery import shared_task
5 |
6 |
7 | @shared_task(ignore_result=True)
8 | def urlopen(url):
9 | print(f"Opening: {url}")
10 | try:
11 | requests.get(url)
12 | except requests.exceptions.RequestException as exc:
13 | print(f"Exception for {url}: {exc!r}")
14 | return url, 0
15 | print(f"Done with: {url}")
16 | return url, 1
17 |
--------------------------------------------------------------------------------
/examples/worker_pool/tests/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/celery/pytest-celery/4d1a1dbcf340abab43d773b928f824a5a4ce7f08/examples/worker_pool/tests/__init__.py
--------------------------------------------------------------------------------
/examples/worker_pool/tests/test_gevent_pool.py:
--------------------------------------------------------------------------------
1 | from __future__ import annotations
2 |
3 | import pytest
4 | import tasks
5 | from celery import Celery
6 | from celery.canvas import Signature
7 | from celery.canvas import group
8 | from celery.result import AsyncResult
9 | from pytest_docker_tools import build
10 | from pytest_docker_tools import container
11 | from pytest_docker_tools import fxtr
12 |
13 | from pytest_celery import RESULT_TIMEOUT
14 | from pytest_celery import CeleryTestSetup
15 | from pytest_celery import CeleryTestWorker
16 | from pytest_celery import CeleryWorkerCluster
17 | from pytest_celery import CeleryWorkerContainer
18 | from pytest_celery import defaults
19 | from pytest_celery import ping
20 |
21 |
22 | class GeventWorkerContainer(CeleryWorkerContainer):
23 | @classmethod
24 | def command(cls, *args: str) -> list[str]:
25 | return super().command("-P", "gevent", "-c", "1000")
26 |
27 |
28 | gevent_worker_image = build(
29 | path=".",
30 | dockerfile="Dockerfile",
31 | tag="pytest-celery/examples/worker_pool:gevent",
32 | buildargs=GeventWorkerContainer.buildargs(),
33 | )
34 |
35 |
36 | gevent_worker_container = container(
37 | image="{gevent_worker_image.id}",
38 | ports=fxtr("default_worker_ports"),
39 | environment=fxtr("default_worker_env"),
40 | network="{default_pytest_celery_network.name}",
41 | volumes={"{default_worker_volume.name}": defaults.DEFAULT_WORKER_VOLUME},
42 | wrapper_class=GeventWorkerContainer,
43 | timeout=defaults.DEFAULT_WORKER_CONTAINER_TIMEOUT,
44 | command=GeventWorkerContainer.command(),
45 | )
46 |
47 |
48 | @pytest.fixture
49 | def gevent_worker(gevent_worker_container: GeventWorkerContainer, celery_setup_app: Celery) -> CeleryTestWorker:
50 | worker = CeleryTestWorker(gevent_worker_container, app=celery_setup_app)
51 | yield worker
52 | worker.teardown()
53 |
54 |
55 | @pytest.fixture
56 | def celery_worker_cluster(gevent_worker: CeleryTestWorker) -> CeleryWorkerCluster:
57 | cluster = CeleryWorkerCluster(gevent_worker)
58 | yield cluster
59 | cluster.teardown()
60 |
61 |
62 | @pytest.fixture
63 | def default_worker_tasks(default_worker_tasks: set) -> set:
64 | default_worker_tasks.add(tasks)
65 | return default_worker_tasks
66 |
67 |
68 | # ----------------------------
69 |
70 |
71 | class TestGeventPool:
72 | def test_celery_banner(self, gevent_worker: CeleryTestWorker):
73 | gevent_worker.assert_log_exists("concurrency: 1000 (gevent)")
74 |
75 | def test_ping(self, celery_setup: CeleryTestSetup):
76 | sig: Signature = ping.s()
77 | res: AsyncResult = sig.apply_async()
78 | assert res.get(timeout=RESULT_TIMEOUT) == "pong"
79 |
80 | def test_celery_gevent_example(self, celery_setup: CeleryTestSetup):
81 | """Based on https://github.com/celery/celery/tree/main/examples/gevent"""
82 | LIST_OF_URLS = [
83 | "https://github.com/celery",
84 | "https://github.com/celery/celery",
85 | "https://github.com/celery/pytest-celery",
86 | ]
87 | group(tasks.urlopen.s(url) for url in LIST_OF_URLS).apply_async()
88 | celery_setup.worker.assert_log_does_not_exist("Exception for")
89 |
--------------------------------------------------------------------------------
/examples/worker_pool/tests/test_solo_pool.py:
--------------------------------------------------------------------------------
1 | from __future__ import annotations
2 |
3 | import pytest
4 | from celery.canvas import Signature
5 | from celery.result import AsyncResult
6 |
7 | from pytest_celery import RESULT_TIMEOUT
8 | from pytest_celery import CeleryTestSetup
9 | from pytest_celery import CeleryTestWorker
10 | from pytest_celery import CeleryWorkerContainer
11 | from pytest_celery import ping
12 |
13 |
14 | class SoloPoolWorker(CeleryWorkerContainer):
15 | @classmethod
16 | def command(cls, *args: str) -> list[str]:
17 | return super().command("-P", "solo")
18 |
19 |
20 | @pytest.fixture
21 | def default_worker_container_cls() -> type[CeleryWorkerContainer]:
22 | return SoloPoolWorker
23 |
24 |
25 | @pytest.fixture(scope="session")
26 | def default_worker_container_session_cls() -> type[CeleryWorkerContainer]:
27 | return SoloPoolWorker
28 |
29 |
30 | class TestSoloPool:
31 | def test_celery_banner(self, celery_worker: CeleryTestWorker):
32 | celery_worker.assert_log_exists("solo")
33 |
34 | def test_ping(self, celery_setup: CeleryTestSetup):
35 | sig: Signature = ping.s()
36 | res: AsyncResult = sig.apply_async()
37 | assert res.get(timeout=RESULT_TIMEOUT) == "pong"
38 |
--------------------------------------------------------------------------------
/src/pytest_celery/api/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/celery/pytest-celery/4d1a1dbcf340abab43d773b928f824a5a4ce7f08/src/pytest_celery/api/__init__.py
--------------------------------------------------------------------------------
/src/pytest_celery/api/backend.py:
--------------------------------------------------------------------------------
1 | """Backend components represents Celery's result backend instances.
2 |
3 | This module provides the base API for creating new backend components by
4 | defining the base classes for backend nodes and clusters.
5 | """
6 |
7 | from __future__ import annotations
8 |
9 | from pytest_celery.api.base import CeleryTestCluster
10 | from pytest_celery.api.base import CeleryTestNode
11 | from pytest_celery.defaults import DEFAULT_WORKER_ENV
12 |
13 |
14 | class CeleryTestBackend(CeleryTestNode):
15 | """This is specialized node type for handling celery backends nodes. It is
16 | used to encapsulate a backend instance.
17 |
18 | Responsibility Scope:
19 | Handling backend specific requirements and configuration.
20 | """
21 |
22 | @classmethod
23 | def default_config(cls) -> dict:
24 | """Default node configurations if not overridden by the user."""
25 | return {
26 | "url": DEFAULT_WORKER_ENV["CELERY_RESULT_BACKEND"],
27 | "host_url": DEFAULT_WORKER_ENV["CELERY_RESULT_BACKEND"],
28 | }
29 |
30 | def restart(self, reload_container: bool = True, force: bool = False) -> None:
31 | """Override restart method to update the app result backend with new
32 | container values."""
33 | super().restart(reload_container, force)
34 | if self.app:
35 | self.app.conf.update(
36 | result_backend=self.config()["host_url"],
37 | )
38 |
39 |
40 | class CeleryBackendCluster(CeleryTestCluster):
41 | """This is a specialized cluster type for handling celery backends. It is
42 | used to define which backend instances are available for the test.
43 |
44 | Responsibility Scope:
45 | Provude useful methods for managing a cluster of celery backends.
46 | """
47 |
48 | @classmethod
49 | def default_config(cls) -> dict:
50 | """Default cluster configurations if not overridden by the user."""
51 | return {
52 | "urls": [DEFAULT_WORKER_ENV["CELERY_RESULT_BACKEND"]],
53 | "host_urls": [DEFAULT_WORKER_ENV["CELERY_RESULT_BACKEND"]],
54 | }
55 |
--------------------------------------------------------------------------------
/src/pytest_celery/api/broker.py:
--------------------------------------------------------------------------------
1 | """Broker components represents Celery's broker instances.
2 |
3 | This module provides the base API for creating new broker components by
4 | defining the base classes for broker nodes and clusters.
5 | """
6 |
7 | from __future__ import annotations
8 |
9 | from pytest_celery.api.base import CeleryTestCluster
10 | from pytest_celery.api.base import CeleryTestNode
11 | from pytest_celery.defaults import DEFAULT_WORKER_ENV
12 |
13 |
14 | class CeleryTestBroker(CeleryTestNode):
15 | """This is specialized node type for handling celery brokers nodes. It is
16 | used to encapsulate a broker instance.
17 |
18 | Responsibility Scope:
19 | Handling broker specific requirements and configuration.
20 | """
21 |
22 | @classmethod
23 | def default_config(cls) -> dict:
24 | """Default node configurations if not overridden by the user."""
25 | return {
26 | "url": DEFAULT_WORKER_ENV["CELERY_BROKER_URL"],
27 | "host_url": DEFAULT_WORKER_ENV["CELERY_BROKER_URL"],
28 | }
29 |
30 | def restart(self, reload_container: bool = True, force: bool = False) -> None:
31 | """Override restart method to update the app broker url with new
32 | container values."""
33 | super().restart(reload_container, force)
34 | if self.app:
35 | self.app.conf.update(
36 | broker_url=self.config()["host_url"],
37 | )
38 |
39 |
40 | class CeleryBrokerCluster(CeleryTestCluster):
41 | """This is a specialized cluster type for handling celery brokers. It is
42 | used to define which broker instances are available for the test.
43 |
44 | Responsibility Scope:
45 | Provude useful methods for managing a cluster of celery brokers.
46 | """
47 |
48 | @classmethod
49 | def default_config(cls) -> dict:
50 | """Default cluster configurations if not overridden by the user."""
51 | return {
52 | "urls": [DEFAULT_WORKER_ENV["CELERY_BROKER_URL"]],
53 | "host_urls": [DEFAULT_WORKER_ENV["CELERY_BROKER_URL"]],
54 | }
55 |
--------------------------------------------------------------------------------
/src/pytest_celery/fixtures/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/celery/pytest-celery/4d1a1dbcf340abab43d773b928f824a5a4ce7f08/src/pytest_celery/fixtures/__init__.py
--------------------------------------------------------------------------------
/src/pytest_celery/fixtures/backend.py:
--------------------------------------------------------------------------------
1 | """Every backend component is added to the test matrix using the fixtures of
2 | this module.
3 |
4 | These fixtures will configure the test setup for all supported celery
5 | backends by default. Every backend will be executed as a separate test
6 | case, and the test will be executed for each supported celery backend.
7 |
8 | You may override these fixtures to customize the test setup for your
9 | specific needs.
10 | """
11 |
12 | # mypy: disable-error-code="misc"
13 |
14 | from __future__ import annotations
15 |
16 | import pytest
17 |
18 | from pytest_celery.api.backend import CeleryBackendCluster
19 | from pytest_celery.api.backend import CeleryTestBackend
20 | from pytest_celery.defaults import ALL_CELERY_BACKENDS
21 | from pytest_celery.defaults import CELERY_BACKEND_CLUSTER
22 |
23 |
24 | @pytest.fixture(params=ALL_CELERY_BACKENDS)
25 | def celery_backend(request: pytest.FixtureRequest) -> CeleryTestBackend: # type: ignore
26 | """Parameterized fixture for all supported celery backends. Responsible for
27 | tearing down the node.
28 |
29 | This fixture will add parametrization to the test function, so that
30 | the test will be executed for each supported celery backend.
31 | """
32 | backend: CeleryTestBackend = request.getfixturevalue(request.param)
33 | yield backend
34 | backend.teardown()
35 |
36 |
37 | @pytest.fixture
38 | def celery_backend_cluster(celery_backend: CeleryTestBackend) -> CeleryBackendCluster: # type: ignore
39 | """Defines the cluster of backend nodes for the test. Responsible for
40 | tearing down the cluster.
41 |
42 | To disable the cluster, override this fixture and return None.
43 |
44 | Args:
45 | celery_backend (CeleryTestBackend): Parameterized fixture for all supported celery backends.
46 |
47 | Returns:
48 | CeleryBackendCluster: Single node cluster for all supported celery backends.
49 | """
50 | cluster = CeleryBackendCluster(celery_backend) # type: ignore
51 | yield cluster
52 | cluster.teardown()
53 |
54 |
55 | @pytest.fixture
56 | def celery_backend_cluster_config(request: pytest.FixtureRequest) -> dict | None:
57 | """Attempts to compile the celery configuration from the cluster."""
58 | try:
59 | use_default_config = pytest.fail.Exception
60 | cluster: CeleryBackendCluster = request.getfixturevalue(CELERY_BACKEND_CLUSTER)
61 | return cluster.config() if cluster else None
62 | except use_default_config:
63 | return CeleryBackendCluster.default_config()
64 |
--------------------------------------------------------------------------------
/src/pytest_celery/fixtures/broker.py:
--------------------------------------------------------------------------------
1 | """Every broker component is added to the test matrix using the fixtures of
2 | this module.
3 |
4 | These fixtures will configure the test setup for all supported celery
5 | brokers by default. Every broker will be executed as a separate test
6 | case, and the test will be executed for each supported celery broker.
7 |
8 | You may override these fixtures to customize the test setup for your
9 | specific needs.
10 | """
11 |
12 | # mypy: disable-error-code="misc"
13 |
14 | from __future__ import annotations
15 |
16 | import pytest
17 |
18 | from pytest_celery.api.broker import CeleryBrokerCluster
19 | from pytest_celery.api.broker import CeleryTestBroker
20 | from pytest_celery.defaults import ALL_CELERY_BROKERS
21 | from pytest_celery.defaults import CELERY_BROKER_CLUSTER
22 |
23 |
24 | @pytest.fixture(params=ALL_CELERY_BROKERS)
25 | def celery_broker(request: pytest.FixtureRequest) -> CeleryTestBroker: # type: ignore
26 | """Parameterized fixture for all supported celery brokers. Responsible for
27 | tearing down the node.
28 |
29 | This fixture will add parametrization to the test function, so that
30 | the test will be executed for each supported celery broker.
31 | """
32 | broker: CeleryTestBroker = request.getfixturevalue(request.param)
33 | yield broker
34 | broker.teardown()
35 |
36 |
37 | @pytest.fixture
38 | def celery_broker_cluster(celery_broker: CeleryTestBroker) -> CeleryBrokerCluster: # type: ignore
39 | """Defines the cluster of broker nodes for the test. Responsible for
40 | tearing down the cluster.
41 |
42 | It is not recommended to disable the broker cluster, but it can be done by
43 | overriding this fixture and returning None.
44 |
45 | Args:
46 | celery_broker (CeleryTestBroker): Parameterized fixture for all supported celery brokers.
47 |
48 | Returns:
49 | CeleryBrokerCluster: Single node cluster for all supported celery brokers.
50 | """
51 | cluster = CeleryBrokerCluster(celery_broker) # type: ignore
52 | yield cluster
53 | cluster.teardown()
54 |
55 |
56 | @pytest.fixture
57 | def celery_broker_cluster_config(request: pytest.FixtureRequest) -> dict | None:
58 | """Attempts to compile the celery configuration from the cluster."""
59 | try:
60 | use_default_config = pytest.fail.Exception
61 | cluster: CeleryBrokerCluster = request.getfixturevalue(CELERY_BROKER_CLUSTER)
62 | return cluster.config() if cluster else None
63 | except use_default_config:
64 | return CeleryBrokerCluster.default_config()
65 |
--------------------------------------------------------------------------------
/src/pytest_celery/fixtures/worker.py:
--------------------------------------------------------------------------------
1 | """The :ref:`built-in-worker` is added to the test matrix using the fixtures of
2 | this module.
3 |
4 | These fixtures will configure the test setup for the built-in Celery
5 | worker by default.
6 |
7 | You may override these fixtures to customize the test setup for your
8 | specific needs.
9 | """
10 |
11 | # mypy: disable-error-code="misc"
12 |
13 | from __future__ import annotations
14 |
15 | import pytest
16 |
17 | from pytest_celery.api.worker import CeleryTestWorker
18 | from pytest_celery.api.worker import CeleryWorkerCluster
19 | from pytest_celery.defaults import ALL_CELERY_WORKERS
20 |
21 |
22 | @pytest.fixture(params=ALL_CELERY_WORKERS)
23 | def celery_worker(request: pytest.FixtureRequest) -> CeleryTestWorker: # type: ignore
24 | """Parameterized fixture for all supported celery workers. Responsible for
25 | tearing down the node.
26 |
27 | This fixture will add parametrization to the test function, so that
28 | the test will be executed for each supported celery worker.
29 | """
30 |
31 | worker: CeleryTestWorker = request.getfixturevalue(request.param)
32 | yield worker
33 | worker.teardown()
34 |
35 |
36 | @pytest.fixture
37 | def celery_worker_cluster(celery_worker: CeleryTestWorker) -> CeleryWorkerCluster: # type: ignore
38 | """Defines the cluster of worker nodes for the test. Responsible for
39 | tearing down the cluster.
40 |
41 | To disable the cluster, override this fixture and return None.
42 |
43 | Args:
44 | celery_worker (CeleryTestWorker): Parameterized fixture for all supported celery workers.
45 |
46 | Returns:
47 | CeleryWorkerCluster: Single node cluster for all supported celery workers.
48 | """
49 | cluster = CeleryWorkerCluster(celery_worker) # type: ignore
50 | yield cluster
51 | cluster.teardown()
52 |
53 |
54 | @pytest.fixture
55 | def celery_worker_cluster_config(celery_broker_cluster_config: dict, celery_backend_cluster_config: dict) -> dict:
56 | """Combine the broker and backend cluster configurations.
57 |
58 | Additional configuration can be added.
59 | """
60 | return {
61 | "celery_broker_cluster_config": celery_broker_cluster_config,
62 | "celery_backend_cluster_config": celery_backend_cluster_config,
63 | }
64 |
--------------------------------------------------------------------------------
/src/pytest_celery/plugin.py:
--------------------------------------------------------------------------------
1 | """Pytest-celery entry point."""
2 |
3 | # Backward compatibility with pytest-celery < 1.0.0
4 | from celery.contrib.pytest import * # noqa
5 |
6 | # pytest-celery >= 1.0.0 infrastructure
7 | from pytest_celery import * # noqa
8 |
--------------------------------------------------------------------------------
/src/pytest_celery/vendors/__init__.py:
--------------------------------------------------------------------------------
1 | """See :ref:`vendors`."""
2 |
3 |
4 | def _is_vendor_installed(vendor_name: str) -> bool:
5 | """Check if a vendor is installed.
6 |
7 | Args:
8 | vendor_name (str): Vendor package name.
9 |
10 | Returns:
11 | bool: True if the vendor is installed, False otherwise.
12 | """
13 |
14 | try:
15 | container_module = f"pytest_celery.vendors.{vendor_name}.container"
16 | __import__(container_module)
17 | return True
18 | except ImportError:
19 | return False
20 |
--------------------------------------------------------------------------------
/src/pytest_celery/vendors/localstack/__init__.py:
--------------------------------------------------------------------------------
1 | """The pytest-celery plugin provides a set of built-in components called
2 | :ref:`vendors`.
3 |
4 | This module is part of the Localstack Broker vendor.
5 | """
6 |
--------------------------------------------------------------------------------
/src/pytest_celery/vendors/localstack/api.py:
--------------------------------------------------------------------------------
1 | """The pytest-celery plugin provides a set of built-in components called
2 | :ref:`vendors`.
3 |
4 | This module is part of the Localstack Broker vendor.
5 | """
6 |
7 | from __future__ import annotations
8 |
9 | from pytest_celery.api.broker import CeleryTestBroker
10 |
11 |
12 | class LocalstackTestBroker(CeleryTestBroker):
13 | pass
14 |
--------------------------------------------------------------------------------
/src/pytest_celery/vendors/localstack/container.py:
--------------------------------------------------------------------------------
1 | """The pytest-celery plugin provides a set of built-in components called
2 | :ref:`vendors`.
3 |
4 | This module is part of the Localstack Broker vendor.
5 | """
6 |
7 | from __future__ import annotations
8 |
9 | from kombu import Connection
10 |
11 | from pytest_celery.api.container import CeleryTestContainer
12 | from pytest_celery.vendors.localstack.defaults import LOCALSTACK_ENV
13 | from pytest_celery.vendors.localstack.defaults import LOCALSTACK_IMAGE
14 | from pytest_celery.vendors.localstack.defaults import LOCALSTACK_PORTS
15 | from pytest_celery.vendors.localstack.defaults import LOCALSTACK_PREFIX
16 |
17 |
18 | class LocalstackContainer(CeleryTestContainer):
19 | """This class manages the lifecycle of a Localstack container."""
20 |
21 | @property
22 | def client(self) -> Connection:
23 | client = Connection(
24 | self.celeryconfig["host_url"],
25 | port=self.celeryconfig["port"],
26 | )
27 | return client
28 |
29 | @property
30 | def celeryconfig(self) -> dict:
31 | return {
32 | "url": self.url,
33 | "host_url": self.host_url,
34 | "hostname": self.hostname,
35 | "port": self.port,
36 | }
37 |
38 | @property
39 | def url(self) -> str:
40 | return f"{self.prefix()}{self.hostname}:4566"
41 |
42 | @property
43 | def host_url(self) -> str:
44 | return f"{self.prefix()}localhost:{self.port}"
45 |
46 | @property
47 | def hostname(self) -> str:
48 | return self.attrs["Config"]["Hostname"]
49 |
50 | @property
51 | def port(self) -> int:
52 | return self._wait_port("4566/tcp")
53 |
54 | @classmethod
55 | def version(cls) -> str:
56 | return cls.image().split("/")[-1]
57 |
58 | @classmethod
59 | def initial_env(cls) -> dict:
60 | return LOCALSTACK_ENV
61 |
62 | @classmethod
63 | def image(cls) -> str:
64 | return LOCALSTACK_IMAGE
65 |
66 | @classmethod
67 | def ports(cls) -> dict:
68 | return LOCALSTACK_PORTS
69 |
70 | @classmethod
71 | def prefix(cls) -> str:
72 | return LOCALSTACK_PREFIX
73 |
74 | @property
75 | def ready_prompt(self) -> str | None:
76 | return "Ready."
77 |
--------------------------------------------------------------------------------
/src/pytest_celery/vendors/localstack/defaults.py:
--------------------------------------------------------------------------------
1 | """The pytest-celery plugin provides a set of built-in components called
2 | :ref:`vendors`.
3 |
4 | This module is part of the Localstack Broker vendor.
5 | """
6 |
7 | CELERY_LOCALSTACK_BROKER = "celery_localstack_broker"
8 | DEFAULT_LOCALSTACK_BROKER = "default_localstack_broker"
9 | LOCALSTACK_IMAGE = "localstack/localstack"
10 | LOCALSTACK_PORTS = {"4566/tcp": None}
11 | LOCALSTACK_CREDS: dict = {
12 | "AWS_DEFAULT_REGION": "us-east-1",
13 | "AWS_ACCESS_KEY_ID": "test",
14 | "AWS_SECRET_ACCESS_KEY": "test",
15 | }
16 | LOCALSTACK_ENV: dict = {
17 | **LOCALSTACK_CREDS,
18 | }
19 | LOCALSTACK_CONTAINER_TIMEOUT = 60
20 | LOCALSTACK_PREFIX = "sqs://"
21 |
--------------------------------------------------------------------------------
/src/pytest_celery/vendors/localstack/fixtures.py:
--------------------------------------------------------------------------------
1 | """The pytest-celery plugin provides a set of built-in components called
2 | :ref:`vendors`.
3 |
4 | This module is part of the Localstack Broker vendor.
5 | """
6 |
7 | # mypy: disable-error-code="misc"
8 |
9 | from __future__ import annotations
10 |
11 | import pytest
12 | from pytest_docker_tools import container
13 | from pytest_docker_tools import fxtr
14 |
15 | from pytest_celery.vendors.localstack.api import LocalstackTestBroker
16 | from pytest_celery.vendors.localstack.container import LocalstackContainer
17 | from pytest_celery.vendors.localstack.defaults import LOCALSTACK_CONTAINER_TIMEOUT
18 |
19 |
20 | @pytest.fixture
21 | def celery_localstack_broker(default_localstack_broker: LocalstackContainer) -> LocalstackTestBroker:
22 | """Creates a LocalstackTestBroker instance. Responsible for tearing down
23 | the node.
24 |
25 | Args:
26 | default_localstack_broker (LocalstackContainer): Instantiated LocalstackContainer.
27 | """
28 | broker = LocalstackTestBroker(default_localstack_broker)
29 | yield broker
30 | broker.teardown()
31 |
32 |
33 | @pytest.fixture
34 | def default_localstack_broker_cls() -> type[LocalstackContainer]:
35 | """Default Localstack broker container class. Override to apply custom
36 | configuration globally.
37 |
38 | See also: :ref:`vendor-class`.
39 |
40 | Returns:
41 | type[LocalstackContainer]: API for managing the vendor's container.
42 | """
43 | return LocalstackContainer
44 |
45 |
46 | default_localstack_broker = container(
47 | image="{default_localstack_broker_image}",
48 | ports=fxtr("default_localstack_broker_ports"),
49 | environment=fxtr("default_localstack_broker_env"),
50 | network="{default_pytest_celery_network.name}",
51 | wrapper_class=LocalstackContainer,
52 | timeout=LOCALSTACK_CONTAINER_TIMEOUT,
53 | )
54 |
55 |
56 | @pytest.fixture
57 | def default_localstack_broker_env(default_localstack_broker_cls: type[LocalstackContainer]) -> dict:
58 | """Environment variables for this vendor.
59 |
60 | Args:
61 | default_localstack_broker_cls (type[LocalstackContainer]): See also: :ref:`vendor-class`.
62 |
63 | Returns:
64 | dict: Items to pass to the container's environment.
65 | """
66 | return default_localstack_broker_cls.initial_env()
67 |
68 |
69 | @pytest.fixture
70 | def default_localstack_broker_image(default_localstack_broker_cls: type[LocalstackContainer]) -> str:
71 | """Sets the image name for this vendor.
72 |
73 | Args:
74 | default_localstack_broker_cls (type[LocalstackContainer]): See also: :ref:`vendor-class`.
75 |
76 | Returns:
77 | str: Docker image name.
78 | """
79 | return default_localstack_broker_cls.image()
80 |
81 |
82 | @pytest.fixture
83 | def default_localstack_broker_ports(default_localstack_broker_cls: type[LocalstackContainer]) -> dict:
84 | """Port bindings for this vendor.
85 |
86 | Args:
87 | default_localstack_broker_cls (type[LocalstackContainer]): See also: :ref:`vendor-class`.
88 |
89 | Returns:
90 | dict: Port bindings.
91 | """
92 | return default_localstack_broker_cls.ports()
93 |
--------------------------------------------------------------------------------
/src/pytest_celery/vendors/memcached/__init__.py:
--------------------------------------------------------------------------------
1 | """The pytest-celery plugin provides a set of built-in components called
2 | :ref:`vendors`.
3 |
4 | This module is part of the Memcached Backend vendor.
5 | """
6 |
--------------------------------------------------------------------------------
/src/pytest_celery/vendors/memcached/api.py:
--------------------------------------------------------------------------------
1 | """The pytest-celery plugin provides a set of built-in components called
2 | :ref:`vendors`.
3 |
4 | This module is part of the Memcached Backend vendor.
5 | """
6 |
7 | from __future__ import annotations
8 |
9 | from pytest_celery.api.backend import CeleryTestBackend
10 |
11 |
12 | class MemcachedTestBackend(CeleryTestBackend):
13 | pass
14 |
--------------------------------------------------------------------------------
/src/pytest_celery/vendors/memcached/container.py:
--------------------------------------------------------------------------------
1 | """The pytest-celery plugin provides a set of built-in components called
2 | :ref:`vendors`.
3 |
4 | This module is part of the Memcached Backend vendor.
5 | """
6 |
7 | from __future__ import annotations
8 |
9 | import memcache
10 |
11 | from pytest_celery.api.container import CeleryTestContainer
12 | from pytest_celery.vendors.memcached.defaults import MEMCACHED_ENV
13 | from pytest_celery.vendors.memcached.defaults import MEMCACHED_IMAGE
14 | from pytest_celery.vendors.memcached.defaults import MEMCACHED_PORTS
15 | from pytest_celery.vendors.memcached.defaults import MEMCACHED_PREFIX
16 |
17 |
18 | class MemcachedContainer(CeleryTestContainer):
19 | """This class manages the lifecycle of a Memcached container."""
20 |
21 | @property
22 | def client(self) -> memcache.Client:
23 | conf = self.celeryconfig
24 | servers = [f"{conf['host_url'][:-1].split('://')[-1]}"]
25 | client = memcache.Client(servers)
26 | return client
27 |
28 | @property
29 | def celeryconfig(self) -> dict:
30 | return {
31 | "url": self.url,
32 | "host_url": self.host_url,
33 | "hostname": self.hostname,
34 | "port": self.port,
35 | }
36 |
37 | @property
38 | def url(self) -> str:
39 | return f"{self.prefix()}{self.hostname}/"
40 |
41 | @property
42 | def host_url(self) -> str:
43 | return f"{self.prefix()}localhost:{self.port}/"
44 |
45 | @property
46 | def hostname(self) -> str:
47 | return self.attrs["Config"]["Hostname"]
48 |
49 | @property
50 | def port(self) -> int:
51 | return self._wait_port("11211/tcp")
52 |
53 | @classmethod
54 | def version(cls) -> str:
55 | return cls.image().split(":")[-1]
56 |
57 | @classmethod
58 | def initial_env(cls) -> dict:
59 | return MEMCACHED_ENV
60 |
61 | @classmethod
62 | def image(cls) -> str:
63 | return MEMCACHED_IMAGE
64 |
65 | @classmethod
66 | def ports(cls) -> dict:
67 | return MEMCACHED_PORTS
68 |
69 | @classmethod
70 | def prefix(cls) -> str:
71 | return MEMCACHED_PREFIX
72 |
--------------------------------------------------------------------------------
/src/pytest_celery/vendors/memcached/defaults.py:
--------------------------------------------------------------------------------
1 | """The pytest-celery plugin provides a set of built-in components called
2 | :ref:`vendors`.
3 |
4 | This module is part of the Memcached Backend vendor.
5 | """
6 |
7 | CELERY_MEMCACHED_BACKEND = "celery_memcached_backend"
8 | DEFAULT_MEMCACHED_BACKEND = "default_memcached_backend"
9 | MEMCACHED_IMAGE = "memcached:latest"
10 | MEMCACHED_PORTS = {"11211/tcp": None}
11 | MEMCACHED_ENV: dict = {}
12 | MEMCACHED_CONTAINER_TIMEOUT = 60
13 | MEMCACHED_PREFIX = "cache+memcached://"
14 |
--------------------------------------------------------------------------------
/src/pytest_celery/vendors/memcached/fixtures.py:
--------------------------------------------------------------------------------
1 | """The pytest-celery plugin provides a set of built-in components called
2 | :ref:`vendors`.
3 |
4 | This module is part of the Memcached Backend vendor.
5 | """
6 |
7 | # mypy: disable-error-code="misc"
8 |
9 | from __future__ import annotations
10 |
11 | import pytest
12 | from pytest_docker_tools import container
13 | from pytest_docker_tools import fxtr
14 |
15 | from pytest_celery.vendors.memcached.api import MemcachedTestBackend
16 | from pytest_celery.vendors.memcached.container import MemcachedContainer
17 | from pytest_celery.vendors.memcached.defaults import MEMCACHED_CONTAINER_TIMEOUT
18 |
19 |
20 | @pytest.fixture
21 | def celery_memcached_backend(default_memcached_backend: MemcachedContainer) -> MemcachedTestBackend:
22 | """Creates a MemcachedTestBackend instance. Responsible for tearing down
23 | the node.
24 |
25 | Args:
26 | default_memcached_backend (MemcachedContainer): Instantiated MemcachedContainer.
27 | """
28 | backend = MemcachedTestBackend(default_memcached_backend)
29 | yield backend
30 | backend.teardown()
31 |
32 |
33 | @pytest.fixture
34 | def default_memcached_backend_cls() -> type[MemcachedContainer]:
35 | """Default Memcached backend container class. Override to apply custom
36 | configuration globally.
37 |
38 | See also: :ref:`vendor-class`.
39 |
40 | Returns:
41 | type[MemcachedContainer]: API for managing the vendor's container.
42 | """
43 | return MemcachedContainer
44 |
45 |
46 | default_memcached_backend = container(
47 | image="{default_memcached_backend_image}",
48 | ports=fxtr("default_memcached_backend_ports"),
49 | environment=fxtr("default_memcached_backend_env"),
50 | network="{default_pytest_celery_network.name}",
51 | wrapper_class=MemcachedContainer,
52 | timeout=MEMCACHED_CONTAINER_TIMEOUT,
53 | )
54 |
55 |
56 | @pytest.fixture
57 | def default_memcached_backend_env(default_memcached_backend_cls: type[MemcachedContainer]) -> dict:
58 | """Environment variables for this vendor.
59 |
60 | Args:
61 | default_memcached_backend_cls (type[MemcachedContainer]): See also: :ref:`vendor-class`.
62 |
63 | Returns:
64 | dict: Items to pass to the container's environment.
65 | """
66 | return default_memcached_backend_cls.initial_env()
67 |
68 |
69 | @pytest.fixture
70 | def default_memcached_backend_image(default_memcached_backend_cls: type[MemcachedContainer]) -> str:
71 | """Docker image for this vendor.
72 |
73 | Args:
74 | default_memcached_backend_cls (type[MemcachedContainer]): See also: :ref:`vendor-class`.
75 |
76 | Returns:
77 | str: Docker image name.
78 | """
79 | return default_memcached_backend_cls.image()
80 |
81 |
82 | @pytest.fixture
83 | def default_memcached_backend_ports(default_memcached_backend_cls: type[MemcachedContainer]) -> dict:
84 | """Port bindings for this vendor.
85 |
86 | Args:
87 | default_memcached_backend_cls (type[MemcachedContainer]): See also: :ref:`vendor-class`.
88 |
89 | Returns:
90 | dict: Port bindings.
91 | """
92 | return default_memcached_backend_cls.ports()
93 |
--------------------------------------------------------------------------------
/src/pytest_celery/vendors/rabbitmq/__init__.py:
--------------------------------------------------------------------------------
1 | """The pytest-celery plugin provides a set of built-in components called
2 | :ref:`vendors`.
3 |
4 | This module is part of the RabbitMQ Broker vendor.
5 | """
6 |
--------------------------------------------------------------------------------
/src/pytest_celery/vendors/rabbitmq/api.py:
--------------------------------------------------------------------------------
1 | """The pytest-celery plugin provides a set of built-in components called
2 | :ref:`vendors`.
3 |
4 | This module is part of the RabbitMQ Broker vendor.
5 | """
6 |
7 | from __future__ import annotations
8 |
9 | from pytest_celery.api.broker import CeleryTestBroker
10 |
11 |
12 | class RabbitMQTestBroker(CeleryTestBroker):
13 | pass
14 |
--------------------------------------------------------------------------------
/src/pytest_celery/vendors/rabbitmq/container.py:
--------------------------------------------------------------------------------
1 | """The pytest-celery plugin provides a set of built-in components called
2 | :ref:`vendors`.
3 |
4 | This module is part of the RabbitMQ Broker vendor.
5 | """
6 |
7 | from __future__ import annotations
8 |
9 | from kombu import Connection
10 |
11 | from pytest_celery.api.container import CeleryTestContainer
12 | from pytest_celery.vendors.rabbitmq.defaults import RABBITMQ_ENV
13 | from pytest_celery.vendors.rabbitmq.defaults import RABBITMQ_IMAGE
14 | from pytest_celery.vendors.rabbitmq.defaults import RABBITMQ_PORTS
15 | from pytest_celery.vendors.rabbitmq.defaults import RABBITMQ_PREFIX
16 |
17 |
18 | class RabbitMQContainer(CeleryTestContainer):
19 | """This class manages the lifecycle of a RabbitMQ container."""
20 |
21 | @property
22 | def client(self) -> Connection:
23 | client = Connection(
24 | self.celeryconfig["host_url"],
25 | port=self.celeryconfig["port"],
26 | )
27 | return client
28 |
29 | @property
30 | def celeryconfig(self) -> dict:
31 | return {
32 | "url": self.url,
33 | "host_url": self.host_url,
34 | "hostname": self.hostname,
35 | "port": self.port,
36 | "vhost": self.vhost,
37 | }
38 |
39 | @property
40 | def url(self) -> str:
41 | return f"{self.prefix()}{self.hostname}/{self.vhost}"
42 |
43 | @property
44 | def host_url(self) -> str:
45 | return f"{self.prefix()}localhost:{self.port}/{self.vhost}"
46 |
47 | @property
48 | def hostname(self) -> str:
49 | return self.attrs["Config"]["Hostname"]
50 |
51 | @property
52 | def port(self) -> int:
53 | return self._wait_port("5672/tcp")
54 |
55 | @property
56 | def vhost(self) -> str:
57 | return "/"
58 |
59 | @classmethod
60 | def version(cls) -> str:
61 | return cls.image().split(":")[-1]
62 |
63 | @classmethod
64 | def initial_env(cls) -> dict:
65 | return RABBITMQ_ENV
66 |
67 | @classmethod
68 | def image(cls) -> str:
69 | return RABBITMQ_IMAGE
70 |
71 | @classmethod
72 | def ports(cls) -> dict:
73 | return RABBITMQ_PORTS
74 |
75 | @classmethod
76 | def prefix(cls) -> str:
77 | return RABBITMQ_PREFIX
78 |
79 | @property
80 | def ready_prompt(self) -> str | None:
81 | return "Server startup complete"
82 |
--------------------------------------------------------------------------------
/src/pytest_celery/vendors/rabbitmq/defaults.py:
--------------------------------------------------------------------------------
1 | """The pytest-celery plugin provides a set of built-in components called
2 | :ref:`vendors`.
3 |
4 | This module is part of the RabbitMQ Broker vendor.
5 | """
6 |
7 | CELERY_RABBITMQ_BROKER = "celery_rabbitmq_broker"
8 | DEFAULT_RABBITMQ_BROKER = "default_rabbitmq_broker"
9 | RABBITMQ_IMAGE = "rabbitmq:latest"
10 | RABBITMQ_PORTS = {"5672/tcp": None}
11 | RABBITMQ_ENV: dict = {}
12 | RABBITMQ_CONTAINER_TIMEOUT = 120
13 | RABBITMQ_PREFIX = "amqp://"
14 |
--------------------------------------------------------------------------------
/src/pytest_celery/vendors/rabbitmq/fixtures.py:
--------------------------------------------------------------------------------
1 | """The pytest-celery plugin provides a set of built-in components called
2 | :ref:`vendors`.
3 |
4 | This module is part of the RabbitMQ Broker vendor.
5 | """
6 |
7 | # mypy: disable-error-code="misc"
8 |
9 | from __future__ import annotations
10 |
11 | import pytest
12 | from pytest_docker_tools import container
13 | from pytest_docker_tools import fxtr
14 |
15 | from pytest_celery.vendors.rabbitmq.api import RabbitMQTestBroker
16 | from pytest_celery.vendors.rabbitmq.container import RabbitMQContainer
17 | from pytest_celery.vendors.rabbitmq.defaults import RABBITMQ_CONTAINER_TIMEOUT
18 |
19 |
20 | @pytest.fixture
21 | def celery_rabbitmq_broker(default_rabbitmq_broker: RabbitMQContainer) -> RabbitMQTestBroker:
22 | """Creates a RabbitMQTestBroker instance. Responsible for tearing down the
23 | node.
24 |
25 | Args:
26 | default_rabbitmq_broker (RabbitMQContainer): Instantiated RabbitMQContainer.
27 | """
28 | broker = RabbitMQTestBroker(default_rabbitmq_broker)
29 | yield broker
30 | broker.teardown()
31 |
32 |
33 | @pytest.fixture
34 | def default_rabbitmq_broker_cls() -> type[RabbitMQContainer]:
35 | """Default RabbitMQ broker container class. Override to apply custom
36 | configuration globally.
37 |
38 | See also: :ref:`vendor-class`.
39 |
40 | Returns:
41 | type[RabbitMQContainer]: API for managing the vendor's container.
42 | """
43 | return RabbitMQContainer
44 |
45 |
46 | default_rabbitmq_broker = container(
47 | image="{default_rabbitmq_broker_image}",
48 | ports=fxtr("default_rabbitmq_broker_ports"),
49 | environment=fxtr("default_rabbitmq_broker_env"),
50 | network="{default_pytest_celery_network.name}",
51 | wrapper_class=RabbitMQContainer,
52 | timeout=RABBITMQ_CONTAINER_TIMEOUT,
53 | )
54 |
55 |
56 | @pytest.fixture
57 | def default_rabbitmq_broker_env(default_rabbitmq_broker_cls: type[RabbitMQContainer]) -> dict:
58 | """Environment variables for this vendor.
59 |
60 | Args:
61 | default_rabbitmq_broker_cls (type[RabbitMQContainer]): See also: :ref:`vendor-class`.
62 |
63 | Returns:
64 | dict: Items to pass to the container's environment.
65 | """
66 | return default_rabbitmq_broker_cls.initial_env()
67 |
68 |
69 | @pytest.fixture
70 | def default_rabbitmq_broker_image(default_rabbitmq_broker_cls: type[RabbitMQContainer]) -> str:
71 | """Sets the image name for this vendor.
72 |
73 | Args:
74 | default_rabbitmq_broker_cls (type[RabbitMQContainer]): See also: :ref:`vendor-class`.
75 |
76 | Returns:
77 | str: Docker image name.
78 | """
79 | return default_rabbitmq_broker_cls.image()
80 |
81 |
82 | @pytest.fixture
83 | def default_rabbitmq_broker_ports(default_rabbitmq_broker_cls: type[RabbitMQContainer]) -> dict:
84 | """Port bindings for this vendor.
85 |
86 | Args:
87 | default_rabbitmq_broker_cls (type[RabbitMQContainer]): See also: :ref:`vendor-class`.
88 |
89 | Returns:
90 | dict: Port bindings.
91 | """
92 | return default_rabbitmq_broker_cls.ports()
93 |
--------------------------------------------------------------------------------
/src/pytest_celery/vendors/redis/__init__.py:
--------------------------------------------------------------------------------
1 | """The pytest-celery plugin provides a set of built-in components called
2 | :ref:`vendors`.
3 |
4 | This module is part of the Redis vendor.
5 | """
6 |
--------------------------------------------------------------------------------
/src/pytest_celery/vendors/redis/backend/__init__.py:
--------------------------------------------------------------------------------
1 | """The pytest-celery plugin provides a set of built-in components called
2 | :ref:`vendors`.
3 |
4 | This module is part of the Redis Backend vendor.
5 | """
6 |
--------------------------------------------------------------------------------
/src/pytest_celery/vendors/redis/backend/api.py:
--------------------------------------------------------------------------------
1 | """The pytest-celery plugin provides a set of built-in components called
2 | :ref:`vendors`.
3 |
4 | This module is part of the Redis Backend vendor.
5 | """
6 |
7 | from __future__ import annotations
8 |
9 | import gc
10 |
11 | from celery.result import AsyncResult
12 |
13 | from pytest_celery.api.backend import CeleryTestBackend
14 |
15 |
16 | class RedisTestBackend(CeleryTestBackend):
17 | def teardown(self) -> None:
18 | """When a test that has a AsyncResult object is finished there's a race
19 | condition between the AsyncResult object and the Redis container.
20 |
21 | The AsyncResult object tries to release the connection but the
22 | Redis container has already exited.
23 | """
24 | # First, force a garbage collection to clean up unreachable objects
25 | gc.collect()
26 |
27 | # Next, find all live AsyncResult objects and clean them up
28 | async_results = [obj for obj in gc.get_objects() if isinstance(obj, AsyncResult)]
29 |
30 | for async_result in async_results:
31 | try:
32 | # Remove the backend reference to prevent interaction with Redis
33 | async_result.backend = None
34 | except Exception:
35 | pass
36 |
37 | super().teardown()
38 |
--------------------------------------------------------------------------------
/src/pytest_celery/vendors/redis/backend/defaults.py:
--------------------------------------------------------------------------------
1 | """The pytest-celery plugin provides a set of built-in components called
2 | :ref:`vendors`.
3 |
4 | This module is part of the Redis Backend vendor.
5 | """
6 |
7 | CELERY_REDIS_BACKEND = "celery_redis_backend"
8 | DEFAULT_REDIS_BACKEND = "default_redis_backend"
9 |
--------------------------------------------------------------------------------
/src/pytest_celery/vendors/redis/broker/__init__.py:
--------------------------------------------------------------------------------
1 | """The pytest-celery plugin provides a set of built-in components called
2 | :ref:`vendors`.
3 |
4 | This module is part of the Redis Broker vendor.
5 | """
6 |
--------------------------------------------------------------------------------
/src/pytest_celery/vendors/redis/broker/api.py:
--------------------------------------------------------------------------------
1 | """The pytest-celery plugin provides a set of built-in components called
2 | :ref:`vendors`.
3 |
4 | This module is part of the Redis Broker vendor.
5 | """
6 |
7 | from __future__ import annotations
8 |
9 | from pytest_celery.api.broker import CeleryTestBroker
10 |
11 |
12 | class RedisTestBroker(CeleryTestBroker):
13 | pass
14 |
--------------------------------------------------------------------------------
/src/pytest_celery/vendors/redis/broker/defaults.py:
--------------------------------------------------------------------------------
1 | """The pytest-celery plugin provides a set of built-in components called
2 | :ref:`vendors`.
3 |
4 | This module is part of the Redis Broker vendor.
5 | """
6 |
7 | CELERY_REDIS_BROKER = "celery_redis_broker"
8 | DEFAULT_REDIS_BROKER = "default_redis_broker"
9 |
--------------------------------------------------------------------------------
/src/pytest_celery/vendors/redis/container.py:
--------------------------------------------------------------------------------
1 | """The pytest-celery plugin provides a set of built-in components called
2 | :ref:`vendors`.
3 |
4 | This module is part of the Redis vendor.
5 | """
6 |
7 | from __future__ import annotations
8 |
9 | from redis import StrictRedis as Redis
10 |
11 | from pytest_celery.api.container import CeleryTestContainer
12 | from pytest_celery.vendors.redis.defaults import REDIS_ENV
13 | from pytest_celery.vendors.redis.defaults import REDIS_IMAGE
14 | from pytest_celery.vendors.redis.defaults import REDIS_PORTS
15 | from pytest_celery.vendors.redis.defaults import REDIS_PREFIX
16 |
17 |
18 | class RedisContainer(CeleryTestContainer):
19 | """This class manages the lifecycle of a Redis container."""
20 |
21 | @property
22 | def client(self) -> Redis | None:
23 | client = Redis.from_url(
24 | self.celeryconfig["host_url"],
25 | decode_responses=True,
26 | )
27 | return client
28 |
29 | @property
30 | def celeryconfig(self) -> dict:
31 | return {
32 | "url": self.url,
33 | "host_url": self.host_url,
34 | "hostname": self.hostname,
35 | "port": self.port,
36 | "vhost": self.vhost,
37 | }
38 |
39 | @classmethod
40 | def command(
41 | cls,
42 | *args: str,
43 | debugpy: bool = False,
44 | wait_for_client: bool = True,
45 | **kwargs: dict,
46 | ) -> list[str]:
47 | return [
48 | "redis-server",
49 | "--save",
50 | "",
51 | "--appendonly",
52 | "no",
53 | "--maxmemory-policy",
54 | "noeviction",
55 | "--protected-mode",
56 | "no",
57 | *args,
58 | ]
59 |
60 | @property
61 | def url(self) -> str:
62 | return f"{self.prefix()}{self.hostname}/{self.vhost}"
63 |
64 | @property
65 | def host_url(self) -> str:
66 | return f"{self.prefix()}localhost:{self.port}/{self.vhost}"
67 |
68 | @property
69 | def hostname(self) -> str:
70 | return self.attrs["Config"]["Hostname"]
71 |
72 | @property
73 | def port(self) -> int:
74 | return self._wait_port("6379/tcp")
75 |
76 | @property
77 | def vhost(self) -> str:
78 | return "0"
79 |
80 | @classmethod
81 | def version(cls) -> str:
82 | return cls.image().split(":")[-1]
83 |
84 | @classmethod
85 | def initial_env(cls) -> dict:
86 | return REDIS_ENV
87 |
88 | @classmethod
89 | def image(cls) -> str:
90 | return REDIS_IMAGE
91 |
92 | @classmethod
93 | def ports(cls) -> dict:
94 | return REDIS_PORTS
95 |
96 | @classmethod
97 | def prefix(cls) -> str:
98 | return REDIS_PREFIX
99 |
100 | @property
101 | def ready_prompt(self) -> str | None:
102 | return "Ready to accept connections"
103 |
--------------------------------------------------------------------------------
/src/pytest_celery/vendors/redis/defaults.py:
--------------------------------------------------------------------------------
1 | """The pytest-celery plugin provides a set of built-in components called
2 | :ref:`vendors`.
3 |
4 | This module is part of the Redis vendor.
5 | """
6 |
7 | REDIS_IMAGE = "redis:latest"
8 | REDIS_PORTS = {"6379/tcp": None}
9 | REDIS_ENV: dict = {}
10 | REDIS_CONTAINER_TIMEOUT = 60
11 | REDIS_PREFIX = "redis://"
12 |
--------------------------------------------------------------------------------
/src/pytest_celery/vendors/worker/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM python:3.10-slim-buster
2 |
3 | # Create a user to run the worker
4 | RUN adduser --disabled-password --gecos "" test_user
5 |
6 | # Install system dependencies
7 | RUN apt-get update && apt-get install -y build-essential \
8 | git \
9 | wget \
10 | make \
11 | curl \
12 | apt-utils \
13 | debconf \
14 | lsb-release \
15 | libmemcached-dev \
16 | libffi-dev \
17 | ca-certificates \
18 | pypy3 \
19 | pypy3-lib \
20 | sudo
21 |
22 | # Set arguments
23 | ARG CELERY_VERSION=""
24 | ARG CELERY_LOG_LEVEL=INFO
25 | ARG CELERY_WORKER_NAME=celery_test_worker
26 | ARG CELERY_WORKER_QUEUE=celery
27 | ENV WORKER_VERSION=$CELERY_VERSION
28 | ENV LOG_LEVEL=$CELERY_LOG_LEVEL
29 | ENV WORKER_NAME=$CELERY_WORKER_NAME
30 | ENV WORKER_QUEUE=$CELERY_WORKER_QUEUE
31 |
32 | ENV PYTHONUNBUFFERED=1
33 | ENV PYTHONDONTWRITEBYTECODE=1
34 |
35 | EXPOSE 5678
36 |
37 | # Install Python dependencies
38 | RUN pip install --no-cache-dir --upgrade \
39 | pip \
40 | celery[redis,pymemcache,gevent]${WORKER_VERSION:+==$WORKER_VERSION} \
41 | pytest-celery[sqs]@git+https://github.com/celery/pytest-celery.git
42 |
43 | # The workdir must be /app
44 | WORKDIR /app
45 |
46 | COPY content/ .
47 |
48 | # Switch to the test_user
49 | USER test_user
50 |
51 | # Start the celery worker
52 | CMD celery -A app worker --loglevel=$LOG_LEVEL -n $WORKER_NAME@%h -Q $WORKER_QUEUE
53 |
--------------------------------------------------------------------------------
/src/pytest_celery/vendors/worker/__init__.py:
--------------------------------------------------------------------------------
1 | """The pytest-celery plugin provides a set of built-in components called
2 | :ref:`vendors`.
3 |
4 | This module is part of the :ref:`built-in-worker` vendor.
5 | """
6 |
--------------------------------------------------------------------------------
/src/pytest_celery/vendors/worker/content/__init__.py:
--------------------------------------------------------------------------------
1 | """The pytest-celery plugin provides a set of built-in components called
2 | :ref:`vendors`.
3 |
4 | This module is part of the :ref:`built-in-worker` vendor.
5 | """
6 |
--------------------------------------------------------------------------------
/src/pytest_celery/vendors/worker/content/app.py:
--------------------------------------------------------------------------------
1 | """The pytest-celery plugin provides a set of built-in components called
2 | :ref:`vendors`. This module is part of the :ref:`built-in-worker` vendor.
3 |
4 | Template for Celery worker application.
5 | """
6 |
7 | from __future__ import annotations
8 |
9 | import json
10 |
11 | from celery import Celery
12 |
13 | imports = None
14 |
15 | app = Celery("celery_test_app")
16 | config = None
17 |
18 | if config:
19 | app.config_from_object(config)
20 | print(f"Changed worker configuration: {json.dumps(config, indent=4)}")
21 |
22 |
23 | if __name__ == "__main__":
24 | app.start()
25 |
--------------------------------------------------------------------------------
/src/pytest_celery/vendors/worker/content/utils.py:
--------------------------------------------------------------------------------
1 | """The pytest-celery plugin provides a set of built-in components called
2 | :ref:`vendors`.
3 |
4 | This module is part of the :ref:`built-in-worker` vendor.
5 | """
6 |
7 | from __future__ import annotations
8 |
9 | import json
10 |
11 | import psutil
12 |
13 |
14 | def get_running_processes_info(columns: list[str] | None = None) -> str:
15 | """Get information about running processes using psutil."""
16 | if not columns:
17 | columns = [
18 | "pid",
19 | "name",
20 | "username",
21 | "cmdline",
22 | "cpu_percent",
23 | "memory_percent",
24 | "create_time",
25 | ]
26 | processes = [proc.info for proc in psutil.process_iter(columns)]
27 | return json.dumps(processes)
28 |
--------------------------------------------------------------------------------
/src/pytest_celery/vendors/worker/defaults.py:
--------------------------------------------------------------------------------
1 | """The pytest-celery plugin provides a set of built-in components called
2 | :ref:`vendors`.
3 |
4 | This module is part of the :ref:`built-in-worker` vendor.
5 | """
6 |
7 | import os
8 |
9 | CELERY_SETUP_WORKER = "celery_setup_worker"
10 | DEFAULT_WORKER = "default_worker_container"
11 | WORKER_DOCKERFILE_ROOTDIR = os.path.dirname(__file__)
12 | WORKER_CELERY_APP_NAME = "celery_test_app"
13 | WORKER_CELERY_VERSION = "" # latest from pypi
14 | WORKER_LOG_LEVEL = "INFO"
15 | WORKER_NAME = "celery_test_worker"
16 | WORKER_QUEUE = "celery"
17 | WORKER_ENV = {
18 | "CELERY_BROKER_URL": "memory://",
19 | "CELERY_RESULT_BACKEND": "cache+memory://",
20 | "PYTHONUNBUFFERED": "1",
21 | "PYTHONDONTWRITEBYTECODE": "1",
22 | "PYTHONPATH": "/app",
23 | }
24 | WORKER_DEBUGPY_PORTS = {
25 | "5678/tcp": "5678",
26 | }
27 | WORKER_VOLUME = {
28 | "bind": "/app",
29 | "mode": "rw",
30 | }
31 | DEFAULT_WORKER_APP_NAME = WORKER_CELERY_APP_NAME
32 | DEFAULT_WORKER_VERSION = WORKER_CELERY_VERSION
33 | DEFAULT_WORKER_LOG_LEVEL = WORKER_LOG_LEVEL
34 | DEFAULT_WORKER_NAME = WORKER_NAME
35 | DEFAULT_WORKER_ENV = WORKER_ENV
36 | DEFAULT_WORKER_PORTS = None
37 | DEFAULT_WORKER_QUEUE = WORKER_QUEUE
38 | DEFAULT_WORKER_CONTAINER_TIMEOUT = 60
39 | DEFAULT_WORKER_VOLUME = WORKER_VOLUME
40 |
--------------------------------------------------------------------------------
/tests/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/celery/pytest-celery/4d1a1dbcf340abab43d773b928f824a5a4ce7f08/tests/__init__.py
--------------------------------------------------------------------------------
/tests/conftest.py:
--------------------------------------------------------------------------------
1 | import os
2 |
3 | import pytest
4 | from celery import Celery
5 |
6 | from pytest_celery import LOCALSTACK_CREDS
7 |
8 |
9 | @pytest.fixture
10 | def default_worker_tasks(default_worker_tasks: set) -> set:
11 | from tests import tasks
12 |
13 | default_worker_tasks.add(tasks)
14 | return default_worker_tasks
15 |
16 |
17 | @pytest.fixture
18 | def default_worker_env(default_worker_env: dict) -> dict:
19 | default_worker_env.update(LOCALSTACK_CREDS)
20 | return default_worker_env
21 |
22 |
23 | @pytest.fixture(scope="session", autouse=True)
24 | def set_aws_credentials():
25 | os.environ.update(LOCALSTACK_CREDS)
26 |
27 |
28 | @pytest.fixture
29 | def default_worker_app(default_worker_app: Celery) -> Celery:
30 | app = default_worker_app
31 | if app.conf.broker_url and app.conf.broker_url.startswith("sqs"):
32 | app.conf.broker_transport_options["region"] = LOCALSTACK_CREDS["AWS_DEFAULT_REGION"]
33 | return app
34 |
--------------------------------------------------------------------------------
/tests/defaults.py:
--------------------------------------------------------------------------------
1 | from pytest_celery import CELERY_BACKEND
2 | from pytest_celery import CELERY_BACKEND_CLUSTER
3 | from pytest_celery import CELERY_BROKER
4 | from pytest_celery import CELERY_BROKER_CLUSTER
5 | from pytest_celery import CELERY_WORKER
6 | from pytest_celery import CELERY_WORKER_CLUSTER
7 | from pytest_celery import DEFAULT_LOCALSTACK_BROKER
8 | from pytest_celery import DEFAULT_MEMCACHED_BACKEND
9 | from pytest_celery import DEFAULT_RABBITMQ_BROKER
10 | from pytest_celery import DEFAULT_REDIS_BACKEND
11 | from pytest_celery import DEFAULT_REDIS_BROKER
12 | from pytest_celery import DEFAULT_WORKER
13 |
14 | DEFAULT_WORKERS = (DEFAULT_WORKER,)
15 | DEFAULT_BACKENDS = (
16 | DEFAULT_REDIS_BACKEND,
17 | DEFAULT_MEMCACHED_BACKEND,
18 | )
19 | DEFAULT_BROKERS = (
20 | DEFAULT_LOCALSTACK_BROKER,
21 | DEFAULT_RABBITMQ_BROKER,
22 | DEFAULT_REDIS_BROKER,
23 | )
24 |
25 | ALL_REDIS_FIXTURES = (
26 | DEFAULT_REDIS_BACKEND,
27 | DEFAULT_REDIS_BROKER,
28 | )
29 | ALL_LOCALSTACK_FIXTURES = (DEFAULT_LOCALSTACK_BROKER,)
30 | ALL_RABBITMQ_FIXTURES = (DEFAULT_RABBITMQ_BROKER,)
31 | ALL_MEMCACHED_FIXTURES = (DEFAULT_MEMCACHED_BACKEND,)
32 | ALL_WORKERS_FIXTURES = (*DEFAULT_WORKERS,)
33 | ALL_BACKENDS_FIXTURES = (*DEFAULT_BACKENDS,)
34 | ALL_BROKERS_FIXTURES = (*DEFAULT_BROKERS,)
35 | ALL_COMPONENTS_FIXTURES = (
36 | *ALL_WORKERS_FIXTURES,
37 | *ALL_BACKENDS_FIXTURES,
38 | *ALL_BROKERS_FIXTURES,
39 | )
40 | ALL_NODES_FIXTURES = (
41 | CELERY_WORKER,
42 | CELERY_BACKEND,
43 | CELERY_BROKER,
44 | )
45 | ALL_CLUSTERS_FIXTURES = (
46 | CELERY_WORKER_CLUSTER,
47 | CELERY_BACKEND_CLUSTER,
48 | CELERY_BROKER_CLUSTER,
49 | )
50 |
--------------------------------------------------------------------------------
/tests/integration/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/celery/pytest-celery/4d1a1dbcf340abab43d773b928f824a5a4ce7f08/tests/integration/__init__.py
--------------------------------------------------------------------------------
/tests/integration/api/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/celery/pytest-celery/4d1a1dbcf340abab43d773b928f824a5a4ce7f08/tests/integration/api/__init__.py
--------------------------------------------------------------------------------
/tests/integration/api/custom_setup/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/celery/pytest-celery/4d1a1dbcf340abab43d773b928f824a5a4ce7f08/tests/integration/api/custom_setup/__init__.py
--------------------------------------------------------------------------------
/tests/integration/api/custom_setup/test_custom_setup.py:
--------------------------------------------------------------------------------
1 | from __future__ import annotations
2 |
3 | import pytest
4 | from celery import Celery
5 |
6 | from pytest_celery import RESULT_TIMEOUT
7 | from pytest_celery import CeleryTestSetup
8 | from pytest_celery import CeleryTestWorker
9 | from pytest_celery import LocalstackTestBroker
10 | from tests.integration.api.custom_setup.conftest import Celery4WorkerContainer
11 | from tests.integration.api.custom_setup.conftest import Celery5WorkerContainer
12 | from tests.tasks import identity
13 |
14 |
15 | class test_custom_setup:
16 | def test_ready(self, celery_setup: CeleryTestSetup):
17 | assert celery_setup.ready()
18 |
19 | def test_worker_is_connected_to_backend(self, celery_setup: CeleryTestSetup):
20 | backend_urls = [
21 | backend.container.celeryconfig["host_url"].replace("cache+", "") for backend in celery_setup.backend_cluster
22 | ]
23 | worker: CeleryTestWorker
24 | for worker in celery_setup.worker_cluster:
25 | app: Celery = worker.app
26 | assert app.backend.as_uri() in backend_urls
27 |
28 | def test_worker_is_connected_to_broker(self, celery_setup: CeleryTestSetup):
29 | def strip_url(url: str) -> str:
30 | while url.endswith("/"):
31 | url = url.rstrip("/")
32 | return url
33 |
34 | broker_urls = [strip_url(broker.container.celeryconfig["host_url"]) for broker in celery_setup.broker_cluster]
35 | worker: CeleryTestWorker
36 | for worker in celery_setup.worker_cluster:
37 | app: Celery = worker.app
38 | as_uri = app.connection().as_uri().replace("guest:**@", "")
39 | as_uri = strip_url(as_uri)
40 | assert as_uri in broker_urls
41 |
42 | def test_log_level(self, celery_setup: CeleryTestSetup):
43 | worker: CeleryTestWorker
44 | for worker in celery_setup.worker_cluster:
45 | if worker.logs():
46 | worker.assert_log_exists(worker.log_level)
47 |
48 | def test_apply_async(self, celery_setup: CeleryTestSetup):
49 | assert celery_setup.app
50 | worker: CeleryTestWorker
51 | for worker in celery_setup.worker_cluster:
52 | if worker.version == Celery4WorkerContainer.version():
53 | if isinstance(celery_setup.broker, LocalstackTestBroker):
54 | pytest.xfail("Probably bug with the test environment")
55 | expected = "test_apply_async"
56 | queue = worker.worker_queue
57 | sig = identity.s(expected)
58 | res = sig.apply_async(queue=queue)
59 | assert res.get(timeout=RESULT_TIMEOUT) == expected
60 |
61 | def test_custom_cluster_version(self, celery_setup: CeleryTestSetup):
62 | assert len(celery_setup.worker_cluster) == 2
63 | assert celery_setup.worker_cluster.versions == {
64 | Celery5WorkerContainer.version(),
65 | Celery4WorkerContainer.version(),
66 | }
67 |
--------------------------------------------------------------------------------
/tests/integration/api/test_backend.py:
--------------------------------------------------------------------------------
1 | from __future__ import annotations
2 |
3 | import pytest
4 |
5 | from pytest_celery import CeleryBackendCluster
6 | from pytest_celery import CeleryTestBackend
7 | from pytest_celery import CeleryTestCluster
8 | from pytest_celery import CeleryTestNode
9 | from tests.integration.api.test_base import BaseCluster
10 | from tests.integration.api.test_base import BaseNodes
11 |
12 |
13 | class test_celey_test_backend(BaseNodes):
14 | @pytest.fixture
15 | def node(self, celery_backend: CeleryTestBackend) -> CeleryTestNode:
16 | return celery_backend
17 |
18 |
19 | class test_celery_backend_cluster(BaseCluster):
20 | @pytest.fixture
21 | def cluster(self, celery_backend_cluster: CeleryBackendCluster) -> CeleryTestCluster:
22 | return celery_backend_cluster
23 |
--------------------------------------------------------------------------------
/tests/integration/api/test_base.py:
--------------------------------------------------------------------------------
1 | from __future__ import annotations
2 |
3 | import pytest
4 |
5 | from pytest_celery import CeleryTestCluster
6 | from pytest_celery import CeleryTestNode
7 | from pytest_celery import RedisTestBackend
8 |
9 |
10 | class BaseNodes:
11 | def test_ready(self, node: CeleryTestNode):
12 | assert node.ready()
13 |
14 | def test_app(self, node: CeleryTestNode):
15 | assert node.app is None
16 |
17 | def test_logs(self, node: CeleryTestNode):
18 | node.logs()
19 |
20 | def test_name(self, node: CeleryTestNode):
21 | assert isinstance(node.name(), str)
22 |
23 | def test_hostname(self, node: CeleryTestNode):
24 | hostname = node.hostname()
25 | assert isinstance(hostname, str)
26 | assert node.container.id[:12] in hostname
27 |
28 | @pytest.mark.parametrize("signal", [None, "SIGKILL"])
29 | def test_kill(self, node: CeleryTestNode, signal: str | int):
30 | node.kill(signal)
31 | assert node.container.status == "exited"
32 |
33 | def test_kill_no_reload(self, node: CeleryTestNode):
34 | node.kill(reload_container=False)
35 | assert node.container.status != "exited"
36 |
37 | @pytest.mark.parametrize("force", [True, False])
38 | def test_restart(self, node: CeleryTestNode, force: bool):
39 | node.restart(force=force)
40 | assert node.container.status == "running"
41 |
42 | def test_restart_no_reload(self, node: CeleryTestNode):
43 | node.restart(reload_container=False)
44 | assert node.container.status == "running"
45 |
46 | def test_teardown(self, node: CeleryTestNode):
47 | if isinstance(node, RedisTestBackend):
48 | pytest.skip("RedisTestBackend.teardown() breaks the testing environment")
49 | node.teardown()
50 |
51 | @pytest.mark.skip(reason="TODO")
52 | def test_wait_for_logs(self, node: CeleryTestNode):
53 | pass
54 |
55 | @pytest.mark.skip(reason="TODO")
56 | def test_assert_log_exists(self, node: CeleryTestNode):
57 | pass
58 |
59 | @pytest.mark.skip(reason="TODO")
60 | def test_assert_log_does_not_exist(self, node: CeleryTestNode):
61 | pass
62 |
63 |
64 | class BaseCluster:
65 | def test_ready(self, cluster: CeleryTestCluster):
66 | assert cluster.ready()
67 |
68 | def test_teardown(self, cluster: CeleryTestCluster):
69 | cluster.teardown()
70 |
71 | def test_app(self, cluster: CeleryTestCluster):
72 | node: CeleryTestNode
73 | for node in cluster:
74 | assert node.app is None
75 |
76 | def test_config(self, cluster: CeleryTestCluster):
77 | expected_keys = {"urls", "host_urls"}
78 | assert set(cluster.config().keys()) == expected_keys
79 |
--------------------------------------------------------------------------------
/tests/integration/api/test_broker.py:
--------------------------------------------------------------------------------
1 | from __future__ import annotations
2 |
3 | import pytest
4 |
5 | from pytest_celery import CeleryBrokerCluster
6 | from pytest_celery import CeleryTestBroker
7 | from pytest_celery import CeleryTestCluster
8 | from pytest_celery import CeleryTestNode
9 | from tests.integration.api.test_base import BaseCluster
10 | from tests.integration.api.test_base import BaseNodes
11 |
12 |
13 | class test_celery_test_broker(BaseNodes):
14 | @pytest.fixture
15 | def node(self, celery_broker: CeleryTestBroker) -> CeleryTestNode:
16 | return celery_broker
17 |
18 |
19 | class test_celery_broker_cluster(BaseCluster):
20 | @pytest.fixture
21 | def cluster(self, celery_broker_cluster: CeleryBrokerCluster) -> CeleryTestCluster:
22 | return celery_broker_cluster
23 |
--------------------------------------------------------------------------------
/tests/integration/api/test_container.py:
--------------------------------------------------------------------------------
1 | from __future__ import annotations
2 |
3 | import pytest
4 |
5 | from pytest_celery import CeleryTestContainer
6 | from tests.defaults import ALL_COMPONENTS_FIXTURES
7 |
8 |
9 | @pytest.fixture
10 | def container(request):
11 | return request.getfixturevalue(request.param)
12 |
13 |
14 | @pytest.mark.parametrize("container", ALL_COMPONENTS_FIXTURES, indirect=["container"])
15 | class test_celery_test_container:
16 | def test_client(self, container: CeleryTestContainer):
17 | assert container.client
18 |
19 | def test_ready(self, container: CeleryTestContainer):
20 | assert container.ready()
21 |
--------------------------------------------------------------------------------
/tests/integration/api/test_worker.py:
--------------------------------------------------------------------------------
1 | from __future__ import annotations
2 |
3 | import pytest
4 | from celery import Celery
5 |
6 | from pytest_celery import CeleryTestCluster
7 | from pytest_celery import CeleryTestNode
8 | from pytest_celery import CeleryTestWorker
9 | from pytest_celery import CeleryWorkerCluster
10 | from pytest_celery import CeleryWorkerContainer
11 | from tests.integration.api.test_base import BaseCluster
12 | from tests.integration.api.test_base import BaseNodes
13 |
14 |
15 | class test_celey_test_worker(BaseNodes):
16 | @pytest.fixture
17 | def node(self, celery_worker: CeleryTestWorker) -> CeleryTestNode:
18 | return celery_worker
19 |
20 | def test_app(self, celery_worker: CeleryTestWorker, celery_setup_app: Celery):
21 | assert celery_worker.app is celery_setup_app
22 |
23 | def test_version(self, celery_worker: CeleryTestWorker):
24 | assert celery_worker.version == CeleryWorkerContainer.version()
25 |
26 | def test_hostname(self, celery_worker: CeleryTestWorker):
27 | hostname = celery_worker.hostname()
28 | assert "@" in hostname
29 | assert celery_worker.worker_name in hostname.split("@")[0]
30 | assert celery_worker.container.id[:12] in hostname.split("@")[1]
31 |
32 | def test_wait_for_log(self, celery_worker: CeleryTestWorker):
33 | log = f"{celery_worker.hostname()} v{celery_worker.version}"
34 | celery_worker.wait_for_log(log, "test_celey_test_worker.test_wait_for_log")
35 |
36 | def test_assert_log_exists(self, celery_worker: CeleryTestWorker):
37 | log = f"{celery_worker.hostname()} v{celery_worker.version}"
38 | celery_worker.assert_log_exists(log, "test_celey_test_worker.test_assert_log_exists")
39 |
40 |
41 | class test_celery_worker_cluster(BaseCluster):
42 | @pytest.fixture
43 | def cluster(self, celery_worker_cluster: CeleryWorkerCluster) -> CeleryTestCluster:
44 | return celery_worker_cluster
45 |
46 | def test_app(self, celery_worker_cluster: CeleryWorkerCluster, celery_setup_app: Celery):
47 | worker: CeleryTestWorker
48 | for worker in celery_worker_cluster:
49 | assert worker.app is celery_setup_app
50 |
51 | def test_config(self, celery_worker_cluster: CeleryWorkerCluster):
52 | with pytest.raises(NotImplementedError):
53 | celery_worker_cluster.config()
54 |
55 | def test_versions(self, celery_worker_cluster: CeleryWorkerCluster):
56 | assert celery_worker_cluster.versions == {CeleryWorkerContainer.version()}
57 |
--------------------------------------------------------------------------------
/tests/integration/conftest.py:
--------------------------------------------------------------------------------
1 | from __future__ import annotations
2 |
3 | from typing import Any
4 |
5 | import pytest
6 | from pytest_docker_tools import build
7 | from pytest_docker_tools import container
8 | from pytest_docker_tools import fxtr
9 |
10 | from pytest_celery import DEFAULT_WORKER_CONTAINER_TIMEOUT
11 | from pytest_celery import DEFAULT_WORKER_VOLUME
12 | from pytest_celery import WORKER_DOCKERFILE_ROOTDIR
13 | from pytest_celery import CeleryWorkerContainer
14 |
15 |
16 | class IntegrationWorkerContainer(CeleryWorkerContainer):
17 | @property
18 | def client(self) -> Any:
19 | # Overriding the worker container until we have a proper client class
20 | return self
21 |
22 | @classmethod
23 | def log_level(cls) -> str:
24 | return "INFO"
25 |
26 | @classmethod
27 | def worker_name(cls) -> str:
28 | return CeleryWorkerContainer.worker_name() + "-integration-worker"
29 |
30 | @classmethod
31 | def worker_queue(cls) -> str:
32 | return CeleryWorkerContainer.worker_queue() + "-integration-tests-queue"
33 |
34 |
35 | @pytest.fixture
36 | def default_worker_container_cls() -> type[CeleryWorkerContainer]:
37 | return IntegrationWorkerContainer
38 |
39 |
40 | @pytest.fixture(scope="session")
41 | def default_worker_container_session_cls() -> type[CeleryWorkerContainer]:
42 | return IntegrationWorkerContainer
43 |
44 |
45 | integration_tests_worker_image = build(
46 | path=WORKER_DOCKERFILE_ROOTDIR,
47 | tag="pytest-celery/components/worker:integration",
48 | buildargs=IntegrationWorkerContainer.buildargs(),
49 | )
50 |
51 |
52 | default_worker_container = container(
53 | image="{integration_tests_worker_image.id}",
54 | ports=fxtr("default_worker_ports"),
55 | environment=fxtr("default_worker_env"),
56 | network="{default_pytest_celery_network.name}",
57 | volumes={"{default_worker_volume.name}": DEFAULT_WORKER_VOLUME},
58 | wrapper_class=IntegrationWorkerContainer,
59 | timeout=DEFAULT_WORKER_CONTAINER_TIMEOUT,
60 | command=fxtr("default_worker_command"),
61 | )
62 |
--------------------------------------------------------------------------------
/tests/integration/vendors/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/celery/pytest-celery/4d1a1dbcf340abab43d773b928f824a5a4ce7f08/tests/integration/vendors/__init__.py
--------------------------------------------------------------------------------
/tests/integration/vendors/test_default_tasks.py:
--------------------------------------------------------------------------------
1 | import pytest
2 |
3 | from pytest_celery import RESULT_TIMEOUT
4 | from pytest_celery import CeleryTestSetup
5 | from pytest_celery import add
6 | from pytest_celery import add_replaced
7 | from pytest_celery import fail
8 | from pytest_celery import identity
9 | from pytest_celery import noop
10 | from pytest_celery import ping
11 | from pytest_celery import sleep
12 | from pytest_celery import xsum
13 |
14 |
15 | class test_default_tasks:
16 | def test_add(self, celery_setup: CeleryTestSetup):
17 | assert add.s(1, 2).apply_async(queue=celery_setup.worker.worker_queue).get(timeout=RESULT_TIMEOUT) == 3
18 |
19 | def test_add_replaced(self, celery_setup: CeleryTestSetup):
20 | queue = celery_setup.worker.worker_queue
21 | add_replaced.s(1, 2, queue=queue).apply_async(queue=queue)
22 | celery_setup.worker.assert_log_exists("ignored")
23 |
24 | def test_fail(self, celery_setup: CeleryTestSetup):
25 | with pytest.raises(RuntimeError):
26 | fail.s().apply_async(queue=celery_setup.worker.worker_queue).get(timeout=RESULT_TIMEOUT)
27 |
28 | def test_identity(self, celery_setup: CeleryTestSetup):
29 | assert identity.s(1).apply_async(queue=celery_setup.worker.worker_queue).get(timeout=RESULT_TIMEOUT) == 1
30 |
31 | def test_noop(self, celery_setup: CeleryTestSetup):
32 | assert noop.s().apply_async(queue=celery_setup.worker.worker_queue).get(timeout=RESULT_TIMEOUT) is None
33 |
34 | def test_ping(self, celery_setup: CeleryTestSetup):
35 | assert ping.s().apply_async(queue=celery_setup.worker.worker_queue).get(timeout=RESULT_TIMEOUT) == "pong"
36 |
37 | def test_sleep(self, celery_setup: CeleryTestSetup):
38 | assert sleep.s().apply_async(queue=celery_setup.worker.worker_queue).get(timeout=RESULT_TIMEOUT) is True
39 |
40 | def test_xsum(self, celery_setup: CeleryTestSetup):
41 | assert xsum.s([1, 2, 3]).apply_async(queue=celery_setup.worker.worker_queue).get(timeout=RESULT_TIMEOUT) == 6
42 |
43 | def test_xsum_nested_list(self, celery_setup: CeleryTestSetup):
44 | assert (
45 | xsum.s([[1, 2], [3, 4], [5, 6]])
46 | .apply_async(queue=celery_setup.worker.worker_queue)
47 | .get(timeout=RESULT_TIMEOUT)
48 | == 21
49 | )
50 |
--------------------------------------------------------------------------------
/tests/integration/vendors/test_localstack.py:
--------------------------------------------------------------------------------
1 | from __future__ import annotations
2 |
3 | import pytest
4 | from kombu import Connection
5 |
6 | from pytest_celery import LocalstackContainer
7 | from pytest_celery import LocalstackTestBroker
8 | from tests.defaults import ALL_LOCALSTACK_FIXTURES
9 |
10 |
11 | @pytest.fixture
12 | def container(request):
13 | return request.getfixturevalue(request.param)
14 |
15 |
16 | @pytest.mark.parametrize("container", ALL_LOCALSTACK_FIXTURES, indirect=["container"])
17 | class test_localstack_container:
18 | def test_client(self, container: LocalstackContainer):
19 | c: Connection = container.client
20 | assert c
21 | try:
22 | assert c.connect()
23 | finally:
24 | c.release()
25 |
26 | def test_celeryconfig(self, container: LocalstackContainer):
27 | expected_keys = {"url", "host_url", "hostname", "port"}
28 | config = container.celeryconfig
29 | assert set(config.keys()) == expected_keys
30 | assert container.prefix() in config["url"]
31 | assert container.prefix() in config["host_url"]
32 |
33 |
34 | class test_localstack_test_broker:
35 | def test_config(self, celery_localstack_broker: LocalstackTestBroker):
36 | expected_keys = {"url", "host_url", "hostname", "port"}
37 | assert set(celery_localstack_broker.config().keys()) == expected_keys
38 | assert celery_localstack_broker.container.prefix() in celery_localstack_broker.config()["url"]
39 | assert celery_localstack_broker.container.prefix() in celery_localstack_broker.config()["host_url"]
40 |
--------------------------------------------------------------------------------
/tests/integration/vendors/test_memcached.py:
--------------------------------------------------------------------------------
1 | from __future__ import annotations
2 |
3 | import pytest
4 |
5 | from pytest_celery import MemcachedContainer
6 | from pytest_celery import MemcachedTestBackend
7 | from tests.defaults import ALL_MEMCACHED_FIXTURES
8 |
9 |
10 | @pytest.fixture
11 | def container(request):
12 | return request.getfixturevalue(request.param)
13 |
14 |
15 | @pytest.mark.parametrize("container", ALL_MEMCACHED_FIXTURES, indirect=["container"])
16 | class test_memcached_container:
17 | def test_client(self, container: MemcachedContainer):
18 | assert container.client
19 | assert not container.client.touch("ready", 1)
20 | assert container.client.set("ready", "1")
21 | assert container.client.get("ready") == "1"
22 | assert container.client.delete("ready")
23 |
24 | def test_celeryconfig(self, container: MemcachedContainer):
25 | expected_keys = {"url", "host_url", "hostname", "port"}
26 | config = container.celeryconfig
27 | assert set(config.keys()) == expected_keys
28 | assert container.prefix() in config["url"]
29 | assert container.prefix() in config["host_url"]
30 |
31 |
32 | class test_memcached_test_backend:
33 | def test_config(self, celery_memcached_backend: MemcachedTestBackend):
34 | expected_keys = {"url", "host_url", "hostname", "port"}
35 | assert set(celery_memcached_backend.config().keys()) == expected_keys
36 | assert celery_memcached_backend.container.prefix() in celery_memcached_backend.config()["url"]
37 | assert celery_memcached_backend.container.prefix() in celery_memcached_backend.config()["host_url"]
38 |
--------------------------------------------------------------------------------
/tests/integration/vendors/test_rabbitmq.py:
--------------------------------------------------------------------------------
1 | from __future__ import annotations
2 |
3 | import pytest
4 | from kombu import Connection
5 |
6 | from pytest_celery import RabbitMQContainer
7 | from pytest_celery import RabbitMQTestBroker
8 | from tests.defaults import ALL_RABBITMQ_FIXTURES
9 |
10 |
11 | @pytest.fixture
12 | def container(request):
13 | return request.getfixturevalue(request.param)
14 |
15 |
16 | @pytest.mark.parametrize("container", ALL_RABBITMQ_FIXTURES, indirect=["container"])
17 | class test_rabbitmq_container:
18 | def test_client(self, container: RabbitMQContainer):
19 | c: Connection = container.client
20 | assert c
21 | try:
22 | assert c.connect()
23 | finally:
24 | c.release()
25 |
26 | def test_celeryconfig(self, container: RabbitMQContainer):
27 | expected_keys = {"url", "host_url", "hostname", "port", "vhost"}
28 | config = container.celeryconfig
29 | assert set(config.keys()) == expected_keys
30 | assert container.prefix() in config["url"]
31 | assert container.prefix() in config["host_url"]
32 |
33 |
34 | class test_rabbitmq_test_broker:
35 | def test_config(self, celery_rabbitmq_broker: RabbitMQTestBroker):
36 | expected_keys = {"url", "host_url", "hostname", "port", "vhost"}
37 | assert set(celery_rabbitmq_broker.config().keys()) == expected_keys
38 | assert celery_rabbitmq_broker.container.prefix() in celery_rabbitmq_broker.config()["url"]
39 | assert celery_rabbitmq_broker.container.prefix() in celery_rabbitmq_broker.config()["host_url"]
40 |
--------------------------------------------------------------------------------
/tests/integration/vendors/test_redis.py:
--------------------------------------------------------------------------------
1 | from __future__ import annotations
2 |
3 | import pytest
4 |
5 | from pytest_celery import RedisContainer
6 | from pytest_celery import RedisTestBackend
7 | from pytest_celery import RedisTestBroker
8 | from tests.defaults import ALL_REDIS_FIXTURES
9 |
10 |
11 | @pytest.fixture
12 | def container(request):
13 | return request.getfixturevalue(request.param)
14 |
15 |
16 | @pytest.mark.parametrize("container", ALL_REDIS_FIXTURES, indirect=["container"])
17 | class test_redis_container:
18 | def test_client(self, container: RedisContainer):
19 | assert container.client
20 | assert container.client.ping()
21 | assert container.client.set("ready", "1")
22 | assert container.client.get("ready") == "1"
23 | assert container.client.delete("ready")
24 |
25 | def test_celeryconfig(self, container: RedisContainer):
26 | expected_keys = {"url", "host_url", "hostname", "port", "vhost"}
27 | config = container.celeryconfig
28 | assert set(config.keys()) == expected_keys
29 | assert container.prefix() in config["url"]
30 | assert container.prefix() in config["host_url"]
31 |
32 |
33 | class test_redis_test_backend:
34 | def test_config(self, celery_redis_backend: RedisTestBackend):
35 | expected_keys = {"url", "host_url", "hostname", "port", "vhost"}
36 | assert set(celery_redis_backend.config().keys()) == expected_keys
37 | assert celery_redis_backend.container.prefix() in celery_redis_backend.config()["url"]
38 | assert celery_redis_backend.container.prefix() in celery_redis_backend.config()["host_url"]
39 |
40 |
41 | class test_redis_test_broker:
42 | def test_config(self, celery_redis_broker: RedisTestBroker):
43 | expected_keys = {"url", "host_url", "hostname", "port", "vhost"}
44 | assert set(celery_redis_broker.config().keys()) == expected_keys
45 | assert celery_redis_broker.container.prefix() in celery_redis_broker.config()["url"]
46 | assert celery_redis_broker.container.prefix() in celery_redis_broker.config()["host_url"]
47 |
--------------------------------------------------------------------------------
/tests/integration/vendors/test_worker.py:
--------------------------------------------------------------------------------
1 | from __future__ import annotations
2 |
3 | from types import ModuleType
4 |
5 | import pytest
6 |
7 | from pytest_celery import DEFAULT_WORKER_ENV
8 | from pytest_celery import CeleryBackendCluster
9 | from pytest_celery import CeleryTestWorker
10 | from pytest_celery import CeleryWorkerContainer
11 | from tests.defaults import ALL_WORKERS_FIXTURES
12 |
13 |
14 | @pytest.fixture
15 | def container(request):
16 | return request.getfixturevalue(request.param)
17 |
18 |
19 | @pytest.mark.parametrize("container", ALL_WORKERS_FIXTURES, indirect=["container"])
20 | class test_celery_worker_container:
21 | def test_client(self, container: CeleryWorkerContainer):
22 | assert container.client
23 | assert container.client == container
24 |
25 | def test_celeryconfig(self, container: CeleryWorkerContainer):
26 | with pytest.raises(NotImplementedError):
27 | container.celeryconfig
28 |
29 | class test_disabling_cluster:
30 | @pytest.fixture
31 | def celery_backend_cluster(self) -> CeleryBackendCluster:
32 | return None
33 |
34 | def test_disabling_backend_cluster(self, container: CeleryWorkerContainer):
35 | assert container.logs().count("results: disabled://")
36 |
37 | results = DEFAULT_WORKER_ENV["CELERY_BROKER_URL"]
38 | assert container.logs().count(f"transport: {results}")
39 |
40 | class test_replacing_app_module:
41 | @pytest.fixture(params=["Default", "Custom"])
42 | def default_worker_app_module(self, request: pytest.FixtureRequest) -> ModuleType:
43 | if request.param == "Default":
44 | return request.getfixturevalue("default_worker_app_module")
45 | else:
46 | from pytest_celery.vendors.worker.content import app
47 |
48 | return app
49 |
50 | def test_replacing_app_module(self, container: CeleryWorkerContainer, default_worker_app_module: ModuleType):
51 | assert container.app_module() == default_worker_app_module
52 |
53 | class test_replacing_command:
54 | @pytest.fixture
55 | def default_worker_command(self, default_worker_command: list[str]) -> list[str]:
56 | return [*default_worker_command, "--pool", "solo"]
57 |
58 | def test_replacing_command(self, container: CeleryWorkerContainer):
59 | assert container.logs().count("solo") == 1
60 |
61 |
62 | class test_base_test_worker:
63 | def test_config(self, celery_setup_worker: CeleryTestWorker):
64 | with pytest.raises(NotImplementedError):
65 | celery_setup_worker.config()
66 |
--------------------------------------------------------------------------------
/tests/smoke/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/celery/pytest-celery/4d1a1dbcf340abab43d773b928f824a5a4ce7f08/tests/smoke/__init__.py
--------------------------------------------------------------------------------
/tests/smoke/signals.py:
--------------------------------------------------------------------------------
1 | from __future__ import annotations
2 |
3 | from celery.signals import worker_init
4 | from celery.signals import worker_process_init
5 | from celery.signals import worker_process_shutdown
6 | from celery.signals import worker_ready
7 | from celery.signals import worker_shutdown
8 |
9 |
10 | @worker_init.connect
11 | def worker_init_handler(sender, **kwargs): # type: ignore
12 | print("worker_init_handler")
13 |
14 |
15 | @worker_process_init.connect
16 | def worker_process_init_handler(sender, **kwargs): # type: ignore
17 | print("worker_process_init_handler")
18 |
19 |
20 | @worker_process_shutdown.connect
21 | def worker_process_shutdown_handler(sender, pid, exitcode, **kwargs): # type: ignore
22 | print("worker_process_shutdown_handler")
23 |
24 |
25 | @worker_ready.connect
26 | def worker_ready_handler(sender, **kwargs): # type: ignore
27 | print("worker_ready_handler")
28 |
29 |
30 | @worker_shutdown.connect
31 | def worker_shutdown_handler(sender, **kwargs): # type: ignore
32 | print("worker_shutdown_handler")
33 |
--------------------------------------------------------------------------------
/tests/smoke/test_control.py:
--------------------------------------------------------------------------------
1 | from __future__ import annotations
2 |
3 | from pytest_celery import CeleryTestSetup
4 |
5 |
6 | class test_control:
7 | def test_control_ping(self, celery_setup: CeleryTestSetup):
8 | r = celery_setup.app.control.ping()
9 | assert all([all([res["ok"] == "pong" for _, res in response.items()]) for response in r])
10 |
--------------------------------------------------------------------------------
/tests/smoke/test_failover.py:
--------------------------------------------------------------------------------
1 | # mypy: disable-error-code="misc"
2 |
3 | from __future__ import annotations
4 |
5 | import pytest
6 | from pytest_docker_tools import container
7 | from pytest_docker_tools import fxtr
8 |
9 | from pytest_celery import RABBITMQ_CONTAINER_TIMEOUT
10 | from pytest_celery import RESULT_TIMEOUT
11 | from pytest_celery import CeleryBrokerCluster
12 | from pytest_celery import CeleryTestSetup
13 | from pytest_celery import CeleryTestWorker
14 | from pytest_celery import RabbitMQContainer
15 | from pytest_celery import RabbitMQTestBroker
16 | from tests.tasks import identity
17 |
18 | failover_broker = container(
19 | image="{default_rabbitmq_broker_image}",
20 | ports=fxtr("default_rabbitmq_broker_ports"),
21 | environment=fxtr("default_rabbitmq_broker_env"),
22 | network="{default_pytest_celery_network.name}",
23 | wrapper_class=RabbitMQContainer,
24 | timeout=RABBITMQ_CONTAINER_TIMEOUT,
25 | )
26 |
27 |
28 | @pytest.fixture
29 | def failover_rabbitmq_broker(failover_broker: RabbitMQContainer) -> RabbitMQTestBroker:
30 | broker = RabbitMQTestBroker(failover_broker)
31 | yield broker
32 | broker.teardown()
33 |
34 |
35 | @pytest.fixture
36 | def celery_broker_cluster(
37 | celery_rabbitmq_broker: RabbitMQTestBroker,
38 | failover_rabbitmq_broker: RabbitMQTestBroker,
39 | ) -> CeleryBrokerCluster:
40 | cluster = CeleryBrokerCluster(celery_rabbitmq_broker, failover_rabbitmq_broker)
41 | yield cluster
42 | cluster.teardown()
43 |
44 |
45 | class test_failover:
46 | def test_broker_failover(self, celery_setup: CeleryTestSetup):
47 | worker: CeleryTestWorker
48 | assert len(celery_setup.broker_cluster) > 1
49 | celery_setup.broker.kill()
50 | for worker in celery_setup.worker_cluster:
51 | expected = "test_broker_failover"
52 | res = identity.s(expected).apply_async(queue=worker.worker_queue)
53 | assert res.get(timeout=RESULT_TIMEOUT) == expected
54 |
--------------------------------------------------------------------------------
/tests/smoke/test_signals.py:
--------------------------------------------------------------------------------
1 | from __future__ import annotations
2 |
3 | import pytest
4 | from celery.signals import after_task_publish
5 | from celery.signals import before_task_publish
6 |
7 | from pytest_celery import CeleryTestSetup
8 | from pytest_celery import LocalstackTestBroker
9 | from tests.tasks import noop
10 |
11 |
12 | @pytest.fixture
13 | def default_worker_signals(default_worker_signals: set) -> set:
14 | from tests.smoke import signals
15 |
16 | default_worker_signals.add(signals)
17 | return default_worker_signals
18 |
19 |
20 | class test_signals:
21 | @pytest.mark.parametrize(
22 | "log, control",
23 | [
24 | ("worker_init_handler", None),
25 | ("worker_process_init_handler", None),
26 | ("worker_ready_handler", None),
27 | ("worker_process_shutdown_handler", "shutdown"),
28 | ("worker_shutdown_handler", "shutdown"),
29 | ],
30 | )
31 | def test_sanity(self, celery_setup: CeleryTestSetup, log: str, control: str):
32 | if isinstance(celery_setup.broker, LocalstackTestBroker) and control == "shutdown":
33 | pytest.xfail("Potential real bug where shutdown signal isn't called with SQS broker")
34 |
35 | if control:
36 | celery_setup.app.control.broadcast(control)
37 | celery_setup.worker.assert_log_exists(log)
38 |
39 | def test_before_task_publish(self, celery_setup: CeleryTestSetup):
40 | @before_task_publish.connect
41 | def before_task_publish_handler(*args, **kwargs):
42 | nonlocal signal_was_called
43 | signal_was_called = True
44 |
45 | signal_was_called = False
46 | noop.s().apply_async(queue=celery_setup.worker.worker_queue)
47 | assert signal_was_called is True
48 |
49 | def test_after_task_publish(self, celery_setup: CeleryTestSetup):
50 | @after_task_publish.connect
51 | def after_task_publish_handler(*args, **kwargs):
52 | nonlocal signal_was_called
53 | signal_was_called = True
54 |
55 | signal_was_called = False
56 | noop.s().apply_async(queue=celery_setup.worker.worker_queue)
57 | assert signal_was_called is True
58 |
--------------------------------------------------------------------------------
/tests/smoke/test_task.py:
--------------------------------------------------------------------------------
1 | from __future__ import annotations
2 |
3 | from celery import signature
4 |
5 | from pytest_celery import RESULT_TIMEOUT
6 | from pytest_celery import CeleryTestSetup
7 | from tests.tasks import add
8 | from tests.tasks import identity
9 | from tests.tasks import replace_with_task
10 |
11 |
12 | class test_replace:
13 | def test_sanity(self, celery_setup: CeleryTestSetup):
14 | queues = [w.worker_queue for w in celery_setup.worker_cluster]
15 | if len(queues) < 2:
16 | queues.append(queues[0])
17 | replace_with = signature(identity, args=(40,), queue=queues[1])
18 | sig1 = replace_with_task.s(replace_with)
19 | sig2 = add.s(2).set(queue=queues[1])
20 | c = sig1 | sig2
21 | r = c.apply_async(queue=queues[0])
22 | assert r.get(timeout=RESULT_TIMEOUT) == 42
23 |
--------------------------------------------------------------------------------
/tests/smoke/test_worker.py:
--------------------------------------------------------------------------------
1 | from __future__ import annotations
2 |
3 | import pytest
4 |
5 | from pytest_celery import CeleryTestSetup
6 |
7 |
8 | @pytest.mark.parametrize(
9 | "pool",
10 | [
11 | "solo",
12 | "prefork",
13 | "threads",
14 | ],
15 | )
16 | class test_replacing_pool:
17 | @pytest.fixture
18 | def default_worker_command(self, default_worker_command: list[str], pool: str) -> list[str]:
19 | return [*default_worker_command, "--pool", pool]
20 |
21 | def test_pool_from_celery_banner(self, celery_setup: CeleryTestSetup, pool: str):
22 | if pool == "threads":
23 | pool = "thread"
24 | celery_setup.worker.assert_log_exists(pool)
25 |
--------------------------------------------------------------------------------
/tests/tasks.py:
--------------------------------------------------------------------------------
1 | from celery import Task
2 | from celery import shared_task
3 | from celery import signature
4 | from celery.canvas import Signature
5 |
6 | from pytest_celery.vendors.worker.tasks import * # noqa
7 |
8 |
9 | @shared_task
10 | def replaced_with_me():
11 | return True
12 |
13 |
14 | @shared_task(bind=True)
15 | def replace_with_task(self: Task, replace_with: Signature = None):
16 | if replace_with is None:
17 | replace_with = replaced_with_me.s()
18 | self.replace(signature(replace_with))
19 |
--------------------------------------------------------------------------------
/tests/test_pytest_celery.py:
--------------------------------------------------------------------------------
1 | import pytest
2 |
3 | import pytest_celery
4 |
5 |
6 | def test_version():
7 | assert pytest_celery.VERSION
8 | assert len(pytest_celery.VERSION) >= 3
9 | pytest_celery.VERSION = (0, 3, 0)
10 | assert pytest_celery.__version__.count(".") >= 2
11 |
12 |
13 | @pytest.mark.parametrize(
14 | "attr",
15 | [
16 | "__author__",
17 | "__contact__",
18 | "__homepage__",
19 | "__docformat__",
20 | ],
21 | )
22 | def test_meta(attr):
23 | assert getattr(pytest_celery, attr, None)
24 |
--------------------------------------------------------------------------------
/tests/unit/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/celery/pytest-celery/4d1a1dbcf340abab43d773b928f824a5a4ce7f08/tests/unit/__init__.py
--------------------------------------------------------------------------------
/tests/unit/api/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/celery/pytest-celery/4d1a1dbcf340abab43d773b928f824a5a4ce7f08/tests/unit/api/__init__.py
--------------------------------------------------------------------------------
/tests/unit/api/test_backend.py:
--------------------------------------------------------------------------------
1 | from __future__ import annotations
2 |
3 | import pytest
4 | from celery import Celery
5 |
6 | from pytest_celery import DEFAULT_WORKER_ENV
7 | from pytest_celery import CeleryBackendCluster
8 | from pytest_celery import CeleryTestBackend
9 |
10 |
11 | class test_celey_test_backend:
12 | def test_default_config_format(self, celery_backend: CeleryTestBackend):
13 | assert celery_backend.default_config()["url"] == DEFAULT_WORKER_ENV["CELERY_RESULT_BACKEND"]
14 | assert celery_backend.default_config()["host_url"] == DEFAULT_WORKER_ENV["CELERY_RESULT_BACKEND"]
15 |
16 | def test_restart_no_app(self, celery_backend: CeleryTestBackend):
17 | assert celery_backend.app is None
18 | celery_backend.restart()
19 |
20 | def test_restart_with_app(self, celery_backend: CeleryTestBackend, celery_setup_app: Celery):
21 | celery_backend._app = celery_setup_app
22 | assert "result_backend" not in celery_setup_app.conf.changes
23 | celery_backend.restart()
24 | assert "result_backend" in celery_setup_app.conf.changes
25 |
26 |
27 | class test_celery_backend_cluster:
28 | def test_default_config_format(self, celery_backend_cluster: CeleryBackendCluster):
29 | assert celery_backend_cluster.default_config()["urls"] == [DEFAULT_WORKER_ENV["CELERY_RESULT_BACKEND"]]
30 | assert celery_backend_cluster.default_config()["host_urls"] == [DEFAULT_WORKER_ENV["CELERY_RESULT_BACKEND"]]
31 |
32 | class test_disabling_cluster:
33 | @pytest.fixture
34 | def celery_backend_cluster(self) -> CeleryBackendCluster:
35 | return None
36 |
37 | def test_disabling_backend_cluster(
38 | self,
39 | celery_backend_cluster: CeleryBackendCluster,
40 | celery_backend_cluster_config: dict,
41 | ):
42 | assert celery_backend_cluster is None
43 | assert celery_backend_cluster_config is None
44 |
--------------------------------------------------------------------------------
/tests/unit/api/test_broker.py:
--------------------------------------------------------------------------------
1 | from __future__ import annotations
2 |
3 | import pytest
4 | from celery import Celery
5 |
6 | from pytest_celery import DEFAULT_WORKER_ENV
7 | from pytest_celery import CeleryBrokerCluster
8 | from pytest_celery import CeleryTestBroker
9 |
10 |
11 | class test_celey_test_broker:
12 | def test_default_config_format(self, celery_broker: CeleryTestBroker):
13 | assert celery_broker.default_config()["url"] == DEFAULT_WORKER_ENV["CELERY_BROKER_URL"]
14 | assert celery_broker.default_config()["host_url"] == DEFAULT_WORKER_ENV["CELERY_BROKER_URL"]
15 |
16 | def test_restart_no_app(self, celery_broker: CeleryTestBroker):
17 | assert celery_broker.app is None
18 | celery_broker.restart()
19 |
20 | def test_restart_with_app(self, celery_broker: CeleryTestBroker, celery_setup_app: Celery):
21 | celery_broker._app = celery_setup_app
22 | assert "broker_url" not in celery_setup_app.conf.changes
23 | celery_broker.restart()
24 | assert "broker_url" in celery_setup_app.conf.changes
25 |
26 |
27 | class test_celery_broker_cluster:
28 | def test_default_config_format(self, celery_broker_cluster: CeleryBrokerCluster):
29 | assert celery_broker_cluster.default_config()["urls"] == [DEFAULT_WORKER_ENV["CELERY_BROKER_URL"]]
30 | assert celery_broker_cluster.default_config()["host_urls"] == [DEFAULT_WORKER_ENV["CELERY_BROKER_URL"]]
31 |
32 | class test_disabling_cluster:
33 | @pytest.fixture
34 | def celery_broker_cluster(self) -> CeleryBrokerCluster:
35 | return None
36 |
37 | def test_disabling_broker_cluster(
38 | self, celery_broker_cluster: CeleryBrokerCluster, celery_broker_cluster_config: dict
39 | ):
40 | assert celery_broker_cluster is None
41 | assert celery_broker_cluster_config is None
42 |
--------------------------------------------------------------------------------
/tests/unit/api/test_container.py:
--------------------------------------------------------------------------------
1 | from __future__ import annotations
2 |
3 | from unittest.mock import Mock
4 |
5 | import pytest
6 |
7 | from pytest_celery import CeleryTestContainer
8 |
9 |
10 | @pytest.fixture
11 | def mocked_container() -> CeleryTestContainer:
12 | docker_container_mock = Mock()
13 | return CeleryTestContainer(container=docker_container_mock)
14 |
15 |
16 | class test_celery_test_container:
17 | def test_client(self, mocked_container: CeleryTestContainer):
18 | with pytest.raises(NotImplementedError):
19 | mocked_container.client
20 |
21 | def test_celeryconfig(self, mocked_container: CeleryTestContainer):
22 | with pytest.raises(NotImplementedError):
23 | mocked_container.celeryconfig
24 |
25 | def test_command(self, mocked_container: CeleryTestContainer):
26 | with pytest.raises(NotImplementedError):
27 | mocked_container.command()
28 |
29 | def test_teardown(self, mocked_container: CeleryTestContainer):
30 | mocked_container.teardown()
31 |
32 | def test_ready_prompt(self, mocked_container: CeleryTestContainer):
33 | assert mocked_container.ready_prompt is None
34 |
35 | def test_wait_port(self, mocked_container: CeleryTestContainer):
36 | with pytest.raises(ValueError):
37 | mocked_container._wait_port(None)
38 |
39 | def test_wait_ready(self, mocked_container: CeleryTestContainer):
40 | assert mocked_container._wait_ready()
41 |
--------------------------------------------------------------------------------
/tests/unit/api/test_worker.py:
--------------------------------------------------------------------------------
1 | from __future__ import annotations
2 |
3 | from celery import Celery
4 |
5 | from pytest_celery import CeleryTestWorker
6 | from pytest_celery import CeleryWorkerCluster
7 |
8 |
9 | class test_celey_test_worker:
10 | def test_app(self, celery_worker: CeleryTestWorker, celery_setup_app: Celery):
11 | assert celery_worker.app is celery_setup_app
12 |
13 | def test_version(self, celery_worker: CeleryTestWorker):
14 | celery_worker.version
15 | celery_worker.container.version.assert_called_once()
16 |
17 | def test_log_level(self, celery_worker: CeleryTestWorker):
18 | celery_worker.log_level
19 | celery_worker.container.log_level.assert_called_once()
20 |
21 | def test_worker_name(self, celery_worker: CeleryTestWorker):
22 | celery_worker.worker_name
23 | celery_worker.container.worker_name.assert_called_once()
24 |
25 | def test_worker_queue(self, celery_worker: CeleryTestWorker):
26 | celery_worker.worker_queue
27 | celery_worker.container.worker_queue.assert_called_once()
28 |
29 |
30 | class test_celery_worker_cluster:
31 | def test_app(self, celery_worker_cluster: CeleryWorkerCluster, celery_setup_app: Celery):
32 | for node in celery_worker_cluster:
33 | assert node.app is celery_setup_app
34 |
35 | def test_versions(self, celery_worker_cluster: CeleryWorkerCluster):
36 | celery_worker_cluster.versions
37 | for node in celery_worker_cluster:
38 | node.container.version.assert_called_once()
39 |
--------------------------------------------------------------------------------
/tests/unit/conftest.py:
--------------------------------------------------------------------------------
1 | from unittest.mock import Mock
2 | from unittest.mock import patch
3 |
4 | import pytest
5 |
6 | from pytest_celery import CeleryWorkerContainer
7 | from pytest_celery import LocalstackContainer
8 | from pytest_celery import MemcachedContainer
9 | from pytest_celery import RabbitMQContainer
10 | from pytest_celery import RedisContainer
11 |
12 |
13 | @pytest.fixture(autouse=True)
14 | def mock_wait_for_callable():
15 | with patch("pytest_celery.api.base.wait_for_callable", new=Mock()):
16 | with patch("pytest_celery.api.container.wait_for_callable", new=Mock()):
17 | yield
18 |
19 |
20 | def mocked_container(spec: type) -> Mock:
21 | mocked_container = Mock(spec=spec)
22 | mocked_container.id = "mocked_test_container_id"
23 | mocked_container.celeryconfig = {
24 | "url": "mocked_url/",
25 | "host_url": "mocked_host_url/",
26 | }
27 | return mocked_container
28 |
29 |
30 | @pytest.fixture
31 | def default_localstack_broker() -> LocalstackContainer:
32 | return mocked_container(LocalstackContainer)
33 |
34 |
35 | @pytest.fixture
36 | def default_memcached_backend() -> MemcachedContainer:
37 | return mocked_container(MemcachedContainer)
38 |
39 |
40 | @pytest.fixture
41 | def default_rabbitmq_broker() -> RabbitMQContainer:
42 | return mocked_container(RabbitMQContainer)
43 |
44 |
45 | @pytest.fixture
46 | def default_redis_backend() -> RedisContainer:
47 | return mocked_container(RedisContainer)
48 |
49 |
50 | @pytest.fixture
51 | def default_redis_broker() -> RedisContainer:
52 | return mocked_container(RedisContainer)
53 |
54 |
55 | @pytest.fixture
56 | def default_worker_container() -> CeleryWorkerContainer:
57 | return mocked_container(CeleryWorkerContainer)
58 |
--------------------------------------------------------------------------------
/tests/unit/vendors/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/celery/pytest-celery/4d1a1dbcf340abab43d773b928f824a5a4ce7f08/tests/unit/vendors/__init__.py
--------------------------------------------------------------------------------
/tests/unit/vendors/test_localstack.py:
--------------------------------------------------------------------------------
1 | from __future__ import annotations
2 |
3 | import pytest
4 |
5 | from pytest_celery import LOCALSTACK_ENV
6 | from pytest_celery import LOCALSTACK_IMAGE
7 | from pytest_celery import LOCALSTACK_PORTS
8 | from pytest_celery import LOCALSTACK_PREFIX
9 | from pytest_celery import LocalstackContainer
10 | from pytest_celery import LocalstackTestBroker
11 |
12 |
13 | class test_localstack_container:
14 | def test_version(self):
15 | assert LocalstackContainer.version() == "localstack"
16 |
17 | def test_env(self):
18 | assert LocalstackContainer.initial_env() == LOCALSTACK_ENV
19 |
20 | def test_image(self):
21 | assert LocalstackContainer.image() == LOCALSTACK_IMAGE
22 |
23 | def test_ports(self):
24 | assert LocalstackContainer.ports() == LOCALSTACK_PORTS
25 |
26 | def test_prefix(self):
27 | assert LocalstackContainer.prefix() == LOCALSTACK_PREFIX
28 |
29 |
30 | class test_localstack_broker_api:
31 | @pytest.mark.skip(reason="Placeholder")
32 | def test_placeholder(self, celery_localstack_broker: LocalstackTestBroker):
33 | # The class LocalstackTestBroker is currently a placeholder
34 | # so we don't have any specific tests for it yet.
35 | # This test suite is pre-configured to test the LocalstackTestBroker
36 | # and ready to be used once the class is implemented.
37 | pass
38 |
--------------------------------------------------------------------------------
/tests/unit/vendors/test_memcached.py:
--------------------------------------------------------------------------------
1 | from __future__ import annotations
2 |
3 | import pytest
4 |
5 | from pytest_celery import MEMCACHED_ENV
6 | from pytest_celery import MEMCACHED_IMAGE
7 | from pytest_celery import MEMCACHED_PORTS
8 | from pytest_celery import MEMCACHED_PREFIX
9 | from pytest_celery import MemcachedContainer
10 | from pytest_celery import MemcachedTestBackend
11 |
12 |
13 | class test_memcached_container:
14 | def test_version(self):
15 | assert MemcachedContainer.version() == "latest"
16 |
17 | def test_env(self):
18 | assert MemcachedContainer.initial_env() == MEMCACHED_ENV
19 |
20 | def test_image(self):
21 | assert MemcachedContainer.image() == MEMCACHED_IMAGE
22 |
23 | def test_ports(self):
24 | assert MemcachedContainer.ports() == MEMCACHED_PORTS
25 |
26 | def test_prefix(self):
27 | assert MemcachedContainer.prefix() == MEMCACHED_PREFIX
28 |
29 |
30 | class test_memcached_backend_api:
31 | @pytest.mark.skip(reason="Placeholder")
32 | def test_placeholder(self, celery_memcached_backend: MemcachedTestBackend):
33 | # The class MemcachedTestBackend is currently a placeholder
34 | # so we don't have any specific tests for it yet.
35 | # This test suite is pre-configured to test the MemcachedTestBackend
36 | # and ready to be used once the class is implemented.
37 | pass
38 |
--------------------------------------------------------------------------------
/tests/unit/vendors/test_rabbitmq.py:
--------------------------------------------------------------------------------
1 | from __future__ import annotations
2 |
3 | import pytest
4 |
5 | from pytest_celery import RABBITMQ_ENV
6 | from pytest_celery import RABBITMQ_IMAGE
7 | from pytest_celery import RABBITMQ_PORTS
8 | from pytest_celery import RABBITMQ_PREFIX
9 | from pytest_celery import RabbitMQContainer
10 | from pytest_celery import RabbitMQTestBroker
11 |
12 |
13 | class test_rabbitmq_container:
14 | def test_version(self):
15 | assert RabbitMQContainer.version() == "latest"
16 |
17 | def test_env(self):
18 | assert RabbitMQContainer.initial_env() == RABBITMQ_ENV
19 |
20 | def test_image(self):
21 | assert RabbitMQContainer.image() == RABBITMQ_IMAGE
22 |
23 | def test_ports(self):
24 | assert RabbitMQContainer.ports() == RABBITMQ_PORTS
25 |
26 | def test_prefix(self):
27 | assert RabbitMQContainer.prefix() == RABBITMQ_PREFIX
28 |
29 |
30 | class test_rabbitmq_broker_api:
31 | @pytest.mark.skip(reason="Placeholder")
32 | def test_placeholder(self, celery_rabbitmq_broker: RabbitMQTestBroker):
33 | # The class RabbitMQTestBroker is currently a placeholder
34 | # so we don't have any specific tests for it yet.
35 | # This test suite is pre-configured to test the RabbitMQTestBroker
36 | # and ready to be used once the class is implemented.
37 | pass
38 |
--------------------------------------------------------------------------------
/tests/unit/vendors/test_redis.py:
--------------------------------------------------------------------------------
1 | from __future__ import annotations
2 |
3 | import pytest
4 |
5 | from pytest_celery import REDIS_ENV
6 | from pytest_celery import REDIS_IMAGE
7 | from pytest_celery import REDIS_PORTS
8 | from pytest_celery import REDIS_PREFIX
9 | from pytest_celery import RedisContainer
10 | from pytest_celery import RedisTestBackend
11 | from pytest_celery import RedisTestBroker
12 |
13 |
14 | class test_redis_container:
15 | def test_version(self):
16 | assert RedisContainer.version() == "latest"
17 |
18 | def test_env(self):
19 | assert RedisContainer.initial_env() == REDIS_ENV
20 |
21 | def test_image(self):
22 | assert RedisContainer.image() == REDIS_IMAGE
23 |
24 | def test_ports(self):
25 | assert RedisContainer.ports() == REDIS_PORTS
26 |
27 | def test_prefix(self):
28 | assert RedisContainer.prefix() == REDIS_PREFIX
29 |
30 |
31 | class test_redis_backend_api:
32 | @pytest.mark.skip(reason="RedisTestBackend.teardown() breaks the testing environment")
33 | def test_teardown(self, celery_redis_backend: RedisTestBackend):
34 | celery_redis_backend.teardown()
35 | celery_redis_backend.container.teardown.assert_called_once()
36 |
37 |
38 | class test_redis_broker_api:
39 | @pytest.mark.skip(reason="Placeholder")
40 | def test_placeholder(self, celery_redis_broker: RedisTestBroker):
41 | # The class RedisTestBroker is currently a placeholder
42 | # so we don't have any specific tests for it yet.
43 | # This test suite is pre-configured to test the RedisTestBroker
44 | # and ready to be used once the class is implemented.
45 | pass
46 |
--------------------------------------------------------------------------------
/tests/unit/vendors/test_worker/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/celery/pytest-celery/4d1a1dbcf340abab43d773b928f824a5a4ce7f08/tests/unit/vendors/test_worker/__init__.py
--------------------------------------------------------------------------------
/tests/unit/vendors/test_worker/test_default_tasks.py:
--------------------------------------------------------------------------------
1 | from unittest.mock import patch
2 |
3 | import pytest
4 | from celery.exceptions import Ignore
5 |
6 | from pytest_celery import add
7 | from pytest_celery import add_replaced
8 | from pytest_celery import fail
9 | from pytest_celery import identity
10 | from pytest_celery import noop
11 | from pytest_celery import ping
12 | from pytest_celery import sleep
13 | from pytest_celery import xsum
14 |
15 |
16 | class test_default_tasks:
17 | def test_add(self):
18 | assert add(1, 2) == 3
19 |
20 | def test_add_replaced(self):
21 | with patch("pytest_celery.add_replaced.replace", side_effect=Ignore):
22 | with pytest.raises(Ignore):
23 | add_replaced(1, 2)
24 |
25 | def test_fail(self):
26 | with pytest.raises(RuntimeError):
27 | fail()
28 |
29 | def test_identity(self):
30 | assert identity(1) == 1
31 |
32 | def test_noop(self):
33 | assert noop() is None
34 |
35 | def test_ping(self):
36 | assert ping() == "pong"
37 |
38 | def test_sleep(self):
39 | assert sleep() is True
40 |
41 | def test_xsum(self):
42 | assert xsum([1, 2, 3]) == 6
43 |
44 | def test_xsum_nested_list(self):
45 | assert xsum([[1, 2], [3, 4], [5, 6]]) == 21
46 |
--------------------------------------------------------------------------------