
# Stratégia testovania

merql používa model testovania riadený referenčným výsledkom. Každý testovací scenár definuje známu množinu vstupov (tri stavy databázy), deterministický očakávaný výsledok (referenčný výsledok) a porovnáva s ním skutočný výstup zlúčenia. 32 scenárov pokrýva čisté zlúčenia, konflikty, určovanie identity, dátové typy, rozsah a okrajové prípady.

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

Príkazy na tejto stránke sa spúšťajú v kópii repozitára merql po
`composer install`. Vývojové skripty v `bin/`, korpus `scenarios/`
a testy nie sú súčasťou balíka Composer. Medzi vývojové závislosti patrí
PHPUnit 13, ktorý vyžaduje PHP 8.4 alebo novšiu verziu, hoci samotná
knižnica funguje na PHP 8.2.

## Testovanie riadené referenčným výsledkom

Neexistuje žiadny existujúci referenčný nástroj na trojcestné zlúčenie databáz, preto sa referenčný výsledok vypočítava zo známych vstupov, a nie pomocou externého nástroja. Každý scenár má deterministický očakávaný výsledok, ktorý možno overiť logicky:

1. **Príprava** -- vytvorte tri stavy databázy (base, ours, theirs) so známym obsahom.
2. **Referenčný výsledok** -- vypočítajte očakávaný výsledok zlúčenia zo známych vstupov.
3. **Skutočný výsledok** -- spustite zlúčenie troch stavov pomocou merql.
4. **Porovnanie** -- skutočný výsledok sa musí presne zhodovať s očakávaným.

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

## Spúšťanie testov

### Úplné overenie

Skript `verify-all` je záverečnou kontrolou. Spúšťa statickú analýzu, kontrolu štandardov kódovania, testy PHPUnit a regresnú sadu referenčných výsledkov:

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

Pred dokončením akejkoľvek práce musí byť tento príkaz úspešný.

### Regresná sada referenčných výsledkov

Spustite všetkých 32 scenárov:

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

Spustite jednu kategóriu:

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

Kategórie sú názvy adresárov v `scenarios/`: `clean`, `conflict`,
`identity`, `types`, `scale` a `edge`. Neznáma kategória vypíše
`No scenarios found` a skončí úspešne. `--category` možno opakovať a spustiť tak
viacero kategórií.

