Vai al contenuto

Strategia di test

merql usa un modello di test guidato da un oracolo. Ogni scenario di test definisce un insieme noto di input (tre stati del database), un risultato atteso deterministico (l'oracolo) e confronta l'output effettivo della fusione con quello atteso. 32 scenari coprono fusioni senza conflitti, conflitti, risoluzione dell'identità, tipi di dati, scalabilità e casi limite.

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

I comandi di questa pagina si eseguono dopo composer install nella copia locale del repository merql. Gli script di sviluppo in bin/, il corpus scenarios/ e i test non fanno parte del pacchetto Composer. Le dipendenze di sviluppo includono PHPUnit 13, che richiede PHP 8.4 o successivo, anche se la libreria stessa funziona con PHP 8.2.

Test guidati da un oracolo

Non esiste uno strumento di riferimento che esegua una fusione di database a tre vie, quindi l'oracolo viene calcolato a partire da input noti anziché essere generato da uno strumento esterno. Ogni scenario ha un risultato atteso deterministico che può essere verificato tramite logica:

  1. Configurazione -- crea tre stati del database (base, ours, theirs) con contenuti noti.
  2. Oracolo -- calcola il risultato atteso della fusione a partire dagli input noti.
  3. Effettivo -- esegue la fusione di merql sui tre stati.
  4. Confronto -- il risultato effettivo deve corrispondere esattamente a quello atteso.
Codice
Known inputs (base, ours, theirs)
         |                    |
    Deterministic          merql merge
    computation
         |                    |
    Expected result       Actual result
         |                    |
         -----> Compare <-----
                  |
            Pass or Fail

Esecuzione dei test

Verifica completa

Lo script verify-all è il controllo finale. Esegue l'analisi statica, verifica gli standard di codifica, i test PHPUnit e la suite di regressione dell'oracolo:

Shell
./bin/verify-all

Tutto questo deve essere superato prima di considerare completo qualsiasi lavoro.

Suite di regressione dell'oracolo

Eseguire tutti i 32 scenari:

Shell
./bin/test-regression

Eseguire una singola categoria:

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

Le categorie corrispondono ai nomi delle directory in scenarios/: clean, conflict, identity, types, scale e edge. Una categoria sconosciuta stampa No scenarios found e termina con successo. È possibile ripetere --category per eseguire più categorie.

Lo script accetta anche --jobs, ma ignora il valore ed esegue gli scenari uno dopo l'altro (#5).

Eseguire con output minimo (solo esito positivo/negativo):

Shell
./bin/test-regression --fast

Singolo scenario

Testare uno scenario attraverso l'intera pipeline:

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

Singoli passaggi dell'oracolo

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

Test unitari

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 esegue solo PHPUnit. La suite oracle PHPUnit punta a tests/Oracle, che contiene le classi del runner degli scenari ma nessun caso di test, quindi i 32 scenari vengono eseguiti solo tramite composer test:oracle, composer verify o ./bin/verify-all.

I test di integrazione MySQL vengono esclusi a meno che MERQL_MYSQL_HOST, MERQL_MYSQL_DATABASE e MERQL_MYSQL_USER (ed eventualmente anche MERQL_MYSQL_PASSWORD e MERQL_MYSQL_PORT) non puntino a un database di test.

Qualità del codice

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

Categorie di scenari

clean (8 scenari)

Fusioni senza conflitti. Entrambi i lati possono modificare i dati, ma mai la stessa colonna della stessa riga.

Scenario Test
insert-only-theirs theirs ha inserito righe, ours non ha apportato modifiche.
update-only-theirs theirs ha aggiornato righe, ours non ha apportato modifiche.
delete-only-theirs theirs ha eliminato righe, ours non ha apportato modifiche.
insert-only-ours ours ha inserito righe, theirs non ha apportato modifiche.
mixed-no-overlap Entrambi hanno modificato tabelle o righe diverse.
both-same-change Entrambi hanno apportato modifiche identiche (nessun conflitto).
column-level-clean Entrambi hanno modificato la stessa riga, ma colonne diverse.
multi-table Modifiche in più tabelle.

conflict (6 scenari)

Scenari che devono generare conflitti.

Scenario Test
both-update-same-column Entrambi hanno modificato la stessa colonna assegnandole valori diversi.
update-vs-delete Un lato ha aggiornato la riga e l'altro l'ha eliminata.
delete-vs-update Direzione inversa del conflitto aggiornamento/eliminazione.
both-insert-same-pk Entrambi hanno inserito una riga con la stessa chiave primaria.
multiple-conflicts Più conflitti in una fusione.
partial-conflict Alcune colonne sono in conflitto, altre si fondono senza conflitti.

identity (4 scenari)

Casi limite relativi all'identità delle righe.

Scenario Test
auto-increment Le nuove righe hanno ID diversi tra i rami.
natural-key Abbinamento tramite colonne univoche anziché PK.
composite-key Chiave primaria composta da più colonne.
no-key Tabella senza chiave primaria (alternativa basata sull'hash dei contenuti).

types (6 scenari)

Gestione dei tipi di dati per tutti i tipi di colonna.

Scenario Test
text-columns Valori VARCHAR, TEXT, LONGTEXT.
numeric-columns Valori INT, DECIMAL, FLOAT.
date-columns Valori DATE, DATETIME, TIMESTAMP.
json-columns Fusione della colonna JSON.
blob-columns Dati binari.
null-handling Transizioni da NULL a un valore e da un valore a NULL.

scale (4 scenari)

Prestazioni sotto carico.

Scenario Test
1k-rows 1,000 righe.
10k-rows 10,000 righe.
100k-rows 100,000 righe.
wide-table Tabella con oltre 50 colonne.

edge (4 scenari)

Casi limite e condizioni al confine.

Scenario Test
empty-changeset Nessuna modifica su uno o entrambi i lati.
schema-mismatch Colonna aggiunta o rimossa tra gli snapshot.
encoding UTF-8, emoji, caratteri speciali.
large-text Valori TEXT/LONGTEXT molto grandi.

Struttura degli scenari

Ogni scenario è una directory in scenarios/<category>/<name>/ con una configurazione 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"
    }
}

