🛡️ شبكة أمان الـRegressions — إزاي تتأكد إن القديم كله شغّال قبل أي تعديل

المشكلة اللي بتواجهها: تضيف فيتشر أو تعدّل حاجة → تبوظ حاجة تانية من غير ما تحس، خصوصًا لإن الموديولات بتشارك خدمات Core مشتركة (محرّك القيود/GL، المخزون، الضرايب). الملف ده تشخيص للوضع الفعلي (مقيس بالكود) + استراتيجية متدرّجة عملية لراجل واحد + خطة 4 أسابيع.

📅 4 يوليو 2026 🧬 Moon ERP — BE (Laravel modular) + FE (Angular) + MoonStack 🔎 فحص كود مقيس + KB (dev-workflow) + استشارة Fable 5 الحالة: استراتيجية — جاهزة للتنفيذ

✅ عندك شبكة أمان ضخمة بالفعل

478 ملف اختبار / 4,672 اختبار في الباك إند. المشكلة مش إنها مش موجودة — المشكلة إن محدش بيشغّلها ومفيش حاجة بتفرضها.

🎯 الفجوة = بوابة (Gate) مش اختبارات

الحل الأساسي: CI يشغّل الـ478 على كل push + MoonStack يرفض يشحن غير لو الكود أخضر. ده اللي بيحوّل "أتمنى إنه شغّال" لـ"متأكد".

⚡ أسرع من المتوقع

السويت ~50-60 دقيقة تسلسلي، بس بـ4-6 shards + ParaTest → أقل من 10 دقايق. الـSQLite in-memory بتاعك مثالي للتوازي.

1 التشخيص — الوضع الفعلي (مقيس)

478
ملف اختبار BE
4,672
اختبار (test method)
~55دق
زمن السويت تسلسلي (تقديري)
0
CI / بوابة تفرض التشغيل
0
unit tests في الفرونت

الباك إند (Laravel modular) — أساس قوي، بس مطفّي

الفرونت إند (Angular) — صفر unit، بس نمطين ممتازين مزروعين

التوزيع (MoonStack) — الـregression بتشحن لعملاء

الخلاصة: إنت فاكر إن مفيش شبكة أمان — الحقيقة إن عندك واحدة كبيرة (478 اختبار) بس مش مطفّية غلط: مفيش حاجة تشغّلها، ومفيش حاجة تمنع الشحن لو حمرا. الحل مش نكتب اختبارات من الصفر — الحل نركّب البوابات ونسدّ فجوات محدّدة.

2 الفكرة الأساسية — الفرض (Enforcement)، مش الانضباط

الاختبارات موجودة والانضباط فشل بالفعل (محدش بيشغّلها). "أتأكد" مش بتيجي من سكربت ممكن تنساه — بتيجي من بوابة بتفرض نفسها.

⚠️ نقطة حرجة لراجل واحد بيـpush مباشر على hazemdev2: بوابة "منع الميرج" (PR gate) مش هتشتغل لإن مفيش PRs أصلاً. البوابة اللي بتشتغل فعلاً:
  • CI على on: push لـhazemdev2 وmain → كل push بياخد حكم أخضر/أحمر تلقائي.
  • moonstack:ship يرفض يعمل package غير لو الـSHA بالظبط عليه CI أخضر (عبر gh api). دي أهم بوابة — بتمنع شحن regression للعملاء.
  • + branch protection على main. لكن push-CI + ship-gate هما الزوج الحامل.

3 البنية — 4 طبقات بوابات، كل واحدة أبطأ وأشمل

① لوكال سريع
verify.sh — الموديول المتغيّر + ng build. ثواني. قبل الكوميت.
② بوابة push (CI)
كل السويت BE بالتوازي + FE prod build + report-contract. <10 دق. كل push.
③ ليلي / قبل release
السويت كاملة على MySQL حقيقي + Playwright smokes. أبطأ، أدق.
④ بوابة release
تمرين ترقية + fresh-install + settings:verify + ship يتأكد SHA أخضر.

