Teststrategi
merql använder en oracle-baserad testmodell. Varje testscenario definierar en känd uppsättning indata (tre databastillstånd), ett deterministiskt förväntat resultat (oraclet) och jämför det faktiska resultatet av sammanslagningen med förväntningen. 32 scenarier täcker konfliktfria sammanslagningar, konflikter, identitetslösning, datatyper, skala och specialfall.
# Run the full verification suite.
./bin/verify-all
Kommandona på den här sidan körs från en utcheckning av merql-arkivet efter
composer install. Utvecklingsskripten i bin/, korpusen scenarios/
och testerna ingår inte i paketet Composer. Utvecklingsberoendena omfattar
PHPUnit 13, som kräver PHP 8.4 eller senare, även om själva biblioteket
körs med PHP 8.2.
Oracle-baserad testning
Det finns inget befintligt referensverktyg som utför en trevägssammanslagning av databaser, så oraclet beräknas utifrån kända indata i stället för att genereras av ett externt verktyg. Varje scenario har ett deterministiskt förväntat resultat som kan verifieras logiskt:
- Förberedelse -- skapa tre databastillstånd (base, ours, theirs) med känt innehåll.
- Oracle -- beräkna det förväntade resultatet av sammanslagningen utifrån de kända indata.
- Faktiskt resultat -- kör merql:s sammanslagning av de tre tillstånden.
- Jämförelse -- det faktiska resultatet måste exakt matcha det förväntade resultatet.
Known inputs (base, ours, theirs)
| |
Deterministic merql merge
computation
| |
Expected result Actual result
| |
-----> Compare <-----
|
Pass or Fail
Köra tester
Fullständig verifiering
Skriptet verify-all är den slutliga kontrollen. Det kör statisk analys, kodningsstandarder, tester med PHPUnit och oracle-regressionstestsviten:
./bin/verify-all
Det här måste godkännas innan något arbete anses vara slutfört.
Oracle-regressionstestsvit
Kör alla 32 scenarier:
./bin/test-regression
Kör en enskild kategori:
./bin/test-regression --category clean
./bin/test-regression --category conflict
./bin/test-regression --category identity
Kategorierna är katalognamnen under scenarios/: clean, conflict,
identity, types, scale och edge. En okänd kategori skriver ut
No scenarios found och avslutas utan fel. --category kan upprepas för att köra
flera kategorier.
Skriptet godtar även --jobs, men ignorerar värdet och kör scenarierna
efter varandra (#5).
Kör med minimal utdata (endast godkänt/underkänt):
./bin/test-regression --fast
Enskilt scenario
Testa ett scenario genom hela processen:
./bin/test-scenario column-level-clean
Enskilda oracle-steg
./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
Enhetstester
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 kör endast PHPUnit. Testsviten oracle PHPUnit pekar på
tests/Oracle, som innehåller scenariokörarens klasser men inga testfall, så
de 32 scenarierna körs endast via composer test:oracle, composer verify eller
./bin/verify-all.
MySQL-integrationstesterna hoppas över om inte MERQL_MYSQL_HOST,
MERQL_MYSQL_DATABASE och MERQL_MYSQL_USER (samt eventuellt
MERQL_MYSQL_PASSWORD och MERQL_MYSQL_PORT) pekar på en testdatabas.
Kodkvalitet
composer cs # PSR-12 coding standards check
composer cs:fix # Auto-fix coding standards
composer analyse # PHPStan level 10 static analysis
Scenariokategorier
clean (8 scenarier)
Konfliktfria sammanslagningar. Båda sidorna kan ändra data, men aldrig samma kolumn i samma rad.
| Scenario | Testar |
|---|---|
insert-only-theirs |
Rader som theirs infogade; ours oförändrat. |
update-only-theirs |
Rader som theirs uppdaterade; ours oförändrat. |
delete-only-theirs |
Rader som theirs tog bort; ours oförändrat. |
insert-only-ours |
Rader som ours infogade; theirs oförändrat. |
mixed-no-overlap |
Båda ändrade olika tabeller eller rader. |
both-same-change |
Båda gjorde identiska ändringar (ingen konflikt). |
column-level-clean |
Båda ändrade samma rad, men olika kolumner. |
multi-table |
Ändringar i flera tabeller. |
conflict (6 scenarier)
Scenarier som måste leda till konflikter.
| Scenario | Testar |
|---|---|
both-update-same-column |
Båda ändrade samma kolumn till olika värden. |
update-vs-delete |
Den ena uppdaterade och den andra tog bort samma rad. |
delete-vs-update |
Motsatt riktning för uppdatering kontra borttagning. |
both-insert-same-pk |
Båda infogade en rad med samma primärnyckel. |
multiple-conflicts |
Flera konflikter i en sammanslagning. |
partial-conflict |
Vissa kolumner har konflikter, andra slås samman utan problem. |
identity (4 scenarier)
Specialfall för radidentitet.
| Scenario | Testar |
|---|---|
auto-increment |
Nya rader har olika ID:n i olika grenar. |
natural-key |
Matchning via unika kolumner i stället för primärnyckel. |
composite-key |
Primärnyckel med flera kolumner. |
no-key |
Tabell utan primärnyckel (reservlösning med innehållshash). |
types (6 scenarier)
Hantering av datatyper för alla kolumntyper.
| Scenario | Testar |
|---|---|
text-columns |
Värden av typen VARCHAR, TEXT och LONGTEXT. |
numeric-columns |
Värden av typen INT, DECIMAL och FLOAT. |
date-columns |
Värden av typen DATE, DATETIME och TIMESTAMP. |
json-columns |
Sammanslagning av kolumner av typen JSON. |
blob-columns |
Binärdata. |
null-handling |
Övergångar från NULL till värde och från värde till NULL. |
scale (4 scenarier)
Prestanda under belastning.
| Scenario | Testar |
|---|---|
1k-rows |
1,000 rader. |
10k-rows |
10,000 rader. |
100k-rows |
100,000 rader. |
wide-table |
Tabell med 50+ kolumner. |
edge (4 scenarier)
Specialfall och gränsvillkor.
| Scenario | Testar |
|---|---|
empty-changeset |
Inga ändringar på ena eller båda sidorna. |
schema-mismatch |
Kolumn tillagd eller borttagen mellan ögonblicksbilder. |
encoding |
UTF-8, emoji och specialtecken. |
large-text |
Mycket stora värden av typen TEXT/LONGTEXT. |
Scenariostruktur
Varje scenario ligger i en katalog under scenarios/<category>/<name>/ med en konfiguration av typen 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"
}
}
För konfliktscenarier anges de förväntade konflikterna i detalj:
{
"expectations": {
"conflicts": 1,
"conflict_details": [
{
"table": "test_posts",
"primary_key": {"id": 42},
"column": "title",
"ours_value": "Welcome",
"theirs_value": "Greetings"
}
]
}
}
ScenarioRunner-processen
ScenarioRunner::run() samordnar ett enskilt scenario:
OracleCapture::compute()bygger tre ögonblicksbilder från scenariots JSON-data och kör sammanslagningen.ScenarioComparator::compare()kontrollerar sammanslagningens resultat mot de förväntade värdena iscenario.json.- Returnerar godkänt/underkänt med en lista över felmeddelanden.
use Merql\Tests\Oracle\ScenarioRunner;
$result = ScenarioRunner::run($scenario);
// ['name' => 'column-level-clean', 'pass' => true, 'failures' => []]
ScenarioRunner::runAll() kör alla scenarier i följd:
$results = ScenarioRunner::runAll($scenarios);
Lägga till ett nytt scenario
- Skapa en katalog:
scenarios/<category>/<name>/. - Skapa
scenario.jsonmed scenariots metadata och förväntningar. - Definiera de tre databastillstånden i konfigurationen JSON: basedata, ändringar i ours, ändringar i theirs.
- Kör scenariot:
./bin/test-scenario <name>. - Verifiera hela testsviten:
./bin/verify-all.
Kodkvalitetsstandarder
merql tillämpar:
- PHPStan nivå 10 -- strikt statisk analys utan undantag från baslinjen.
- PSR-12 -- kodningsstandarder som kontrolleras av
phpcs. - Oracle-regression -- alla 32 scenarier måste godkännas.
- Enhetstester -- isolerade tester för varje komponent.
Skriptet verify-all kör alla fyra kontroller i följd. Delvis godkännande accepteras inte.
Kontinuerlig integration
CI kör kontroller av kodningsstandarder, PHPStan, composer test, integrationssviten
mot MySQL 8.4 via mysqli, täckning av sammanslagningens grundfunktioner och
composer audit vid varje push och pull-begäran till main. Den kör inte
oracle-regressionskorpusen (#6), så
kör composer test:oracle eller ./bin/verify-all lokalt innan du förlitar dig på en
ändring.