Zum Inhalt springen

Teststrategie

merql verwendet ein testmodell auf Grundlage von Orakeln. Jedes Testszenario legt bekannte Eingaben (drei Datenbankzustände) und ein deterministisches erwartetes Ergebnis (das Orakel) fest und vergleicht die tatsächliche Merge-Ausgabe damit. 32 Szenarien decken saubere Merges, Konflikte, Identitätsauflösung, Datentypen, Skalierung und Sonderfälle ab.

Shell
# Run the full verification suite.
./bin/verify-all

Die Befehle auf dieser Seite werden in einem Checkout des merql-Repositorys nach composer install ausgeführt. Die Entwicklungsskripte in bin/, das Korpus scenarios/ und die Tests gehören nicht zum Composer-Paket. Zu den Entwicklungsabhängigkeiten gehört PHPUnit 13, das PHP 8.4 oder höher benötigt, obwohl die Bibliothek selbst mit PHP 8.2 läuft.

Testen auf Grundlage von Orakeln

Es gibt kein vorhandenes Referenzwerkzeug für Dreiwege-Merges von Datenbanken. Daher wird das Orakel aus bekannten Eingaben berechnet und nicht von einem externen Werkzeug erzeugt. Jedes Szenario hat ein deterministisches erwartetes Ergebnis, das sich anhand der Logik überprüfen lässt:

  1. Einrichtung -- drei Datenbankzustände (Basis, uns, sie) mit bekannten Inhalten erstellen.
  2. Orakel -- das erwartete Merge-Ergebnis aus den bekannten Eingaben berechnen.
  3. Tatsächliches Ergebnis -- den Merge der drei Zustände mit merql ausführen.
  4. Vergleich -- das tatsächliche Ergebnis muss exakt mit dem erwarteten übereinstimmen.
Code
Known inputs (base, ours, theirs)
         |                    |
    Deterministic          merql merge
    computation
         |                    |
    Expected result       Actual result
         |                    |
         -----> Compare <-----
                  |
            Pass or Fail

Tests ausführen

Vollständige Überprüfung

Das Skript verify-all ist die abschließende Prüfstufe. Es führt statische Analyse, Prüfung der Codierungsstandards, PHPUnit-Tests und die Oracle-Regressionstestsuite aus:

Shell
./bin/verify-all

Dies muss erfolgreich sein, bevor eine Arbeit als abgeschlossen gilt.

Oracle-Regressionstestsuite

Alle 32 Szenarien ausführen:

Shell
./bin/test-regression

Eine einzelne Kategorie ausführen:

Shell
./bin/test-regression --category clean
./bin/test-regression --category conflict
./bin/test-regression --category identity

Die Kategorien sind die Verzeichnisnamen unter scenarios/: clean, conflict, identity, types, scale und edge. Bei einer unbekannten Kategorie wird No scenarios found ausgegeben und das Skript erfolgreich beendet. --category kann wiederholt werden, um mehrere Kategorien auszuführen.

