Ga naar de inhoud

Teststrategie

merql gebruikt een oraclegestuurd testmodel. Elk testscenario definieert een bekende set invoer (drie databasetoestanden), een deterministisch verwacht resultaat (de oracle) en vergelijkt de werkelijke uitvoer van de merge met die verwachting. 32 scenario's bestrijken schone merges, conflicten, identiteitsbepaling, gegevenstypen, schaal en uitzonderingsgevallen.

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

De opdrachten op deze pagina worden uitgevoerd vanuit een checkout van de merql-repository na composer install. De ontwikkelscripts in bin/, het corpus van scenarios/ en de tests maken geen deel uit van het Composer-pakket. De ontwikkelafhankelijkheden omvatten PHPUnit 13, waarvoor PHP 8.4 of hoger vereist is, hoewel de bibliotheek zelf draait op PHP 8.2.

Oraclegestuurd testen

Er bestaat geen referentietool voor het uitvoeren van een drieweg-databasemerge. Daarom wordt de oracle berekend op basis van bekende invoer, niet gegenereerd door een externe tool. Elk scenario heeft een deterministisch verwacht resultaat dat aan de hand van logica kan worden gecontroleerd:

  1. Setup -- maak drie databasetoestanden (base, ours, theirs) met bekende inhoud.
  2. Oracle -- bereken het verwachte mergeresultaat op basis van de bekende invoer.
  3. Werkelijke uitvoering -- voer de merge van merql uit op de drie toestanden.
  4. Vergelijking -- het werkelijke resultaat moet exact overeenkomen met het verwachte resultaat.
Code
Known inputs (base, ours, theirs)
         |                    |
    Deterministic          merql merge
    computation
         |                    |
    Expected result       Actual result
         |                    |
         -----> Compare <-----
                  |
            Pass or Fail

Tests uitvoeren

Volledige verificatie

Het verify-all-script is de laatste controle. Het voert statische analyse, coderingsstandaarden, PHPUnit-tests en de oracle-regressiesuite uit:

Shell
./bin/verify-all

Dit moet slagen voordat werk als voltooid wordt beschouwd.

Oracle-regressiesuite

Voer alle 32 scenario's uit:

Shell
./bin/test-regression

Voer één categorie uit:

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

Categorieën zijn de mapnamen onder scenarios/: clean, conflict, identity, types, scale en edge. Bij een onbekende categorie wordt No scenarios found weergegeven en eindigt het script succesvol. --category kan worden herhaald om meerdere categorieën uit te voeren.

Het script accepteert ook --jobs, maar negeert de waarde en voert scenario's na elkaar uit (#5).

Uitvoeren met minimale uitvoer (alleen geslaagd/mislukt):

Shell
./bin/test-regression --fast

Eén scenario

Test één scenario via de volledige pipeline:

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

Afzonderlijke oracle-stappen

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

Unittests

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 voert alleen PHPUnit uit. De oracle-suite van PHPUnit verwijst naar tests/Oracle, met de klassen van de scenariorunner maar zonder testgevallen. Daarom worden de 32 scenario's alleen uitgevoerd via composer test:oracle, composer verify of ./bin/verify-all.

De MySQL-integratietests worden overgeslagen tenzij MERQL_MYSQL_HOST, MERQL_MYSQL_DATABASE en MERQL_MYSQL_USER (en eventueel ook MERQL_MYSQL_PASSWORD en MERQL_MYSQL_PORT) naar een testdatabase verwijzen.

Codekwaliteit

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

Scenariocategorieën

