
# Strategie testování

merql používá model testování řízený orákulem. Každý testovací scénář určuje známou sadu vstupů (tři stavy databáze), deterministický očekávaný výsledek (orákulum) a porovnává skutečný výstup sloučení s tímto očekáváním. Celkem 32 scénářů pokrývá čistá sloučení, konflikty, určování identity, datové typy, rozsah a okrajové případy.

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

Příkazy na této stránce spusťte z checkoutu repozitáře merql po `composer install`. Vývojové skripty v `bin/`, korpus `scenarios/` a testy nejsou součástí balíčku Composer. Mezi vývojové závislosti patří PHPUnit 13, který vyžaduje PHP 8.4 nebo novější, přestože samotná knihovna funguje na PHP 8.2.

## Testování řízené orákulem

Neexistuje žádný referenční nástroj pro třístranné slučování databází, takže se orákulum vypočítává ze známých vstupů, nikoli pomocí externího nástroje. Každý scénář má deterministický očekávaný výsledek, který lze ověřit logickým postupem:

1. **Příprava** -- vytvořte tři stavy databáze (base, ours, theirs) se známým obsahem.
2. **Orákulum** -- ze známých vstupů vypočítejte očekávaný výsledek sloučení.
3. **Skutečné provedení** -- spusťte sloučení tří stavů pomocí merql.
4. **Porovnání** -- skutečný výsledek se musí přesně shodovat s očekávaným.

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

## Spouštění testů

### Úplné ověření

Skript `verify-all` je závěrečnou kontrolou. Spouští statickou analýzu, kontrolu standardů kódování, testy PHPUnit a regresní sadu orákula:

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

Tato kontrola musí projít, než lze jakoukoli práci považovat za dokončenou.

### Regresní sada orákula

Spusťte všech 32 scénářů:

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

Spusťte jednu kategorii:

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

Kategorie odpovídají názvům adresářů v `scenarios/`: `clean`, `conflict`, `identity`, `types`, `scale` a `edge`. Neznámá kategorie vypíše `No scenarios found` a skončí úspěšně. `--category` lze opakovat a spustit tak více kategorií.

