
# Strategia de testare

merql folosește un model de testare bazat pe rezultate de referință. Fiecare scenariu de test definește un set cunoscut de intrări (trei stări ale bazei de date), un rezultat determinist așteptat (referința) și compară rezultatul efectiv al îmbinării cu așteptarea. 32 de scenarii acoperă îmbinări fără conflicte, conflicte, rezolvarea identității, tipuri de date, scară și cazuri limită.

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

Comenzile de pe această pagină se execută dintr-un checkout al depozitului merql după
`composer install`. Scripturile de dezvoltare din `bin/`, corpusul `scenarios/`
și testele nu fac parte din pachetul Composer. Dependențele de dezvoltare includ PHPUnit 13, care necesită PHP 8.4 sau o versiune ulterioară, deși biblioteca în sine rulează pe PHP 8.2.

## Testare bazată pe rezultate de referință

Nu există un instrument de referință care să efectueze îmbinări tridirecționale ale bazelor de date, așa că rezultatul de referință este calculat din intrări cunoscute, nu generat de un instrument extern. Fiecare scenariu are un rezultat determinist așteptat, care poate fi verificat logic:

1. **Configurare** -- creați trei stări ale bazei de date (base, ours, theirs) cu conținut cunoscut.
2. **Referință** -- calculați rezultatul așteptat al îmbinării din intrările cunoscute.
3. **Rezultat efectiv** -- rulați îmbinarea merql pentru cele trei stări.
4. **Comparare** -- rezultatul efectiv trebuie să corespundă exact rezultatului așteptat.

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

## Rularea testelor

### Verificare completă

Scriptul `verify-all` este verificarea finală. Rulează analiza statică, verificarea standardelor de cod, testele PHPUnit și suita de regresie bazată pe rezultate de referință:

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

Acesta trebuie să treacă înainte ca orice activitate să fie considerată completă.

### Suita de regresie bazată pe rezultate de referință

Rulați toate cele 32 de scenarii:

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

Rulați o singură categorie:

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

Categoriile sunt numele directoarelor din `scenarios/`: `clean`, `conflict`,
`identity`, `types`, `scale` și `edge`. O categorie necunoscută afișează
`No scenarios found` și se închide cu succes. `--category` se poate repeta pentru a rula
mai multe categorii.

