Strategia de testare
merql folosește un model de testare bazat pe rezultate de referință. Fiecare scenariu de test definește un set cunoscut de intrări (trei stări ale bazei de date), un rezultat determinist așteptat (referința) și compară rezultatul efectiv al îmbinării cu așteptarea. 32 de scenarii acoperă îmbinări fără conflicte, conflicte, rezolvarea identității, tipuri de date, scară și cazuri limită.
# Run the full verification suite.
./bin/verify-all
Comenzile de pe această pagină se execută dintr-un checkout al depozitului merql după
composer install. Scripturile de dezvoltare din bin/, corpusul scenarios/
și testele nu fac parte din pachetul Composer. Dependențele de dezvoltare includ PHPUnit 13, care necesită PHP 8.4 sau o versiune ulterioară, deși biblioteca în sine rulează pe PHP 8.2.
Testare bazată pe rezultate de referință
Nu există un instrument de referință care să efectueze îmbinări tridirecționale ale bazelor de date, așa că rezultatul de referință este calculat din intrări cunoscute, nu generat de un instrument extern. Fiecare scenariu are un rezultat determinist așteptat, care poate fi verificat logic:
- Configurare -- creați trei stări ale bazei de date (base, ours, theirs) cu conținut cunoscut.
- Referință -- calculați rezultatul așteptat al îmbinării din intrările cunoscute.
- Rezultat efectiv -- rulați îmbinarea merql pentru cele trei stări.
- Comparare -- rezultatul efectiv trebuie să corespundă exact rezultatului așteptat.
Known inputs (base, ours, theirs)
| |
Deterministic merql merge
computation
| |
Expected result Actual result
| |
-----> Compare <-----
|
Pass or Fail
Rularea testelor
Verificare completă
Scriptul verify-all este verificarea finală. Rulează analiza statică, verificarea standardelor de cod, testele PHPUnit și suita de regresie bazată pe rezultate de referință:
./bin/verify-all
Acesta trebuie să treacă înainte ca orice activitate să fie considerată completă.
Suita de regresie bazată pe rezultate de referință
Rulați toate cele 32 de scenarii:
./bin/test-regression
Rulați o singură categorie:
./bin/test-regression --category clean
./bin/test-regression --category conflict
./bin/test-regression --category identity
Categoriile sunt numele directoarelor din scenarios/: clean, conflict,
identity, types, scale și edge. O categorie necunoscută afișează
No scenarios found și se închide cu succes. --category se poate repeta pentru a rula
mai multe categorii.
Scriptul acceptă și --jobs, dar ignoră valoarea și rulează scenariile
unul după altul (#5).
Rulați cu rezultate minime (numai reușit/eșuat):
./bin/test-regression --fast
Un singur scenariu
Testați un scenariu prin întregul flux:
./bin/test-scenario column-level-clean
Pași individuali ai oracle-ului
./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
Teste unitare
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 rulează doar PHPUnit. Suita oracle PHPUnit indică spre
tests/Oracle, care conține clasele pentru rularea scenariilor, dar nu și cazuri de test, așa că
cele 32 de scenarii rulează doar prin composer test:oracle, composer verify sau
./bin/verify-all.
Testele de integrare MySQL sunt omise dacă MERQL_MYSQL_HOST,
MERQL_MYSQL_DATABASE și MERQL_MYSQL_USER (plus, opțional,
MERQL_MYSQL_PASSWORD și MERQL_MYSQL_PORT) nu indică spre o bază de date de test.
Calitatea codului
composer cs # PSR-12 coding standards check
composer cs:fix # Auto-fix coding standards
composer analyse # PHPStan level 10 static analysis
Categorii de scenarii
clean (8 scenarii)
Îmbinări curate, fără conflicte. Ambele părți pot modifica date, dar niciodată aceeași coloană din același rând.
| Scenariu | Teste |
|---|---|
insert-only-theirs |
Partea lor a inserat rânduri, partea noastră nu a schimbat nimic. |
update-only-theirs |
Partea lor a actualizat rânduri, partea noastră nu a schimbat nimic. |
delete-only-theirs |
Partea lor a șters rânduri, partea noastră nu a schimbat nimic. |
insert-only-ours |
Partea noastră a inserat rânduri, partea lor nu a schimbat nimic. |
mixed-no-overlap |
Ambele părți au modificat tabele sau rânduri diferite. |
both-same-change |
Ambele părți au făcut modificări identice (fără conflict). |
column-level-clean |
Ambele părți au modificat același rând, dar coloane diferite. |
multi-table |
Modificări în mai multe tabele. |
conflict (6 scenarii)
Scenarii care trebuie să genereze conflicte.
| Scenariu | Teste |
|---|---|
both-update-same-column |
Ambele părți au modificat aceeași coloană cu valori diferite. |
update-vs-delete |
O parte a actualizat, iar cealaltă a șters același rând. |
delete-vs-update |
Direcția inversă a operației de actualizare versus ștergere. |
both-insert-same-pk |
Ambele părți au inserat un rând cu aceeași cheie primară. |
multiple-conflicts |
Mai multe conflicte într-o singură îmbinare. |
partial-conflict |
Unele coloane au conflicte, altele se îmbină fără probleme. |
identity (4 scenarii)
Cazuri-limită privind identitatea rândurilor.
| Scenariu | Teste |
|---|---|
auto-increment |
Rândurile noi au ID-uri diferite în ramuri. |
natural-key |
Potrivire după coloane unice în loc de PK. |
composite-key |
Cheie primară cu mai multe coloane. |
no-key |
Tabel fără cheie primară (alternativă prin hash de conținut). |
types (6 scenarii)
Gestionarea tipurilor de date pentru toate tipurile de coloane.
| Scenariu | Teste |
|---|---|
text-columns |
Valori VARCHAR, TEXT, LONGTEXT. |
numeric-columns |
Valori INT, DECIMAL, FLOAT. |
date-columns |
Valori DATE, DATETIME, TIMESTAMP. |
json-columns |
Îmbinarea coloanei JSON. |
blob-columns |
Date binare. |
null-handling |
Tranziții de la NULL la valoare și de la valoare la NULL. |
scale (4 scenarii)
Performanță sub sarcină.
| Scenariu | Teste |
|---|---|
1k-rows |
1,000 rânduri. |
10k-rows |
10,000 rânduri. |
100k-rows |
100,000 rânduri. |
wide-table |
Tabel cu peste 50 de coloane. |
edge (4 scenarii)
Cazuri-limită și condiții de margine.
| Scenariu | Teste |
|---|---|
empty-changeset |
Nicio modificare pe una sau pe ambele părți. |
schema-mismatch |
Coloană adăugată sau eliminată între instantanee. |
encoding |
UTF-8, emoji, caractere speciale. |
large-text |
Valori TEXT/LONGTEXT foarte mari. |
Structura scenariilor
Fiecare scenariu este un director în scenarios/<category>/<name>/ cu o configurație scenario.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"
}
}
Pentru scenariile cu conflicte, conflictele așteptate sunt specificate în detaliu:
{
"expectations": {
"conflicts": 1,
"conflict_details": [
{
"table": "test_posts",
"primary_key": {"id": 42},
"column": "title",
"ours_value": "Welcome",
"theirs_value": "Greetings"
}
]
}
}
Fluxul ScenarioRunner
ScenarioRunner::run() coordonează un singur scenariu:
OracleCapture::compute()construiește trei instantanee din datele JSON ale scenariului și execută îmbinarea.ScenarioComparator::compare()verifică rezultatul îmbinării în raport cu valorile așteptate dinscenario.json.- Returnează reușita/eșecul și o listă de mesaje de eroare.
use Merql\Tests\Oracle\ScenarioRunner;
$result = ScenarioRunner::run($scenario);
// ['name' => 'column-level-clean', 'pass' => true, 'failures' => []]
ScenarioRunner::runAll() rulează toate scenariile în ordine:
$results = ScenarioRunner::runAll($scenarios);
Adăugarea unui scenariu nou
- Creați un director:
scenarios/<category>/<name>/. - Creați
scenario.jsoncu metadatele și așteptările scenariului. - Definiți cele trei stări ale bazei de date în configurația JSON: datele inițiale, modificările noastre, modificările lor.
- Rulați scenariul:
./bin/test-scenario <name>. - Verificați întreaga suită:
./bin/verify-all.
Standarde de calitate a codului
merql impune:
- PHPStan nivelul 10 -- analiză statică strictă, fără excepții de bază.
- PSR-12 -- standarde de cod verificate de
phpcs. - Regresie oracle -- toate cele 32 de scenarii trebuie să treacă.
- Teste unitare -- teste izolate pentru fiecare componentă.
Scriptul verify-all rulează toate cele patru verificări în ordine. Nu se acceptă aprobarea parțială.
Integrare continuă
CI rulează verificările standardelor de cod, PHPStan, composer test, suita de integrare
cu MySQL 8.4 prin mysqli, verificarea acoperirii primitivelor de îmbinare și
composer audit la fiecare push și pull request către main. Nu rulează
corpusul de regresie oracle (#6), așa că
rulați local composer test:oracle sau ./bin/verify-all înainte de a vă baza pe o
modificare.