From 2683ad4f0ee7118e8f15978d329d6e120466c97a Mon Sep 17 00:00:00 2001 From: archipelago Date: Thu, 9 Jul 2026 03:02:22 -0400 Subject: [PATCH] fix(tests): exempt user-stopped apps from quadlet unit active-state asserts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit backend_quadlet_units() enumerates every *.container file on disk and the active-state tests asserted each unit active + container running. A user-stopped app keeps its unit file (e.g. the inactive half of the bitcoin-core/bitcoin-knots multi-version pair after tonight's crash-loop wave left core's unit on disk), so its inactive service / absent container is the CORRECT state — gate run E false-failed on it (tests 123/124, .228 2026-07-09). Honour the orchestrator's user-stopped.json intent marker and skip those units in both tests. Verified on .228: bitcoin-core's lingering unit now exempted; suite parses (bats --count = 6) and the two fixed tests pass user-stopped filtering. Co-Authored-By: Claude Fable 5 --- .../bats/use-quadlet-backends-install.bats | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/tests/lifecycle/bats/use-quadlet-backends-install.bats b/tests/lifecycle/bats/use-quadlet-backends-install.bats index cbb23298..4730e478 100644 --- a/tests/lifecycle/bats/use-quadlet-backends-install.bats +++ b/tests/lifecycle/bats/use-quadlet-backends-install.bats @@ -50,6 +50,22 @@ backend_quadlet_units() { done } +# A unit file on disk does NOT imply the app should be running: an +# explicitly user-stopped app keeps its .container file (e.g. the inactive +# half of the bitcoin-core/bitcoin-knots multi-version pair), and its +# .service being inactive / container absent is the CORRECT state. The +# orchestrator persists that intent in user-stopped.json; honour it here so +# the active-state assertions below don't false-fail on stopped-on-purpose +# apps (gate tests 123/124, .228 2026-07-09). +USER_STOPPED_FILE="${ARCHY_DATA_DIR:-/var/lib/archipelago}/user-stopped.json" + +is_user_stopped() { + local name="$1" + [[ -r "$USER_STOPPED_FILE" ]] || return 1 + jq -e --arg n "$name" --arg s "${name#archy-}" \ + 'index($n) != null or index($s) != null' "$USER_STOPPED_FILE" >/dev/null 2>&1 +} + # Read the cgroup path of a running container's main process. For # rootless podman the conmon-run target lands the container's pid1 in # the cgroup that owns its supervising .service. @@ -127,6 +143,7 @@ require_quadlet_backends() { require_quadlet_backends while read -r name; do [[ -z "$name" ]] && continue + is_user_stopped "$name" && continue run systemctl --user is-active "$name.service" [[ "$status" -eq 0 ]] || fail "$name.service is '$output' — expected 'active'" done < <(backend_quadlet_units) @@ -136,6 +153,7 @@ require_quadlet_backends() { require_quadlet_backends while read -r name; do [[ -z "$name" ]] && continue + is_user_stopped "$name" && continue run sh -c "podman inspect --format '{{.State.Running}}' '$name'" [[ "$status" -eq 0 ]] || fail "$name not present in podman" [[ "$output" == "true" ]] || fail "$name container exists but not running (state=$output)"