"""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_traefik_spec_is_stateless_with_setup(): # WC1.1 traefik = stateless (version-rollback-only, NO snapshot) + its own cert/file-provider # setup + health probed on a ROUTED host (the dashboard), not traefik's own domain. t = wr.SPECS["traefik"] assert t["stateful"] is False assert callable(t.get("setup")) assert t["health_domain"] == "ci.commoninternet.net" assert t["domain"] == "traefik.ci.commoninternet.net" # keycloak stays stateful with no custom setup (default path) assert wr.SPECS["keycloak"]["stateful"] is True assert "setup" not in wr.SPECS["keycloak"] 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("")