clean (8 scenario's)

Schone merges zonder conflicten. Beide zijden kunnen gegevens wijzigen, maar nooit dezelfde kolom van dezelfde rij.

Scenario Tests
insert-only-theirs Theirs heeft rijen ingevoegd, ours is ongewijzigd.
update-only-theirs Theirs heeft rijen bijgewerkt, ours is ongewijzigd.
delete-only-theirs Theirs heeft rijen verwijderd, ours is ongewijzigd.
insert-only-ours Ours heeft rijen ingevoegd, theirs is ongewijzigd.
mixed-no-overlap Beide hebben verschillende tabellen of rijen gewijzigd.
both-same-change Beide hebben identieke wijzigingen aangebracht (geen conflict).
column-level-clean Beide hebben dezelfde rij gewijzigd, maar verschillende kolommen.
multi-table Wijzigingen in meerdere tabellen.

conflict (6 scenario's)

Scenario's die conflicten moeten opleveren.

Scenario Tests
both-update-same-column Beide hebben dezelfde kolom gewijzigd in verschillende waarden.
update-vs-delete De ene heeft dezelfde rij bijgewerkt en de andere verwijderd.
delete-vs-update Omgekeerde richting van update versus verwijdering.
both-insert-same-pk Beide hebben een rij met dezelfde primaire sleutel ingevoegd.
multiple-conflicts Meerdere conflicten in één merge.
partial-conflict Sommige kolommen conflicteren, andere worden zonder conflict samengevoegd.

identity (4 scenario's)

Randgevallen rond rij-identiteit.

Scenario Tests
auto-increment Nieuwe rijen hebben verschillende ID's in verschillende branches.
natural-key Koppelen op basis van unieke kolommen in plaats van PK.
composite-key Primaire sleutel met meerdere kolommen.
no-key Tabel zonder primaire sleutel (terugvaloptie op basis van inhoudshash).

types (6 scenario's)

Verwerking van gegevenstypen voor alle kolomtypen.

Scenario Tests
text-columns Waarden van het type VARCHAR, TEXT en LONGTEXT.
numeric-columns Waarden van het type INT, DECIMAL en FLOAT.
date-columns Waarden van het type DATE, DATETIME en TIMESTAMP.
json-columns JSON-kolommerge.
blob-columns Binaire gegevens.
null-handling Overgangen van NULL naar een waarde en van een waarde naar NULL.

scale (4 scenario's)

Prestaties onder belasting.

Scenario Tests
1k-rows 1,000 rijen.
10k-rows 10,000 rijen.
100k-rows 100,000 rijen.
wide-table Tabel met 50+ kolommen.

edge (4 scenario's)

Uitzonderingsgevallen en randvoorwaarden.

Scenario Tests
empty-changeset Geen wijzigingen aan één of beide zijden.
schema-mismatch Kolom toegevoegd of verwijderd tussen snapshots.
encoding UTF-8, emoji en speciale tekens.
large-text Zeer grote TEXT/LONGTEXT-waarden.

Scenariostructuur

Elk scenario is een map onder scenarios/<category>/<name>/ met een configuratie 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"
    }
}

Voor conflictscenario's worden de verwachte conflicten gedetailleerd opgegeven:

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

De ScenarioRunner-pipeline

ScenarioRunner::run() coördineert één scenario:

  1. OracleCapture::compute() bouwt drie snapshots op basis van de gegevens van JSON en voert de merge uit.
  2. ScenarioComparator::compare() controleert het mergeresultaat aan de hand van de verwachte waarden in scenario.json.
  3. Retourneert geslaagd/mislukt met een lijst foutmeldingen.
PHP
use Merql\Tests\Oracle\ScenarioRunner;

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

ScenarioRunner::runAll() voert elk scenario achtereenvolgens uit:

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

Een nieuw scenario toevoegen

  1. Maak een map: scenarios/<category>/<name>/.
  2. Maak scenario.json met de scenariometagegevens en verwachtingen.
  3. Definieer de drie databasestatussen in de configuratie van JSON: basisgegevens, wijzigingen van ours en wijzigingen van theirs.
  4. Voer het scenario uit: ./bin/test-scenario <name>.
  5. Controleer de volledige suite: ./bin/verify-all.

Codekwaliteitsnormen

merql hanteert:

  • PHPStan niveau 10 -- strikte statische analyse zonder uitzonderingen op basis van een baseline.
  • PSR-12 -- coderingsnormen gecontroleerd door phpcs.
  • Oracle-regressie -- alle 32 scenario's moeten slagen.
  • Unit-tests -- geïsoleerde tests voor elk onderdeel.

Het script verify-all voert alle vier controles achter elkaar uit. Gedeeltelijke goedkeuring wordt niet geaccepteerd.

Continue integratie

CI voert bij elke push en pull request naar main de controles voor coderingsnormen, PHPStan, composer test, de integratiesuite met MySQL 8.4 via mysqli, de dekking van merge-primitieven en composer audit uit. De oracle-regressiecorpus wordt niet uitgevoerd (#6); voer daarom lokaal composer test:oracle of ./bin/verify-all uit voordat u op een wijziging vertrouwt.