From bf714a4bbeafaad11cabd98307f4f765cd1c50da Mon Sep 17 00:00:00 2001 From: Jimisola Laursen Date: Wed, 4 Mar 2026 12:27:35 +0100 Subject: [PATCH 1/5] test: add e2e test for sdist build and standardise pytest config - Add tests/e2e/ with a full sdist-build e2e test that verifies the plugin bundles all reqstool artifacts and annotations.yml contains expected decorator IDs (REQ_001, SVC_001) - Add tests/integration/ stub directory for future integration tests - Fix addopts: remove broken '-m not slow or not integration' filter; use @pytest.mark.skipif on each test for conditional execution - Add integration and e2e markers; drop unused flaky/slow markers - Update CI: bump poetry to 2.3.2, build wheel first, install plugin via 'poetry self add', run full pytest with --junitxml --- .github/workflows/build.yml | 12 ++-- pyproject.toml | 4 +- tests/e2e/__init__.py | 0 .../reqstool_python_poetry_plugin/__init__.py | 0 .../test_build_e2e.py | 64 +++++++++++++++++++ tests/integration/__init__.py | 0 .../reqstool_python_poetry_plugin/__init__.py | 0 7 files changed, 73 insertions(+), 7 deletions(-) create mode 100644 tests/e2e/__init__.py create mode 100644 tests/e2e/reqstool_python_poetry_plugin/__init__.py create mode 100644 tests/e2e/reqstool_python_poetry_plugin/test_build_e2e.py create mode 100644 tests/integration/__init__.py create mode 100644 tests/integration/reqstool_python_poetry_plugin/__init__.py diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a6de2ae..f76e219 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -28,17 +28,21 @@ jobs: uses: actions/setup-python@v6 with: python-version: "3.13" - - name: Install and configure Poetry (this should ideally be done from pyproject.toml but..) + - name: Install and configure Poetry uses: snok/install-poetry@v1 with: - version: 1.8.5 + version: 2.3.2 virtualenvs-create: true - name: Install poetry-dynamic-versioning run: poetry self add "poetry-dynamic-versioning[plugin]" - name: Install dependencies run: poetry install --with dev - - name: Run unit and integrations tests - run: poetry run pytest --junitxml=build/junit.xml --cov --cov-report=xml:build/coverage.xml tests + - name: Build wheel (used by e2e tests) + run: poetry build --format wheel + - name: Install plugin into Poetry (activates it for e2e tests) + run: poetry self add dist/reqstool_python_poetry_plugin-*.whl + - name: Run tests + run: poetry run pytest --junitxml=build/junit.xml --cov --cov-report=xml:build/coverage.xml - name: Build project run: poetry build # Upload artifacts for later use diff --git a/pyproject.toml b/pyproject.toml index 15d1075..c32d7c3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -45,15 +45,13 @@ addopts = [ "-s", "--import-mode=importlib", "--log-cli-level=DEBUG", - '-m not slow or not integration', ] pythonpath = [".", "src", "tests"] testpaths = ["tests"] norecursedirs = ["tests/fixtures"] markers = [ - "flaky: tests that can randomly fail through no change to the code", - "slow: marks tests as slow (deselect with '-m \"not slow\"')", "integration: tests that require external resources", + "e2e: end-to-end tests that run the full pipeline locally", ] [tool.black] diff --git a/tests/e2e/__init__.py b/tests/e2e/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/e2e/reqstool_python_poetry_plugin/__init__.py b/tests/e2e/reqstool_python_poetry_plugin/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/e2e/reqstool_python_poetry_plugin/test_build_e2e.py b/tests/e2e/reqstool_python_poetry_plugin/test_build_e2e.py new file mode 100644 index 0000000..030ed7e --- /dev/null +++ b/tests/e2e/reqstool_python_poetry_plugin/test_build_e2e.py @@ -0,0 +1,64 @@ +# Copyright © LFV +import shutil +import subprocess +import tarfile +import tempfile +from pathlib import Path + +import pytest + +FIXTURE_DIR = Path(__file__).parents[2] / "fixtures" / "test_project" + +EXPECTED_IN_TARBALL = [ + "reqstool_config.yml", + "annotations.yml", + "requirements.yml", + "software_verification_cases.yml", +] + + +def _plugin_installed() -> bool: + """Return True if the reqstool poetry plugin is registered as an application plugin.""" + from importlib.metadata import entry_points + + eps = entry_points(group="poetry.application.plugin") + return any(ep.name == "reqstool" for ep in eps) + + +@pytest.mark.e2e +@pytest.mark.skipif(not shutil.which("poetry"), reason="poetry not on PATH") +@pytest.mark.skipif(not _plugin_installed(), reason="reqstool-python-poetry-plugin not installed in poetry") +def test_poetry_build_sdist_contains_reqstool_artifacts(): + """poetry build (sdist) triggers the reqstool plugin and bundles all artifacts.""" + with tempfile.TemporaryDirectory() as tmpdir: + tmp_project = Path(tmpdir) / "test_project" + shutil.copytree( + FIXTURE_DIR, + tmp_project, + ignore=shutil.ignore_patterns("dist", "build", "__pycache__", ".venv", "poetry.lock"), + ) + + result = subprocess.run( + ["poetry", "build", "--format", "sdist"], + cwd=tmp_project, + capture_output=True, + text=True, + ) + assert result.returncode == 0, f"poetry build failed:\n{result.stderr}" + + tarballs = sorted((tmp_project / "dist").glob("mypackage-*.tar.gz")) + assert tarballs, "No tarball found in dist/" + + with tarfile.open(tarballs[-1]) as tf: + names = tf.getnames() + + for expected in EXPECTED_IN_TARBALL: + assert any(expected in n for n in names), f"{expected!r} missing from {tarballs[-1].name};\ngot: {names}" + + # Verify annotations.yml content — confirms the decorator processor ran + with tarfile.open(tarballs[-1]) as tf: + member = next(m for m in tf.getmembers() if "annotations.yml" in m.name) + annotations_content = tf.extractfile(member).read().decode() + + assert "REQ_001" in annotations_content, "annotations.yml missing REQ_001" + assert "SVC_001" in annotations_content, "annotations.yml missing SVC_001" diff --git a/tests/integration/__init__.py b/tests/integration/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/integration/reqstool_python_poetry_plugin/__init__.py b/tests/integration/reqstool_python_poetry_plugin/__init__.py new file mode 100644 index 0000000..e69de29 From 13b6ae4d2ce53dcaf08129f393afd85951169455 Mon Sep 17 00:00:00 2001 From: Jimisola Laursen Date: Wed, 4 Mar 2026 12:35:50 +0100 Subject: [PATCH 2/5] fix(ci): use absolute path for poetry self add to install local wheel --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f76e219..953a220 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -40,7 +40,7 @@ jobs: - name: Build wheel (used by e2e tests) run: poetry build --format wheel - name: Install plugin into Poetry (activates it for e2e tests) - run: poetry self add dist/reqstool_python_poetry_plugin-*.whl + run: poetry self add $(ls $PWD/dist/reqstool_python_poetry_plugin-*.whl | tail -1) - name: Run tests run: poetry run pytest --junitxml=build/junit.xml --cov --cov-report=xml:build/coverage.xml - name: Build project From ff47948ca36725e6be301872f2a277821fd77545 Mon Sep 17 00:00:00 2001 From: Jimisola Laursen Date: Wed, 4 Mar 2026 13:41:18 +0100 Subject: [PATCH 3/5] fix(ci): remove pull_request_target trigger from semantic PR check --- .github/workflows/check-semantic-pr.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/check-semantic-pr.yml b/.github/workflows/check-semantic-pr.yml index 5bacd57..459b868 100644 --- a/.github/workflows/check-semantic-pr.yml +++ b/.github/workflows/check-semantic-pr.yml @@ -2,8 +2,6 @@ name: Check Semantic PR on: pull_request: types: [opened, edited, synchronize, reopened] - pull_request_target: - types: [opened, edited, synchronize, reopened] jobs: check: From 97e5bdfbb2b1a6535eb6fdb93ba8b6c2a5d5be6f Mon Sep 17 00:00:00 2001 From: Jimisola Laursen Date: Wed, 4 Mar 2026 13:57:07 +0100 Subject: [PATCH 4/5] fix(tests): strip POETRY_ACTIVE env var and merge tarfile opens in e2e test --- .../reqstool_python_poetry_plugin/test_build_e2e.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/tests/e2e/reqstool_python_poetry_plugin/test_build_e2e.py b/tests/e2e/reqstool_python_poetry_plugin/test_build_e2e.py index 030ed7e..c38075c 100644 --- a/tests/e2e/reqstool_python_poetry_plugin/test_build_e2e.py +++ b/tests/e2e/reqstool_python_poetry_plugin/test_build_e2e.py @@ -1,4 +1,5 @@ # Copyright © LFV +import os import shutil import subprocess import tarfile @@ -43,6 +44,7 @@ def test_poetry_build_sdist_contains_reqstool_artifacts(): cwd=tmp_project, capture_output=True, text=True, + env={k: v for k, v in os.environ.items() if k != "POETRY_ACTIVE"}, ) assert result.returncode == 0, f"poetry build failed:\n{result.stderr}" @@ -51,12 +53,11 @@ def test_poetry_build_sdist_contains_reqstool_artifacts(): with tarfile.open(tarballs[-1]) as tf: names = tf.getnames() - - for expected in EXPECTED_IN_TARBALL: - assert any(expected in n for n in names), f"{expected!r} missing from {tarballs[-1].name};\ngot: {names}" - - # Verify annotations.yml content — confirms the decorator processor ran - with tarfile.open(tarballs[-1]) as tf: + for expected in EXPECTED_IN_TARBALL: + assert any( + expected in n for n in names + ), f"{expected!r} missing from {tarballs[-1].name};\ngot: {names}" + # Verify annotations.yml content — confirms the decorator processor ran member = next(m for m in tf.getmembers() if "annotations.yml" in m.name) annotations_content = tf.extractfile(member).read().decode() From aabeb0082333b4a044f54957448d699563ba7fe1 Mon Sep 17 00:00:00 2001 From: Jimisola Laursen Date: Fri, 6 Mar 2026 22:14:50 +0100 Subject: [PATCH 5/5] fix(ci): pin snok/install-poetry to commit hash for immutability --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 953a220..3740004 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -29,7 +29,7 @@ jobs: with: python-version: "3.13" - name: Install and configure Poetry - uses: snok/install-poetry@v1 + uses: snok/install-poetry@76e04a911780d5b312d89783f7b1cd627778900a # v1 with: version: 2.3.2 virtualenvs-create: true