استراتيجية الاختبار
يستخدم merql نموذج اختبار موجّهًا بالنتائج المرجعية. يحدد كل سيناريو اختبار مجموعة معروفة من المدخلات (ثلاث حالات لقاعدة البيانات) ونتيجة متوقعة حتمية (المرجع)، ثم يقارن مخرجات الدمج الفعلية بتلك النتيجة. تغطي 32 حالة عمليات الدمج النظيفة والتعارضات وحل الهوية وأنواع البيانات والحجم والحالات الطرفية.
# Run the full verification suite.
./bin/verify-all
تُنفَّذ الأوامر في هذه الصفحة من نسخة مستنسخة من مستودع merql بعد
composer install. ليست نصوص التطوير في bin/ ومجموعة scenarios/ والاختبارات جزءًا من حزمة Composer. تشمل تبعيات التطوير PHPUnit 13، الذي يتطلب PHP 8.4 أو أحدث، مع أن المكتبة نفسها تعمل على PHP 8.2.
الاختبار الموجّه بالنتائج المرجعية
لا توجد أداة مرجعية قائمة تنفذ دمجًا ثلاثيًا لقواعد البيانات، لذا تُحسب النتيجة المرجعية انطلاقًا من مدخلات معروفة بدلًا من توليدها بواسطة أداة خارجية. لكل سيناريو نتيجة متوقعة حتمية يمكن التحقق منها منطقيًا:
- الإعداد -- أنشئ ثلاث حالات لقاعدة البيانات (base وours وtheirs) بمحتوى معروف.
- النتيجة المرجعية -- احسب نتيجة الدمج المتوقعة من المدخلات المعروفة.
- النتيجة الفعلية -- نفّذ الدمج في merql على الحالات الثلاث.
- المقارنة -- يجب أن تطابق النتيجة الفعلية النتيجة المتوقعة تمامًا.
Known inputs (base, ours, theirs)
| |
Deterministic merql merge
computation
| |
Expected result Actual result
| |
-----> Compare <-----
|
Pass or Fail
تشغيل الاختبارات
التحقق الكامل
يُعد البرنامج النصي verify-all بوابة التحقق النهائية. فهو يشغّل التحليل الثابت ومعايير كتابة الأكواد واختبارات PHPUnit ومجموعة اختبارات الانحدار المرجعية:
./bin/verify-all
يجب أن ينجح هذا قبل اعتبار أي عمل مكتملًا.
مجموعة اختبارات الانحدار المرجعية
شغّل السيناريوهات الـ32 كلها:
./bin/test-regression
شغّل فئة واحدة:
./bin/test-regression --category clean
./bin/test-regression --category conflict
./bin/test-regression --category identity
أسماء المجلدات تحت scenarios/ هي الفئات: clean وconflict و
identity وtypes وscale وedge. تؤدي الفئة غير المعروفة إلى طباعة
No scenarios found ثم الخروج بنجاح. يمكن تكرار --category لتشغيل
عدة فئات.
يقبل البرنامج النصي أيضًا --jobs، لكنه يتجاهل القيمة ويشغّل السيناريوهات واحدًا تلو الآخر (#5).
شغّل مع أقل قدر من المخرجات (نجاح/فشل فقط):
./bin/test-regression --fast
سيناريو واحد
اختبر سيناريو واحدًا عبر مسار التنفيذ الكامل:
./bin/test-scenario column-level-clean
خطوات مرجعية فردية
./bin/oracle column-level-clean # Compute expected result
./bin/actual column-level-clean # Run merql, capture output
./bin/compare column-level-clean # Diff oracle vs actual
اختبارات الوحدات
composer test:unit # Isolated component tests
composer test:integration # Integration tests (SQLite, plus MySQL when configured)
composer test # All PHPUnit suites
composer test:oracle # Oracle regression corpus (bin/test-regression)
composer verify # analyse + cs + test + test:oracle
يشغّل composer test PHPUnit فقط. تشير مجموعة PHPUnit oracle الحالية إلى tests/Oracle، الذي يحتوي على فئات مشغّل السيناريوهات لكنه لا يحتوي على حالات اختبار، لذلك لا تُشغّل السيناريوهات الـ32 إلا عبر composer test:oracle أو composer verify أو ./bin/verify-all.
تُتخطى اختبارات تكامل MySQL ما لم تكن MERQL_MYSQL_HOST وMERQL_MYSQL_DATABASE وMERQL_MYSQL_USER (وMERQL_MYSQL_PASSWORD وMERQL_MYSQL_PORT اختياريًا) تشير إلى قاعدة بيانات اختبار.
جودة الكود
composer cs # PSR-12 coding standards check
composer cs:fix # Auto-fix coding standards
composer analyse # PHPStan level 10 static analysis
فئات السيناريوهات
clean (8 سيناريوهات)
عمليات دمج نظيفة بلا تعارضات. يمكن لكلا الجانبين تغيير البيانات، لكن ليس العمود نفسه في الصف نفسه.
| السيناريو | الاختبارات |
|---|---|
insert-only-theirs |
أدرج theirs صفوفًا، بينما لم يغيّر ours شيئًا. |
update-only-theirs |
حدّث theirs صفوفًا، بينما لم يغيّر ours شيئًا. |
delete-only-theirs |
حذف theirs صفوفًا، بينما لم يغيّر ours شيئًا. |
insert-only-ours |
أدرج ours صفوفًا، بينما لم يغيّر theirs شيئًا. |
mixed-no-overlap |
غيّر الجانبان جداول أو صفوفًا مختلفة. |
both-same-change |
أجرى الجانبان تغييرات متطابقة (بلا تعارض). |
column-level-clean |
غيّر الجانبان الصف نفسه، لكن أعمدة مختلفة. |
multi-table |
تغييرات عبر جداول متعددة. |
conflict (6 سيناريوهات)
سيناريوهات يجب أن تنتج عنها تعارضات.
| السيناريو | الاختبارات |
|---|---|
both-update-same-column |
غيّر الجانبان العمود نفسه إلى قيمتين مختلفتين. |
update-vs-delete |
حدّث أحدهما الصف نفسه وحذفه الآخر. |
delete-vs-update |
الاتجاه المعاكس للتحديث مقابل الحذف. |
both-insert-same-pk |
أدرج الجانبان صفًا بالمفتاح الأساسي نفسه. |
multiple-conflicts |
عدة تعارضات في عملية دمج واحدة. |
partial-conflict |
تتعارض بعض الأعمدة وتُدمج أعمدة أخرى بسلاسة. |
identity (4 سيناريوهات)
حالات طرفية لهوية الصفوف.
| السيناريو | الاختبارات |
|---|---|
auto-increment |
للصفوف الجديدة معرّفات مختلفة عبر الفروع. |
natural-key |
المطابقة باستخدام أعمدة فريدة بدلًا من PK. |
composite-key |
مفتاح أساسي متعدد الأعمدة. |
no-key |
جدول بلا مفتاح أساسي (استخدام تجزئة المحتوى كبديل). |
types (6 سيناريوهات)
معالجة أنواع البيانات عبر جميع أنواع الأعمدة.
| السيناريو | الاختبارات |
|---|---|
text-columns |
قيم VARCHAR وTEXT وLONGTEXT. |
numeric-columns |
قيم INT وDECIMAL وFLOAT. |
date-columns |
قيم DATE وDATETIME وTIMESTAMP. |
json-columns |
دمج عمود JSON. |
blob-columns |
بيانات ثنائية. |
null-handling |
انتقالات من NULL إلى قيمة ومن قيمة إلى NULL. |
scale (4 سيناريوهات)
الأداء تحت الضغط.
| السيناريو | الاختبارات |
|---|---|
1k-rows |
1,000 صف. |
10k-rows |
10,000 صف. |
100k-rows |
100,000 صف. |
wide-table |
جدول يضم أكثر من 50 عمودًا. |
edge (4 سيناريوهات)
الحالات الطرفية وشروط الحدود.
| السيناريو | الاختبارات |
|---|---|
empty-changeset |
لا تغييرات على أحد الجانبين أو كليهما. |
schema-mismatch |
إضافة عمود أو حذفه بين اللقطات. |
encoding |
UTF-8 والرموز التعبيرية والمحارف الخاصة. |
large-text |
قيم TEXT/LONGTEXT كبيرة جدًا. |
بنية السيناريو
كل سيناريو مجلد تحت scenarios/<category>/<name>/ يحتوي على إعداد scenario.json:
{
"name": "column-level-clean",
"category": "clean",
"description": "Both sides modify same row but different columns",
"tables": ["test_posts"],
"expectations": {
"conflicts": 0,
"operations": 3,
"changeset_match": "exact",
"merge_match": "exact",
"sql_match": "semantic"
}
}
في سيناريوهات التعارض، تُحدَّد التعارضات المتوقعة بالتفصيل:
{
"expectations": {
"conflicts": 1,
"conflict_details": [
{
"table": "test_posts",
"primary_key": {"id": 42},
"column": "title",
"ours_value": "Welcome",
"theirs_value": "Greetings"
}
]
}
}
مسار تنفيذ ScenarioRunner
ينسّق ScenarioRunner::run() تنفيذ سيناريو واحد:
- ينشئ
OracleCapture::compute()ثلاث لقطات من بيانات JSON الخاصة بالسيناريو ويشغّل الدمج. - يتحقق
ScenarioComparator::compare()من نتيجة الدمج بمقارنتها بالقيم المتوقعة فيscenario.json. - يعرض نتيجة النجاح أو الفشل مع قائمة برسائل الإخفاق.
use Merql\Tests\Oracle\ScenarioRunner;
$result = ScenarioRunner::run($scenario);
// ['name' => 'column-level-clean', 'pass' => true, 'failures' => []]
يشغّل ScenarioRunner::runAll() كل سيناريو بالتتابع:
$results = ScenarioRunner::runAll($scenarios);
إضافة سيناريو جديد
- أنشئ مجلدًا:
scenarios/<category>/<name>/. - أنشئ
scenario.jsonمع بيانات السيناريو الوصفية وتوقعاته. - حدّد حالات قاعدة البيانات الثلاث في إعداد JSON: البيانات الأساسية وتعديلات ours وتعديلات theirs.
- شغّل السيناريو:
./bin/test-scenario <name>. - تحقّق من المجموعة كاملة:
./bin/verify-all.
معايير جودة الكود
يفرض merql ما يلي:
- PHPStan المستوى 10 -- تحليل ثابت صارم بلا استثناءات في الخط الأساس.
- PSR-12 -- معايير كتابة الأكواد التي يتحقق منها
phpcs. - اختبار الانحدار المرجعي -- يجب أن تنجح السيناريوهات الـ32 كلها.
- اختبارات الوحدات -- اختبارات معزولة لكل مكوّن.
يشغّل البرنامج النصي verify-all الفحوصات الأربعة بالتتابع. لا يُقبل اعتماد جزئي.
التكامل المستمر
يشغّل CI معايير كتابة الأكواد وPHPStan وcomposer test ومجموعة اختبارات التكامل على MySQL 8.4 عبر mysqli وتغطية أساسيات الدمج وcomposer audit مع كل عملية دفع وطلب سحب إلى main. لا يشغّل مجموعة اختبارات الانحدار المرجعية (#6)، لذا شغّل composer test:oracle أو ./bin/verify-all محليًا قبل الاعتماد على أي تغيير.