
# Tesztelési stratégia

A merql oracle-alapú tesztelési modellt használ. Minden tesztforgatókönyv meghatározott bemeneteket (három adatbázis-állapotot), determinisztikus elvárt eredményt (az oracle-t) határoz meg, majd ezt veti össze a tényleges merge kimenetével. A 32 forgatókönyv tiszta merge-eket, ütközéseket, azonosítási feloldást, adattípusokat, méretezhetőséget és szélső eseteket fed le.

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

Az ezen az oldalon szereplő parancsok a merql repository egy checkoutjából futtathatók, a `composer install` után. A `bin/` fejlesztői szkriptek, a `scenarios/` korpusz és a tesztek nem részei a Composer csomagnak. A fejlesztői függőségek között szerepel a PHPUnit 13, amelyhez PHP 8.4-es vagy újabb verziója szükséges, noha maga a könyvtár PHP 8.2-n fut.

## Oracle-alapú tesztelés

Nincs olyan meglévő referenciaeszköz, amely háromutas adatbázis-merge-et végezne, ezért az oracle ismert bemenetekből, nem pedig külső eszközből származik. Minden forgatókönyvhöz tartozik egy determinisztikus elvárt eredmény, amely logikai úton ellenőrizhető:

1. **Előkészítés** -- hozzon létre három adatbázis-állapotot (base, ours, theirs) ismert tartalommal.
2. **Oracle** -- számítsa ki az elvárt merge-eredményt az ismert bemenetekből.
3. **Tényleges futtatás** -- futtassa a merql merge műveletét a három állapoton.
4. **Összehasonlítás** -- a tényleges eredménynek pontosan egyeznie kell az elvárttal.

```
Known inputs (base, ours, theirs)
         |                    |
    Deterministic          merql merge
    computation
         |                    |
    Expected result       Actual result
         |                    |
         -----> Compare <-----
                  |
            Pass or Fail
```

## Tesztek futtatása

### Teljes ellenőrzés

A `verify-all` szkript az utolsó ellenőrzési pont. Statikus elemzést, kódolási szabványok ellenőrzését, PHPUnit teszteket és oracle-regressziós tesztcsomagot futtat:

```bash
./bin/verify-all
```

Ezt teljesíteni kell, mielőtt bármely munka befejezettnek tekinthető.

### Oracle-regressziós tesztcsomag

Futtassa mind a 32 forgatókönyvet:

```bash
./bin/test-regression
```

Futtasson egyetlen kategóriát:

```bash
./bin/test-regression --category clean
./bin/test-regression --category conflict
./bin/test-regression --category identity
```

A kategóriák a `scenarios/` alatti könyvtárnevek: `clean`, `conflict`, `identity`, `types`, `scale` és `edge`. Ismeretlen kategória esetén kiírja a `No scenarios found` értéket, majd sikeresen kilép. A `--category` többször is megadható több kategória futtatásához.

