Files
EventHubBack/scripts/run-stand-api-tests.sh
T
aleksey a061674408
CI / test (push) Successful in 7m38s
CI / deploy-ift (push) Successful in 3m37s
CI / e2e-ift (push) Successful in 1m19s
CI / deploy-stage (push) Successful in 2m47s
CI / e2e-stage (push) Failing after 1m15s
ci(e2e): readiness-wait перед API-сьюитами на стенде
После deploy-stage контейнер ещё стартует, traefik отвечает 502/503,
и первые сьюиты падали с {badmatch,{ok,502}}. run-stand-api-tests.sh
теперь до 150s ждёт, пока HTTP-фронт API_HOST/ADMIN_API_HOST начнёт
отвечать (любой код, кроме 5xx), прежде чем запускать CT.
2026-08-23 23:17:32 +03:00

182 lines
6.8 KiB
Bash

#!/usr/bin/env bash
# Full API CT suite against live stand (remote) + smoke-core on stand host.
# Usage: run-stand-api-tests.sh <ift|stage>
# Requires: ADMIN_* from load-stand-api-env.sh; SSH_* for smoke-core on stand.
#
# Litter cleanup: after each CT suite, api_test_runner:cleanup_tracked/0
# soft-deletes tracked calendars/users via admin API (and @test.local safety-net).
# Skip teardown: CT_KEEP_DATA=1 (data remains on the stand).
# Soft-delete ≠ hard wipe; full clean slate = ops Mnesia volume wipe.
set -euo pipefail
STAND="${1:?stand: ift|stage}"
case "${STAND}" in
ift)
[[ -n "${API_HOST:-}" ]] || API_HOST="https://api.ift.eventhub.local"
[[ -n "${ADMIN_API_HOST:-}" ]] || ADMIN_API_HOST="https://admin-api.ift.eventhub.local"
[[ -n "${WS_HOST:-}" ]] || WS_HOST="wss://ws.ift.eventhub.local"
[[ -n "${ADMIN_WS_HOST:-}" ]] || ADMIN_WS_HOST="wss://admin-ws.ift.eventhub.local"
DEVOPS_SMOKE="/opt/eventhub-ift/devops/scripts/smoke-core.sh"
;;
stage)
# Публичный stage (канон STAGE-BROWSER.md). Пустые E2E_STAGE_* из CI
# не должны оставлять API_HOST="" (иначе CT бьёт в *.eventhub.local).
[[ -n "${API_HOST:-}" ]] || API_HOST="https://api.stage.calentiq.com"
[[ -n "${ADMIN_API_HOST:-}" ]] || ADMIN_API_HOST="https://admin-api.stage.calentiq.com"
[[ -n "${WS_HOST:-}" ]] || WS_HOST="wss://ws.stage.calentiq.com"
[[ -n "${ADMIN_WS_HOST:-}" ]] || ADMIN_WS_HOST="wss://admin-ws.stage.calentiq.com"
DEVOPS_SMOKE="/opt/eventhub-stage/devops/scripts/smoke-core.sh"
;;
*)
echo "Unknown stand: ${STAND}"
exit 1
;;
esac
if [[ -n "${SSH_HOST:-}" && -n "${SSH_USER:-}" && -n "${SSH_KEY:-}" ]]; then
KEY="$(mktemp)"
printf '%s\n' "${SSH_KEY}" > "${KEY}"
chmod 600 "${KEY}"
echo "=== smoke-core on ${STAND} (via SSH) ==="
ssh -i "${KEY}" -o BatchMode=yes -o StrictHostKeyChecking=accept-new \
"${SSH_USER}@${SSH_HOST}" "bash ${DEVOPS_SMOKE} ${STAND}"
rm -f "${KEY}"
else
echo "::warning::SSH_* not set — skip smoke-core on stand host"
fi
echo "=== API CT remote (${STAND}) ==="
echo " API_HOST=${API_HOST}"
echo " ADMIN_API_HOST=${ADMIN_API_HOST}"
echo " WS_HOST=${WS_HOST}"
echo " ADMIN_WS_HOST=${ADMIN_WS_HOST}"
# После deploy-stage контейнер стенда может ещё стартовать; traefik в
# это время отвечает 502/503, и первые сьюиты падают с {badmatch,{ok,502}}.
# Ждём, пока HTTP-фронт приложений начнёт отвечать (любой код, кроме 5xx).
for probe in "${API_HOST}/v1/calendars" "${ADMIN_API_HOST}/v1/admin/health"; do
ready=0
for i in $(seq 1 30); do
code=$(curl -sS -o /dev/null -w '%{http_code}' --max-time 5 "${probe}" 2>/dev/null || echo 000)
case "${code}" in
5*) : ;;
000) : ;;
*) ready=1; break ;;
esac
sleep 5
done
if [[ "${ready}" -eq 1 ]]; then
echo " readiness OK: ${probe} (HTTP ${code})"
else
echo "::warning::${probe} не отвечает после 150s — стартуем сьюиты как есть"
fi
done
# Docker Desktop/WSL: --network=host часто НЕ наследует /etc/hosts стенда,
# из‑за чего CT remote не резолвит *.ift.eventhub.local (health timeout 30s).
docker_add_hosts=()
for url in "${API_HOST}" "${ADMIN_API_HOST}" "${WS_HOST}" "${ADMIN_WS_HOST}"; do
host="$(python3 -c 'from urllib.parse import urlparse; import sys; print(urlparse(sys.argv[1]).hostname or "")' "${url}")"
[[ -n "${host}" ]] || continue
# уже добавили этот host
skip=0
for existing in "${docker_add_hosts[@]:-}"; do
case "${existing}" in
--add-host="${host}:"*) skip=1; break ;;
esac
done
[[ "${skip}" -eq 1 ]] && continue
ip="$(getent ahostsv4 "${host}" 2>/dev/null | awk '{print $1; exit}' || true)"
if [[ -z "${ip}" ]]; then
ip="$(getent hosts "${host}" 2>/dev/null | awk '{print $1; exit}' || true)"
fi
if [[ -z "${ip}" ]]; then
ip=host-gateway
fi
echo " docker --add-host ${host}:${ip}"
docker_add_hosts+=(--add-host="${host}:${ip}")
done
IMAGE="eventhub-api-tests:local"
REG_IMAGE=""
if [[ -n "${GITHUB_SHA:-}" ]]; then
IMAGE="eventhub-api-tests:sha-${GITHUB_SHA:0:12}"
REG_IMAGE="${REGISTRY:-git.sabilin.com/eventhub}/eventhub-api-tests:sha-${GITHUB_SHA:0:12}"
fi
echo "=== ensure API test image (${IMAGE}) ==="
have_image=0
if docker image inspect "${IMAGE}" >/dev/null 2>&1; then
echo " local image present: ${IMAGE}"
have_image=1
elif docker image inspect eventhub-tests:latest >/dev/null 2>&1; then
# act/hostexecutor: test job уже собрал тот же Dockerfile
docker tag eventhub-tests:latest "${IMAGE}"
echo " reused local eventhub-tests:latest → ${IMAGE}"
have_image=1
elif [[ -n "${REG_IMAGE}" ]]; then
if [[ -n "${REGISTRY_USER:-}" && -n "${REGISTRY_PASSWORD:-}" ]]; then
echo "${REGISTRY_PASSWORD}" | docker login git.sabilin.com -u "${REGISTRY_USER}" --password-stdin >/dev/null
fi
if docker pull "${REG_IMAGE}"; then
docker tag "${REG_IMAGE}" "${IMAGE}"
echo " pulled ${REG_IMAGE}"
have_image=1
else
echo " pull failed — will rebuild"
fi
fi
if [[ "${have_image}" -ne 1 ]]; then
# plain: иначе BUILDKIT_PROGRESS=quiet прячет причину падения rebar3
export BUILDKIT_PROGRESS="${BUILDKIT_PROGRESS:-plain}"
bash scripts/retry-docker-build.sh 5 60 -- \
docker buildx build \
--network=host \
--progress=plain \
--file docker/ApiTests.Dockerfile \
--tag "${IMAGE}" \
--load \
--provenance=false \
--sbom=false \
.
fi
CT_OUT="${CT_REPORT_DIR:-$(pwd)/ci-out/ct-${STAND}}"
mkdir -p "${CT_OUT}"
set +e
docker run --rm --network=host \
${docker_add_hosts[@]+"${docker_add_hosts[@]}"} \
-v "${CT_OUT}:/app/logs/test/ct" \
-e CT_MODE=remote \
-e "API_HOST=${API_HOST}" \
-e "ADMIN_API_HOST=${ADMIN_API_HOST}" \
-e "WS_HOST=${WS_HOST}" \
-e "ADMIN_WS_HOST=${ADMIN_WS_HOST}" \
-e "ADMIN_SUPER_EMAIL=${ADMIN_SUPER_EMAIL:-}" \
-e "ADMIN_SUPER_PASSWORD=${ADMIN_SUPER_PASSWORD:-}" \
-e "ADMIN_EMAIL=${ADMIN_EMAIL:-}" \
-e "ADMIN_PASSWORD=${ADMIN_PASSWORD:-}" \
-e "ADMIN_MODER_EMAIL=${ADMIN_MODER_EMAIL:-}" \
-e "ADMIN_MODER_PASSWORD=${ADMIN_MODER_PASSWORD:-}" \
-e "ADMIN_SUPPORT_EMAIL=${ADMIN_SUPPORT_EMAIL:-}" \
-e "ADMIN_SUPPORT_PASSWORD=${ADMIN_SUPPORT_PASSWORD:-}" \
-e "CT_KEEP_DATA=${CT_KEEP_DATA:-}" \
"${IMAGE}"
CT_RC=$?
set -e
if [[ -d "${CT_OUT}" ]] && compgen -G "${CT_OUT}/*" >/dev/null; then
RUN_ID="${GITHUB_RUN_NUMBER:-local}"
mkdir -p /var/eventhub/ci-reports 2>/dev/null || true
bash scripts/publish-ci-report.sh EventHubBack "${RUN_ID}" "e2e-${STAND}" "${CT_OUT}" || true
fi
if [[ "${CT_RC}" -ne 0 ]]; then
echo "=== API CT FAILED (${STAND}) rc=${CT_RC} ===" >&2
exit "${CT_RC}"
fi
echo "=== API CT passed (${STAND}) ==="