
# Testimisstrateegia

merql kasutab teadaoleva tulemiga juhitud testimismudelit. Iga testimisstsenaarium määrab kindla sisendite komplekti (kolm andmebaasi olekut), deterministliku oodatava tulemuse (etalonväärtuse) ja võrdleb tegelikku ühendamist selle ootusega. 32 stsenaariumi katavad puhtad ühendamised, konfliktid, identiteedi lahendamise, andmetüübid, mahu ja erandjuhud.

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

Selle lehe käsud käivitatakse merql-hoidla väljavõttest pärast
`composer install`. Arendusskriptid asukohas `bin/`, korpus `scenarios/`
ja testid ei kuulu paketti Composer. Arendussõltuvuste hulka kuulub PHPUnit 13, mis vajab versiooni PHP 8.4 või uuemat, kuigi teek ise töötab versiooniga PHP 8.2.

## Tulemuspõhine testimine

Olemas pole võrdlustööriista, mis teeks andmebaasi kolmepoolset ühendamist, seega arvutatakse etalon teadaolevate sisendite põhjal, mitte välise tööriista abil. Igal stsenaariumil on deterministlik oodatav tulemus, mida saab loogika põhjal kontrollida:

1. **Seadistus** -- loo kolm teadaoleva sisuga andmebaasi olekut (alus, meie oma, nende oma).
2. **Etalon** -- arvuta teadaolevate sisendite põhjal oodatav ühendamistulemus.
3. **Tegelik tulemus** -- käivita merqli ühendamine kolme olekuga.
4. **Võrdlus** -- tegelik tulemus peab täpselt vastama oodatud tulemusele.

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

## Testide käivitamine

### Täielik kontrollimine

Skript `verify-all` on viimane kontrollvärav. See käivitab staatilise analüüsi, koodistandardite kontrolli, PHPUnit testid ja etalonväärtustel põhineva regressioonitestide komplekti:

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

See peab läbima enne, kui tööd saab lõpetatuks pidada.

### Etalonväärtustel põhinev regressioonitestide komplekt

Käivita kõik 32 stsenaariumi:

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

Käivita üks kategooria:

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

Kategooriad on kataloogide nimed asukohas `scenarios/`: `clean`, `conflict`,
`identity`, `types`, `scale` ja `edge`. Tundmatu kategooria korral kuvatakse `No scenarios found` ja väljutakse edukalt. Mitme kategooria käivitamiseks võib `--category` korrata.

