Към съдържанието

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

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. Наборът 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

Категории сценарии

чисто (8 сценария)

Сливания без конфликти. Двете страни може да променят данни, но никога една и съща колона на един и същ ред.

Сценарий Проверява
insert-only-theirs В тяхната версия са вмъкнати редове, нашата не е променена.
update-only-theirs В тяхната версия са обновени редове, нашата не е променена.
delete-only-theirs В тяхната версия са изтрити редове, нашата не е променена.
insert-only-ours В нашата версия са вмъкнати редове, тяхната не е променена.
mixed-no-overlap Двете страни са променили различни таблици или редове.
both-same-change Двете страни са направили еднакви промени (без конфликт).
column-level-clean Двете страни са променили един и същ ред, но различни колони.
multi-table Промени в няколко таблици.

конфликти (6 сценария)

Сценарии, които трябва да доведат до конфликти.

Сценарий Проверява
both-update-same-column Двете страни са променили една и съща колона на различни стойности.
update-vs-delete Едната страна е обновила реда, а другата го е изтрила.
delete-vs-update Обратна посока на конфликта при обновяване и изтриване.
both-insert-same-pk Двете страни са вмъкнали ред със същия първичен ключ.
multiple-conflicts Няколко конфликта в едно сливане.
partial-conflict Някои колони са в конфликт, а други се сливат без проблем.

идентичност (4 сценария)

Крайни случаи при идентичността на редовете.

Сценарий Проверява
auto-increment Новите редове имат различни идентификатори в отделните клонове.
natural-key Съпоставяне по уникални колони вместо по PK.
composite-key Първичен ключ с няколко колони.
no-key Таблица без първичен ключ (резервно използване на хеш на съдържанието).

типове (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.

мащаб (4 сценария)

Производителност при натоварване.

Сценарий Проверява
1k-rows 1,000 реда.
10k-rows 10,000 реда.
100k-rows 100,000 реда.
wide-table Таблица с над 50 колони.

крайни случаи (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, преди да разчитате на промяна.