A szkript a `--jobs` argumentumot is elfogadja, de az értékét figyelmen kívül hagyja, és egymás után futtatja a forgatókönyveket ([#5](https://github.com/inline0/merql/issues/5)).

Futtatás minimális kimenettel (csak sikeres/sikertelen):

```bash
./bin/test-regression --fast
```

### Egyetlen forgatókönyv

Teszteljen egy forgatókönyvet a teljes folyamaton keresztül:

```bash
./bin/test-scenario column-level-clean
```

### Egyedi oracle-lépések

```bash
./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
```

### Egységtesztek

```bash
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
```

A `composer test` csak a PHPUnit teszteket futtatja. A `oracle` PHPUnit tesztcsomag a `tests/Oracle` helyre mutat, amely a forgatókönyv-futtató osztályokat tartalmazza, de teszteseteket nem. Ezért a 32 forgatókönyv csak a `composer test:oracle`, a `composer verify` vagy a `./bin/verify-all` használatával fut.

A MySQL integrációs tesztek kimaradnak, kivéve ha a `MERQL_MYSQL_HOST`, a `MERQL_MYSQL_DATABASE` és a `MERQL_MYSQL_USER` (valamint opcionálisan a `MERQL_MYSQL_PASSWORD` és a `MERQL_MYSQL_PORT`) egy tesztadatbázisra mutat.

### Kódminőség

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

## Forgatókönyv-kategóriák

### clean (8 forgatókönyv)

Tiszta merge-ek ütközések nélkül. Mindkét oldal módosíthat adatokat, de ugyanannak a sornak ugyanazt az oszlopát soha.

| Forgatókönyv | Teszt |
|---|---|
| `insert-only-theirs` | A theirs sorokat szúrt be, az ours nem változott. |
| `update-only-theirs` | A theirs sorokat frissített, az ours nem változott. |
| `delete-only-theirs` | A theirs sorokat törölt, az ours nem változott. |
| `insert-only-ours` | Az ours sorokat szúrt be, a theirs nem változott. |
| `mixed-no-overlap` | Mindkét oldal különböző táblákat vagy sorokat módosított. |
| `both-same-change` | Mindkét oldal azonos módosításokat végzett (nincs ütközés). |
| `column-level-clean` | Mindkét oldal ugyanazt a sort módosította, de különböző oszlopokat. |
| `multi-table` | Módosítások több táblában. |

### conflict (6 forgatókönyv)

Ütközést okozó forgatókönyvek.

| Forgatókönyv | Tesztek |
|---|---|
| `both-update-same-column` | Mindkét oldal ugyanazt az oszlopot módosította, de eltérő értékre. |
| `update-vs-delete` | Az egyik oldal frissítette, a másik törölte ugyanazt a sort. |
| `delete-vs-update` | A frissítés és törlés fordított iránya. |
| `both-insert-same-pk` | Mindkét oldal azonos elsődleges kulccsal szúrt be sort. |
| `multiple-conflicts` | Több ütközés egyetlen összevonásban. |
| `partial-conflict` | Egyes oszlopok ütköznek, mások összevonása problémamentes. |

### identity (4 forgatókönyv)

A sorazonosság szélső esetei.

| Forgatókönyv | Tesztek |
|---|---|
| `auto-increment` | Az új sorok azonosítói eltérnek az ágak között. |
| `natural-key` | Egyezés egyedi oszlopok alapján az elsődleges kulcs helyett. |
| `composite-key` | Több oszlopból álló elsődleges kulcs. |
| `no-key` | Elsődleges kulcs nélküli tábla (tartalomkivonat-alapú tartalékmegoldás). |

### types (6 forgatókönyv)

Adattípusok kezelése az összes oszloptípus esetén.

| Forgatókönyv | Tesztek |
|---|---|
| `text-columns` | VARCHAR, TEXT, LONGTEXT értékek. |
| `numeric-columns` | INT, DECIMAL, FLOAT értékek. |
| `date-columns` | DATE, DATETIME, TIMESTAMP értékek. |
| `json-columns` | JSON oszlop összevonása. |
| `blob-columns` | Bináris adatok. |
| `null-handling` | NULL értékről értékre, illetve értékről NULL-ra váltás. |

### scale (4 forgatókönyv)

Terhelés alatti teljesítmény.

| Forgatókönyv | Tesztek |
|---|---|
| `1k-rows` | 1,000 sor. |
| `10k-rows` | 10,000 sor. |
| `100k-rows` | 100,000 sor. |
| `wide-table` | Tábla több mint 50 oszloppal. |

### edge (4 forgatókönyv)

Szélső esetek és határfeltételek.

| Forgatókönyv | Tesztek |
|---|---|
| `empty-changeset` | Nincs változás az egyik vagy mindkét oldalon. |
| `schema-mismatch` | Oszlop hozzáadása vagy eltávolítása a pillanatképek között. |
| `encoding` | UTF-8, emoji és speciális karakterek. |
| `large-text` | Nagyon nagy TEXT/LONGTEXT értékek. |

## A forgatókönyvek felépítése

Minden forgatókönyv egy könyvtár a `scenarios/<category>/<name>/` alatt, egy `scenario.json` konfigurációval:

```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"
    }
}
```

Ütközési forgatókönyvek esetén a várt ütközések részletesen meg vannak adva:

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

## A ScenarioRunner folyamatlépései

A `ScenarioRunner::run()` egyetlen forgatókönyvet vezényel le:

1. A `OracleCapture::compute()` létrehozza a három pillanatképet a forgatókönyv JSON adataiból, majd végrehajtja az összevonást.
2. A `ScenarioComparator::compare()` összeveti az összevonás eredményét a `scenario.json` fájlban megadott várt értékekkel.
3. Sikeres vagy sikertelen eredményt ad vissza a hibaüzenetek listájával.

```php
use Merql\Tests\Oracle\ScenarioRunner;

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

A `ScenarioRunner::runAll()` sorban lefuttatja az összes forgatókönyvet:

```php
$results = ScenarioRunner::runAll($scenarios);
```

## Új forgatókönyv hozzáadása

1. Hozzon létre egy könyvtárat: `scenarios/<category>/<name>/`.
2. Hozza létre a `scenario.json` fájlt a forgatókönyv metaadataival és elvárásaival.
3. Határozza meg a három adatbázis-állapotot a JSON konfigurációban: alapadatok, a saját módosításaink és az ő módosításaik.
4. Futtassa a forgatókönyvet: `./bin/test-scenario <name>`.
5. Ellenőrizze a teljes tesztcsomagot: `./bin/verify-all`.

## Kódminőségi szabványok

A merql az alábbiakat írja elő:

- **PHPStan 10-es szint** -- szigorú statikus elemzés, kivétel nélküli alapszabállyal.
- **PSR-12** -- a kódolási szabványok ellenőrzését a `phpcs` végzi.
- **Oracle-regresszió** -- mind a 32 forgatókönyvnek sikeresen kell lefutnia.
- **Egységtesztek** -- minden komponens elszigetelt tesztjei.

A `verify-all` parancsfájl egymás után futtatja mind a négy ellenőrzést. Részleges jóváhagyás nem fogadható el.

## Folyamatos integráció

A CI minden, a `main`-re irányuló push és pull request esetén lefuttatja a kódolási szabványok ellenőrzését, a PHPStan-t, a `composer test`-t, az integrációs tesztcsomagot a MySQL 8.4-es verziójával, mysqli-n keresztül, az összevonási alapműveletek lefedettségi ellenőrzését, valamint a `composer audit`-t. Az oracle-regressziós tesztkorpuszt nem futtatja ([#6](https://github.com/inline0/merql/issues/6)), ezért a változtatásra hagyatkozás előtt futtassa helyben a `composer test:oracle`-t vagy a `./bin/verify-all`-t.
