diff --git a/.github/workflows/code-quality.yaml b/.github/workflows/code-quality.yaml index f8c288651..198340635 100644 --- a/.github/workflows/code-quality.yaml +++ b/.github/workflows/code-quality.yaml @@ -28,6 +28,46 @@ jobs: exit 1 fi + pytest-tests: + runs-on: ubuntu-latest + env: + poetry_version: '1.8.3' + steps: + - uses: actions/checkout@v4 + + - name: Cache poetry in ~/.local + uses: actions/cache/restore@v4 + id: cache-poetry-restore + with: + path: ~/.local + key: "${{ runner.os }}-local-${{ env.poetry_version }}" + + - name: Install poetry + if: steps.cache-poetry-restore.outputs.cache-hit != 'true' + run: pip install poetry==${{ env.poetry_version }} + + - name: Save cache + if: steps.cache-poetry-restore.outputs.cache-hit != 'true' + uses: actions/cache/save@v4 + with: + path: ~/.local + key: ${{ steps.cache-poetry-restore.outputs.cache-primary-key }} + + - name: Set up Python + id: setup-python + uses: actions/setup-python@v5 + with: + python-version: '3.12' + cache: 'poetry' + + - name: Configure poetry + run: poetry env use "${{ steps.setup-python.outputs.python-path }}" + + - name: Install deps + run: poetry install --sync + + - run: poetry run pytest + code-static-analysis: runs-on: ubuntu-latest steps: diff --git a/README.md b/README.md index b11d98df5..4eb69624a 100644 --- a/README.md +++ b/README.md @@ -51,6 +51,17 @@ podman run -it -p 8888:8888 quay.io/opendatahub/workbench-images:jupyter-mini ### Deploy & Test +#### Running Python selftests in Pytest + +```shell +pip install poetry +poetry env use /usr/bin/python3.12 +poetry config virtualenvs.in-project true +poetry install --sync + +poetry run pytest +``` + #### Notebooks Deploy the notebook images in your Kubernetes environment using: diff --git a/poetry.lock b/poetry.lock new file mode 100644 index 000000000..fcf541bac --- /dev/null +++ b/poetry.lock @@ -0,0 +1,108 @@ +# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand. + +[[package]] +name = "attrs" +version = "23.2.0" +description = "Classes Without Boilerplate" +optional = false +python-versions = ">=3.7" +files = [ + {file = "attrs-23.2.0-py3-none-any.whl", hash = "sha256:99b87a485a5820b23b879f04c2305b44b951b502fd64be915879d77a7e8fc6f1"}, + {file = "attrs-23.2.0.tar.gz", hash = "sha256:935dc3b529c262f6cf76e50877d35a4bd3c1de194fd41f47a2b7ae8f19971f30"}, +] + +[package.extras] +cov = ["attrs[tests]", "coverage[toml] (>=5.3)"] +dev = ["attrs[tests]", "pre-commit"] +docs = ["furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier", "zope-interface"] +tests = ["attrs[tests-no-zope]", "zope-interface"] +tests-mypy = ["mypy (>=1.6)", "pytest-mypy-plugins"] +tests-no-zope = ["attrs[tests-mypy]", "cloudpickle", "hypothesis", "pympler", "pytest (>=4.3.0)", "pytest-xdist[psutil]"] + +[[package]] +name = "colorama" +version = "0.4.6" +description = "Cross-platform colored terminal text." +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +files = [ + {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, + {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, +] + +[[package]] +name = "iniconfig" +version = "2.0.0" +description = "brain-dead simple config-ini parsing" +optional = false +python-versions = ">=3.7" +files = [ + {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, + {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, +] + +[[package]] +name = "packaging" +version = "24.1" +description = "Core utilities for Python packages" +optional = false +python-versions = ">=3.8" +files = [ + {file = "packaging-24.1-py3-none-any.whl", hash = "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124"}, + {file = "packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002"}, +] + +[[package]] +name = "pluggy" +version = "1.5.0" +description = "plugin and hook calling mechanisms for python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"}, + {file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"}, +] + +[package.extras] +dev = ["pre-commit", "tox"] +testing = ["pytest", "pytest-benchmark"] + +[[package]] +name = "pytest" +version = "8.2.2" +description = "pytest: simple powerful testing with Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pytest-8.2.2-py3-none-any.whl", hash = "sha256:c434598117762e2bd304e526244f67bf66bbd7b5d6cf22138be51ff661980343"}, + {file = "pytest-8.2.2.tar.gz", hash = "sha256:de4bb8104e201939ccdc688b27a89a7be2079b22e2bd2b07f806b6ba71117977"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "sys_platform == \"win32\""} +iniconfig = "*" +packaging = "*" +pluggy = ">=1.5,<2.0" + +[package.extras] +dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] + +[[package]] +name = "pytest-subtests" +version = "0.12.1" +description = "unittest subTest() support and subtests fixture" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pytest-subtests-0.12.1.tar.gz", hash = "sha256:d6605dcb88647e0b7c1889d027f8ef1c17d7a2c60927ebfdc09c7b0d8120476d"}, + {file = "pytest_subtests-0.12.1-py3-none-any.whl", hash = "sha256:100d9f7eb966fc98efba7026c802812ae327e8b5b37181fb260a2ea93226495c"}, +] + +[package.dependencies] +attrs = ">=19.2.0" +pytest = ">=7.0" + +[metadata] +lock-version = "2.0" +python-versions = "~3.12" +content-hash = "b083c8ea5b4dc42a2c76a0c2b51af47f8813ba5f40929bafa3f370f3290d971f" diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 000000000..87cd2fc69 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,19 @@ +[tool.poetry] +name = "notebooks" +version = "2024.1" +authors = [] +description = "Open Data Hub / OpenShift AI Notebook / Workbench images, and tests for the same in Python." +readme = "README.md" +package-mode = false + +[tool.poetry.dependencies] +python = "~3.12" + + +[tool.poetry.group.dev.dependencies] +pytest = "^8.2.2" +pytest-subtests = "^0.12.1" + +[build-system] +requires = ["poetry-core"] +build-backend = "poetry.core.masonry.api" diff --git a/pytest.ini b/pytest.ini new file mode 100644 index 000000000..2b320d7a0 --- /dev/null +++ b/pytest.ini @@ -0,0 +1,17 @@ +# https://docs.pytest.org/en/7.1.x/reference/reference.html#configuration-options + +[pytest] +addopts = --strict-markers --capture=no --tb=short + +required_plugins = pytest-subtests + +junit_logging = all +junit_log_passing_tests = False + +log_cli = True +log_cli_date_format = %Y-%m-%d %H:%M:%S +log_cli_format = %(asctime)s %(levelname)s %(message)s +log_cli_level = INFO + +log_file = logs/pytest-logs.txt +log_file_level = DEBUG diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/test_main.py b/tests/test_main.py new file mode 100644 index 000000000..609e7f810 --- /dev/null +++ b/tests/test_main.py @@ -0,0 +1,22 @@ +from __future__ import annotations + +import pathlib +import tomllib +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + import pytest_subtests + +PROJECT_ROOT = pathlib.Path(__file__).parent.parent + + +def test_image_pipfiles(subtests: pytest_subtests.plugin.SubTests): + for file in PROJECT_ROOT.glob("**/Pipfile"): + with subtests.test(msg="checking Pipfile", pipfile=file): + directory = file.parent # "ubi9-python-3.9" + ubi, lang, python = directory.name.split("-") + + with open(file, "rb") as fp: + pipfile = tomllib.load(fp) + assert "requires" in pipfile, "Pipfile is missing a [[requires]] section" + assert pipfile["requires"]["python_version"] == python, "Pipfile does not declare the expected Python version"