Files
workflows-py/tests/dev_cli/test_changesets.py
T

1099 lines
33 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 (
DockerConfig,
HelmConfig,
PackageJson,
PyProjectContainer,
_resolve_template,
_write_toml_values,
_write_yaml_values,
apply_sync_values,
current_version,
docker_image_tags,
is_docker_image_published,
is_helm_chart_published,
is_published,
is_rc_version,
maybe_publish_docker,
maybe_publish_helm,
pep440_to_semver,
semver_to_pep440,
)
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()
)
pkg = PackageJson(
name="test-js-package",
version="2.0.0",
path=package_dir,
private=False,
)
apply_sync_values(pkg, {"test-js-package": pkg})
_, 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()
pkg = PackageJson(
name="test-js-package",
version="2.0.0",
path=package_dir,
private=False,
)
changed = apply_sync_values(pkg, {"test-js-package": pkg})
assert changed is False
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)
pkg = PackageJson(
name="test-js-package",
version="2.0.0",
path=package_dir,
private=False,
)
apply_sync_values(pkg, {"test-js-package": pkg})
# 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()
)
pkg = PackageJson(
name="test-js-package",
version="1.2.3-a.4",
path=package_dir,
private=False,
)
apply_sync_values(pkg, {"test-js-package": pkg})
_, 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
# -- chart version sync tests --
def _make_helm_package(
path: Path, version: str = "0.4.14", name: str = "llama-agents"
) -> PackageJson:
return PackageJson(
name=name,
version=version,
path=path,
private=True,
helm=HelmConfig(registry="oci://docker.io/llamaindex"),
)
def test_sync_chart_version_updates_chart_yaml(tmp_path: Path) -> None:
chart_yaml = tmp_path / "Chart.yaml"
chart_yaml.write_text('apiVersion: v2\nname: llama-agents\nversion: "0.4.13"\n')
pkg = _make_helm_package(tmp_path, version="0.4.14")
changed = apply_sync_values(pkg, {pkg.name: pkg})
assert changed is True
content = chart_yaml.read_text()
assert "0.4.14" in content
def test_sync_chart_version_no_change_when_already_matching(tmp_path: Path) -> None:
chart_yaml = tmp_path / "Chart.yaml"
chart_yaml.write_text('apiVersion: v2\nname: llama-agents\nversion: "0.4.14"\n')
pkg = _make_helm_package(tmp_path, version="0.4.14")
changed = apply_sync_values(pkg, {pkg.name: pkg})
assert changed is False
def test_sync_chart_version_returns_false_when_no_chart(tmp_path: Path) -> None:
pkg = _make_helm_package(tmp_path, version="0.4.14")
changed = apply_sync_values(pkg, {pkg.name: pkg})
assert changed is False
def test_apply_sync_values_bakes_image_tags(tmp_path: Path) -> None:
"""syncValues on the chart resolves dependency docker tags into values.yaml."""
chart_yaml = tmp_path / "Chart.yaml"
chart_yaml.write_text('apiVersion: v2\nname: llama-agents\nversion: "0.4.13"\n')
values_yaml = tmp_path / "values.yaml"
values_yaml.write_text(
"""images:
controlPlane:
repository: llamaindex/llama-agents-control-plane
tag: "0.4.13"
pullPolicy: IfNotPresent
operator:
repository: llamaindex/llama-agents-operator
tag: "operator-0.4.13"
pullPolicy: IfNotPresent
appserver:
repository: llamaindex/llama-agents-appserver
tag: "appserver-0.4.13"
pullPolicy: IfNotPresent
"""
)
chart_pkg = PackageJson(
name="llama-agents",
version="0.4.14",
path=tmp_path,
private=True,
helm=HelmConfig(registry="oci://docker.io/llamaindex"),
syncValues={
"values.yaml": {
"images.controlPlane.tag": "{llama-agents-control-plane:dockerTag}",
"images.operator.tag": "{llama-agents-operator:dockerTag}",
"images.appserver.tag": "{llama-agents-appserver:dockerTag}",
}
},
)
workspace = {
"llama-agents": chart_pkg,
"llama-agents-control-plane": PackageJson(
name="llama-agents-control-plane",
version="0.4.14",
path=Path("/fake/cp"),
private=False,
docker=DockerConfig(
dockerfile="docker/Dockerfile",
target="controlplane",
imageName="llamaindex/llama-agents-control-plane",
platforms=["linux/amd64"],
),
),
"llama-agents-operator": PackageJson(
name="llama-agents-operator",
version="0.4.15",
path=Path("/fake/op"),
private=False,
docker=DockerConfig(
dockerfile="docker/operator.Dockerfile",
imageName="llamaindex/llama-agents-operator",
platforms=["linux/amd64"],
),
),
"llama-agents-appserver": PackageJson(
name="llama-agents-appserver",
version="0.4.16",
path=Path("/fake/as"),
private=False,
docker=DockerConfig(
dockerfile="docker/Dockerfile",
target="appserver",
imageName="llamaindex/llama-agents-appserver",
platforms=["linux/amd64"],
),
),
}
changed = apply_sync_values(chart_pkg, workspace)
assert changed is True
content = values_yaml.read_text()
assert "0.4.14" in content # controlPlane
assert "0.4.15" in content
assert "0.4.16" in content
# Chart.yaml version also updated via implicit helm sync
assert "0.4.14" in chart_yaml.read_text()
# -- is_rc_version tests --
@pytest.mark.parametrize(
("version", "expected"),
[
("0.4.14", False),
("1.0.0", False),
("0.4.14-rc.1", True),
("0.4.14-a.1", True),
("0.4.14-b.1", True),
("1.0.0rc1", True),
("1.0.0a1", True),
("1.0.0b1", True),
],
)
def test_is_rc_version(version: str, expected: bool) -> None:
assert is_rc_version(version) == expected
# -- is_docker_image_published tests --
def test_is_docker_image_published_returns_true() -> None:
with patch("urllib.request.urlopen"):
result = is_docker_image_published(
"llamaindex/llama-agents-appserver", "0.4.14"
)
assert result is True
def test_is_docker_image_published_returns_false_on_404() -> None:
mock_error = HTTPError("url", 404, "Not Found", {}, None) # type: ignore[arg-type]
with patch("urllib.request.urlopen", side_effect=mock_error):
result = is_docker_image_published(
"llamaindex/llama-agents-appserver", "0.4.14"
)
assert result is False
def test_is_docker_image_published_raises_on_500() -> None:
mock_error = HTTPError("url", 500, "Server Error", {}, None) # type: ignore[arg-type]
with patch("urllib.request.urlopen", side_effect=mock_error):
with pytest.raises(HTTPError) as exc_info:
is_docker_image_published("llamaindex/llama-agents-appserver", "0.4.14")
assert exc_info.value.code == 500
# -- docker_image_tags tests --
def test_docker_image_tags_non_rc() -> None:
image = DockerConfig(
dockerfile="docker/Dockerfile",
target="controlplane",
platforms=["linux/amd64", "linux/arm64"],
imageName="llamaindex/llama-agents-control-plane",
)
tags = docker_image_tags(image, "0.4.14", is_rc=False)
assert tags == [
"docker.io/llamaindex/llama-agents-control-plane:0.4.14",
"docker.io/llamaindex/llama-agents-control-plane:latest",
"docker.io/llamaindex/llama-agents-control-plane:0.4",
]
def test_docker_image_tags_rc() -> None:
image = DockerConfig(
dockerfile="docker/Dockerfile",
target="controlplane",
platforms=["linux/amd64", "linux/arm64"],
imageName="llamaindex/llama-agents-control-plane",
)
tags = docker_image_tags(image, "0.4.14-rc.1", is_rc=True)
assert tags == ["docker.io/llamaindex/llama-agents-control-plane:0.4.14-rc.1"]
def test_docker_image_tags_appserver() -> None:
image = DockerConfig(
dockerfile="docker/Dockerfile",
target="appserver",
platforms=["linux/amd64", "linux/arm64"],
imageName="llamaindex/llama-agents-appserver",
)
tags = docker_image_tags(image, "0.4.14", is_rc=False)
assert tags == [
"docker.io/llamaindex/llama-agents-appserver:0.4.14",
"docker.io/llamaindex/llama-agents-appserver:latest",
"docker.io/llamaindex/llama-agents-appserver:0.4",
]
# -- _read_package_json_config tests --
def test_read_package_json_config_docker(tmp_path: Path) -> None:
from dev_cli.changesets import _read_package_json_config
pkg_json = tmp_path / "package.json"
pkg_json.write_text(
json.dumps(
{
"name": "test-pkg",
"docker": {
"dockerfile": "docker/Dockerfile",
"target": "myapp",
"imageName": "llamaindex/test-myapp",
"platforms": ["linux/amd64"],
},
}
)
)
config = _read_package_json_config(tmp_path)
assert config is not None
assert config.docker is not None
assert config.docker.dockerfile == "docker/Dockerfile"
assert config.docker.target == "myapp"
assert config.docker.imageName == "llamaindex/test-myapp"
assert config.docker.platforms == ["linux/amd64"]
def test_read_package_json_config_helm(tmp_path: Path) -> None:
from dev_cli.changesets import _read_package_json_config
pkg_json = tmp_path / "package.json"
pkg_json.write_text(
json.dumps(
{"name": "my-chart", "helm": {"registry": "oci://docker.io/llamaindex"}}
)
)
config = _read_package_json_config(tmp_path)
assert config is not None
assert config.helm is not None
assert config.helm.registry == "oci://docker.io/llamaindex"
def test_read_package_json_config_defaults(tmp_path: Path) -> None:
from dev_cli.changesets import _read_package_json_config
pkg_json = tmp_path / "package.json"
pkg_json.write_text(json.dumps({"name": "test-pkg"}))
config = _read_package_json_config(tmp_path)
assert config is not None
assert config.docker is None
assert config.helm is None
def test_read_package_json_config_returns_none_when_no_file(tmp_path: Path) -> None:
from dev_cli.changesets import _read_package_json_config
assert _read_package_json_config(tmp_path) is None
def test_read_package_json_config_docker_missing_dockerfile(tmp_path: Path) -> None:
from pydantic import ValidationError
from dev_cli.changesets import _read_package_json_config
pkg_json = tmp_path / "package.json"
pkg_json.write_text(json.dumps({"name": "test-pkg", "docker": {"target": "app"}}))
with pytest.raises(ValidationError):
_read_package_json_config(tmp_path)
def test_read_package_json_config_helm_missing_registry(tmp_path: Path) -> None:
from pydantic import ValidationError
from dev_cli.changesets import _read_package_json_config
pkg_json = tmp_path / "package.json"
pkg_json.write_text(json.dumps({"name": "my-chart", "helm": {}}))
with pytest.raises(ValidationError):
_read_package_json_config(tmp_path)
# -- maybe_publish_docker tests --
def _make_docker_packages() -> list[PackageJson]:
"""Create test PackageJson instances with Docker configs."""
return [
PackageJson(
name="llama-agents-control-plane",
version="0.4.14",
path=Path("/fake/controlplane"),
private=False,
docker=DockerConfig(
dockerfile="docker/Dockerfile",
target="controlplane",
platforms=["linux/amd64", "linux/arm64"],
imageName="llamaindex/llama-agents-control-plane",
),
),
PackageJson(
name="llama-agents-operator",
version="0.4.14",
path=Path("/fake/operator"),
private=False,
docker=DockerConfig(
dockerfile="docker/operator.Dockerfile",
platforms=["linux/amd64"],
imageName="llamaindex/llama-agents-operator",
),
),
PackageJson(
name="llama-agents-appserver",
version="0.4.14",
path=Path("/fake/appserver"),
private=False,
docker=DockerConfig(
dockerfile="docker/Dockerfile",
target="appserver",
platforms=["linux/amd64", "linux/arm64"],
imageName="llamaindex/llama-agents-appserver",
),
),
]
def test_maybe_publish_docker_skips_when_already_published() -> None:
with (
patch("dev_cli.changesets.shutil.which", return_value="/usr/bin/docker"),
patch("dev_cli.changesets.is_docker_image_published", return_value=True),
patch("dev_cli.changesets.build_and_push_docker_image") as mock_build,
):
maybe_publish_docker(dry_run=False, packages=_make_docker_packages())
mock_build.assert_not_called()
def test_maybe_publish_docker_builds_when_not_published() -> None:
packages = _make_docker_packages()
with (
patch("dev_cli.changesets.shutil.which", return_value="/usr/bin/docker"),
patch("dev_cli.changesets.is_docker_image_published", return_value=False),
patch("dev_cli.changesets.build_and_push_docker_image") as mock_build,
):
maybe_publish_docker(dry_run=False, packages=packages)
assert mock_build.call_count == len(packages)
def test_maybe_publish_docker_dry_run_does_not_build() -> None:
with (
patch("dev_cli.changesets.shutil.which", return_value="/usr/bin/docker"),
patch("dev_cli.changesets.is_docker_image_published", return_value=False),
patch("dev_cli.changesets.build_and_push_docker_image") as mock_build,
):
maybe_publish_docker(dry_run=True, packages=_make_docker_packages())
mock_build.assert_not_called()
def test_maybe_publish_docker_skips_when_docker_not_found() -> None:
with (
patch("dev_cli.changesets.shutil.which", return_value=None),
patch("dev_cli.changesets.is_docker_image_published") as mock_check,
):
maybe_publish_docker(dry_run=False, packages=_make_docker_packages())
mock_check.assert_not_called()
def test_maybe_publish_docker_skips_packages_without_docker() -> None:
"""Packages without docker config should be silently skipped."""
packages = [
PackageJson(
name="no-docker-pkg",
version="1.0.0",
path=Path("/fake/no-docker"),
private=True,
docker=None,
),
]
with (
patch("dev_cli.changesets.shutil.which", return_value="/usr/bin/docker"),
patch("dev_cli.changesets.is_docker_image_published") as mock_check,
patch("dev_cli.changesets.build_and_push_docker_image") as mock_build,
):
maybe_publish_docker(dry_run=False, packages=packages)
mock_check.assert_not_called()
mock_build.assert_not_called()
def test_maybe_publish_docker_uses_per_package_version() -> None:
"""Each Docker image uses its own package version, not a shared one."""
packages = [
PackageJson(
name="pkg-a",
version="1.0.0",
path=Path("/fake/a"),
private=False,
docker=DockerConfig(
dockerfile="docker/Dockerfile",
target="a",
platforms=["linux/amd64"],
imageName="llamaindex/test-a",
),
),
PackageJson(
name="pkg-b",
version="2.0.0",
path=Path("/fake/b"),
private=False,
docker=DockerConfig(
dockerfile="docker/Dockerfile",
target="b",
platforms=["linux/amd64"],
imageName="llamaindex/test-b",
),
),
]
with (
patch("dev_cli.changesets.shutil.which", return_value="/usr/bin/docker"),
patch("dev_cli.changesets.is_docker_image_published", return_value=False),
patch("dev_cli.changesets.build_and_push_docker_image") as mock_build,
):
maybe_publish_docker(dry_run=False, packages=packages)
assert mock_build.call_count == 2
# First call uses pkg-a's version 1.0.0
first_tags = mock_build.call_args_list[0][0][1]
assert any("1.0.0" in t for t in first_tags)
# Second call uses pkg-b's version 2.0.0
second_tags = mock_build.call_args_list[1][0][1]
assert any("2.0.0" in t for t in second_tags)
# -- maybe_publish_helm tests --
def _make_helm_packages() -> list[PackageJson]:
"""Create test PackageJson instances with Helm configs."""
return [
PackageJson(
name="llama-agents",
version="0.4.14",
path=Path("/fake/charts/llama-agents"),
private=False,
helm=HelmConfig(registry="oci://docker.io/llamaindex"),
),
PackageJson(
name="llama-agents-crds",
version="0.4.14",
path=Path("/fake/charts/llama-agents-crds"),
private=False,
helm=HelmConfig(registry="oci://docker.io/llamaindex"),
),
]
def test_maybe_publish_helm_already_published() -> None:
with (
patch("dev_cli.changesets.shutil.which", return_value="/usr/bin/helm"),
patch("dev_cli.changesets.is_helm_chart_published", return_value=True),
patch("dev_cli.changesets.run_command") as mock_run,
):
maybe_publish_helm(dry_run=False, packages=_make_helm_packages())
mock_run.assert_not_called()
def test_maybe_publish_helm_dry_run() -> None:
with (
patch("dev_cli.changesets.shutil.which", return_value="/usr/bin/helm"),
patch("dev_cli.changesets.is_helm_chart_published", return_value=False),
patch("dev_cli.changesets.run_command") as mock_run,
):
maybe_publish_helm(dry_run=True, packages=_make_helm_packages())
mock_run.assert_not_called()
def test_maybe_publish_helm_runs_commands() -> None:
with (
patch("dev_cli.changesets.shutil.which", return_value="/usr/bin/helm"),
patch("dev_cli.changesets.is_helm_chart_published", return_value=False),
patch("dev_cli.changesets.run_command") as mock_run,
):
maybe_publish_helm(dry_run=False, packages=_make_helm_packages())
# 2 charts × 2 commands each = 4
assert mock_run.call_count == 4
mock_run.assert_any_call(["helm", "package", "/fake/charts/llama-agents"])
mock_run.assert_any_call(
[
"helm",
"push",
"llama-agents-0.4.14.tgz",
"oci://docker.io/llamaindex",
]
)
mock_run.assert_any_call(["helm", "package", "/fake/charts/llama-agents-crds"])
mock_run.assert_any_call(
[
"helm",
"push",
"llama-agents-crds-0.4.14.tgz",
"oci://docker.io/llamaindex",
]
)
def test_maybe_publish_helm_skips_when_helm_not_found() -> None:
with (
patch("dev_cli.changesets.shutil.which", return_value=None),
patch("dev_cli.changesets.is_helm_chart_published") as mock_check,
):
maybe_publish_helm(dry_run=False, packages=_make_helm_packages())
mock_check.assert_not_called()
def test_maybe_publish_helm_skips_packages_without_helm() -> None:
packages = [
PackageJson(
name="no-helm-pkg",
version="1.0.0",
path=Path("/fake"),
private=True,
helm=None,
),
]
with (
patch("dev_cli.changesets.shutil.which", return_value="/usr/bin/helm"),
patch("dev_cli.changesets.is_helm_chart_published") as mock_check,
):
maybe_publish_helm(dry_run=False, packages=packages)
mock_check.assert_not_called()
def test_is_helm_chart_published_uses_chart_name() -> None:
with patch("urllib.request.urlopen") as mock_urlopen:
result = is_helm_chart_published("llama-agents-crds", "0.7.1")
assert result is True
called_url = mock_urlopen.call_args[0][0]
assert "llamaindex/llama-agents-crds" in called_url
assert "0.7.1" in called_url
# -- template resolution tests --
def _make_workspace() -> tuple[PackageJson, dict[str, PackageJson]]:
"""Create a self_pkg and workspace for template tests."""
self_pkg = PackageJson(
name="my-chart",
version="1.2.3",
path=Path("/fake/chart"),
private=True,
helm=HelmConfig(registry="oci://docker.io/llamaindex"),
)
workspace = {
"my-chart": self_pkg,
"my-app": PackageJson(
name="my-app",
version="2.0.0",
path=Path("/fake/app"),
private=False,
docker=DockerConfig(
dockerfile="docker/Dockerfile",
imageName="llamaindex/test-app",
platforms=["linux/amd64"],
),
),
"no-docker": PackageJson(
name="no-docker",
version="3.0.0",
path=Path("/fake/nodock"),
private=True,
),
}
return self_pkg, workspace
def test_resolve_template_version() -> None:
self_pkg, workspace = _make_workspace()
assert _resolve_template("{my-app:version}", self_pkg, workspace) == "2.0.0"
def test_resolve_template_self_version() -> None:
self_pkg, workspace = _make_workspace()
assert _resolve_template("{self:version}", self_pkg, workspace) == "1.2.3"
def test_resolve_template_docker_tag() -> None:
self_pkg, workspace = _make_workspace()
assert _resolve_template("{my-app:dockerTag}", self_pkg, workspace) == "2.0.0"
def test_resolve_template_pep440_version() -> None:
self_pkg, workspace = _make_workspace()
self_pkg.version = "1.2.3-a.4"
assert _resolve_template("{self:pep440Version}", self_pkg, workspace) == "1.2.3a4"
def test_resolve_template_unknown_package() -> None:
self_pkg, workspace = _make_workspace()
with pytest.raises(ValueError, match="unknown package 'nonexistent'"):
_resolve_template("{nonexistent:version}", self_pkg, workspace)
def test_resolve_template_docker_tag_no_docker() -> None:
self_pkg, workspace = _make_workspace()
with pytest.raises(ValueError, match="has no docker config"):
_resolve_template("{no-docker:dockerTag}", self_pkg, workspace)
def test_resolve_template_unknown_property() -> None:
self_pkg, workspace = _make_workspace()
with pytest.raises(ValueError, match="unknown property 'badProp'"):
_resolve_template("{my-app:badProp}", self_pkg, workspace)
# -- YAML writer tests --
def test_write_yaml_values_sets_dot_path(tmp_path: Path) -> None:
yaml_file = tmp_path / "values.yaml"
yaml_file.write_text(
"""# A comment
images:
controlPlane:
repository: llamaindex/llama-agents-control-plane
tag: "0.4.13"
pullPolicy: IfNotPresent
"""
)
changed = _write_yaml_values(yaml_file, {"images.controlPlane.tag": "0.4.14"})
assert changed is True
content = yaml_file.read_text()
assert "0.4.14" in content
assert "# A comment" in content # comments preserved
def test_write_yaml_values_preserves_comments(tmp_path: Path) -> None:
yaml_file = tmp_path / "values.yaml"
original = """# Top comment
images:
controlPlane: # the control plane
repository: llamaindex/llama-agents-control-plane
tag: "0.8.0"
pullPolicy: IfNotPresent
operator: # the operator
repository: llamaindex/llama-agents-operator
tag: "operator-0.8.0"
pullPolicy: IfNotPresent
"""
yaml_file.write_text(original)
changed = _write_yaml_values(yaml_file, {"images.controlPlane.tag": "0.9.0"})
assert changed is True
content = yaml_file.read_text()
assert "0.9.0" in content
assert "# Top comment" in content
assert "# the control plane" in content
assert "# the operator" in content
def test_write_yaml_values_no_change(tmp_path: Path) -> None:
yaml_file = tmp_path / "values.yaml"
yaml_file.write_text('version: "0.8.0"\n')
changed = _write_yaml_values(yaml_file, {"version": "0.8.0"})
assert changed is False
def test_write_yaml_values_missing_file(tmp_path: Path) -> None:
yaml_file = tmp_path / "nonexistent.yaml"
changed = _write_yaml_values(yaml_file, {"key": "val"})
assert changed is False
def test_write_yaml_values_creates_intermediate_keys(tmp_path: Path) -> None:
yaml_file = tmp_path / "test.yaml"
yaml_file.write_text("existing: value\n")
changed = _write_yaml_values(yaml_file, {"new.nested.key": "hello"})
assert changed is True
content = yaml_file.read_text()
assert "hello" in content
# -- TOML writer tests --
def test_write_toml_values_sets_dot_path(tmp_path: Path) -> None:
toml_file = tmp_path / "pyproject.toml"
toml_file.write_text(
"""[project]
name = "test"
version = "1.0.0"
"""
)
changed = _write_toml_values(toml_file, {"project.version": "2.0.0"})
assert changed is True
content = toml_file.read_text()
assert '"2.0.0"' in content
def test_write_toml_values_no_change(tmp_path: Path) -> None:
toml_file = tmp_path / "pyproject.toml"
toml_file.write_text(
"""[project]
name = "test"
version = "1.0.0"
"""
)
changed = _write_toml_values(toml_file, {"project.version": "1.0.0"})
assert changed is False
def test_write_toml_values_missing_file(tmp_path: Path) -> None:
toml_file = tmp_path / "nonexistent.toml"
changed = _write_toml_values(toml_file, {"key": "val"})
assert changed is False
# -- apply_sync_values tests --
def test_apply_sync_values_explicit_yaml(tmp_path: Path) -> None:
values_yaml = tmp_path / "values.yaml"
values_yaml.write_text(
"""images:
app:
tag: "old"
"""
)
pkg = PackageJson(
name="my-chart",
version="1.0.0",
path=tmp_path,
private=True,
syncValues={
"values.yaml": {
"images.app.tag": "{dep-app:dockerTag}",
}
},
)
workspace = {
"my-chart": pkg,
"dep-app": PackageJson(
name="dep-app",
version="2.0.0",
path=Path("/fake"),
private=False,
docker=DockerConfig(
dockerfile="Dockerfile",
imageName="llamaindex/test-dep-app",
platforms=["linux/amd64"],
),
),
}
changed = apply_sync_values(pkg, workspace)
assert changed is True
assert "2.0.0" in values_yaml.read_text()
def test_apply_sync_values_implicit_helm(tmp_path: Path) -> None:
chart_yaml = tmp_path / "Chart.yaml"
chart_yaml.write_text('apiVersion: v2\nversion: "0.1.0"\n')
pkg = PackageJson(
name="my-chart",
version="0.2.0",
path=tmp_path,
private=True,
helm=HelmConfig(registry="oci://docker.io/llamaindex"),
)
changed = apply_sync_values(pkg, {"my-chart": pkg})
assert changed is True
assert "0.2.0" in chart_yaml.read_text()
def test_apply_sync_values_implicit_pyproject(tmp_path: Path) -> None:
pyproject = tmp_path / "pyproject.toml"
pyproject.write_text('[project]\nname = "test"\nversion = "1.0.0"\n')
pkg = PackageJson(
name="test-pkg",
version="2.0.0",
path=tmp_path,
private=False,
)
changed = apply_sync_values(pkg, {"test-pkg": pkg})
assert changed is True
content = pyproject.read_text()
assert '"2.0.0"' in content