ci: execute tagged module pytest suites
This commit is contained in:
@@ -28,8 +28,7 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
python -m venv .venv
|
python -m venv .venv
|
||||||
.venv/bin/python tools/repo/sync-python-environment.py --requirements requirements-release.txt --python .venv/bin/python --upgrade-pip
|
.venv/bin/python tools/repo/sync-python-environment.py --requirements requirements-release.txt --python .venv/bin/python --upgrade-pip
|
||||||
.venv/bin/python -m pip install '../govoplan-core[dev]'
|
.venv/bin/python -m pip install -r requirements-release-tests.txt '../govoplan-core[dev]'
|
||||||
.venv/bin/python -m pip install -r requirements-release-tests.txt
|
|
||||||
- name: Install WebUI release dependencies
|
- name: Install WebUI release dependencies
|
||||||
working-directory: govoplan
|
working-directory: govoplan
|
||||||
run: bash tools/release/install-webui-release-dependencies.sh ../govoplan-core/webui
|
run: bash tools/release/install-webui-release-dependencies.sh ../govoplan-core/webui
|
||||||
|
|||||||
@@ -32,5 +32,6 @@ idna>=3.15
|
|||||||
jsonschema>=4,<5
|
jsonschema>=4,<5
|
||||||
pip>=26.1.2
|
pip>=26.1.2
|
||||||
pip-audit>=2.9,<3
|
pip-audit>=2.9,<3
|
||||||
|
pytest>=8,<9
|
||||||
python-multipart>=0.0.31
|
python-multipart>=0.0.31
|
||||||
ruff>=0.14,<1
|
ruff>=0.14,<1
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import os
|
||||||
import tempfile
|
import tempfile
|
||||||
import unittest
|
import unittest
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
from unittest.mock import patch
|
||||||
|
|
||||||
from tools.checks.release_integration import (
|
from tools.checks.release_integration import (
|
||||||
SOURCE_COUPLED_CORE_TESTS,
|
SOURCE_COUPLED_CORE_TESTS,
|
||||||
@@ -10,6 +12,7 @@ from tools.checks.release_integration import (
|
|||||||
artifact_contract_issues,
|
artifact_contract_issues,
|
||||||
filter_test_suite,
|
filter_test_suite,
|
||||||
release_package_names,
|
release_package_names,
|
||||||
|
run_module_release_tests,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -89,8 +92,43 @@ class ReleaseIntegrationTests(unittest.TestCase):
|
|||||||
|
|
||||||
self.assertLess(artifact_check, core_tests)
|
self.assertLess(artifact_check, core_tests)
|
||||||
self.assertLess(core_tests, module_tests)
|
self.assertLess(core_tests, module_tests)
|
||||||
|
self.assertNotIn("unittest discover", script)
|
||||||
self.assertNotIn('"$PYTHON" -m unittest \\\n tests.test_module_system', script)
|
self.assertNotIn('"$PYTHON" -m unittest \\\n tests.test_module_system', script)
|
||||||
|
|
||||||
|
def test_tagged_module_runner_executes_pytest_functions_from_module_root(self) -> None:
|
||||||
|
with tempfile.TemporaryDirectory(prefix="govoplan-release-module-tests-") as temp_dir:
|
||||||
|
module_root = Path(temp_dir)
|
||||||
|
tests_root = module_root / "tests"
|
||||||
|
tests_root.mkdir()
|
||||||
|
marker = module_root / "pytest-function-ran"
|
||||||
|
(tests_root / "test_probe.py").write_text(
|
||||||
|
"""from pathlib import Path
|
||||||
|
import os
|
||||||
|
|
||||||
|
|
||||||
|
def test_pytest_style_function():
|
||||||
|
expected_root = Path(os.environ[\"GOVOPLAN_TEST_MODULE_ROOT\"])
|
||||||
|
assert Path.cwd() == expected_root
|
||||||
|
Path(os.environ[\"GOVOPLAN_TEST_MARKER\"]).write_text(\"ran\", encoding=\"utf-8\")
|
||||||
|
""",
|
||||||
|
encoding="utf-8",
|
||||||
|
)
|
||||||
|
with patch.dict(
|
||||||
|
os.environ,
|
||||||
|
{
|
||||||
|
"GOVOPLAN_TEST_MODULE_ROOT": str(module_root),
|
||||||
|
"GOVOPLAN_TEST_MARKER": str(marker),
|
||||||
|
},
|
||||||
|
):
|
||||||
|
status = run_module_release_tests(module_root)
|
||||||
|
|
||||||
|
self.assertEqual(status, 0)
|
||||||
|
self.assertEqual(marker.read_text(encoding="utf-8"), "ran")
|
||||||
|
|
||||||
|
def test_tagged_module_runner_skips_missing_test_tree(self) -> None:
|
||||||
|
with tempfile.TemporaryDirectory(prefix="govoplan-release-module-tests-") as temp_dir:
|
||||||
|
self.assertEqual(run_module_release_tests(Path(temp_dir)), 0)
|
||||||
|
|
||||||
def test_module_matrix_uses_artifact_aware_core_suite(self) -> None:
|
def test_module_matrix_uses_artifact_aware_core_suite(self) -> None:
|
||||||
meta_root = Path(__file__).resolve().parents[1]
|
meta_root = Path(__file__).resolve().parents[1]
|
||||||
script = (meta_root / "tools" / "checks" / "check-module-matrix.sh").read_text(encoding="utf-8")
|
script = (meta_root / "tools" / "checks" / "check-module-matrix.sh").read_text(encoding="utf-8")
|
||||||
|
|||||||
@@ -60,24 +60,6 @@ retry() {
|
|||||||
done
|
done
|
||||||
}
|
}
|
||||||
|
|
||||||
run_discovered_tests() {
|
|
||||||
local suite_dir="$1"
|
|
||||||
local status
|
|
||||||
if ! find "$suite_dir" -type f -name 'test*.py' -print -quit | grep -q .; then
|
|
||||||
echo "No unittest test files found under $suite_dir; skipping."
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
set +e
|
|
||||||
"$PYTHON" -m unittest discover -s "$suite_dir"
|
|
||||||
status=$?
|
|
||||||
set -e
|
|
||||||
if [[ "$status" == 5 ]]; then
|
|
||||||
echo "No unittest tests discovered under $suite_dir; skipping."
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
return "$status"
|
|
||||||
}
|
|
||||||
|
|
||||||
install_cloned_webui_dependencies() {
|
install_cloned_webui_dependencies() {
|
||||||
local webui_dir="$1"
|
local webui_dir="$1"
|
||||||
if [[ ! -f "$webui_dir/package.json" ]]; then
|
if [[ ! -f "$webui_dir/package.json" ]]; then
|
||||||
@@ -253,9 +235,10 @@ run_step "Run core backend release tests"
|
|||||||
"$PYTHON" "$META_ROOT/tools/checks/check_dependency_boundaries.py"
|
"$PYTHON" "$META_ROOT/tools/checks/check_dependency_boundaries.py"
|
||||||
|
|
||||||
run_step "Run cloned module backend tests"
|
run_step "Run cloned module backend tests"
|
||||||
run_discovered_tests "$WORK_ROOT/govoplan-mail/tests"
|
for repo in govoplan-mail govoplan-calendar govoplan-campaign; do
|
||||||
run_discovered_tests "$WORK_ROOT/govoplan-calendar/tests"
|
"$PYTHON" "$META_ROOT/tools/checks/release_integration.py" module-tests \
|
||||||
run_discovered_tests "$WORK_ROOT/govoplan-campaign/tests"
|
--module-root "$WORK_ROOT/$repo"
|
||||||
|
done
|
||||||
|
|
||||||
run_step "Run core WebUI release tests and build"
|
run_step "Run core WebUI release tests and build"
|
||||||
cd "$ROOT/webui"
|
cd "$ROOT/webui"
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import argparse
|
|||||||
import importlib.metadata as metadata
|
import importlib.metadata as metadata
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
import tempfile
|
import tempfile
|
||||||
import unittest
|
import unittest
|
||||||
@@ -354,6 +355,24 @@ def run_core_release_tests(core_root: Path) -> int:
|
|||||||
return 0 if result.wasSuccessful() else 1
|
return 0 if result.wasSuccessful() else 1
|
||||||
|
|
||||||
|
|
||||||
|
def run_module_release_tests(module_root: Path) -> int:
|
||||||
|
resolved_root = module_root.resolve()
|
||||||
|
tests_root = resolved_root / "tests"
|
||||||
|
if not tests_root.is_dir() or not any(tests_root.rglob("test*.py")):
|
||||||
|
print(f"No test files found under {tests_root}; skipping.")
|
||||||
|
return 0
|
||||||
|
|
||||||
|
result = subprocess.run(
|
||||||
|
(sys.executable, "-m", "pytest", "-q", "tests"),
|
||||||
|
cwd=resolved_root,
|
||||||
|
check=False,
|
||||||
|
)
|
||||||
|
if result.returncode == 5:
|
||||||
|
print(f"No tests collected under {tests_root}; skipping.")
|
||||||
|
return 0
|
||||||
|
return result.returncode
|
||||||
|
|
||||||
|
|
||||||
def _parser() -> argparse.ArgumentParser:
|
def _parser() -> argparse.ArgumentParser:
|
||||||
meta_root = Path(__file__).resolve().parents[2]
|
meta_root = Path(__file__).resolve().parents[2]
|
||||||
parser = argparse.ArgumentParser(description="Artifact-aware GovOPlaN release integration checks")
|
parser = argparse.ArgumentParser(description="Artifact-aware GovOPlaN release integration checks")
|
||||||
@@ -366,6 +385,8 @@ def _parser() -> argparse.ArgumentParser:
|
|||||||
)
|
)
|
||||||
core_parser = subparsers.add_parser("core-tests", help="Run Core tests that are valid for tagged module artifacts")
|
core_parser = subparsers.add_parser("core-tests", help="Run Core tests that are valid for tagged module artifacts")
|
||||||
core_parser.add_argument("--core-root", type=Path, required=True)
|
core_parser.add_argument("--core-root", type=Path, required=True)
|
||||||
|
module_parser = subparsers.add_parser("module-tests", help="Run tests from one tagged module source checkout")
|
||||||
|
module_parser.add_argument("--module-root", type=Path, required=True)
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
|
|
||||||
@@ -373,7 +394,9 @@ def main(argv: Sequence[str] | None = None) -> int:
|
|||||||
args = _parser().parse_args(argv)
|
args = _parser().parse_args(argv)
|
||||||
if args.command == "artifacts":
|
if args.command == "artifacts":
|
||||||
return run_installed_artifact_checks(args.requirements)
|
return run_installed_artifact_checks(args.requirements)
|
||||||
return run_core_release_tests(args.core_root)
|
if args.command == "core-tests":
|
||||||
|
return run_core_release_tests(args.core_root)
|
||||||
|
return run_module_release_tests(args.module_root)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|||||||
Reference in New Issue
Block a user