Przejdź do treści

Strategia testowania

merql korzysta z modelu testowania sterowanego wyrocznią. Każdy scenariusz testowy określa znany zestaw danych wejściowych (trzy stany bazy danych), deterministyczny oczekiwany wynik (wyrocznię) i porównuje go z rzeczywistym wynikiem scalania. 32 scenariusze obejmują poprawne scalania, konflikty, rozpoznawanie tożsamości, typy danych, skalę i przypadki brzegowe.

Powłoka
# Run the full verification suite.
./bin/verify-all

Polecenia na tej stronie uruchamia się w kopii repozytorium merql po composer install. Skrypty programistyczne w bin/, korpus scenarios/ i testy nie należą do pakietu Composer. Zależności programistyczne obejmują PHPUnit 13, który wymaga PHP 8.4 lub nowszego, choć sama biblioteka działa na PHP 8.2.

Testowanie sterowane wyrocznią

Nie istnieje narzędzie referencyjne wykonujące trójdrożne scalanie baz danych, dlatego wyrocznię oblicza się na podstawie znanych danych wejściowych, a nie generuje za pomocą zewnętrznego narzędzia. Każdy scenariusz ma deterministyczny oczekiwany wynik, który można zweryfikować logicznie:

  1. Przygotowanie -- utwórz trzy stany bazy danych (base, ours, theirs) o znanej zawartości.
  2. Wyrocznia -- oblicz oczekiwany wynik scalania na podstawie znanych danych wejściowych.
  3. Wynik rzeczywisty -- uruchom scalanie merql na trzech stanach.
  4. Porównanie -- wynik rzeczywisty musi dokładnie odpowiadać oczekiwanemu.
Kod
Known inputs (base, ours, theirs)
         |                    |
    Deterministic          merql merge
    computation
         |                    |
    Expected result       Actual result
         |                    |
         -----> Compare <-----
                  |
            Pass or Fail

Uruchamianie testów

Pełna weryfikacja

Skrypt verify-all jest ostatnią bramką. Uruchamia analizę statyczną, kontrolę standardów kodowania, testy PHPUnit i zestaw testów regresyjnych oracle:

Powłoka
./bin/verify-all

Wszystkie te kontrole muszą przejść, zanim jakiekolwiek prace zostaną uznane za zakończone.

Zestaw testów regresyjnych oracle

Uruchom wszystkie 32 scenariusze:

Powłoka
./bin/test-regression

Uruchom jedną kategorię:

Powłoka
./bin/test-regression --category clean
./bin/test-regression --category conflict
./bin/test-regression --category identity

Kategorie to nazwy katalogów w scenarios/: clean, conflict, identity, types, scale i edge. Nieznana kategoria wypisuje No scenarios found i kończy działanie powodzeniem. Opcję --category można powtarzać, aby uruchomić kilka kategorii.