Per gli scenari con conflitti, i conflitti attesi sono specificati nel dettaglio:

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

La pipeline ScenarioRunner

ScenarioRunner::run() orchestra un singolo scenario:

  1. OracleCapture::compute() crea tre snapshot dai dati JSON dello scenario ed esegue la fusione.
  2. ScenarioComparator::compare() verifica il risultato della fusione rispetto ai valori attesi in scenario.json.
  3. Restituisce l'esito positivo/negativo con un elenco di messaggi di errore.
PHP
use Merql\Tests\Oracle\ScenarioRunner;

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

ScenarioRunner::runAll() esegue tutti gli scenari in sequenza:

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

Aggiungere un nuovo scenario

  1. Create una directory: scenarios/<category>/<name>/.
  2. Create scenario.json con i metadati e le aspettative dello scenario.
  3. Definite i tre stati del database nella configurazione JSON: dati base, modifiche di ours, modifiche di theirs.
  4. Eseguite lo scenario: ./bin/test-scenario <name>.
  5. Verificate la suite completa: ./bin/verify-all.

Standard di qualità del codice

merql applica:

  • PHPStan livello 10 -- analisi statica rigorosa senza eccezioni nella baseline.
  • PSR-12 -- standard di codifica verificati da phpcs.
  • Regressione dell'oracolo -- tutti i 32 scenari devono essere superati.
  • Test unitari -- test isolati per ogni componente.

Lo script verify-all esegue tutti e quattro i controlli in sequenza. Non sono ammesse approvazioni parziali.

Integrazione continua

L'integrazione continua esegue gli standard di codifica, PHPStan, composer test, la suite di integrazione con MySQL 8.4 tramite mysqli, la copertura delle primitive di fusione e composer audit a ogni push e richiesta di pull verso main. Non esegue il corpus di regressione dell'oracolo (#6), quindi eseguite composer test:oracle o ./bin/verify-all in locale prima di affidarvi a una modifica.