From cc6076d0834e6cef633e1cc69c47ee01823d1244 Mon Sep 17 00:00:00 2001 From: DanCardin Date: Tue, 21 Feb 2023 14:23:07 -0500 Subject: [PATCH] fix: Add an option to configure the default test registration path. --- .github/workflows/release.yml | 36 +++++++++++ docs/source/running.rst | 61 +++++++++++++++++-- .../conftest.py | 0 pyproject.toml | 2 +- src/pytest_alembic/plugin/hooks.py | 19 +++--- src/pytest_alembic/plugin/plugin.py | 18 +++--- tests/plugin/test_plugin.py | 13 +++- tests/test_runner.py | 2 + 8 files changed, 128 insertions(+), 23 deletions(-) create mode 100644 .github/workflows/release.yml create mode 100644 examples/test_experimental_all_models_register_namespace_package/conftest.py diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..34d5970 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,36 @@ +name: Github Release/Publish PyPi + +on: + push: + tags: + - "v*.*.*" + +jobs: + gh-release: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v1 + - name: Release + uses: softprops/action-gh-release@v1 + with: + generate_release_notes: true + + publish-pypi: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-python@v4 + with: + python-version: "3.9" + - name: Run image + uses: abatilo/actions-poetry@v2.0.0 + with: + poetry-version: 1.2.0 + + - name: Publish + env: + PYPI_TOKEN: ${{ secrets.PYPI_TOKEN }} + run: | + poetry config pypi-token.pypi $PYPI_TOKEN + poetry publish --build diff --git a/docs/source/running.rst b/docs/source/running.rst index 61820c1..11749e1 100644 --- a/docs/source/running.rst +++ b/docs/source/running.rst @@ -1,8 +1,44 @@ Running Tests ============= -:code:`Pytest Alembic` automatically adds a flag, :code:`pytest --test-alembic`, which will -automatically invoke the baked-in tests. +You have two primary options for running the configured set of tests: + +1. Automatically at the command-line via ``--test-alembic`` + + :code:`Pytest Alembic` automatically adds a flag, :code:`pytest --test-alembic`, which will + automatically invoke the baked-in tests. + + This can be convenient if you want to exclude migrations tests most of the time, but include + them for e.g. CI. By default, ``pytest tests`` would then, **not** run migrations tests. + + Additionally, it means you don't need to manually include the tests in a test file somewhere + in your project. + + If your tests dont generally reside at/below a ``tests/`` directory with a ``tests/conftest.py`` + file, you can/should set the :code:`pytest_alembic_tests_path` option, described + below. + +2. You can directly import the tests you want to include at any point in your project. + + .. code-block:: python + :caption: tests/test_migrations.py + + from pytest_alembic.tests import ( + test_model_definitions_match_ddl, + test_single_head_revision, + test_up_down_consistency, + test_upgrade, + ) + + This can be convenient if you always want the migrations tests to run, or else want a reference + to the tests' existence somewhere in your source code. Pytest would automatically include + the tests every time you run i.e. :code:`pytest tests`. + +In either case, you can exclude migrations tests using pytest's "marker" system, i.e. +``pytest -m "not alembic"``. + + + Configuration ------------- @@ -34,9 +70,24 @@ behavior. .. note:: - As of pytest-alembic version 0.8.5, this option is ignored. Tests will be registered - at the top level, and `--test-alembic` will automatically include the tests regardless - of the provided path. + As of pytest-alembic version 0.8.5, this option is ignored. Instead, if you require customizing + the registration location, you should use :code:`pytest_alembic_tests_path` instead. + +* :code:`pytest_alembic_tests_path` + + .. note:: + + Introduced in v0.10.1. + + The location at which the built-in tests will be bound. This defaults to 'tests/conftest.py'. + Typically, you would want this to coincide with the path at which your `alembic_engine` is being + defined/registered. Note that this path must be the full path, relative to the root location + at which pytest is being invoked. + + This option has replaced :code:`pytest_alembic_tests_folder` due to changes in how pytest test collection + needed to be performed in around pytest ~7.0. + + Additionally, this option is only required if you are using the :code:`--test-alembic` flag. Alembic Config diff --git a/examples/test_experimental_all_models_register_namespace_package/conftest.py b/examples/test_experimental_all_models_register_namespace_package/conftest.py new file mode 100644 index 0000000..e69de29 diff --git a/pyproject.toml b/pyproject.toml index 745b85c..677ccf7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "pytest-alembic" -version = "0.10.0" +version = "0.10.1" description = "A pytest plugin for verifying alembic migrations." authors = [ "Dan Cardin ", diff --git a/src/pytest_alembic/plugin/hooks.py b/src/pytest_alembic/plugin/hooks.py index b21adf3..e5612ab 100644 --- a/src/pytest_alembic/plugin/hooks.py +++ b/src/pytest_alembic/plugin/hooks.py @@ -26,13 +26,12 @@ def pytest_addoption(parser): f"included. Valid options include: {experimental_tests}", ) parser.addini( - "pytest_alembic_tests_folder", - "The location under which the built-in tests will be bound. This defaults to 'tests/' " - "(the tests themselves then being executed from tests/pytest_alembic/*), the typical test " - "location. However this can be customized if pytest is, for example, invoked from a parent " - "directory like `pytest folder/tests`, or the tests are otherwise located at a different " - "location, relative to `pytest`s invocation.", - default="tests", + "pytest_alembic_tests_path", + "The location at which the built-in tests will be bound. This defaults to 'tests/conftest.py'. " + "Typically, you would want this to coincide with the path at which your `alembic_engine` is being " + "defined/registered. Note that this path must be the full path, relative to the root location " + "at which pytest is being invoked.", + default="tests/conftest.py", ) group = parser.getgroup("collect") @@ -49,6 +48,12 @@ def pytest_addoption(parser): help=f"List of built-in tests to exclude. Valid options include: {default_tests}", dest="pytest_alembic_exclude", ) + group.addoption( + "--alembic-tests-path", + default="tests/conftest.py", + help="The location at which the built-in tests will be bound.", + dest="pytest_alembic_tests_path", + ) def pytest_configure(config): diff --git a/src/pytest_alembic/plugin/plugin.py b/src/pytest_alembic/plugin/plugin.py index 977fd50..791038a 100644 --- a/src/pytest_alembic/plugin/plugin.py +++ b/src/pytest_alembic/plugin/plugin.py @@ -1,6 +1,6 @@ import re from dataclasses import dataclass -from pathlib import Path +from pathlib import Path, PurePath from typing import Callable, Dict, List, Optional import pytest @@ -31,12 +31,16 @@ def pytest_collect_file(self, path, parent): # type: ignore return TestCollector.from_parent(parent, fspath=path) def should_register(self, path): - if path.suffix != ".py": - return False - - if not self.registered: - self.registered = True - return True + tests_path = PurePath( + self.config.option.pytest_alembic_tests_path + or self.config.getini("pytest_alembic_tests_path") + or "tests/conftest.py" + ) + relative_path = path.relative_to(self.config.rootpath) + if relative_path == tests_path: + if not self.registered: + self.registered = True + return True return False diff --git a/tests/plugin/test_plugin.py b/tests/plugin/test_plugin.py index ca061e3..3ced733 100644 --- a/tests/plugin/test_plugin.py +++ b/tests/plugin/test_plugin.py @@ -2,6 +2,13 @@ from pytest_alembic.plugin.plugin import OptionResolver, parse_test_names +pytest_options = ( + "--test-alembic", + "--alembic-tests-path", + "conftest.py", + "-vv", +) + def test_parse_raw_test_names_empty_skips(): result = sorted(parse_test_names("up_down_consistency,foo\n\n\nbar\n")) @@ -88,7 +95,7 @@ def test_disabled_cli(self, testdir): def test_include_cfg(self, testdir): testdir.copy_example("test_no_data") testdir.makefile(".ini", pytest="[pytest]\npytest_alembic_include=single_head_revision\n") - result = testdir.runpytest("--test-alembic", "-vv") + result = testdir.runpytest(*pytest_options) stdout = result.stdout.str() print(stdout) @@ -98,7 +105,7 @@ def test_include_cfg(self, testdir): def test_exclude_cfg(self, testdir): testdir.copy_example("test_no_data") testdir.makefile(".ini", pytest="[pytest]\npytest_alembic_exclude=single_head_revision\n") - result = testdir.runpytest("--test-alembic", "-vv") + result = testdir.runpytest(*pytest_options) stdout = result.stdout.str() print(stdout) @@ -107,7 +114,7 @@ def test_exclude_cfg(self, testdir): def test_included_tests_start_with_tests(self, testdir): testdir.copy_example("test_no_data") - result = testdir.runpytest("--test-alembic", "-vv") + result = testdir.runpytest(*pytest_options) stdout = result.stdout.str() print(stdout) diff --git a/tests/test_runner.py b/tests/test_runner.py index 2629c9c..69f70c4 100644 --- a/tests/test_runner.py +++ b/tests/test_runner.py @@ -4,6 +4,8 @@ def run_pytest(pytester, *, success=True, passed=4, skipped=0, failed=0, test_alembic=True): args = [ "--test-alembic", + "--alembic-tests-path", + "conftest.py", "-vv", "-s", ]