test(lifecycle): add immich stack lifecycle suite

RPC-based (host-agnostic) lifecycle coverage for the manifest-driven immich stack
(immich + immich-postgres + immich-redis): presence + valid state of all 3 members,
a guard that no legacy underscore containers exist (catches botched migration /
legacy-installer fallback), destructive stop/start/restart of the server with
postgres+redis staying up, and cascade uninstall/reinstall (preserve_data).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
archipelago 2026-06-21 09:01:19 -04:00
parent c548705147
commit b1f175b927

View File

@ -0,0 +1,122 @@
#!/usr/bin/env bats
# tests/lifecycle/bats/immich.bats
#
# Lifecycle tests for the immich stack (manifest-driven: immich + immich-postgres
# + immich-redis, installed via the orchestrator — NOT the legacy install_immich_stack).
#
# Tiers:
# - Read-only (always): presence + valid state of all 3 members
# - Destructive (ARCHY_ALLOW_DESTRUCTIVE=1): stop → start → restart the server
# - Cascade (ARCHY_ALLOW_CASCADE_DESTRUCTIVE=1): uninstall → reinstall the stack
# (preserve_data so the photo library + DB survive)
#
# All checks are RPC-based so the suite is correct whether run on the host or
# against a remote ARCHY_HOST.
load '../lib/rpc.bash'
IMMICH_IMAGE="146.59.87.168:3000/lfg2025/immich-server:release"
setup_file() {
: "${ARCHY_PASSWORD:?Set ARCHY_PASSWORD env var to the UI password}"
export ARCHY_FORCE_LOGIN=1
rpc_login
unset ARCHY_FORCE_LOGIN
}
teardown_file() {
rpc_logout_local
}
container_state() {
rpc_result container-list | jq -r --arg n "$1" '.[] | select(.name == $n) | .state'
}
# ────────────────────────────────────────────────────────────────────
# Read-only tier
# ────────────────────────────────────────────────────────────────────
@test "container-list includes the immich stack members" {
run rpc_result container-list
[ "$status" -eq 0 ]
echo "$output" | jq -e '.[] | select(.name == "immich")' >/dev/null
echo "$output" | jq -e '.[] | select(.name == "immich-postgres")' >/dev/null
echo "$output" | jq -e '.[] | select(.name == "immich-redis")' >/dev/null
}
@test "immich stack members report valid states" {
for c in immich immich-postgres immich-redis; do
local state
state=$(container_state "$c")
[[ "$state" =~ ^(running|stopped|exited|created|paused)$ ]] || {
echo "unexpected state for $c: $state"; return 1
}
done
}
@test "no legacy underscore immich containers (immich_postgres/_redis/_server)" {
# The legacy install_immich_stack named members with underscores. The
# manifest-driven stack uses hyphenated app_id names. Underscore containers
# mean a botched migration (or a fallback to the legacy installer).
run rpc_result container-list
[ "$status" -eq 0 ]
echo "$output" | jq -e '[.[] | select(.name | test("^immich_"))] | length == 0' >/dev/null
}
# ────────────────────────────────────────────────────────────────────
# Destructive tier (stop → start → restart the server)
# ────────────────────────────────────────────────────────────────────
@test "package.stop transitions immich to stopped" {
[[ "${ARCHY_ALLOW_DESTRUCTIVE:-0}" == "1" ]] || skip "ARCHY_ALLOW_DESTRUCTIVE not set"
run rpc_result package.stop '{"id":"immich"}'
[ "$status" -eq 0 ]
run wait_for_container_status immich stopped 60
[ "$status" -eq 0 ]
}
@test "package.start brings immich back to running" {
[[ "${ARCHY_ALLOW_DESTRUCTIVE:-0}" == "1" ]] || skip "ARCHY_ALLOW_DESTRUCTIVE not set"
run rpc_result package.start '{"id":"immich"}'
[ "$status" -eq 0 ]
run wait_for_container_status immich running 120
[ "$status" -eq 0 ]
}
@test "package.restart leaves immich in running state" {
[[ "${ARCHY_ALLOW_DESTRUCTIVE:-0}" == "1" ]] || skip "ARCHY_ALLOW_DESTRUCTIVE not set"
run rpc_result package.restart '{"id":"immich"}'
[ "$status" -eq 0 ]
run wait_for_container_status immich running 120
[ "$status" -eq 0 ]
}
@test "postgres + redis stay running across an immich server restart" {
[[ "${ARCHY_ALLOW_DESTRUCTIVE:-0}" == "1" ]] || skip "ARCHY_ALLOW_DESTRUCTIVE not set"
[ "$(container_state immich-postgres)" == "running" ]
[ "$(container_state immich-redis)" == "running" ]
}
# ────────────────────────────────────────────────────────────────────
# Cascade tier (uninstall + reinstall the stack)
# ────────────────────────────────────────────────────────────────────
@test "package.uninstall removes the immich stack (data preserved)" {
[[ "${ARCHY_ALLOW_CASCADE_DESTRUCTIVE:-0}" == "1" ]] || skip "ARCHY_ALLOW_CASCADE_DESTRUCTIVE not set"
run rpc_result package.uninstall '{"id":"immich","preserve_data":true}'
[ "$status" -eq 0 ]
run wait_for_container_status immich absent 120
[ "$status" -eq 0 ]
}
@test "package.install immich brings the whole stack back to running" {
[[ "${ARCHY_ALLOW_CASCADE_DESTRUCTIVE:-0}" == "1" ]] || skip "ARCHY_ALLOW_CASCADE_DESTRUCTIVE not set"
run rpc_result package.install "{\"id\":\"immich\",\"dockerImage\":\"${IMMICH_IMAGE}\"}"
[ "$status" -eq 0 ]
run wait_for_container_status immich running 180
[ "$status" -eq 0 ]
run wait_for_container_status immich-postgres running 120
[ "$status" -eq 0 ]
run wait_for_container_status immich-redis running 120
[ "$status" -eq 0 ]
}