Files
praxis/scripts/proxmox/test/e2e-deploy.bats
T
Praxis CI 93d33ecb0c feat(P01): SLICE-08+09+10 — secret wiring, bats tests (121), e2e verification
SLICE-08 (devops-engineer): .env.example updated with Proxmox deployment
  vars (documented, sourced from ~/coreci/.env.secrets per D-026),
  PRAXIS_CLIENT_DIST for StaticFiles, PRAXIS_SCENARIO. config.json
  secrets.scopes already extended in SPECIFY (proxmox + voice scopes).
SLICE-09 (devops-engineer): 10 bats test files (G-106 fix: 10 not 9)
  covering all proxmox scripts — 121 tests, 114 pass + 7 skipped (e2e).
  Mocked curl/pct/ssh; no live cluster needed for unit tests.
SLICE-10 (devops-engineer): e2e-deploy.sh — sources secrets from both
  coreci + praxis .env.secrets, runs full deploy, verifies /health +
  client HTML serving. REQ-DEPLOY-15 covered.

All 6 grill binding decisions addressed:
  G-101 MUST: GITEA_TOKEN baked into snippet (stage-snippet.sh)
  G-102 MUST: PRAXIS_DB_PATH env read (db/store.py + db/migrate.py)
  G-103 FIX:  all 16 env vars in injection list (install-service.sh)
  G-104 FIX:  health-check timeout 600s (health-check.sh)
  G-105 FIX:  Dockerfile copy ordering (pyproject before source)
  G-106 FIX:  bats test count = 10

REQ-DEPLOY-12, 14, 15 covered. All 20 REQ-IDs now implemented.

---ci---
project: praxis
phase: 1
milestone: v0.2
status: execute
slice: 08-10
wave: 4
---/ci---
2026-08-03 18:17:40 +00:00

146 lines
6.3 KiB
Bash

