chore: gate the test loop behind a real health check
ضاعت دورتان كاملتان على السبب نفسه: تسلسل `up -d && restart && <اختبار>` كان يشغّل الاختبار بينما الـapi ساقطة، فيظهر خطأ الإقلاع كـ`EAI_AGAIN` داخل الاختبار — أي في المكان الخطأ تماماً، فيُشخَّص كعطل شبكة أو عطل سكربت. `verify-and-test.sh` يجعل الدورة واحدة ومحروسة: - يفحص `.env` قبل الإقلاع ويصلّح التعارض القاتل (OTP_DEV_MODE=true مع NODE_ENV=production — حارس `main.ts` ينهي العملية لا يحذّر، فتدخل الحاوية حلقة إعادة تشغيل). - يميّز «حلقة إعادة تشغيل» عن «بطء إقلاع»: الأولى خطأ إقلاع يُطبع سببه من اللوغ فوراً بدل انتظار عقيم. - **لا اختبار قبل ردّ /health فعلياً** — هذه هي البوابة التي كانت غائبة. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
5838e92980
commit
f58f2e2e2c
Executable
+112
@@ -0,0 +1,112 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
#
|
||||||
|
# دورة تحقّق واحدة: افحص البيئة → أقلِع → **انتظر الصحة فعلاً** → اختبر.
|
||||||
|
#
|
||||||
|
# سبب وجوده: تسلسل `up -d --build && restart && <اختبار>` كان يشغّل الاختبار
|
||||||
|
# بينما الـapi ساقطة، فيظهر الفشل كـ`EAI_AGAIN` في الاختبار — أي في المكان
|
||||||
|
# الخطأ تماماً. ضاعت دورتان كاملتان على تشخيص خاطئ لهذا السبب وحده.
|
||||||
|
# البوابة هنا تمنع تكراره: لا اختبار قبل ردّ /health الفعلي.
|
||||||
|
#
|
||||||
|
# التشغيل من backend/: bash scripts/verify-and-test.sh
|
||||||
|
# بلا اختبار (نشر فقط): bash scripts/verify-and-test.sh --deploy-only
|
||||||
|
# مع إعادة بناء: bash scripts/verify-and-test.sh --build
|
||||||
|
|
||||||
|
set -uo pipefail
|
||||||
|
|
||||||
|
BUILD=0
|
||||||
|
DEPLOY_ONLY=0
|
||||||
|
for arg in "$@"; do
|
||||||
|
case "$arg" in
|
||||||
|
--build) BUILD=1 ;;
|
||||||
|
--deploy-only) DEPLOY_ONLY=1 ;;
|
||||||
|
*) echo "خيار غير معروف: $arg"; exit 2 ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
cd "$(dirname "$0")/.." || exit 1
|
||||||
|
ENV_FILE=".env"
|
||||||
|
FAILED=0
|
||||||
|
|
||||||
|
hr() { printf '%s\n' "────────────────────────────────────────────────"; }
|
||||||
|
|
||||||
|
# ── 1) فحص البيئة قبل أي إقلاع ───────────────────────────────────────
|
||||||
|
hr; echo "1) فحص البيئة"
|
||||||
|
|
||||||
|
if [[ ! -f "$ENV_FILE" ]]; then
|
||||||
|
echo "❌ لا يوجد $ENV_FILE"; exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
NODE_ENV_VAL=$(grep -E '^NODE_ENV=' "$ENV_FILE" | cut -d= -f2- | tr -d '"'"'"'\r ' || true)
|
||||||
|
OTP_DEV_VAL=$(grep -E '^OTP_DEV_MODE=' "$ENV_FILE" | cut -d= -f2- | tr -d '"'"'"'\r ' || true)
|
||||||
|
echo " NODE_ENV=${NODE_ENV_VAL:-<غير مضبوط>} OTP_DEV_MODE=${OTP_DEV_VAL:-<غير مضبوط>}"
|
||||||
|
|
||||||
|
# التعارض القاتل: الحارس في main.ts ينهي العملية (exit 1) لا يحذّر فقط،
|
||||||
|
# فالحاوية تدخل حلقة إعادة تشغيل ويظهر العطل لاحقاً كفشل شبكة مضلِّل.
|
||||||
|
if [[ "$NODE_ENV_VAL" == "production" && "$OTP_DEV_VAL" == "true" ]]; then
|
||||||
|
echo " ⚠️ تعارض قاتل: OTP_DEV_MODE=true مع NODE_ENV=production → الـapi لن تقلع."
|
||||||
|
echo " ↪︎ أطفئ OTP_DEV_MODE تلقائياً (الاختبار لا يحتاجه — يقرأ الرمز من Redis)."
|
||||||
|
sed -i.bak 's/^OTP_DEV_MODE=.*/OTP_DEV_MODE=false/' "$ENV_FILE"
|
||||||
|
echo " ✅ OTP_DEV_MODE=false (نسخة احتياطية: ${ENV_FILE}.bak)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! grep -qE '^PLATFORM_SECRET=.+' "$ENV_FILE"; then
|
||||||
|
echo " ❌ PLATFORM_SECRET غير مضبوط — نقاط السوبر-أدمن مغلقة والاختبار يحتاجها."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ── 2) الإقلاع ───────────────────────────────────────────────────────
|
||||||
|
hr; echo "2) الإقلاع"
|
||||||
|
if [[ "$BUILD" == "1" ]]; then
|
||||||
|
echo " بناء (الاختبارات تعمل داخل مرحلة البناء وتحجب النشر عند الفشل)…"
|
||||||
|
docker compose up -d --build || { echo "❌ فشل البناء"; exit 1; }
|
||||||
|
else
|
||||||
|
docker compose up -d || { echo "❌ فشل الإقلاع"; exit 1; }
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ── 3) بوابة الصحة — لا اختبار قبلها ─────────────────────────────────
|
||||||
|
hr; echo "3) انتظار /health"
|
||||||
|
HEALTH_OK=0
|
||||||
|
for i in $(seq 1 30); do
|
||||||
|
STATUS=$(docker compose ps --format '{{.Name}} {{.Status}}' | grep '^tripz-api' || true)
|
||||||
|
# الحاوية في حلقة إعادة تشغيل = خطأ إقلاع، والانتظار لن يصلحه.
|
||||||
|
if [[ "$STATUS" == *Restarting* ]]; then
|
||||||
|
echo " ❌ tripz-api في حلقة إعادة تشغيل — خطأ إقلاع، لا مشكلة شبكة."
|
||||||
|
hr; echo "سبب السقوط (آخر أخطاء):"
|
||||||
|
docker compose logs api --tail=200 2>/dev/null | grep -E 'ERROR|⛔|Error:' | tail -15
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if docker run --rm --network tripz-net curlimages/curl:latest \
|
||||||
|
-fsS --max-time 3 http://tripz-api:4010/api/health >/dev/null 2>&1; then
|
||||||
|
HEALTH_OK=1
|
||||||
|
echo " ✅ الـapi تستجيب (بعد ${i} محاولة)"
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
sleep 2
|
||||||
|
done
|
||||||
|
|
||||||
|
if [[ "$HEALTH_OK" != "1" ]]; then
|
||||||
|
echo " ❌ لم تستجب /health خلال 60 ثانية."
|
||||||
|
hr; docker compose ps
|
||||||
|
hr; docker compose logs api --tail=40
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
docker compose ps --format 'table {{.Name}}\t{{.Status}}'
|
||||||
|
|
||||||
|
if [[ "$DEPLOY_ONLY" == "1" ]]; then
|
||||||
|
hr; echo "✅ النشر سليم (بلا اختبار)."; exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ── 4) اختبار التسعير ────────────────────────────────────────────────
|
||||||
|
hr; echo "4) اختبار التسعير"
|
||||||
|
SECRET=$(grep -E '^PLATFORM_SECRET=' "$ENV_FILE" | cut -d= -f2- | tr -d '"'"'"'\r ')
|
||||||
|
docker run --rm --network tripz-net \
|
||||||
|
-e BASE=http://tripz-api:4010/api \
|
||||||
|
-e PLATFORM_SECRET="$SECRET" \
|
||||||
|
-v "$(pwd)/scripts:/s" \
|
||||||
|
node:22-alpine node /s/pricing-test.mjs
|
||||||
|
FAILED=$?
|
||||||
|
|
||||||
|
hr
|
||||||
|
if [[ "$FAILED" == "0" ]]; then echo "✅ تمّت الدورة بنجاح."; else echo "❌ فشل الاختبار (رمز $FAILED)."; fi
|
||||||
|
exit $FAILED
|
||||||
Reference in New Issue
Block a user