Перейти к содержимому

Стратегия тестирования

В merql используется модель тестирования по эталонным результатам. В каждом тестовом сценарии задан известный набор входных данных (три состояния базы данных) и детерминированный ожидаемый результат (эталон), с которым сравнивается фактический результат слияния. 32 сценария охватывают слияния без конфликтов, конфликты, определение идентификаторов, типы данных, масштаб и граничные случаи.

Командная оболочка
# Run the full verification suite.
./bin/verify-all

Команды на этой странице нужно запускать из клона репозитория merql после composer install. Скрипты разработки в bin/, набор данных scenarios/ и тесты не входят в пакет Composer. Среди зависимостей для разработки есть PHPUnit 13, которому требуется PHP 8.4 или новее, хотя сама библиотека работает на PHP 8.2.

Тестирование по эталонным результатам

Поскольку не существует готового эталонного инструмента для трехстороннего слияния баз данных, ожидаемый результат вычисляется по известным входным данным, а не формируется внешним инструментом. Для каждого сценария задан детерминированный ожидаемый результат, который можно проверить логически:

  1. Подготовка -- создайте три состояния базы данных (база, наш вариант, их вариант) с известным содержимым.
  2. Эталон -- вычислите ожидаемый результат слияния по известным входным данным.
  3. Фактический результат -- выполните слияние трех состояний с помощью merql.
  4. Сравнение -- фактический результат должен точно совпасть с ожидаемым.
Код
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 Другая сторона добавила строки, наша сторона ничего не меняла.
update-only-theirs Другая сторона обновила строки, наша сторона ничего не меняла.
delete-only-theirs Другая сторона удалила строки, наша сторона ничего не меняла.
insert-only-ours Наша сторона добавила строки, другая сторона ничего не меняла.
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:

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"
    }
}

Для сценариев с конфликтами ожидаемые конфликты указаны подробно:

JSON
{
    "expectations": {
        "conflicts": 1,
        "conflict_details": [
            {
                "table": "test_posts",
                "primary_key": {"id": 42},
                "column": "title",
                "ours_value": "Welcome",
                "theirs_value": "Greetings"
            }
        ]
    }
}

Конвейер ScenarioRunner

ScenarioRunner::run() управляет выполнением одного сценария:

  1. OracleCapture::compute() создаёт три снимка на основе данных JSON сценария и выполняет слияние.
  2. ScenarioComparator::compare() сверяет результат слияния с ожидаемыми значениями в scenario.json.
  3. Возвращает результат: пройдено или не пройдено, а также список сообщений об ошибках.
PHP
use Merql\Tests\Oracle\ScenarioRunner;

$result = ScenarioRunner::run($scenario);
// ['name' => 'column-level-clean', 'pass' => true, 'failures' => []]

ScenarioRunner::runAll() последовательно запускает каждый сценарий:

PHP
$results = ScenarioRunner::runAll($scenarios);

Добавление нового сценария

  1. Создайте каталог: scenarios/<category>/<name>/.
  2. Создайте scenario.json с метаданными сценария и ожидаемыми результатами.
  3. Определите три состояния базы данных в конфигурации JSON: исходные данные, изменения нашей стороны, изменения другой стороны.
  4. Запустите сценарий: ./bin/test-scenario <name>.
  5. Проверьте весь набор тестов: ./bin/verify-all.

Стандарты качества кода

merql обеспечивает:

  • Уровень PHPStan 10 -- строгий статический анализ без исключений на основе базового набора.
  • PSR-12 -- соблюдение стандартов кодирования проверяет phpcs.
  • Регрессионные проверки оракула -- все 32 сценария должны пройти.
  • Модульные тесты -- изолированные проверки каждого компонента.

Скрипт verify-all последовательно выполняет все четыре проверки. Частичное подтверждение не принимается.

Непрерывная интеграция

При каждом отправлении изменений и запросе на слияние в main CI запускает проверку стандартов кодирования, PHPStan, composer test, набор интеграционных тестов на MySQL 8.4 через mysqli, проверку покрытия примитивов слияния и composer audit. Регрессионный набор сценариев оракула не запускается (#6), поэтому перед тем, как полагаться на изменение, локально запустите composer test:oracle или ./bin/verify-all.