Scriptul acceptă și `--jobs`, dar ignoră valoarea și rulează scenariile
unul după altul ([#5](https://github.com/inline0/merql/issues/5)).

Rulați cu rezultate minime (numai reușit/eșuat):

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

### Un singur scenariu

Testați un scenariu prin întregul flux:

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

### Pași individuali ai oracle-ului

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

### Teste unitare

```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` rulează doar PHPUnit. Suita `oracle` PHPUnit indică spre
`tests/Oracle`, care conține clasele pentru rularea scenariilor, dar nu și cazuri de test, așa că
cele 32 de scenarii rulează doar prin `composer test:oracle`, `composer verify` sau
`./bin/verify-all`.

Testele de integrare MySQL sunt omise dacă `MERQL_MYSQL_HOST`,
`MERQL_MYSQL_DATABASE` și `MERQL_MYSQL_USER` (plus, opțional,
`MERQL_MYSQL_PASSWORD` și `MERQL_MYSQL_PORT`) nu indică spre o bază de date de test.

### Calitatea codului

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

## Categorii de scenarii

### clean (8 scenarii)

Îmbinări curate, fără conflicte. Ambele părți pot modifica date, dar niciodată aceeași coloană din același rând.

| Scenariu | Teste |
|---|---|
| `insert-only-theirs` | Partea lor a inserat rânduri, partea noastră nu a schimbat nimic. |
| `update-only-theirs` | Partea lor a actualizat rânduri, partea noastră nu a schimbat nimic. |
| `delete-only-theirs` | Partea lor a șters rânduri, partea noastră nu a schimbat nimic. |
| `insert-only-ours` | Partea noastră a inserat rânduri, partea lor nu a schimbat nimic. |
| `mixed-no-overlap` | Ambele părți au modificat tabele sau rânduri diferite. |
| `both-same-change` | Ambele părți au făcut modificări identice (fără conflict). |
| `column-level-clean` | Ambele părți au modificat același rând, dar coloane diferite. |
| `multi-table` | Modificări în mai multe tabele. |

### conflict (6 scenarii)

Scenarii care trebuie să genereze conflicte.

| Scenariu | Teste |
|---|---|
| `both-update-same-column` | Ambele părți au modificat aceeași coloană cu valori diferite. |
| `update-vs-delete` | O parte a actualizat, iar cealaltă a șters același rând. |
| `delete-vs-update` | Direcția inversă a operației de actualizare versus ștergere. |
| `both-insert-same-pk` | Ambele părți au inserat un rând cu aceeași cheie primară. |
| `multiple-conflicts` | Mai multe conflicte într-o singură îmbinare. |
| `partial-conflict` | Unele coloane au conflicte, altele se îmbină fără probleme. |

### identity (4 scenarii)

Cazuri-limită privind identitatea rândurilor.

| Scenariu | Teste |
|---|---|
| `auto-increment` | Rândurile noi au ID-uri diferite în ramuri. |
| `natural-key` | Potrivire după coloane unice în loc de PK. |
| `composite-key` | Cheie primară cu mai multe coloane. |
| `no-key` | Tabel fără cheie primară (alternativă prin hash de conținut). |

### types (6 scenarii)

Gestionarea tipurilor de date pentru toate tipurile de coloane.

| Scenariu | Teste |
|---|---|
| `text-columns` | Valori VARCHAR, TEXT, LONGTEXT. |
| `numeric-columns` | Valori INT, DECIMAL, FLOAT. |
| `date-columns` | Valori DATE, DATETIME, TIMESTAMP. |
| `json-columns` | Îmbinarea coloanei JSON. |
| `blob-columns` | Date binare. |
| `null-handling` | Tranziții de la NULL la valoare și de la valoare la NULL. |

### scale (4 scenarii)

Performanță sub sarcină.

| Scenariu | Teste |
|---|---|
| `1k-rows` | 1,000 rânduri. |
| `10k-rows` | 10,000 rânduri. |
| `100k-rows` | 100,000 rânduri. |
| `wide-table` | Tabel cu peste 50 de coloane. |

### edge (4 scenarii)

Cazuri-limită și condiții de margine.

| Scenariu | Teste |
|---|---|
| `empty-changeset` | Nicio modificare pe una sau pe ambele părți. |
| `schema-mismatch` | Coloană adăugată sau eliminată între instantanee. |
| `encoding` | UTF-8, emoji, caractere speciale. |
| `large-text` | Valori TEXT/LONGTEXT foarte mari. |

## Structura scenariilor

Fiecare scenariu este un director în `scenarios/<category>/<name>/` cu o configurație `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"
    }
}
```

Pentru scenariile cu conflicte, conflictele așteptate sunt specificate în detaliu:

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

## Fluxul ScenarioRunner

`ScenarioRunner::run()` coordonează un singur scenariu:

1. `OracleCapture::compute()` construiește trei instantanee din datele JSON ale scenariului și execută îmbinarea.
2. `ScenarioComparator::compare()` verifică rezultatul îmbinării în raport cu valorile așteptate din `scenario.json`.
3. Returnează reușita/eșecul și o listă de mesaje de eroare.

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

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

`ScenarioRunner::runAll()` rulează toate scenariile în ordine:

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

## Adăugarea unui scenariu nou

1. Creați un director: `scenarios/<category>/<name>/`.
2. Creați `scenario.json` cu metadatele și așteptările scenariului.
3. Definiți cele trei stări ale bazei de date în configurația JSON: datele inițiale, modificările noastre, modificările lor.
4. Rulați scenariul: `./bin/test-scenario <name>`.
5. Verificați întreaga suită: `./bin/verify-all`.

## Standarde de calitate a codului

merql impune:

- **PHPStan nivelul 10** -- analiză statică strictă, fără excepții de bază.
- **PSR-12** -- standarde de cod verificate de `phpcs`.
- **Regresie oracle** -- toate cele 32 de scenarii trebuie să treacă.
- **Teste unitare** -- teste izolate pentru fiecare componentă.

Scriptul `verify-all` rulează toate cele patru verificări în ordine. Nu se acceptă aprobarea parțială.

## Integrare continuă

CI rulează verificările standardelor de cod, PHPStan, `composer test`, suita de integrare
cu MySQL 8.4 prin mysqli, verificarea acoperirii primitivelor de îmbinare și
`composer audit` la fiecare push și pull request către `main`. Nu rulează
corpusul de regresie oracle ([#6](https://github.com/inline0/merql/issues/6)), așa că
rulați local `composer test:oracle` sau `./bin/verify-all` înainte de a vă baza pe o
modificare.