Das Skript akzeptiert auch --jobs, ignoriert den Wert jedoch und führt die Szenarien nacheinander aus (#5).

Mit minimaler Ausgabe ausführen (nur Erfolg/Fehlschlag):

Shell
./bin/test-regression --fast

Einzelnes Szenario

Ein Szenario durch die gesamte Pipeline testen:

Shell
./bin/test-scenario column-level-clean

Einzelne Orakelschritte

Shell
./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

Unit-Tests

Shell
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 führt nur PHPUnit aus. Die PHPUnit-Suite unter oracle verweist auf tests/Oracle, wo sich die Szenario-Runner-Klassen, aber keine Testfälle befinden. Daher werden die 32 Szenarien nur über composer test:oracle, composer verify oder ./bin/verify-all ausgeführt.

Die MySQL-Integrationstests werden übersprungen, sofern MERQL_MYSQL_HOST, MERQL_MYSQL_DATABASE und MERQL_MYSQL_USER (sowie optional MERQL_MYSQL_PASSWORD und MERQL_MYSQL_PORT) nicht auf eine Testdatenbank verweisen.

Codequalität

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

Szenariokategorien

sauber (8 Szenarien)

Saubere Merges ohne Konflikte. Beide Seiten können Daten ändern, aber niemals dieselbe Spalte derselben Zeile.

Szenario Tests
insert-only-theirs Sie haben Zeilen eingefügt, wir haben nichts geändert.
update-only-theirs Sie haben Zeilen aktualisiert, wir haben nichts geändert.
delete-only-theirs Sie haben Zeilen gelöscht, wir haben nichts geändert.
insert-only-ours Wir haben Zeilen eingefügt, sie haben nichts geändert.
mixed-no-overlap Beide haben unterschiedliche Tabellen oder Zeilen geändert.
both-same-change Beide haben identische Änderungen vorgenommen (kein Konflikt).
column-level-clean Beide haben dieselbe Zeile, aber unterschiedliche Spalten geändert.
multi-table Änderungen an mehreren Tabellen.

Konflikt (6 Szenarien)

Szenarien, bei denen Konflikte auftreten müssen.

Szenario Tests
both-update-same-column Beide haben dieselbe Spalte auf unterschiedliche Werte geändert.
update-vs-delete Eine Seite hat dieselbe Zeile aktualisiert, die andere gelöscht.
delete-vs-update Umgekehrte Richtung bei Aktualisieren gegenüber Löschen.
both-insert-same-pk Beide haben eine Zeile mit demselben Primärschlüssel eingefügt.
multiple-conflicts Mehrere Konflikte in einem Merge.
partial-conflict Einige Spalten verursachen Konflikte, andere lassen sich problemlos zusammenführen.

Identität (4 Szenarien)

Sonderfälle bei der Zeilenidentität.

Szenario Tests
auto-increment Neue Zeilen haben in den Branches unterschiedliche IDs.
natural-key Abgleich anhand eindeutiger Spalten statt des Primärschlüssels.
composite-key Zusammengesetzter Primärschlüssel.
no-key Tabelle ohne Primärschlüssel (Fallback auf Inhalts-Hash).

Typen (6 Szenarien)

Verarbeitung aller Spaltendatentypen.

Szenario Tests
text-columns VARCHAR-, TEXT- und LONGTEXT-Werte.
numeric-columns INT-, DECIMAL- und FLOAT-Werte.
date-columns DATE-, DATETIME- und TIMESTAMP-Werte.
json-columns Zusammenführen von JSON-Spalten.
blob-columns Binärdaten.
null-handling Übergänge von NULL zu einem Wert und von einem Wert zu NULL.

Skalierung (4 Szenarien)

Leistung unter Last.

Szenario Tests
1k-rows 1,000 Zeilen.
10k-rows 10,000 Zeilen.
100k-rows 100,000 Zeilen.
wide-table Tabelle mit 50+ Spalten.

Randfälle (4 Szenarien)

Randfälle und Grenzbedingungen.

Szenario Tests
empty-changeset Keine Änderungen auf einer oder beiden Seiten.
schema-mismatch Spalte zwischen Snapshots hinzugefügt oder entfernt.
encoding UTF-8, Emoji und Sonderzeichen.
large-text Sehr große TEXT-/LONGTEXT-Werte.

Szenarioaufbau

Jedes Szenario liegt als Verzeichnis unter scenarios/<category>/<name>/ und enthält eine Konfiguration 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"
    }
}

Bei Konfliktszenarien werden die erwarteten Konflikte genau angegeben:

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

Die ScenarioRunner-Pipeline

ScenarioRunner::run() steuert ein einzelnes Szenario:

  1. OracleCapture::compute() erstellt Snapshots der drei Zustände anhand der Daten in JSON und führt den Merge aus.
  2. ScenarioComparator::compare() vergleicht das Merge-Ergebnis mit den erwarteten Werten in scenario.json.
  3. Gibt Erfolg oder Fehlschlag mit einer Liste von Fehlermeldungen zurück.
PHP
use Merql\Tests\Oracle\ScenarioRunner;

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

ScenarioRunner::runAll() führt alle Szenarien der Reihe nach aus:

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

Neues Szenario hinzufügen

  1. Erstelle ein Verzeichnis: scenarios/<category>/<name>/.
  2. Erstelle scenario.json mit den Szenariometadaten und Erwartungen.
  3. Definiere die drei Datenbankzustände in der Konfiguration JSON: Basisdaten, unsere Änderungen, ihre Änderungen.
  4. Führe das Szenario aus: ./bin/test-scenario <name>.
  5. Überprüfe die gesamte Testsuite: ./bin/verify-all.

Codequalitätsstandards

merql setzt Folgendes voraus:

  • PHPStan-Stufe 10 -- strenge statische Analyse ohne Ausnahmen in der Baseline.
  • PSR-12 -- Codierungsstandards, geprüft von phpcs.
  • Oracle-Regression -- alle 32 Szenarien müssen erfolgreich sein.
  • Unit-Tests -- isolierte Tests für jede Komponente.

Das Skript verify-all führt alle vier Prüfungen nacheinander aus. Eine teilweise Freigabe wird nicht akzeptiert.

Kontinuierliche Integration

CI führt bei jedem Push und Pull Request zu main Codierungsstandards, PHPStan, composer test, die Integrationssuite mit MySQL 8.4 über mysqli, Tests der Merge-Grundfunktionen und composer audit aus. Das Oracle-Regressionstestkorpus wird nicht ausgeführt (#6). Führe daher lokal composer test:oracle oder ./bin/verify-all aus, bevor du dich auf eine Änderung verlässt.