From 23c2c0389eeb2606b81556e6ffd103dc29c99e67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Marques?= Date: Wed, 7 Jul 2021 14:03:34 +0100 Subject: [PATCH] Add base for Python tests As discussed in https://github.com/Tecnativa/docker-whitelist/pull/8#discussion_r665251294 --- .copier-answers.image-template.yml | 2 +- .github/workflows/ci.yml | 44 ++++++++++++++++++++++++++++++ .vscode/settings.json | 5 ++++ README.md | 42 ++++++++++++++++++++++++++++ pyproject.toml | 20 ++++++++++++++ pytest.ini | 2 ++ tests/conftest.py | 41 ++++++++++++++++++++++++++++ 7 files changed, 155 insertions(+), 1 deletion(-) create mode 100644 .vscode/settings.json create mode 100644 pyproject.toml create mode 100644 pytest.ini create mode 100644 tests/conftest.py diff --git a/.copier-answers.image-template.yml b/.copier-answers.image-template.yml index 9297211..c313aa0 100644 --- a/.copier-answers.image-template.yml +++ b/.copier-answers.image-template.yml @@ -10,6 +10,6 @@ main_branches: project_name: docker-whitelist project_owner: Tecnativa push_to_ghcr: true -pytest: false +pytest: true python_versions: - "3.9" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 25e9523..017a497 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -8,12 +8,56 @@ name: Build, Test & Deploy tags: - "v*" workflow_dispatch: + inputs: + pytest_addopts: + description: + Extra options for pytest; use -vv for full details; see + https://docs.pytest.org/en/latest/example/simple.html#how-to-change-command-line-options-defaults + required: false env: LANG: "en_US.utf-8" LC_ALL: "en_US.utf-8" + PIP_CACHE_DIR: ${{ github.workspace }}/.cache.~/pip + PIPX_HOME: ${{ github.workspace }}/.cache.~/pipx + POETRY_CACHE_DIR: ${{ github.workspace }}/.cache.~/pypoetry + POETRY_VIRTUALENVS_IN_PROJECT: "true" + PYTEST_ADDOPTS: ${{ github.event.inputs.pytest_addopts }} + PYTHONIOENCODING: "UTF-8" jobs: + build-test: + runs-on: ubuntu-20.04 + strategy: + matrix: + python: + - 3.9 + steps: + # Prepare environment + - uses: actions/checkout@v2 + # Set up and run tests + - name: Install python + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python }} + - name: Generate cache key CACHE + run: + echo "CACHE=${{ secrets.CACHE_DATE }} ${{ runner.os }} $(python -VV | + sha256sum | cut -d' ' -f1) ${{ hashFiles('pyproject.toml') }} ${{ + hashFiles('poetry.lock') }}" >> $GITHUB_ENV + - uses: actions/cache@v2 + with: + path: | + .cache.~ + .venv + ~/.local/bin + key: venv ${{ env.CACHE }} + - run: pip install poetry + - name: Patch $PATH + run: echo "$HOME/.local/bin" >> $GITHUB_PATH + - run: poetry install + # Run tests + - run: poetry run pytest --prebuild build-push: runs-on: ubuntu-20.04 services: diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..884fb3b --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,5 @@ +{ + "cSpell.words": ["pytest"], + "python.pythonPath": ".venv/bin/python", + "python.testing.pytestEnabled": true +} diff --git a/README.md b/README.md index 1c3e485..9c3fb05 100644 --- a/README.md +++ b/README.md @@ -237,3 +237,45 @@ networks: ``` And voilà! `app` has fonts, but nothing more. ✋👮 + +## Development + +All the dependencies you need to develop this project (apart from Docker itself) are +managed with [poetry](https://python-poetry.org/). + +To set up your development environment, run: + +```bash +pip install pipx # If you don't have pipx installed +pipx install poetry # Install poetry itself +poetry install # Install the python dependencies and setup the development environment +``` + +### Testing + +To run the tests locally, add `--prebuild` to autobuild the image before testing: + +```sh +poetry run pytest --prebuild +``` + +By default, the image that the tests use (and optionally prebuild) is named +`test:docker-whitelist`. If you prefer, you can build it separately before testing, and +remove the `--prebuild` flag, to run the tests with that image you built: + +```sh +docker image build -t test:docker-whitelist . +poetry run pytest +``` + +If you want to use a different image, pass the `--image` command line argument with the +name you want: + +```sh +# To build it automatically +poetry run pytest --prebuild --image my_custom_image + +# To prebuild it separately +docker image build -t my_custom_image . +poetry run pytest --image my_custom_image +``` diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..a3ed521 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,20 @@ + +[tool.poetry] +name = "docker-whitelist" +version = "0.0.0" +description = "" +authors = ["Tecnativa"] + +[tool.poetry.dependencies] +python = "^3.9" +plumbum = "^1.6.9" + +[tool.poetry.dev-dependencies] +black = {version = "^20.8b1", allow-prereleases = true} +flake8 = "^3.8.4" +plumbum = "^1.6.9" +pytest-xdist = "^2.1.0" + +[build-system] +requires = ["poetry-core>=1.0.0"] +build-backend = "poetry.core.masonry.api" diff --git a/pytest.ini b/pytest.ini new file mode 100644 index 0000000..0bf3b48 --- /dev/null +++ b/pytest.ini @@ -0,0 +1,2 @@ +[pytest] +addopts = -n auto -ra diff --git a/tests/conftest.py b/tests/conftest.py new file mode 100644 index 0000000..814712d --- /dev/null +++ b/tests/conftest.py @@ -0,0 +1,41 @@ +import logging +from pathlib import Path + +import pytest +from plumbum.cmd import docker + +_logger = logging.getLogger(__name__) + + +def pytest_addoption(parser): + """Allow prebuilding image for local testing.""" + parser.addoption( + "--prebuild", action="store_true", help="Build local image before testing" + ) + parser.addoption( + "--image", + action="store", + default="test:docker-whitelist", + help="Specify testing image name", + ) + + +@pytest.fixture(scope="session") +def image(request): + """Get image name. Builds it if needed.""" + image = request.config.getoption("--image") + if request.config.getoption("--prebuild"): + build = docker["image", "build", "-t", image, Path(__file__).parent.parent] + retcode, stdout, stderr = build.run() + _logger.log( + # Pytest prints warnings if a test fails, so this is a warning if + # the build succeeded, to allow debugging the build logs + logging.ERROR if retcode else logging.WARNING, + "Build logs for COMMAND: %s\nEXIT CODE:%d\nSTDOUT:%s\nSTDERR:%s", + build.bound_command(), + retcode, + stdout, + stderr, + ) + assert not retcode, "Image build failed" + return image