chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,156 @@
|
||||
"""
|
||||
Unit tests for ``scripts/update_versions.py`` (the lockstep version bumper).
|
||||
|
||||
The ``repo_copy`` fixture copies the repo's *real* ``pyproject.toml``
|
||||
files into a temp tree, so the regex anchors are exercised against the
|
||||
actual file formatting — a drift in either the script or the files
|
||||
fails here.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import importlib.util
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
_REPO_ROOT = Path(__file__).resolve().parents[2]
|
||||
|
||||
# ``scripts`` is a namespace package (no ``__init__.py``), so a bare
|
||||
# ``from scripts import update_versions`` is shadowed by the regular
|
||||
# ``tests/scripts`` package when both resolve as a top-level ``scripts`` during
|
||||
# a full-suite collection (pytest's default "prepend" import mode), failing with
|
||||
# ``ImportError: cannot import name 'update_versions' from 'scripts'``. Load the
|
||||
# module by its repo-root file path instead, which is immune to the collision.
|
||||
_UPDATE_VERSIONS_SPEC = importlib.util.spec_from_file_location(
|
||||
"_update_versions_under_test", _REPO_ROOT / "scripts" / "update_versions.py"
|
||||
)
|
||||
assert _UPDATE_VERSIONS_SPEC is not None and _UPDATE_VERSIONS_SPEC.loader is not None
|
||||
update_versions = importlib.util.module_from_spec(_UPDATE_VERSIONS_SPEC)
|
||||
# Register before exec so dataclasses defined in the module can resolve their
|
||||
# defining module via ``sys.modules`` during class creation.
|
||||
sys.modules[_UPDATE_VERSIONS_SPEC.name] = update_versions
|
||||
_UPDATE_VERSIONS_SPEC.loader.exec_module(update_versions)
|
||||
_PYPROJECTS = [
|
||||
"pyproject.toml",
|
||||
"sdks/python-client/pyproject.toml",
|
||||
"sdks/ui/pyproject.toml",
|
||||
]
|
||||
# The runtime version constant is stamped/verified alongside the pyprojects.
|
||||
_VERSION_PY = "omnigent/version.py"
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def repo_copy(tmp_path: Path) -> Path:
|
||||
"""Copy the real pyproject.toml files + version.py into a temp repo root."""
|
||||
root = tmp_path / "repo"
|
||||
for rel in (*_PYPROJECTS, _VERSION_PY):
|
||||
dst = root / rel
|
||||
dst.parent.mkdir(parents=True, exist_ok=True)
|
||||
dst.write_text((_REPO_ROOT / rel).read_text())
|
||||
return root
|
||||
|
||||
|
||||
def test_set_version_rewrites_every_location(repo_copy: Path) -> None:
|
||||
changed = update_versions.set_version(repo_copy, "9.9.9")
|
||||
# Three pyprojects plus omnigent/version.py.
|
||||
assert len(changed) == 4
|
||||
# root: version line + two sibling pins; SDKs: version line + one pin.
|
||||
assert (repo_copy / "pyproject.toml").read_text().count("9.9.9") == 3
|
||||
assert (repo_copy / "sdks/python-client/pyproject.toml").read_text().count("9.9.9") == 2
|
||||
assert (repo_copy / "sdks/ui/pyproject.toml").read_text().count("9.9.9") == 2
|
||||
# The runtime constant is stamped too.
|
||||
assert 'VERSION = "9.9.9"' in (repo_copy / _VERSION_PY).read_text()
|
||||
# check() round-trips: all agree and pins are exact.
|
||||
assert update_versions.check(repo_copy, expect="9.9.9") == "9.9.9"
|
||||
|
||||
|
||||
def test_set_version_updates_runtime_constant(repo_copy: Path) -> None:
|
||||
"""The bump path keeps omnigent/version.py's VERSION in lockstep.
|
||||
|
||||
This is the gap that would otherwise make the automated bot bump commit a
|
||||
stale constant and trip the ``test_version_matches_pyproject`` backstop.
|
||||
"""
|
||||
version_py = repo_copy / _VERSION_PY
|
||||
assert 'VERSION = "9.9.9"' not in version_py.read_text()
|
||||
changed = update_versions.set_version(repo_copy, "9.9.9")
|
||||
assert version_py in changed
|
||||
assert 'VERSION = "9.9.9"' in version_py.read_text()
|
||||
|
||||
|
||||
def test_check_detects_version_py_drift(repo_copy: Path) -> None:
|
||||
"""A stale VERSION constant (pyprojects consistent) fails check()."""
|
||||
update_versions.set_version(repo_copy, "9.9.9")
|
||||
version_py = repo_copy / _VERSION_PY
|
||||
version_py.write_text(version_py.read_text().replace('VERSION = "9.9.9"', 'VERSION = "9.9.8"'))
|
||||
with pytest.raises(ValueError, match=r"omnigent/version\.py VERSION"):
|
||||
update_versions.check(repo_copy)
|
||||
|
||||
|
||||
def test_set_version_fails_loud_when_constant_absent(repo_copy: Path) -> None:
|
||||
"""A version.py missing the VERSION line must raise, not silently no-op."""
|
||||
version_py = repo_copy / _VERSION_PY
|
||||
version_py.write_text('"""No constant here."""\n')
|
||||
with pytest.raises(ValueError, match="expected exactly 1 match"):
|
||||
update_versions.set_version(repo_copy, "9.9.9")
|
||||
|
||||
|
||||
def test_set_version_preserves_unrelated_version_literals(repo_copy: Path) -> None:
|
||||
root_pyproject = repo_copy / "pyproject.toml"
|
||||
before = root_pyproject.read_text()
|
||||
# Real third-party floor that shares the old version digits — must
|
||||
# survive a bump untouched (anchored-on-name replacement, not blind).
|
||||
assert '"databricks-mcp>=0.1.0",' in before
|
||||
update_versions.set_version(repo_copy, "9.9.9")
|
||||
assert '"databricks-mcp>=0.1.0",' in root_pyproject.read_text()
|
||||
|
||||
|
||||
def test_check_detects_version_drift(repo_copy: Path) -> None:
|
||||
update_versions.set_version(repo_copy, "9.9.9")
|
||||
# Knock one package out of lockstep but keep it internally consistent
|
||||
# (version + its own sibling pin both move) so the cross-package
|
||||
# disagreement is what surfaces, not a missing pin.
|
||||
ui = repo_copy / "sdks/ui/pyproject.toml"
|
||||
ui.write_text(ui.read_text().replace("9.9.9", "9.9.8"))
|
||||
with pytest.raises(ValueError, match="disagree"):
|
||||
update_versions.check(repo_copy)
|
||||
|
||||
|
||||
def test_check_detects_missing_pin(repo_copy: Path) -> None:
|
||||
update_versions.set_version(repo_copy, "9.9.9")
|
||||
# Break the sibling pin while leaving the version intact.
|
||||
client = repo_copy / "sdks/python-client/pyproject.toml"
|
||||
client.write_text(client.read_text().replace('"omnigent==9.9.9"', '"omnigent==9.9.8"'))
|
||||
with pytest.raises(ValueError, match="missing exact pin"):
|
||||
update_versions.check(repo_copy)
|
||||
|
||||
|
||||
def test_set_version_fails_loud_when_line_absent(tmp_path: Path) -> None:
|
||||
# A pyproject missing the version line must raise, not silently no-op.
|
||||
root = tmp_path / "repo"
|
||||
(root / "sdks/python-client").mkdir(parents=True)
|
||||
(root / "sdks/ui").mkdir(parents=True)
|
||||
(root / "pyproject.toml").write_text('[project]\nname = "omnigent"\n')
|
||||
(root / "sdks/python-client/pyproject.toml").write_text("[project]\n")
|
||||
(root / "sdks/ui/pyproject.toml").write_text("[project]\n")
|
||||
with pytest.raises(ValueError, match="expected exactly 1 match"):
|
||||
update_versions.set_version(root, "9.9.9")
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("released", "expected"),
|
||||
[
|
||||
("0.1.2", "0.1.3.dev0"),
|
||||
("1.0.0", "1.0.1.dev0"),
|
||||
("0.1.2rc1", "0.1.3.dev0"),
|
||||
("2.5.9", "2.5.10.dev0"),
|
||||
],
|
||||
)
|
||||
def test_next_dev_version(released: str, expected: str) -> None:
|
||||
assert update_versions.next_dev_version(released) == expected
|
||||
|
||||
|
||||
def test_validate_pep440_rejects_junk() -> None:
|
||||
with pytest.raises(SystemExit, match="invalid version"):
|
||||
update_versions._validate_pep440("not-a-version")
|
||||
Reference in New Issue
Block a user