الطبقة ① اختيارية للسرعة المحلية (بتعتمد على انضباطك). الطبقات ②③④ هي اللي بتفرض نفسها — دي شبكة الأمان الحقيقية.

4 نخلّي البوابة سريعة (علشان متتسابش)

50-60 دقيقة تسلسلي بطيء جدًا كبوابة على كل push. الهدف: <10 دقايق.

الأسلوبالقرار
Matrix shards4-6 shards متوازنة — مش 16 job (تكلفة إعداد كل job عالية). نوازن بالزمن المقيس (LIS لوحده shard).
ParaTest داخل كل shardأيوه brianium/paratest + artisan test --parallel. الـSQLite :memory: مثالي: كل process بياخد DB معزول تلقائيًا (per-connection) → مفيش تصادم DB الكلاسيكي.
changed-module فقطلأ — بيفوّت الـcross-module (§5). السويت صغيرة كفاية نشغّلها كلها.
Test-Impact Analysisأبدًا — أدوات PHP غير ناضجة، عبء صيانة.
🔴 مصايد لازم ننتبه لها:
  • مصدر الـflake الحقيقي مش الـDB — هو DomPDF وأي حاجة بتكتب في storage/ مشترك. نراجع اختبارات الـPDF/التقارير تستخدم Storage::fake().
  • اختبارات بتعتمد على ترتيب التنفيذ أو static state هتطفو تحت التوازي → جولة triage صغيرة تانية متوقّعة.
  • 🟥 SQLite ≠ MySQL. العملاء بيشغّلوا الـmigrations على MySQL (FK، strict mode، DDL، JSON functions بتختلف). نخلّي SQLite للبوابة السريعة، بس تشغيلة ليلية + قبل-release على MySQL حقيقي (service container)غير قابلة للتفاوض لمنتج بيشحن migrations.
  • تكلفة Actions: الفري 2000 دقيقة/شهر للريبو الخاص ≈ 15-25 تشغيلة كاملة. نوفّر بـconcurrency: cancel-in-progress (أكبر موفّر لراجل بيـpush كتير) + cache للـcomposer/npm + حصر on:push على الفرعين. لو اتخطّى → ~1$ للتشغيلة، أرخص من regression واحد بيتشحن.

5 الخوف الحقيقي — "أعدّل Core فيبوظ LIS"

بوابة بتشغّل الموديول المتغيّر بس هتفوّت ده بالظبط. الحل مش selection ذكي:

✅ خلّي السويت سريعة كفاية إنك متختارش أبدًا — شغّلها كلها كل مرة. أي قاعدة اختيار صحيحة ("المتغيّر + Core + التابعين") بتنهار لـ"السويت كاملة" في نفس الحالات اللي بتخاف منها. و4,672 اختبار صغيرة بمقاييس الصناعة — تحت 10 دقايق بالتوازي، "شغّل كل حاجة" هو الأبسط والأصح. ليه بتمسك الـcross-module؟ لإن اختبارات LIS بتمرّن خدمات Core لما بتشتغل → الطريقة الوحيدة إنه يفوت = إنك متشغّلش اختبارات LIS.
➕ أضف "golden tests" لمسارات الفلوس بدري (مش مرحلة أخيرة): 10-20 اختبار characterization بيثبّتوا السلوك المضبوط للمحرّكات المشتركة:
  • قيد قانوني نموذجي → تأكيد صفوف المدين/الدائن بالظبط.
  • حركة مخزون → تأكيد الأرصدة.
  • حالات VAT (table-driven).
  • إجماليات الفاتورة.
دول بيحوّلوا "انحراف دلالي صامت عبر الموديولات" لـ"فشل صريح باسم Core" — أعلى قيمة لأي اختبارات جديدة، يوم-يومين شغل. (لو السويت طلعت فوق 15 دقيقة حتى بالتوازي: خريطة اعتماديات يدوية 16 سطر — "تغيير Core/Accounting/Inventory ⇒ السويت كاملة؛ الموديولات الطرفية CMMS/QMS/WebStore ممكن لوحدها". مش أكتر.)

