utils/dep_freshness/tests/test_versions.py
Krzysztof kuhy Rudnicki 28d3fb1361 dep_freshness: accept PEP 440 post pins
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-11-01 22:26:45 +01:00

128 lines
4.6 KiB
Python

"""The fail-green class: a pre-release accepted as latest stable."""
from __future__ import annotations
import pytest
from dep_freshness.versions import (
behind,
exact_pin,
is_prerelease,
newest_stable,
reference,
split_tag_prefix,
)
@pytest.mark.parametrize("raw", [
"2.0.0-beta.1", "1.0.0-rc1", "3.0.0-dev.4", "2.0.0rc1", "1.2.3a1",
"v4.0.0-alpha", "1.0.0.dev3",
])
def test_prereleases_are_recognised(raw):
assert is_prerelease(raw)
@pytest.mark.parametrize("raw", ["1.0.0", "10.3.0", "v2.1.5", "0.14.4", "1.6.0+1"])
def test_stable_versions_are_not_prereleases(raw):
assert not is_prerelease(raw)
def test_newest_stable_skips_prereleases():
assert newest_stable(["1.0.0", "2.0.0-beta.1", "1.9.3"]) == "1.9.3"
def test_newest_stable_is_none_when_everything_is_a_prerelease():
assert newest_stable(["1.0.0-a", "2.0.0-b"]) is None
def test_newest_stable_ignores_unparseable_entries():
assert newest_stable(["not-a-version", "", None, "1.2.0"]) == "1.2.0"
@pytest.mark.parametrize("raw,expected", [
("1.6.0", "1.6.0"), ("==1.6.0", "1.6.0"), ("v2.0.0", "2.0.0"),
(" 1.0.0 ", "1.0.0"), ("1.9", "1.9"), ("3", "3"),
# PyPI stub packages date-stamp a fourth component. Capping the regex at
# three reported this exact pin as unpinned *against its own value*.
("==2.33.0.20260712", "2.33.0.20260712"),
# PEP 440 post/dev/pre releases are exact pins too (pytest-asyncio).
("1.4.1.post1", "1.4.1.post1"), ("==1.4.1.post1", "1.4.1.post1"),
("2.0.post3", "2.0.post3"), ("1.0.dev2", "1.0.dev2"),
("==2.0rc1", "2.0rc1"), ("1.2.3a1", "1.2.3a1"), ("1.2b4", "1.2b4"),
("1.0.post1.dev2", "1.0.post1.dev2"),
])
def test_exact_pins_are_recognised(raw, expected):
assert exact_pin(raw) == expected
@pytest.mark.parametrize("raw", ["^1.6.0", ">=1.6.0", "any", "*", "", ">=1,<2", "~1.2",
">=1.4.1.post1", "~=1.4", "1.4.post", "1.4.postx", "1.4.1.post1,<2",
])
def test_ranges_are_not_exact_pins(raw):
assert exact_pin(raw) is None
def test_behind_compares_versions():
assert behind("1.6.0", "1.7.0")
assert not behind("1.7.0", "1.7.0")
assert not behind("1.8.0", "1.7.0")
def test_a_post_release_is_newer_than_its_base_release():
assert behind("1.4.1", "1.4.1.post1")
assert not behind("1.4.1.post1", "1.4.1")
assert not is_prerelease("1.4.1.post1")
assert newest_stable(["1.4.1", "1.4.1.post1", "1.4.0"]) == "1.4.1.post1"
def test_behind_never_fires_on_unparseable_input():
"""An unpinned constraint is UNPINNED, never dressed up as staleness."""
assert not behind("^1.6.0", "1.7.0")
assert not behind("1.6.0", "latest")
@pytest.mark.parametrize("raw,expected", [
("natural-comparator-1.1", ("natural-comparator-", "1.1")),
("release-v2.0", ("release-", "v2.0")),
("2.3.0-rc1", ("", "2.3.0-rc1")),
("v1.0.0", ("", "v1.0.0")),
("alpha", ("", "alpha")),
])
def test_a_project_named_tag_splits_into_prefix_and_version(raw, expected):
"""JitPack serves java-nat-sort only as `natural-comparator-1.1`."""
assert split_tag_prefix(raw) == expected
def test_a_project_named_tag_is_a_stable_version_with_its_prefix_kept():
assert not is_prerelease("natural-comparator-1.1")
assert is_prerelease("natural-comparator-1.2-rc1")
assert newest_stable(["natural-comparator-1.1", "natural-comparator-1.0"]) == (
"natural-comparator-1.1"
)
assert behind("natural-comparator-1.0", "natural-comparator-1.1")
assert exact_pin("natural-comparator-1.1") == "natural-comparator-1.1"
assert exact_pin("version-v1.2") == "version-1.2"
def test_reference_is_newest_stable_when_one_exists():
assert reference(["1.0.0", "2.0.0-alpha01"]) == "1.0.0"
def test_reference_falls_back_to_newest_prerelease_only_when_nothing_is_stable():
"""androidx.biometric:biometric-ktx: alpha-only since 2021."""
assert reference(["1.2.0-alpha05", "1.4.0-alpha02", "1.3.0-alpha01"]) == "1.4.0-alpha02"
assert reference(iter(["1.4.0-alpha02", "junk"])) == "1.4.0-alpha02"
assert reference([]) is None
assert reference(["junk", ""]) is None
def test_an_api_generation_is_not_a_project_name():
"""Google's `v3-rev197-1.25.0`: splitting on `v3-rev197-` compared the
Drive v3 client against a v2 artifact and called v2 newer."""
assert split_tag_prefix("v3-rev197-1.25.0") == ("", "v3-rev197-1.25.0")
assert is_prerelease("v3-rev197-1.25.0")
assert newest_stable(["v3-rev197-1.25.0", "v2-rev20220709-2.0.0"]) is None
# ... and the no-stable fallback must not pick one up either: loosely
# parsed, every v3 revision is "3" and the 2019 pin reads as current.
assert reference(["v3-rev197-1.25.0", "v3-rev20250723-2.0.0"]) is None