Files
tripz-llc/backend-archive/scripts/verify-and-test.sh
T
Hamza-AyedandClaude Opus 5 fff76c949f chore: أرشفة باك إند NestJS إلى backend-archive
قرار المالك 2026-07-27: الباك إند المعتمد صار باك إند سيرو PHP، ويُنقل
إلى هذا المستودع كنسخة جديدة باسم «انطلق» على api.intaleqapp.com.

backend/ → backend-archive/ (305 ملفاً، إعادة تسمية بلا تعديل محتوى)
+ README-ARCHIVE.md يوضّح سبب الأرشفة وسبب عدم الحذف: scripts/e2e-test.mjs
  هو أدق توثيق سلوكي لدورة الرحلة كاملة، ويبقى مرجعاً عند بناء الوحدات
  الناقصة في باك إند PHP (المحفظتان · الاستحقاقات · محرّك التسعير · الوحدات).

مسارات backend/ في docs/*.md لم تُعدَّل عمداً — سجلّ تاريخي.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-27 04:54:35 +03:00

129 lines
6.3 KiB
Bash
Executable File

#!/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) إبطال كاش التعرفة ─────────────────────────────────────────────
# `getActive` تكاشي في Redis 900 ثانية، و**Redis حاوية منفصلة لا تتأثر
# بإعادة تشغيل الـapi**. تطبيق `tariffs.sql` مباشرةً على القاعدة يتجاوز
# التطبيق فلا يُبطل شيئاً — فبقي النظام يخدم أسعاراً قديمة عبر دورتَي نشر
# كاملتين وهو يعلن النجاح. المسح هنا يجعل الطزاجة مضمونة بالبناء لا
# بالتذكّر.
hr; echo "4) إبطال كاش التعرفة"
KEYS=$(docker compose exec -T redis redis-cli -n "${REDIS_DB:-3}" --scan --pattern 'tripz:tariff:*' 2>/dev/null | tr -d '\r')
if [[ -n "$KEYS" ]]; then
COUNT=$(printf '%s\n' "$KEYS" | grep -c . || true)
printf '%s\n' "$KEYS" | xargs -r docker compose exec -T redis redis-cli -n "${REDIS_DB:-3}" DEL >/dev/null
echo " ✅ مُسح $COUNT مفتاح تعرفة"
else
echo " ℹ️ لا مفاتيح تعرفة مكاشية"
fi
# ── 5) اختبار التسعير ────────────────────────────────────────────────
hr; echo "5) اختبار التسعير"
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