Skript přijímá také `--jobs`, ale hodnotu ignoruje a spouští scénáře jeden po druhém ([#5](https://github.com/inline0/merql/issues/5)).

Spuštění s minimálním výstupem (pouze úspěch/neúspěch):

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

### Jeden scénář

Otestujte jeden scénář celým postupem:

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

### Jednotlivé kroky orákula

```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` spouští pouze PHPUnit. Sada `oracle` PHPUnit odkazuje na `tests/Oracle`, který obsahuje třídy spouštěče scénářů, ale žádné testovací případy. Těchto 32 scénářů se proto spouští pouze prostřednictvím `composer test:oracle`, `composer verify` nebo `./bin/verify-all`.

Integrační testy MySQL se přeskočí, pokud `MERQL_MYSQL_HOST`, `MERQL_MYSQL_DATABASE` a `MERQL_MYSQL_USER` (a případně také `MERQL_MYSQL_PASSWORD` a `MERQL_MYSQL_PORT`) neodkazují na testovací databázi.

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

## Kategorie scénářů

### čisté (8 scénářů)

Čistá sloučení bez konfliktů. Obě strany mohou měnit data, ale nikdy nemění tentýž sloupec stejného řádku.

| Scénář | Test |
|---|---|
| `insert-only-theirs` | Theirs vložila řádky, ours beze změny. |
| `update-only-theirs` | Theirs aktualizovala řádky, ours beze změny. |
| `delete-only-theirs` | Theirs odstranila řádky, ours beze změny. |
| `insert-only-ours` | Ours vložila řádky, theirs beze změny. |
| `mixed-no-overlap` | Obě strany změnily různé tabulky nebo řádky. |
| `both-same-change` | Obě strany provedly stejné změny (bez konfliktu). |
| `column-level-clean` | Obě strany změnily stejný řádek, ale různé sloupce. |
| `multi-table` | Změny napříč více tabulkami. |

### konflikt (6 scénářů)

Scénáře, které musí vyvolat konflikty.

| Scénář | Testy |
|---|---|
| `both-update-same-column` | Obě strany změnily stejný sloupec na různé hodnoty. |
| `update-vs-delete` | Jedna strana aktualizovala řádek, druhá tentýž řádek smazala. |
| `delete-vs-update` | Opačný směr aktualizace a smazání. |
| `both-insert-same-pk` | Obě strany vložily řádek se stejným primárním klíčem. |
| `multiple-conflicts` | Několik konfliktů v jednom sloučení. |
| `partial-conflict` | Některé sloupce jsou v konfliktu, ostatní se sloučí bez problémů. |

### identita (4 scénáře)

Okrajové případy identity řádků.

| Scénář | Testy |
|---|---|
| `auto-increment` | Nové řádky mají v různých větvích odlišná ID. |
| `natural-key` | Párování podle jedinečných sloupců místo primárního klíče. |
| `composite-key` | Primární klíč složený z více sloupců. |
| `no-key` | Tabulka bez primárního klíče (náhradní řešení pomocí hashe obsahu). |

### typy (6 scénářů)

Zpracování všech typů dat ve sloupcích.

| Scénář | Testy |
|---|---|
| `text-columns` | Hodnoty VARCHAR, TEXT, LONGTEXT. |
| `numeric-columns` | Hodnoty INT, DECIMAL, FLOAT. |
| `date-columns` | Hodnoty DATE, DATETIME, TIMESTAMP. |
| `json-columns` | Sloučení sloupce JSON. |
| `blob-columns` | Binární data. |
| `null-handling` | Přechody z NULL na hodnotu a z hodnoty na NULL. |

### rozsah (4 scénáře)

Výkon při zátěži.

| Scénář | Testy |
|---|---|
| `1k-rows` | 1,000 řádků. |
| `10k-rows` | 10,000 řádků. |
| `100k-rows` | 100,000 řádků. |
| `wide-table` | Tabulka s více než 50 sloupci. |

### okrajové případy (4 scénáře)

Okrajové případy a mezní podmínky.

| Scénář | Testy |
|---|---|
| `empty-changeset` | Žádné změny na jedné nebo obou stranách. |
| `schema-mismatch` | Sloupec přidaný nebo odebraný mezi snímky. |
| `encoding` | UTF-8, emoji, speciální znaky. |
| `large-text` | Velmi rozsáhlé hodnoty TEXT/LONGTEXT. |

## Struktura scénářů

Každý scénář je adresářem v `scenarios/<category>/<name>/` s konfigurací `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"
    }
}
```

U konfliktních scénářů jsou očekávané konflikty podrobně specifikovány:

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

## Průběh ScenarioRunneru

`ScenarioRunner::run()` řídí jeden scénář:

1. `OracleCapture::compute()` vytvoří tři snímky z dat JSON daného scénáře a spustí sloučení.
2. `ScenarioComparator::compare()` ověří výsledek sloučení podle očekávaných hodnot v `scenario.json`.
3. Vrátí výsledek úspěch/neúspěch se seznamem chybových zpráv.

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

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

`ScenarioRunner::runAll()` postupně spustí všechny scénáře:

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

## Přidání nového scénáře

1. Vytvořte adresář: `scenarios/<category>/<name>/`.
2. Vytvořte `scenario.json` s metadaty scénáře a očekávanými výsledky.
3. Definujte tři stavy databáze v konfiguraci JSON: základní data, změny na naší straně a změny na jejich straně.
4. Spusťte scénář: `./bin/test-scenario <name>`.
5. Ověřte celou sadu testů: `./bin/verify-all`.

## Standardy kvality kódu

merql vynucuje:

- **PHPStan úroveň 10** -- přísná statická analýza bez výjimek ze základní konfigurace.
- **PSR-12** -- standardy kódování kontroluje `phpcs`.
- **Regresní testy Oracle** -- všech 32 scénářů musí projít.
- **Jednotkové testy** -- izolované testy jednotlivých komponent.

Skript `verify-all` spustí všechny čtyři kontroly postupně. Částečné schválení se nepřijímá.

## Průběžná integrace

CI při každém odeslání změn a pull requestu do `main` spouští kontrolu standardů kódování, PHPStan, `composer test`, integrační sadu proti MySQL 8.4 přes mysqli, testy pokrytí primitiv pro slučování a `composer audit`. Nespouští regresní sadu Oracle ([#6](https://github.com/inline0/merql/issues/6)), proto před spoléháním na změnu spusťte místně `composer test:oracle` nebo `./bin/verify-all`.
