Стратегія тестування
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. Набір тестів oracle PHPUnit вказує на 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 |
Нові рядки мають різні ID у різних гілках. |
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 послідовно виконує всі чотири перевірки. Часткове схвалення не приймається.
Безперервна інтеграція
Під час кожного push і pull request до main CI перевіряє стандарти кодування, запускає PHPStan, composer test, набір інтеграційних тестів через mysqli на MySQL 8.4, перевірку базових операцій злиття та composer audit. Корпус регресійних перевірок за оракулом не запускається (#6), тому перед тим, як покладатися на зміну, локально запустіть composer test:oracle або ./bin/verify-all.