6 الفرونت (صفر unit) — الترتيب: d ← b ← a ← c

  1. (d) بوابة prod build + typecheck أسبوع 1 — تكلفة شبه صفر. ng build --configuration production بقوالب Angular الصارمة بيمسك فئة كبيرة من الـregressions الحقيقية (حقول اتغيّر اسمها، bindings مكسورة، أخطاء i18n في القوالب).
  2. (b) Playwright critical smokes أسبوع 2-3 — نكتب playwright.config، نوصّل runner، ونكبّر لـ5-8 رحلات (سقف ~10): تسجيل دخول، إنشاء طلب تحليل → نتيجة → اعتماد، إنشاء فاتورة → إجماليات، ترحيل قيد، حركة مخزون. E2E هو المكان اللي بتظهر فيه أخطاء ربط NgRx. شغّلها ليلي/قبل-release الأول؛ ماترقّيهاش لبوابة push غير بعد أسابيع من إثبات إنها مش flaky (بوابة flaky بتعوّدك تتجاهل الأحمر = أسوأ من مفيش بوابة).
  3. (a) وسّع report-contract للفواتير/القيود فرصة — النمط مُثبت ورخيص، والفواتير/التقارير المطبوعة مستندات فلوس بتوصل العميل. تأكيدات موجّهة بس (حقول موجودة، إجماليات، بنية) — مش snapshots صفحة كاملة.
  4. (c) unit tests عريضة لـAngular لأ — أقل عائد/ساعة لراجل واحد. استثناء: منطق نقي بس (pipes، حاسبات الأسعار/الإجماليات، أدوات التاريخ/i18n) لما تلمسها.
الخلاصة: build + typecheck + E2E رفيّع + الـcontract harness = كفاية لراجل واحد. مش محتاج unit coverage عريضة.

7 بوابة الـRelease — العميل بيشغّل migrations مش السويت بتاعتك

أهم إضافة على الإطلاق: تمرين مسار الترقية (Upgrade rehearsal) — بيمسك فئة كاملة من الأخطاء اللي السويت مبنيويًا مش بتشوفها.

  1. تمرين ترقية في CI (قبل النشر): ناخد DB النسخة السابقة المنشورة بعد الـmigrate، ونطبّق التحديث الجديد زي ما العميل بالظبط بيعمل (migrate → sync-reference seeders → health-check) ونتأكد أخضر + smoke request. ده بيمسك الـseeder gap نهائيًا + فشل الـmigrations على داتا واقعية. 🔑 نخلّي الـfixture طازة آليًا: كل release الـCI بيحفظ dump ما بعد التركيب كـartifact → يبقى مدخل تمرين الـrelease اللي بعده (وإلا الـfixture بيتعفّن في 3 releases).
  2. تمرين fresh-install: نفك الـinstaller الحقيقي على MySQL نظيف، نركّب، نقلّع، health-check، smoke request. بيختبر الحاجة اللي العميل بياخدها، مش الريبو.
  3. settings:verify — نبنيه ونحطه في المكانين: أمر artisan بيقارن مفاتيح الإعدادات اللي الكود بيستخدمها ضد اللي الـseeders بتوفّرها → في CI و جوه health-check العميل. (طويل الأمد: نخلّي قراءة الإعداد fail-soft بـdefault مُعرّف في الكود → الصف الناقص يتدهور مش يكسر.)
  4. ship بوابة صلبة: moonstack:ship يتأكد: CI أخضر على الـSHA بالظبط · release من main · التوقيع بيتحقق ضد مفتاح العميل · versions.json سليم · الإصدار بيزيد تصاعديًا.
  5. Canary (رخيص، لاحقًا): نسختك بتـpoll manifest ما-قبل-النشر → التحديث الحقيقي بيتطبّق عليها الأول → smoke → بعدين تنشر للكل.
  6. مرحلة 2 تمرين مسار الفشل مرة: نحاكي health-check فاشل وسط التحديث ونتأكد إن الـbackup/rollback بيرجّع فعلاً.

