80 lines
3.4 KiB
Python
80 lines
3.4 KiB
Python
"""Unit tests for `run_recipe_ci.upgrade_base` — the deploy-once base-version decision.
|
|
|
|
Phase bsky: a recipe whose published versions ALL pin a moving image tag that upstream
|
|
republished with incompatible builds (bluesky-pds) has no deployable upgrade base — deploying
|
|
one fails the INSTALL tier before the PR head is ever exercised. The sanctioned escape hatch is
|
|
the EXISTING declared-intentional-skip mechanism: EXPECTED_NA["upgrade"] now also suppresses
|
|
the base deploy (single deploy = PR head; the tier records "skip"; derive_rungs classifies it
|
|
intentional with the declared reason). These tests lock the decision matrix; derive_rungs'
|
|
classification of the resulting skip is already covered in test_results.py.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import os
|
|
import sys
|
|
from types import SimpleNamespace
|
|
|
|
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "..", "runner"))
|
|
import run_recipe_ci # noqa: E402
|
|
from harness import lifecycle # noqa: E402
|
|
|
|
ALL = {"install", "upgrade", "backup", "restore", "custom"}
|
|
|
|
|
|
def _meta(expected_na=None, upgrade_base_version=None):
|
|
return SimpleNamespace(EXPECTED_NA=expected_na, UPGRADE_BASE_VERSION=upgrade_base_version)
|
|
|
|
|
|
def test_default_prev_published(monkeypatch):
|
|
# upgrade in stages, ≥2 published versions, nothing declared → recipe_versions[-2]
|
|
monkeypatch.setattr(lifecycle, "previous_version", lambda r: "0.1.0+v1")
|
|
assert run_recipe_ci.upgrade_base(ALL, _meta(), "somerecipe") == "0.1.0+v1"
|
|
|
|
|
|
def test_single_published_version_no_base(monkeypatch):
|
|
monkeypatch.setattr(lifecycle, "previous_version", lambda r: None)
|
|
assert run_recipe_ci.upgrade_base(ALL, _meta(), "somerecipe") is None
|
|
|
|
|
|
def test_upgrade_not_in_stages_no_base(monkeypatch):
|
|
monkeypatch.setattr(
|
|
lifecycle,
|
|
"previous_version",
|
|
lambda r: (_ for _ in ()).throw(AssertionError("not consulted")),
|
|
)
|
|
assert run_recipe_ci.upgrade_base(ALL - {"upgrade"}, _meta(), "somerecipe") is None
|
|
|
|
|
|
def test_upgrade_base_version_override_wins(monkeypatch):
|
|
monkeypatch.setattr(
|
|
lifecycle,
|
|
"previous_version",
|
|
lambda r: (_ for _ in ()).throw(AssertionError("not consulted")),
|
|
)
|
|
meta = _meta(upgrade_base_version="0.7.0+3.3.1")
|
|
assert run_recipe_ci.upgrade_base(ALL, meta, "discourse") == "0.7.0+3.3.1"
|
|
|
|
|
|
def test_expected_na_upgrade_suppresses_base(monkeypatch):
|
|
# bluesky-pds shape: published versions exist but are undeployable — declared EXPECTED_NA
|
|
# upgrade → NO base (single deploy is the PR head), even though previous_version would
|
|
# return one and even if UPGRADE_BASE_VERSION is set (the declaration is the stronger,
|
|
# documented fact).
|
|
monkeypatch.setattr(lifecycle, "previous_version", lambda r: "0.1.1+v0.4")
|
|
declared = {"upgrade": "no deployable upgrade base (moving tag republished)"}
|
|
assert run_recipe_ci.upgrade_base(ALL, _meta(expected_na=declared), "bluesky-pds") is None
|
|
assert (
|
|
run_recipe_ci.upgrade_base(
|
|
ALL, _meta(expected_na=declared, upgrade_base_version="0.2.0+v0.4"), "bluesky-pds"
|
|
)
|
|
is None
|
|
)
|
|
|
|
|
|
def test_expected_na_other_rung_does_not_suppress(monkeypatch):
|
|
# an EXPECTED_NA for backup_restore (custom-html-tiny shape) must NOT touch the upgrade base
|
|
monkeypatch.setattr(lifecycle, "previous_version", lambda r: "0.1.0+v1")
|
|
meta = _meta(expected_na={"backup_restore": "stateless"})
|
|
assert run_recipe_ci.upgrade_base(ALL, meta, "custom-html-tiny") == "0.1.0+v1"
|