Files
cc-ci/tests/unit/test_warm_reconcile.py
autonomic-bot a044abb298 feat(2w): W0.6 unpinned warm reconciler + WC1.2 safety gate + WC1.1 scaffold
runner/warm_reconcile.py (python, packaged into nix store, replaces bash
reconcile): UNPIN keycloak (deploy latest published version TAG; recipe fetched
at runtime -> D8 closure byte-identical). WC1.2 pre-deploy safety gate (runs
FIRST): major recipe/app-version bump OR releaseNotes manual-migration marker
-> hold-on-current + alert sentinel (no deploy churn). WC1.1 health-gated
upgrade-with-rollback: record last-good -> [keycloak: undeploy->warmsnap.snapshot
->deploy latest] -> health-gate -> commit-or-(restore+redeploy-prior+alert).
Alerts = /var/lib/ci-warm/alerts/*.json (Builder loop relays). current version
read from abra TYPE=<recipe>:<version>. CCCI_SKIP_FETCH test hook.
+8 unit tests for the version gate (56 unit pass).

Proven on cc-ci: nixos-rebuild switch -> warm-keycloak.service runs the python
reconciler -> noop-healthy (system 0-failed, /realms/master=200). WC1.2 holds
proven live: MAJOR bump -> held-major (keycloak untouched); minor+manual-
migration notes -> held-manual-migration (alert carries notes); no deploy churn.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-29 00:42:02 +01:00

63 lines
2.3 KiB
Python

"""Unit tests for the WC1.2 safety-gate + version helpers in runner/warm_reconcile.py.
Pure logic only (no abra/docker). The reconcile flow itself is proven live on cc-ci against the warm
keycloak (W0.6). These lock the gate's correctness: which bumps auto-apply vs hold, and the
manual-migration marker scan.
"""
from __future__ import annotations
import os
import sys
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", ".."))
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "..", "runner"))
import warm_reconcile as wr # noqa: E402
def test_is_version_tag():
assert wr.is_version_tag("10.7.1+26.6.2")
assert wr.is_version_tag("3.2.0")
assert not wr.is_version_tag("main")
assert not wr.is_version_tag("latest")
assert not wr.is_version_tag("")
def test_sort_and_latest():
tags = ["10.6.0+26.5.4", "10.7.1+26.6.2", "10.5.1+26.4.5", "main", "10.7.0+26.6.1"]
assert wr.latest_version(tags) == "10.7.1+26.6.2"
assert wr.sort_versions(tags)[0] == "10.5.1+26.4.5"
def test_latest_none_when_no_tags():
assert wr.latest_version(["main", "feature-x"]) is None
def test_minor_patch_bump_not_major():
# recipe-semver 10.7.0 -> 10.7.1 (patch); app 26.6.1 -> 26.6.2 (patch). Auto-apply.
assert wr.is_major_bump("10.7.0+26.6.1", "10.7.1+26.6.2") is False
# minor recipe bump 10.7.1 -> 10.8.0. Auto-apply.
assert wr.is_major_bump("10.7.1+26.6.2", "10.8.0+26.6.2") is False
def test_recipe_major_bump_held():
# recipe-semver 10.x -> 11.0 (major). HELD.
assert wr.is_major_bump("10.7.1+26.6.2", "11.0.0+26.6.2") is True
def test_app_major_bump_held():
# app version 26.x -> 27.0 (major, e.g. keycloak DB migration era). HELD (conservative).
assert wr.is_major_bump("10.7.1+26.6.2", "10.8.0+27.0.0") is True
def test_app_major_bump_held_even_if_no_plus_on_current():
assert wr.is_major_bump("0", "11.0.0+1.0.0") is True
def test_manual_migration_markers():
assert wr.notes_flag_manual_migration("This release requires a MANUAL MIGRATION of the DB.")
assert wr.notes_flag_manual_migration("Breaking change: action required before upgrade.")
assert wr.notes_flag_manual_migration("You must run the migration by hand.")
assert not wr.notes_flag_manual_migration("Routine patch. Automatic, no action needed.")
assert not wr.notes_flag_manual_migration("")