mirror of
https://github.com/run-llama/workflows-py.git
synced 2026-07-19 08:46:21 -04:00
293 lines
7.3 KiB
Python
293 lines
7.3 KiB
Python
# SPDX-License-Identifier: MIT
|
|
# Copyright (c) 2026 LlamaIndex Inc.
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
from pathlib import Path
|
|
from unittest.mock import Mock, patch
|
|
from urllib.error import HTTPError
|
|
|
|
import pytest
|
|
|
|
from dev_cli.changesets import (
|
|
PackageJson,
|
|
PyProjectContainer,
|
|
current_version,
|
|
is_published,
|
|
pep440_to_semver,
|
|
semver_to_pep440,
|
|
sync_package_version_with_pyproject,
|
|
)
|
|
|
|
|
|
def test_current_version(tmp_path: Path) -> None:
|
|
pyproject = tmp_path / "pyproject.toml"
|
|
pyproject.write_text(
|
|
"""
|
|
[project]
|
|
name = "test-package"
|
|
version = "1.2.3"
|
|
dependencies = []
|
|
""".strip()
|
|
)
|
|
name, version = current_version(pyproject)
|
|
assert name == "test-package"
|
|
assert version == "1.2.3"
|
|
|
|
|
|
def test_current_version_normalizes_version(tmp_path: Path) -> None:
|
|
pyproject = tmp_path / "pyproject.toml"
|
|
pyproject.write_text(
|
|
"""
|
|
[project]
|
|
name = "test-package"
|
|
version = "01.02.03"
|
|
dependencies = []
|
|
""".strip()
|
|
)
|
|
name, version = current_version(pyproject)
|
|
assert name == "test-package"
|
|
assert version == "1.2.3"
|
|
|
|
|
|
def test_pyproject_container_parse() -> None:
|
|
toml_text = """
|
|
[project]
|
|
name = "my-package"
|
|
version = "0.1.0"
|
|
dependencies = ["requests>=2.0.0"]
|
|
""".strip()
|
|
toml_doc, py_doc = PyProjectContainer.parse(toml_text)
|
|
assert py_doc.project.name == "my-package"
|
|
assert py_doc.project.version == "0.1.0"
|
|
assert py_doc.project.dependencies == ["requests>=2.0.0"]
|
|
|
|
|
|
def test_is_published_returns_true_when_version_exists() -> None:
|
|
mock_response = Mock()
|
|
mock_response.read.return_value = json.dumps(
|
|
{"releases": {"1.0.0": [], "1.1.0": []}}
|
|
).encode()
|
|
|
|
with patch("urllib.request.urlopen", return_value=mock_response):
|
|
result = is_published("test-package", "1.0.0")
|
|
assert result is True
|
|
|
|
|
|
def test_is_published_returns_false_when_version_missing() -> None:
|
|
mock_response = Mock()
|
|
mock_response.read.return_value = json.dumps({"releases": {"1.0.0": []}}).encode()
|
|
|
|
with patch("urllib.request.urlopen", return_value=mock_response):
|
|
result = is_published("test-package", "1.1.0")
|
|
assert result is False
|
|
|
|
|
|
def test_is_published_returns_false_when_package_not_found() -> None:
|
|
mock_error = HTTPError("url", 404, "Not Found", {}, None) # type: ignore[arg-type]
|
|
|
|
with patch("urllib.request.urlopen", side_effect=mock_error):
|
|
result = is_published("nonexistent-package", "1.0.0")
|
|
assert result is False
|
|
|
|
|
|
def test_is_published_raises_on_other_http_errors() -> None:
|
|
mock_error = HTTPError("url", 500, "Internal Server Error", {}, None) # type: ignore[arg-type]
|
|
|
|
with patch("urllib.request.urlopen", side_effect=mock_error):
|
|
with pytest.raises(HTTPError) as exc_info:
|
|
is_published("test-package", "1.0.0")
|
|
assert exc_info.value.code == 500
|
|
|
|
|
|
def test_sync_package_version_updates_version(tmp_path: Path) -> None:
|
|
package_dir = tmp_path / "package"
|
|
package_dir.mkdir()
|
|
pyproject = package_dir / "pyproject.toml"
|
|
pyproject.write_text(
|
|
"""
|
|
[project]
|
|
name = "test-package"
|
|
version = "1.0.0"
|
|
dependencies = []
|
|
""".strip()
|
|
)
|
|
|
|
packages = {
|
|
"test-js-package": PackageJson(
|
|
name="test-js-package",
|
|
version="2.0.0",
|
|
path=package_dir,
|
|
private=False,
|
|
)
|
|
}
|
|
|
|
sync_package_version_with_pyproject(package_dir, packages, "test-js-package")
|
|
|
|
_, py_doc = PyProjectContainer.parse(pyproject.read_text())
|
|
assert py_doc.project.version == "2.0.0"
|
|
|
|
|
|
def test_sync_package_version_skips_when_no_pyproject(tmp_path: Path) -> None:
|
|
package_dir = tmp_path / "package"
|
|
package_dir.mkdir()
|
|
|
|
packages = {
|
|
"test-js-package": PackageJson(
|
|
name="test-js-package",
|
|
version="2.0.0",
|
|
path=package_dir,
|
|
private=False,
|
|
)
|
|
}
|
|
|
|
# Should not raise an error
|
|
sync_package_version_with_pyproject(package_dir, packages, "test-js-package")
|
|
|
|
|
|
def test_sync_package_version_skips_when_versions_match(tmp_path: Path) -> None:
|
|
package_dir = tmp_path / "package"
|
|
package_dir.mkdir()
|
|
pyproject = package_dir / "pyproject.toml"
|
|
original_content = """
|
|
[project]
|
|
name = "test-package"
|
|
version = "2.0.0"
|
|
dependencies = []
|
|
""".strip()
|
|
pyproject.write_text(original_content)
|
|
|
|
packages = {
|
|
"test-js-package": PackageJson(
|
|
name="test-js-package",
|
|
version="2.0.0",
|
|
path=package_dir,
|
|
private=False,
|
|
)
|
|
}
|
|
|
|
sync_package_version_with_pyproject(package_dir, packages, "test-js-package")
|
|
|
|
# Content should be unchanged
|
|
assert pyproject.read_text() == original_content
|
|
|
|
|
|
def test_sync_package_version_converts_semver_prerelease(tmp_path: Path) -> None:
|
|
package_dir = tmp_path / "package"
|
|
package_dir.mkdir()
|
|
pyproject = package_dir / "pyproject.toml"
|
|
pyproject.write_text(
|
|
"""
|
|
[project]
|
|
name = "test-package"
|
|
version = "1.0.0"
|
|
dependencies = []
|
|
""".strip()
|
|
)
|
|
|
|
packages = {
|
|
"test-js-package": PackageJson(
|
|
name="test-js-package",
|
|
version="1.2.3-a.4",
|
|
path=package_dir,
|
|
private=False,
|
|
)
|
|
}
|
|
|
|
sync_package_version_with_pyproject(package_dir, packages, "test-js-package")
|
|
|
|
_, py_doc = PyProjectContainer.parse(pyproject.read_text())
|
|
assert py_doc.project.version == "1.2.3a4"
|
|
|
|
|
|
# -- semver_to_pep440 tests --
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
("semver", "expected"),
|
|
[
|
|
("1.2.3-a.0", "1.2.3a0"),
|
|
("1.2.3-a.4", "1.2.3a4"),
|
|
("2.0.0-b.1", "2.0.0b1"),
|
|
("0.1.0-rc.3", "0.1.0rc3"),
|
|
("10.20.30-a.99", "10.20.30a99"),
|
|
],
|
|
)
|
|
def test_semver_to_pep440_prerelease(semver: str, expected: str) -> None:
|
|
assert semver_to_pep440(semver) == expected
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"version",
|
|
[
|
|
"1.2.3",
|
|
"0.0.1",
|
|
"10.20.30",
|
|
],
|
|
)
|
|
def test_semver_to_pep440_stable_passthrough(version: str) -> None:
|
|
assert semver_to_pep440(version) == version
|
|
|
|
|
|
def test_semver_to_pep440_rejects_non_pep440_label() -> None:
|
|
with pytest.raises(ValueError, match="Unsupported pre-release label 'alpha'"):
|
|
semver_to_pep440("1.2.3-alpha.1")
|
|
|
|
|
|
# -- pep440_to_semver tests --
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
("pep440", "expected"),
|
|
[
|
|
("1.2.3a0", "1.2.3-a.0"),
|
|
("1.2.3a4", "1.2.3-a.4"),
|
|
("2.0.0b1", "2.0.0-b.1"),
|
|
("0.1.0rc3", "0.1.0-rc.3"),
|
|
("10.20.30a99", "10.20.30-a.99"),
|
|
],
|
|
)
|
|
def test_pep440_to_semver_prerelease(pep440: str, expected: str) -> None:
|
|
assert pep440_to_semver(pep440) == expected
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"version",
|
|
[
|
|
"1.2.3",
|
|
"0.0.1",
|
|
"10.20.30",
|
|
],
|
|
)
|
|
def test_pep440_to_semver_stable_passthrough(version: str) -> None:
|
|
assert pep440_to_semver(version) == version
|
|
|
|
|
|
# -- roundtrip tests --
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"semver",
|
|
[
|
|
"1.2.3-a.4",
|
|
"2.0.0-b.1",
|
|
"0.1.0-rc.3",
|
|
],
|
|
)
|
|
def test_roundtrip_semver_to_pep440_and_back(semver: str) -> None:
|
|
pep440 = semver_to_pep440(semver)
|
|
assert pep440_to_semver(pep440) == semver
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"pep440",
|
|
[
|
|
"1.2.3a4",
|
|
"2.0.0b1",
|
|
"0.1.0rc3",
|
|
],
|
|
)
|
|
def test_roundtrip_pep440_to_semver_and_back(pep440: str) -> None:
|
|
semver = pep440_to_semver(pep440)
|
|
assert semver_to_pep440(semver) == pep440
|