8 ⛔ اللي متعملوش (تجنّب مسرح الاختبارات)

الخط الفاصل: الشبكة "كفاية" لما كل push بياخد تلقائيًا (سويت BE كاملة + FE prod build + contracts) في أقل من ~15 دقيقة، وكل release بياخد كمان install + upgrade rehearsal. بعد كده، أي check جديد لازم يشاور على فئة خطأ اتشحنت فعلاً — أي حاجة مبرّرة بـ"best practice" مش بحادثة حقيقية = مسرح، ولراجل واحد المسرح مش بس بيضيّع وقت، بيآكل الثقة اللي بتخلّيك تحترم البوابة أصلاً.

9 خطة 4 أسابيع

الأسبوعالمهام
أسبوع 1
الأساس + البوابات
يوم 1 — baseline triage: شغّل السويت كاملة مرة محليًا وسجّل الحقيقة (هيبقى فيه فشل/افتراضات بيئة). صلّح الرخيص، وعزل الباقي في skip-list مؤرّخة (بند/تاريخ لكل واحد). امنع CI على baseline أخضر-معروف، مش سويت مثالية.
يوم 2-3 — CI الباك إند: GitHub Actions، on:push لـhazemdev2+main، 4-6 shards + ParaTest، أخضر على الـbaseline.
يوم 3 — CI الفرونت (~20 دقيقة YAML): ng build --configuration production + report-contract.mjs.
يوم 4-5 — ship-SHA gate: moonstack:ship يرفض بلا CI أخضر على الـSHA + branch protection على main.
أسبوع 2-3
ملء الفجوات عالية القيمة
golden money-path tests (10-20: قيود، مخزون، VAT، إجماليات).
Playwright: config + runner + 5-8 smokes حرجة (ليلي الأول).
تشغيلة ليلية على MySQL حقيقي (service container) — تمسك فروقات SQLite↔MySQL.
أسبوع 4
بوابة الـrelease
upgrade rehearsal (DB النسخة السابقة → migrate → seeders → health-check → smoke) + حفظ dump كـartifact للـrelease الجاي.
fresh-install rehearsal على MySQL نظيف.
أمر settings:verify في CI + جوه health-check العميل.
بعد كدهفرصة أو أبدًا: توسيع report-contract، canary channel، تمرين مسار الفشل/rollback. مفيش حاجة تانية إلا لو خطأ حقيقي طلب.

10 أول خطوة ملموسة — دلوقتي

قبل أي YAML، محتاجين نعرف الحقيقة: السويت خضرا فعلاً ولا فيها فشل خفي؟ وبتاخد قد إيه؟ ده أساس كل حاجة بعده.

# 1) شغّل السويت كاملة مرة واحدة، سجّل النتيجة + الزمن (BE) cd /home/moonui2/moon-erp-be time php vendor/bin/pest 2>&1 | tee /tmp/baseline-run.log # 2) ثبّت ParaTest وقيس الفرق بالتوازي php composer.phar require --dev brianium/paratest time php artisan test --parallel --processes=4 # 3) تأكد الفرونت بيـbuild + الـcontract أخضر cd /home/moonui2/public_html/moon-erp npx ng build --configuration production node scripts/report-contract.mjs
أنا أقدر أعمل ده معاك دلوقتي: أشغّل الـbaseline (الخطوة 1+2) وأطلعلك تقرير — كام اختبار أخضر/أحمر، الزمن التسلسلي مقابل المتوازي، وقايمة أي فشل نعزله — ونبني عليه أول workflow لـGitHub Actions. قوللي أبدأ.