Skript prijíma aj `--jobs`, ale hodnotu ignoruje a spúšťa scenáre
jeden po druhom ([#5](https://github.com/inline0/merql/issues/5)).

Spustite s minimálnym výstupom (iba úspech/neúspech):

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

### Jeden scenár

Otestujte jeden scenár v rámci celého procesu:

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

### Jednotlivé kroky referenčného výsledku

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

### Jednotkové testy

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

`composer test` spúšťa iba PHPUnit. Sada `oracle` PHPUnit smeruje na
`tests/Oracle`, ktorá obsahuje triedy spúšťača scenárov, ale žiadne testovacie prípady, takže
32 scenárov sa spúšťa iba cez `composer test:oracle`, `composer verify` alebo
`./bin/verify-all`.

Integračné testy MySQL sa preskočia, pokiaľ `MERQL_MYSQL_HOST`,
`MERQL_MYSQL_DATABASE` a `MERQL_MYSQL_USER` (prípadne aj
`MERQL_MYSQL_PASSWORD` a `MERQL_MYSQL_PORT`) neukazujú na testovaciu databázu.

### Kvalita kódu

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

## Kategórie scenárov

### clean (8 scenárov)

Čisté zlúčenia bez konfliktov. Obe strany môžu meniť údaje, ale nikdy ten istý stĺpec v tom istom riadku.

| Scenár | Testuje |
|---|---|
| `insert-only-theirs` | Riadky vložené v theirs, ours nezmenené. |
| `update-only-theirs` | Riadky aktualizované v theirs, ours nezmenené. |
| `delete-only-theirs` | Riadky odstránené v theirs, ours nezmenené. |
| `insert-only-ours` | Riadky vložené v ours, theirs nezmenené. |
| `mixed-no-overlap` | Obe strany zmenili rôzne tabuľky alebo riadky. |
| `both-same-change` | Obe strany vykonali rovnaké zmeny (bez konfliktu). |
| `column-level-clean` | Obe strany zmenili ten istý riadok, ale rôzne stĺpce. |
| `multi-table` | Zmeny vo viacerých tabuľkách. |

### conflict (6 scenárov)

Scenáre, pri ktorých musí dôjsť ku konfliktom.

| Scenár | Testuje |
|---|---|
| `both-update-same-column` | Obe strany zmenili ten istý stĺpec na rôzne hodnoty. |
| `update-vs-delete` | Jedna strana aktualizovala riadok, druhá ho odstránila. |
| `delete-vs-update` | Opačný prípad aktualizácie oproti odstráneniu. |
| `both-insert-same-pk` | Obe strany vložili riadok s rovnakým primárnym kľúčom. |
| `multiple-conflicts` | Viacero konfliktov v jednom zlúčení. |
| `partial-conflict` | Niektoré stĺpce sú v konflikte, iné sa zlúčia bez problémov. |

### identity (4 scenáre)

Okrajové prípady identity riadka.

| Scenár | Testuje |
|---|---|
| `auto-increment` | Nové riadky majú v rôznych vetvách odlišné ID. |
| `natural-key` | Párovanie podľa jedinečných stĺpcov namiesto PK. |
| `composite-key` | Viacstĺpcový primárny kľúč. |
| `no-key` | Tabuľka bez primárneho kľúča (záložná možnosť: hash obsahu). |

### types (6 scenárov)

Spracovanie všetkých typov stĺpcov.

| Scenár | Testuje |
|---|---|
| `text-columns` | Hodnoty VARCHAR, TEXT a LONGTEXT. |
| `numeric-columns` | Hodnoty INT, DECIMAL a FLOAT. |
| `date-columns` | Hodnoty DATE, DATETIME a TIMESTAMP. |
| `json-columns` | Zlúčenie stĺpca JSON. |
| `blob-columns` | Binárne údaje. |
| `null-handling` | Prechody z NULL na hodnotu a z hodnoty na NULL. |

### scale (4 scenáre)

Výkon pri záťaži.

| Scenár | Testuje |
|---|---|
| `1k-rows` | 1,000 riadkov. |
| `10k-rows` | 10,000 riadkov. |
| `100k-rows` | 100,000 riadkov. |
| `wide-table` | Tabuľka s viac ako 50 stĺpcami. |

### edge (4 scenáre)

Okrajové prípady a hraničné podmienky.

| Scenár | Testuje |
|---|---|
| `empty-changeset` | Žiadne zmeny na jednej alebo oboch stranách. |
| `schema-mismatch` | Stĺpec pridaný alebo odstránený medzi snímkami. |
| `encoding` | UTF-8, emoji a špeciálne znaky. |
| `large-text` | Veľmi veľké hodnoty TEXT/LONGTEXT. |

## Štruktúra scenára

Každý scenár je adresár v `scenarios/<category>/<name>/` s konfiguráciou `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"
    }
}
```

Pri konfliktných scenároch sú očakávané konflikty podrobne špecifikované:

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

## Proces ScenarioRunner

`ScenarioRunner::run()` koordinuje jeden scenár:

1. `OracleCapture::compute()` vytvorí zo scenárových údajov JSON tri snímky a spustí zlúčenie.
2. `ScenarioComparator::compare()` overí výsledok zlúčenia podľa očakávaných hodnôt v `scenario.json`.
3. Vráti úspech/neúspech so zoznamom chybových hlásení.

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

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

`ScenarioRunner::runAll()` spúšťa všetky scenáre postupne:

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

## Pridanie nového scenára

1. Vytvorte adresár: `scenarios/<category>/<name>/`.
2. Vytvorte `scenario.json` s metadátami a očakávanými výsledkami scenára.
3. V konfigurácii JSON definujte tri stavy databázy: základné údaje, zmeny ours a zmeny theirs.
4. Spustite scenár: `./bin/test-scenario <name>`.
5. Overte celú sadu: `./bin/verify-all`.

## Štandardy kvality kódu

merql vyžaduje:

- **Úroveň 10 PHPStan** -- prísna statická analýza bez výnimiek v základnej konfigurácii.
- **PSR-12** -- štandardy kódovania kontroluje `phpcs`.
- **Regresné testy s referenčnými výsledkami** -- všetkých 32 scenárov musí prejsť.
- **Jednotkové testy** -- izolované testy pre každý komponent.

Skript `verify-all` spúšťa všetky štyri kontroly postupne. Čiastočné schválenie sa neuznáva.

## Priebežná integrácia

CI pri každom odoslaní zmien a každej požiadavke na zlúčenie do `main` spúšťa kontrolu štandardov kódovania, PHPStan, `composer test`, integračnú sadu proti MySQL 8.4 cez mysqli, pokrytie primitív zlúčenia a `composer audit`. Nespúšťa regresný korpus referenčných výsledkov ([#6](https://github.com/inline0/merql/issues/6)), preto pred tým, ako sa na zmenu spoľahnete, spustite lokálne `composer test:oracle` alebo `./bin/verify-all`.
