test: meta gates for architecture, CI, docs and packaging
This commit is contained in:
parent
f1d58273b7
commit
ff09e38b7c
5 changed files with 388 additions and 0 deletions
72
tests/unit/test_packaging.py
Normal file
72
tests/unit/test_packaging.py
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
"""Packaging invariants — version/solver single-sourcing and dependency hygiene.
|
||||
|
||||
Pure static checks over the repository files. No imports of :mod:`otko`,
|
||||
Qt or the solver stack, so this test runs anywhere (including Windows 3.12).
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import re
|
||||
from pathlib import Path
|
||||
|
||||
try: # Python 3.11+
|
||||
import tomllib
|
||||
except ModuleNotFoundError: # pragma: no cover - Python 3.10 backport
|
||||
import tomli as tomllib
|
||||
|
||||
REPO_ROOT = Path(__file__).resolve().parents[2]
|
||||
SRC_ROOT = REPO_ROOT / "src" / "otko"
|
||||
PYPROJECT = REPO_ROOT / "pyproject.toml"
|
||||
|
||||
_VERSION_RE = re.compile(r"^__version__\s*=", re.MULTILINE)
|
||||
|
||||
|
||||
def _pyproject() -> dict:
|
||||
with PYPROJECT.open("rb") as handle:
|
||||
return tomllib.load(handle)
|
||||
|
||||
|
||||
def _src_files() -> list[Path]:
|
||||
return sorted(SRC_ROOT.rglob("*.py"))
|
||||
|
||||
|
||||
def _dep_name(requirement: str) -> str:
|
||||
"""Return the lowercased distribution name from a PEP 508 requirement."""
|
||||
match = re.match(r"^\s*([A-Za-z0-9][A-Za-z0-9._-]*)", requirement)
|
||||
return match.group(1).lower().replace("_", "-") if match else ""
|
||||
|
||||
|
||||
def test_console_script_entrypoint() -> None:
|
||||
assert _pyproject()["project"]["scripts"]["otko"] == "otko.__main__:main"
|
||||
|
||||
|
||||
def test_project_version_is_dynamic() -> None:
|
||||
project = _pyproject()["project"]
|
||||
assert "version" not in project
|
||||
assert project["dynamic"] == ["version"]
|
||||
|
||||
|
||||
def test_single_version_assignment() -> None:
|
||||
hits = [path for path in _src_files() if _VERSION_RE.search(path.read_text(encoding="utf-8"))]
|
||||
assert len(hits) == 1, f"expected exactly one __version__ assignment, got {hits}"
|
||||
assert hits[0] == SRC_ROOT / "_const.py"
|
||||
|
||||
|
||||
def test_solver_version_literal_only_in_const() -> None:
|
||||
hits = [path for path in _src_files() if "3.8.0.0" in path.read_text(encoding="utf-8")]
|
||||
assert hits == [SRC_ROOT / "_const.py"], f"unexpected 3.8.0.0 occurrences: {hits}"
|
||||
|
||||
|
||||
def test_ruff_line_length_and_ignore() -> None:
|
||||
ruff = _pyproject()["tool"]["ruff"]
|
||||
assert ruff["line-length"] == 100
|
||||
assert "E501" in ruff["lint"]["ignore"]
|
||||
|
||||
|
||||
def test_no_phantom_scipy_pandas_dependencies() -> None:
|
||||
project = _pyproject()["project"]
|
||||
groups = [project.get("dependencies", [])]
|
||||
groups.extend(project.get("optional-dependencies", {}).values())
|
||||
declared = {_dep_name(req) for group in groups for req in group}
|
||||
assert "scipy" not in declared
|
||||
assert "pandas" not in declared
|
||||
Loading…
Reference in a new issue