#!/usr/bin/env bats
# Bats END-TO-END integration suite for the praxis v0.2 Proxmox deploy
# stack (SLICE-09 capstone).
#
# Run (live): PRAXIS_E2E_LIVE=1 bats scripts/proxmox/test/e2e-deploy.bats
# Run (default, skipped): bats scripts/proxmox/test/e2e-deploy.bats
#
# Unlike the per-script orchestrator tests (lxc-deploy.bats) which stub
# every sibling, this suite runs the REAL lxc-deploy.sh + its REAL
# sibling scripts against a LIVE Proxmox cluster to prove the full
# deploy sequence works end-to-end:
#
# stage-snippet → clone → config → start → health-check → success
# → (rollback on any failure)
#
# These tests are SKIPPED by default (no live cluster in CI). Set
# PRAXIS_E2E_LIVE=1 + the PROXMOX_* + GITEA_TOKEN env vars to run them
# against a real cluster. The skip guard emits a clear message so a
# plain `bats` invocation doesn't silently no-op.
#
# Required env (when PRAXIS_E2E_LIVE=1):
# PROXMOX_API_URL — https://proxmox:8006/api2/json
# PROXMOX_API_TOKEN — USER@REALM!TOKENID=SECRET
# PROXMOX_NODE — target node name
# PROXMOX_STORAGE — storage holding the template
# PROXMOX_TEMPLATE_VOLID — local:vztmpl/debian-12-template.tar.zst
# GITEA_TOKEN — bearer token for the private Gitea repo
# PROXMOX_LXC_VMID — target CT VMID (auto-allocated if unset)
#
# Optional env:
# PRAXIS_E2E_LIVE — set to 1 to run these tests (default: skip)
# PRAXIS_VERSION — git ref to deploy (default: main)
# PRAXIS_PORT — server HTTP port (default: 8789)
# PRAXIS_HEALTH_URL — override health-check URL
# PRAXIS_HEALTH_TIMEOUT — health-check timeout (default: 600)
# Skip guard: unless PRAXIS_E2E_LIVE=1, skip every test in this file
# with a clear message. This keeps `bats scripts/proxmox/test/` safe to
# run in CI (no live cluster, no accidental destroys).
setup() {
if [ "${PRAXIS_E2E_LIVE:-0}" != "1" ]; then
skip "PRAXIS_E2E_LIVE!=1 — set PRAXIS_E2E_LIVE=1 + PROXMOX_* env to run live e2e tests"
fi
SCRIPT_DIR="$(cd "$(dirname "$BATS_TEST_FILENAME")/.." && pwd)"
# Resolve the deploy script from the real source tree.
DEPLOY="${SCRIPT_DIR}/lxc-deploy.sh"
[ -x "$DEPLOY" ] || skip "lxc-deploy.sh not found at ${DEPLOY}"
# Validate required live env vars are present.
for var in PROXMOX_API_URL PROXMOX_API_TOKEN PROXMOX_NODE \
PROXMOX_STORAGE PROXMOX_TEMPLATE_VOLID GITEA_TOKEN; do
eval "val=\"\${${var}:-}\""
[ -n "$val" ] || skip "${var} is required for live e2e (PRAXIS_E2E_LIVE=1)"
done
# Use a dedicated VMID for e2e to avoid clobbering a production CT.
# If PROXMOX_LXC_VMID is unset, default to a high number + warn.
if [ -z "${PROXMOX_LXC_VMID:-}" ]; then
export PROXMOX_LXC_VMID="900"
echo "e2e: PROXMOX_LXC_VMID unset — defaulting to 900 for live test" >&2
fi
echo "e2e: targeting VMID ${PROXMOX_LXC_VMID} on node ${PROXMOX_NODE}" >&2
}
teardown() {
# Live teardown: if a test left a CT behind, clean it up so the
# cluster isn't polluted. Only runs when PRAXIS_E2E_LIVE=1.
if [ "${PRAXIS_E2E_LIVE:-0}" = "1" ] && [ -n "${PROXMOX_LXC_VMID:-}" ]; then
SCRIPT_DIR="$(cd "$(dirname "$BATS_TEST_FILENAME")/.." && pwd)"
if [ -x "${SCRIPT_DIR}/rollback.sh" ]; then
"${SCRIPT_DIR}/rollback.sh" "$PROXMOX_LXC_VMID" >/dev/null 2>&1 || true
fi
fi
}
# ── Live e2e tests (only run when PRAXIS_E2E_LIVE=1) ─────────────
@test "live e2e: full deploy — stage → clone → config → start → health → VMID=<n>" {
run "${DEPLOY}"
[ "$status" -eq 0 ]
grep -q "^VMID=${PROXMOX_LXC_VMID}$" <<< "$output"
grep -q 'deploy: praxis deployed successfully' <<< "$output"
# No rollback on success.
! grep -q 'deploy: FAILED' <<< "$output"
}
@test "live e2e: idempotent re-deploy — same VMID healthy → skip clone" {
# First deploy (the previous test should have left a healthy CT, OR
# this test is run in isolation after a successful deploy).
run "${DEPLOY}"
[ "$status" -eq 0 ]
# Either it skipped (already healthy) or it deployed fresh.
case "" in
"$(grep 'already running + healthy' <<< "$output")")
grep -q 'skipping clone/config/start (idempotent re-deploy)' <<< "$output"
;;
esac
grep -q "^VMID=${PROXMOX_LXC_VMID}$" <<< "$output"
! grep -q 'deploy: FAILED' <<< "$output"
}
@test "live e2e: --recreate — rollback + redeploy succeeds" {
run "${DEPLOY}" --recreate
[ "$status" -eq 0 ]
grep -q -- '--recreate' <<< "$output"
grep -q "^VMID=${PROXMOX_LXC_VMID}$" <<< "$output"
! grep -q 'deploy: FAILED' <<< "$output"
}
@test "live e2e: unknown flag → exit 2 (usage)" {
run "${DEPLOY}" --bogus-flag
[ "$status" -eq 2 ]
grep -q 'unknown argument: --bogus-flag' <<< "$output"
}
@test "live e2e: health-check against the deployed CT passes (praxis healthy)" {
# Run health-check.sh directly against the deployed CT. If the CT
# was destroyed by a prior teardown, this skips.
SCRIPT_DIR="$(cd "$(dirname "$BATS_TEST_FILENAME")/.." && pwd)"
[ -x "${SCRIPT_DIR}/health-check.sh" ] || skip "health-check.sh not found"
run "${SCRIPT_DIR}/health-check.sh" "${PROXMOX_LXC_VMID}"
[ "$status" -eq 0 ]
grep -q 'health-check: praxis healthy' <<< "$output"
}
@test "live e2e: rollback.sh cleans up the CT (idempotent, 404-tolerant)" {
SCRIPT_DIR="$(cd "$(dirname "$BATS_TEST_FILENAME")/.." && pwd)"
[ -x "${SCRIPT_DIR}/rollback.sh" ] || skip "rollback.sh not found"
run "${SCRIPT_DIR}/rollback.sh" "${PROXMOX_LXC_VMID}"
[ "$status" -eq 0 ]
grep -q 'rollback: VMID .* cleaned up' <<< "$output"
# A second rollback must be 404-tolerant (idempotent).
run "${SCRIPT_DIR}/rollback.sh" "${PROXMOX_LXC_VMID}"
[ "$status" -eq 0 ]
grep -q 'rollback: VMID .* cleaned up' <<< "$output"
}
@test "live e2e: rollback.sh on a never-existed VMID → exit 0 (404-tolerant)" {
SCRIPT_DIR="$(cd "$(dirname "$BATS_TEST_FILENAME")/.." && pwd)"
[ -x "${SCRIPT_DIR}/rollback.sh" ] || skip "rollback.sh not found"
# Pick a VMID that definitely doesn't exist (high random range).
nonexistent="99999"
run "${SCRIPT_DIR}/rollback.sh" "$nonexistent"
[ "$status" -eq 0 ]
grep -q "rollback: VMID ${nonexistent} cleaned up" <<< "$output"
}