153 lines
5.9 KiB
Python
153 lines
5.9 KiB
Python
"""Unit tests for the phase-prevb `previous/` overlay surface + COMPOSE_FILE layering (lifecycle).
|
|
|
|
Covers: the version-guarded apply/skip/stale decision (`previous_status`), the VERSION-marker reader
|
|
(`previous_target_version`), folder discovery (`has_previous`), and the pure COMPOSE_FILE add/remove
|
|
helpers used to layer compose.previous.yml onto the base deploy and strip it before the head redeploy.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import os
|
|
import sys
|
|
|
|
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "..", "runner"))
|
|
from harness import lifecycle # noqa: E402
|
|
from harness import meta as meta_mod # noqa: E402
|
|
|
|
# ---- pure COMPOSE_FILE helpers --------------------------------------------------------------------
|
|
|
|
|
|
def test_compose_file_add_appends_when_absent():
|
|
assert (
|
|
lifecycle.compose_file_add("compose.yml:compose.ccci.yml", "compose.previous.yml")
|
|
== "compose.yml:compose.ccci.yml:compose.previous.yml"
|
|
)
|
|
|
|
|
|
def test_compose_file_add_idempotent():
|
|
cf = "compose.yml:compose.previous.yml"
|
|
assert lifecycle.compose_file_add(cf, "compose.previous.yml") == cf
|
|
|
|
|
|
def test_compose_file_add_defaults_missing_base():
|
|
assert (
|
|
lifecycle.compose_file_add("", "compose.previous.yml") == "compose.yml:compose.previous.yml"
|
|
)
|
|
|
|
|
|
def test_compose_file_remove_strips_only_overlay():
|
|
assert (
|
|
lifecycle.compose_file_remove(
|
|
"compose.yml:compose.ccci.yml:compose.previous.yml", "compose.previous.yml"
|
|
)
|
|
== "compose.yml:compose.ccci.yml"
|
|
)
|
|
|
|
|
|
def test_compose_file_remove_keeps_compose_yml_when_emptied():
|
|
assert (
|
|
lifecycle.compose_file_remove("compose.previous.yml", "compose.previous.yml")
|
|
== "compose.yml"
|
|
)
|
|
|
|
|
|
# ---- marker reader + discovery (filesystem, via a temp TESTS_DIR) ---------------------------------
|
|
|
|
|
|
def _stage(monkeypatch, tmp_path, recipe, *, compose=True, marker=None):
|
|
monkeypatch.setattr(meta_mod, "TESTS_DIR", str(tmp_path))
|
|
prev = tmp_path / recipe / "previous"
|
|
prev.mkdir(parents=True)
|
|
if compose:
|
|
(prev / "compose.previous.yml").write_text("services: {}\n")
|
|
if marker is not None:
|
|
(prev / "VERSION").write_text(marker)
|
|
return prev
|
|
|
|
|
|
def test_has_previous_true_only_with_compose(monkeypatch, tmp_path):
|
|
_stage(monkeypatch, tmp_path, "rx", compose=True)
|
|
assert lifecycle.has_previous("rx") is True
|
|
assert lifecycle.has_previous("other") is False
|
|
|
|
|
|
def test_previous_target_version_reads_first_real_line(monkeypatch, tmp_path):
|
|
_stage(monkeypatch, tmp_path, "rx", marker="# the previous published version\n\n0.7.0+3.3.1\n")
|
|
assert lifecycle.previous_target_version("rx") == "0.7.0+3.3.1"
|
|
|
|
|
|
def test_previous_target_version_none_without_marker(monkeypatch, tmp_path):
|
|
_stage(monkeypatch, tmp_path, "rx", marker=None)
|
|
assert lifecycle.previous_target_version("rx") is None
|
|
|
|
|
|
# ---- the apply/skip/stale decision matrix ---------------------------------------------------------
|
|
|
|
|
|
def test_previous_status_no_folder(monkeypatch):
|
|
monkeypatch.setattr(lifecycle, "has_previous", lambda r: False)
|
|
st = lifecycle.previous_status("rx", "version", "0.7.0+3.3.1")
|
|
assert st == {"apply": False, "stale": False, "reason": ""}
|
|
|
|
|
|
def test_previous_status_applies_on_version_match(monkeypatch):
|
|
monkeypatch.setattr(lifecycle, "has_previous", lambda r: True)
|
|
monkeypatch.setattr(lifecycle, "previous_target_version", lambda r: "0.7.0+3.3.1")
|
|
st = lifecycle.previous_status("rx", "version", "0.7.0+3.3.1")
|
|
assert st["apply"] is True and st["stale"] is False
|
|
|
|
|
|
def test_previous_status_stale_on_version_mismatch(monkeypatch):
|
|
monkeypatch.setattr(lifecycle, "has_previous", lambda r: True)
|
|
monkeypatch.setattr(lifecycle, "previous_target_version", lambda r: "0.6.0+3.1.1")
|
|
st = lifecycle.previous_status("rx", "version", "0.7.0+3.3.1")
|
|
assert st["apply"] is False and st["stale"] is True and "stale" in st["reason"]
|
|
|
|
|
|
def test_previous_status_stale_on_main_tip_base(monkeypatch):
|
|
# previous/ can only repair a pinned published base; a main-tip (ref) base flags it for review.
|
|
monkeypatch.setattr(lifecycle, "has_previous", lambda r: True)
|
|
st = lifecycle.previous_status("rx", "ref", None)
|
|
assert st["apply"] is False and st["stale"] is True
|
|
|
|
|
|
def test_previous_status_stale_without_marker(monkeypatch):
|
|
monkeypatch.setattr(lifecycle, "has_previous", lambda r: True)
|
|
monkeypatch.setattr(lifecycle, "previous_target_version", lambda r: None)
|
|
st = lifecycle.previous_status("rx", "version", "0.7.0+3.3.1")
|
|
assert st["apply"] is False and st["stale"] is True and "marker" in st["reason"]
|
|
|
|
|
|
def test_previous_overlay_deploys_checked_out_base_without_clean_tree_gate(monkeypatch):
|
|
events = []
|
|
for name in (
|
|
"acquire_app_lock",
|
|
"provide_ccci_overlay",
|
|
"provide_previous_overlay",
|
|
"prepull_images",
|
|
):
|
|
monkeypatch.setattr(lifecycle, name, lambda *a, **kw: None)
|
|
for name in ("app_config_remove", "app_new", "env_set", "secret_generate"):
|
|
monkeypatch.setattr(lifecycle.abra, name, lambda *a, **kw: None)
|
|
monkeypatch.setattr(lifecycle.meta_mod, "extra_env", lambda *a: {})
|
|
monkeypatch.setattr(lifecycle.meta_mod, "hook_ctx", lambda *a: None)
|
|
monkeypatch.setattr(lifecycle.abra, "has_lightweight_version_tags", lambda r: False)
|
|
monkeypatch.setattr(lifecycle, "has_ccci_overlay", lambda r: False)
|
|
monkeypatch.setattr(
|
|
lifecycle.abra, "recipe_checkout", lambda r, v: events.append(("checkout", v))
|
|
)
|
|
monkeypatch.setattr(
|
|
lifecycle.abra, "deploy", lambda d, **kw: events.append(("deploy", kw["chaos"]))
|
|
)
|
|
for apply_previous in (False, True):
|
|
events.clear()
|
|
lifecycle.deploy_app(
|
|
"example",
|
|
"example.invalid",
|
|
version="1.0.0",
|
|
apply_previous=apply_previous,
|
|
meta=object(),
|
|
_count_deploy=False,
|
|
)
|
|
assert events == [("checkout", "1.0.0"), ("deploy", apply_previous)]
|