Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 9 additions & 5 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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..)
uses: snok/install-poetry@v1
- name: Install and configure Poetry
uses: snok/install-poetry@76e04a911780d5b312d89783f7b1cd627778900a # 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 $(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
run: poetry build
# Upload artifacts for later use
Expand Down
2 changes: 0 additions & 2 deletions .github/workflows/check-semantic-pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
4 changes: 1 addition & 3 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down
Empty file added tests/e2e/__init__.py
Empty file.
Empty file.
65 changes: 65 additions & 0 deletions tests/e2e/reqstool_python_poetry_plugin/test_build_e2e.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# Copyright © LFV
import os
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,
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}"

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
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"
Empty file added tests/integration/__init__.py
Empty file.
Empty file.