railiance-platform/tests/test_forgejo_package_prune.py

98 lines
4 KiB
Python
Raw Normal View History

from __future__ import annotations
import importlib.util
import sys
import unittest
from pathlib import Path
from unittest import mock
REPO_DIR = Path(__file__).resolve().parents[1]
SPEC = importlib.util.spec_from_file_location(
"forgejo_package_prune", REPO_DIR / "scripts/forgejo_package_prune.py"
)
prune = importlib.util.module_from_spec(SPEC)
assert SPEC.loader is not None
sys.modules[SPEC.name] = prune
SPEC.loader.exec_module(prune)
class ForgejoPackagePruneTests(unittest.TestCase):
def test_collect_protected_versions_from_helm_values(self) -> None:
import tempfile
with tempfile.TemporaryDirectory() as tmpdir:
root = Path(tmpdir)
helm = root / "helm"
helm.mkdir()
(helm / "vergabe-teilnahme-values.yaml").write_text(
"""
image:
repository: forgejo.coulomb.social/coulomb/vergabe-teilnahme
tag: "064d295"
""".lstrip(),
encoding="utf-8",
)
protected = prune.collect_protected_versions(root)
self.assertIn(("container", "vergabe-teilnahme", "064d295"), protected)
def test_build_delete_plans_respects_depth_and_protection(self) -> None:
protected = {("container", "vergabe-teilnahme", "old-prod")}
# Forgejo's package list endpoint returns one entry per (name, version);
# build_delete_plans groups these by name (no separate /versions call).
packages = [
{"name": "vergabe-teilnahme", "version": "v4", "created_at": "2026-07-12T10:00:00Z"},
{"name": "vergabe-teilnahme", "version": "v3", "created_at": "2026-07-11T10:00:00Z"},
{"name": "vergabe-teilnahme", "version": "v2", "created_at": "2026-07-10T10:00:00Z"},
{"name": "vergabe-teilnahme", "version": "v1", "created_at": "2026-07-09T10:00:00Z"},
{"name": "vergabe-teilnahme", "version": "old-prod", "created_at": "2026-07-08T10:00:00Z"},
]
with mock.patch.object(prune, "list_packages", return_value=packages):
plans, errors = prune.build_delete_plans(
base_url="https://forgejo.example",
token="token",
owner="coulomb",
package_types=["container"],
max_versions=3,
protected=protected,
)
self.assertEqual(errors, [])
deletable = {(p.name, p.version) for p in plans if not p.protected}
protected_plans = {(p.name, p.version) for p in plans if p.protected}
self.assertEqual(deletable, {("vergabe-teilnahme", "v1")})
self.assertEqual(protected_plans, {("vergabe-teilnahme", "old-prod")})
def test_collect_live_cluster_versions_protects_forgejo_tags(self) -> None:
images = "\n".join([
"forgejo.coulomb.social/coulomb/state-hub:f2e042a",
"forgejo.coulomb.social/coulomb/vergabe-teilnahme:064d295",
"registry.k8s.io/pause:3.9", # unrelated registry → ignored
"gitea.coulomb.social/coulomb/old-app:x", # non-forgejo → ignored
"",
])
with mock.patch.object(prune.shutil, "which", return_value="/usr/bin/kubectl"), \
mock.patch.object(
prune.subprocess, "run",
return_value=mock.Mock(stdout=images, returncode=0),
):
protected, notes = prune.collect_live_cluster_versions()
self.assertEqual(notes, [])
self.assertEqual(
protected,
{
("container", "state-hub", "f2e042a"),
("container", "vergabe-teilnahme", "064d295"),
},
)
def test_collect_live_cluster_versions_skips_without_kubectl(self) -> None:
with mock.patch.object(prune.shutil, "which", return_value=None):
protected, notes = prune.collect_live_cluster_versions()
self.assertEqual(protected, set())
self.assertTrue(notes and "kubectl not found" in notes[0])
if __name__ == "__main__":
unittest.main()