Skript aktsepteerib ka väärtust `--jobs`, kuid eirab seda ja käivitab stsenaariumid
üksteise järel ([#5](https://github.com/inline0/merql/issues/5)).

Käivita minimaalse väljundiga (ainult läbitud/ebaõnnestunud):

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

### Üksik stsenaarium

Testi üht stsenaariumi kogu töövooga:

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

### Üksikud etalonarvutuse etapid

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

### Ühiktestid

```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` käivitab ainult PHPUnit. `oracle` PHPUnit komplekt osutab asukohale
`tests/Oracle`, mis sisaldab stsenaariumide käivitaja klasse, kuid mitte testjuhtumeid. Seega käivitatakse 32 stsenaariumi ainult käsuga `composer test:oracle`, `composer verify` või
`./bin/verify-all`.

MySQL integratsioonitestid jäetakse vahele, kui `MERQL_MYSQL_HOST`, `MERQL_MYSQL_DATABASE` ja `MERQL_MYSQL_USER` (ning soovi korral `MERQL_MYSQL_PASSWORD` ja `MERQL_MYSQL_PORT`) ei osuta testandmebaasile.

### Koodi kvaliteet

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

## Stsenaariumide kategooriad

### clean (8 stsenaariumi)

Puhtad ühendamised ilma konfliktideta. Mõlemad pooled võivad andmeid muuta, kuid mitte kunagi sama rea sama veergu.

| Stsenaarium | Testitav |
|---|---|
| `insert-only-theirs` | Nende poolt lisatud read, meie pool muutmata. |
| `update-only-theirs` | Nende poolt uuendatud read, meie pool muutmata. |
| `delete-only-theirs` | Nende poolt kustutatud read, meie pool muutmata. |
| `insert-only-ours` | Meie poolt lisatud read, nende pool muutmata. |
| `mixed-no-overlap` | Mõlemad muutsid eri tabeleid või ridu. |
| `both-same-change` | Mõlemad tegid samad muudatused (konflikti pole). |
| `column-level-clean` | Mõlemad muutsid sama rea eri veerge. |
| `multi-table` | Muudatused mitmes tabelis. |

### conflict (6 stsenaariumi)

Stsenaariumid, mille tulemuseks peavad olema konfliktid.

| Stsenaarium | Testitav |
|---|---|
| `both-update-same-column` | Mõlemad muutsid sama veeru eri väärtuseks. |
| `update-vs-delete` | Üks pool uuendas ja teine kustutas sama rea. |
| `delete-vs-update` | Uuendamise ja kustutamise vastupidine suund. |
| `both-insert-same-pk` | Mõlemad lisasid sama primaarvõtmega rea. |
| `multiple-conflicts` | Ühes ühendamises mitu konflikti. |
| `partial-conflict` | Mõnes veerus on konflikt, teised ühinevad tõrgeteta. |

### identity (4 stsenaariumi)

Rea identiteedi erijuhud.

| Stsenaarium | Testitav |
|---|---|
| `auto-increment` | Uute ridade ID-d on harudes erinevad. |
| `natural-key` | Sobitamine kordumatute veergude, mitte PK järgi. |
| `composite-key` | Mitmeveeruline primaarvõti. |
| `no-key` | Primaarvõtmeta tabel (sisu räsi varuvariandina). |

### types (6 stsenaariumi)

Kõigi veerutüüpide andmetüüpide käsitlemine.

| Stsenaarium | Testitav |
|---|---|
| `text-columns` | VARCHAR-, TEXT- ja LONGTEXT-väärtused. |
| `numeric-columns` | INT-, DECIMAL- ja FLOAT-väärtused. |
| `date-columns` | DATE-, DATETIME- ja TIMESTAMP-väärtused. |
| `json-columns` | JSON veeru ühendamine. |
| `blob-columns` | Binaarandmed. |
| `null-handling` | Üleminek NULL-ist väärtuseks ja väärtusest NULL-iks. |

### scale (4 stsenaariumi)

Jõudlus koormuse all.

| Stsenaarium | Testitav |
|---|---|
| `1k-rows` | 1,000 rida. |
| `10k-rows` | 10,000 rida. |
| `100k-rows` | 100,000 rida. |
| `wide-table` | Tabel 50+ veeruga. |

### edge (4 stsenaariumi)

Erandjuhud ja piiritingimused.

| Stsenaarium | Testitav |
|---|---|
| `empty-changeset` | Ühel või mõlemal poolel muudatusi pole. |
| `schema-mismatch` | Hetktõmmiste vahele lisati või eemaldati veerg. |
| `encoding` | UTF-8, emotikonid ja erimärgid. |
| `large-text` | Väga suured TEXT-/LONGTEXT-väärtused. |

## Stsenaariumi ülesehitus

Iga stsenaarium on kataloog asukohas `scenarios/<category>/<name>/` ja sisaldab `scenario.json` konfiguratsiooni:

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

Konfliktistsenaariumide puhul määratakse oodatavad konfliktid üksikasjalikult:

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

## ScenarioRunneri töövoog

`ScenarioRunner::run()` korraldab ühe stsenaariumi täitmise:

1. `OracleCapture::compute()` loob stsenaariumi JSON andmetest kolm hetktõmmist ja käivitab ühendamise.
2. `ScenarioComparator::compare()` võrdleb ühendamistulemust asukohas `scenario.json` olevate oodatud väärtustega.
3. Tagastab läbitud/ebaõnnestunud tulemuse koos tõrketeadete loendiga.

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

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

`ScenarioRunner::runAll()` käivitab järjest kõik stsenaariumid:

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

## Uue stsenaariumi lisamine

1. Loo kataloog: `scenarios/<category>/<name>/`.
2. Loo `scenario.json` stsenaariumi metaandmete ja ootustega.
3. Määra konfiguratsioonis JSON kolm andmebaasi olekut: algandmed, meie muudatused ja nende muudatused.
4. Käivita stsenaarium: `./bin/test-scenario <name>`.
5. Kontrolli kogu testikomplekti: `./bin/verify-all`.

## Koodi kvaliteedistandardid

merql jõustab järgmised nõuded:

- **PHPStan tase 10** -- range staatiline analüüs ilma lähtetaseme eranditeta.
- **PSR-12** -- kodeerimisstandardeid kontrollib `phpcs`.
- **Oracle'i regressioonitestid** -- kõik 32 stsenaariumi peavad läbima.
- **Ühiktestid** -- iga komponendi eraldiseisvad testid.

`verify-all` skript käivitab kõik neli kontrolli järjest. Osalist heakskiitu ei aktsepteerita.

## Pidev integratsioon

CI käivitab kodeerimisstandardite kontrolli, PHPStan, `composer test`, integratsioonitestid versiooniga MySQL 8.4 mysqli kaudu, ühendamisprimitiivide katvuse ja `composer audit` iga `main`-i harusse tehtud tõuke ja iga pull request'i korral. Oracle'i regressioonitestide korpust ([#6](https://github.com/inline0/merql/issues/6)) ei käivitata, seega käivita enne muudatusele toetumist kohapeal `composer test:oracle` või `./bin/verify-all`.