Skrypt przyjmuje też --jobs, ale ignoruje jego wartość i uruchamia scenariusze jeden po drugim (#5).

Uruchom z minimalnym wyjściem (tylko powodzenie/niepowodzenie):

Powłoka
./bin/test-regression --fast

Pojedynczy scenariusz

Przetestuj jeden scenariusz w całym potoku:

Powłoka
./bin/test-scenario column-level-clean

Poszczególne kroki oracle

Powłoka
./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

Testy jednostkowe

Powłoka
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 uruchamia tylko PHPUnit. Zestaw oracle PHPUnit wskazuje na tests/Oracle, który zawiera klasy uruchamiające scenariusze, ale nie zawiera przypadków testowych, więc 32 scenariusze są uruchamiane tylko przez composer test:oracle, composer verify lub ./bin/verify-all.

Testy integracyjne MySQL są pomijane, chyba że MERQL_MYSQL_HOST, MERQL_MYSQL_DATABASE i MERQL_MYSQL_USER (oraz opcjonalnie MERQL_MYSQL_PASSWORD i MERQL_MYSQL_PORT) wskazują bazę danych do testów.

Jakość kodu

Powłoka
composer cs            # PSR-12 coding standards check
composer cs:fix        # Auto-fix coding standards
composer analyse       # PHPStan level 10 static analysis

Kategorie scenariuszy

clean (8 scenariuszy)

Scenariusze czystego scalania bez konfliktów. Obie strony mogą zmieniać dane, ale nigdy tę samą kolumnę w tym samym wierszu.

Scenariusz Testy
insert-only-theirs Ich strona wstawiła wiersze, nasza strona bez zmian.
update-only-theirs Ich strona zaktualizowała wiersze, nasza strona bez zmian.
delete-only-theirs Ich strona usunęła wiersze, nasza strona bez zmian.
insert-only-ours Nasza strona wstawiła wiersze, ich strona bez zmian.
mixed-no-overlap Obie strony zmieniły różne tabele lub wiersze.
both-same-change Obie strony wprowadziły identyczne zmiany (brak konfliktu).
column-level-clean Obie strony zmieniły ten sam wiersz, ale różne kolumny.
multi-table Zmiany w wielu tabelach.

conflict (6 scenariuszy)

Scenariusze, które muszą powodować konflikty.

Scenariusz Testy
both-update-same-column Obie strony zmieniły tę samą kolumnę na różne wartości.
update-vs-delete Jedna strona zaktualizowała wiersz, a druga usunęła ten sam wiersz.
delete-vs-update Odwrotna kolejność aktualizacji i usunięcia.
both-insert-same-pk Obie strony wstawiły wiersz z tym samym kluczem głównym.
multiple-conflicts Kilka konfliktów w jednym scalaniu.
partial-conflict Konflikty w niektórych kolumnach, pozostałe scalają się bez konfliktów.

identity (4 scenariusze)

Przypadki brzegowe tożsamości wierszy.

Scenariusz Testy
auto-increment Nowe wiersze mają różne identyfikatory w poszczególnych gałęziach.
natural-key Dopasowanie według unikatowych kolumn zamiast PK.
composite-key Klucz główny złożony z wielu kolumn.
no-key Tabela bez klucza głównego (zapasowe użycie skrótu treści).

types (6 scenariuszy)

Obsługa typów danych we wszystkich typach kolumn.

Scenariusz Testy
text-columns Wartości VARCHAR, TEXT, LONGTEXT.
numeric-columns Wartości INT, DECIMAL, FLOAT.
date-columns Wartości DATE, DATETIME, TIMESTAMP.
json-columns Scalanie kolumny JSON.
blob-columns Dane binarne.
null-handling Przejścia z NULL na wartość i z wartości na NULL.

scale (4 scenariusze)

Wydajność pod obciążeniem.

Scenariusz Testy
1k-rows 1,000 wierszy.
10k-rows 10,000 wierszy.
100k-rows 100,000 wierszy.
wide-table Tabela z ponad 50 kolumnami.

edge (4 scenariusze)

Przypadki brzegowe i graniczne warunki.

Scenariusz Testy
empty-changeset Brak zmian po jednej lub obu stronach.
schema-mismatch Kolumna dodana lub usunięta między migawkami.
encoding UTF-8, emoji, znaki specjalne.
large-text Bardzo duże wartości TEXT/LONGTEXT.

Struktura scenariusza

Każdy scenariusz to katalog w scenarios/<category>/<name>/ z konfiguracją 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"
    }
}

W przypadku scenariuszy konfliktowych oczekiwane konflikty są szczegółowo określone:

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

Potok ScenarioRunner

ScenarioRunner::run() koordynuje wykonanie pojedynczego scenariusza:

  1. OracleCapture::compute() tworzy trzy migawki na podstawie danych JSON scenariusza i uruchamia scalanie.
  2. ScenarioComparator::compare() sprawdza wynik scalania, porównując go z oczekiwanymi wartościami w scenario.json.
  3. Zwraca wynik powodzenia lub niepowodzenia wraz z listą komunikatów o błędach.
PHP
use Merql\Tests\Oracle\ScenarioRunner;

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

ScenarioRunner::runAll() uruchamia kolejno wszystkie scenariusze:

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

Dodawanie nowego scenariusza

  1. Utwórz katalog: scenarios/<category>/<name>/.
  2. Utwórz scenario.json z metadanymi scenariusza i oczekiwanymi wynikami.
  3. Zdefiniuj trzy stany bazy danych w konfiguracji JSON: dane bazowe, zmiany po naszej stronie i zmiany po ich stronie.
  4. Uruchom scenariusz: ./bin/test-scenario <name>.
  5. Sprawdź cały zestaw testów: ./bin/verify-all.

Standardy jakości kodu

merql wymusza:

  • PHPStan poziom 10 -- rygorystyczna analiza statyczna bez wyjątków od wartości bazowych.
  • PSR-12 -- standardy kodowania sprawdzane przez phpcs.
  • Testy regresyjne oracle -- wszystkie 32 scenariusze muszą przejść.
  • Testy jednostkowe -- izolowane testy poszczególnych komponentów.

Skrypt verify-all uruchamia kolejno wszystkie cztery kontrole. Nie dopuszcza się częściowego zatwierdzenia.

Integracja ciągła

CI uruchamia kontrolę standardów kodowania, PHPStan, composer test, zestaw testów integracyjnych z MySQL 8.4 przez mysqli, testy pokrycia prymitywów scalania oraz composer audit przy każdym wypchnięciu zmian i żądaniu ściągnięcia do main. Nie uruchamia zestawu testów regresyjnych oracle (#6), dlatego przed poleganiem na zmianie uruchom lokalnie composer test:oracle lub ./bin/verify-all.