
# Στρατηγική δοκιμών

Το merql χρησιμοποιεί μοντέλο δοκιμών με βάση oracle. Κάθε σενάριο δοκιμής ορίζει ένα γνωστό σύνολο εισόδων (τρεις καταστάσεις βάσης δεδομένων), ένα ντετερμινιστικό αναμενόμενο αποτέλεσμα (το oracle) και συγκρίνει το πραγματικό αποτέλεσμα συγχώνευσης με αυτό. 32 σενάρια καλύπτουν καθαρές συγχωνεύσεις, διενέξεις, επίλυση ταυτότητας, τύπους δεδομένων, κλίμακα και οριακές περιπτώσεις.

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

Οι εντολές σε αυτή τη σελίδα εκτελούνται από ένα checkout του αποθετηρίου merql μετά το `composer install`. Τα σκριπτ ανάπτυξης στο `bin/`, το σώμα δεδομένων `scenarios/` και οι δοκιμές δεν περιλαμβάνονται στο πακέτο Composer. Οι εξαρτήσεις ανάπτυξης περιλαμβάνουν το PHPUnit 13, που απαιτεί το PHP 8.4 ή νεότερη έκδοση, παρότι η ίδια η βιβλιοθήκη λειτουργεί με το PHP 8.2.

## Δοκιμές βάσει oracle

Δεν υπάρχει υπάρχον εργαλείο αναφοράς που να εκτελεί συγχώνευση βάσεων δεδομένων τριών κατευθύνσεων, επομένως το oracle υπολογίζεται από γνωστές εισόδους και όχι από εξωτερικό εργαλείο. Κάθε σενάριο έχει ντετερμινιστικό αναμενόμενο αποτέλεσμα που μπορεί να επαληθευτεί λογικά:

1. **Ρύθμιση** -- δημιουργία τριών καταστάσεων βάσης δεδομένων (base, ours, theirs) με γνωστό περιεχόμενο.
2. **Oracle** -- υπολογισμός του αναμενόμενου αποτελέσματος συγχώνευσης από τις γνωστές εισόδους.
3. **Πραγματικό αποτέλεσμα** -- εκτέλεση της συγχώνευσης merql στις τρεις καταστάσεις.
4. **Σύγκριση** -- το πραγματικό αποτέλεσμα πρέπει να αντιστοιχεί ακριβώς στο αναμενόμενο.

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

## Εκτέλεση δοκιμών

### Πλήρης επαλήθευση

Το σκριπτ `verify-all` είναι ο τελικός έλεγχος. Εκτελεί στατική ανάλυση, πρότυπα κώδικα, δοκιμές PHPUnit και τη σουίτα παλινδρόμησης oracle:

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

Αυτό πρέπει να περάσει προτού θεωρηθεί ολοκληρωμένη οποιαδήποτε εργασία.

### Σουίτα παλινδρόμησης oracle

Εκτελέστε και τα 32 σενάρια:

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

Εκτελέστε μία κατηγορία:

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

Οι κατηγορίες είναι τα ονόματα καταλόγων κάτω από το `scenarios/`: `clean`, `conflict`,
`identity`, `types`, `scale` και `edge`. Μια άγνωστη κατηγορία εμφανίζει
`No scenarios found` και τερματίζει επιτυχώς. Το `--category` μπορεί να επαναληφθεί για την εκτέλεση
πολλών κατηγοριών.

Το σκριπτ δέχεται επίσης το `--jobs`, αλλά αγνοεί την τιμή και εκτελεί τα σενάρια
το ένα μετά το άλλο ([#5](https://github.com/inline0/merql/issues/5)).

Εκτέλεση με ελάχιστη έξοδο (μόνο επιτυχία/αποτυχία):

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

### Μεμονωμένο σενάριο

Δοκιμάστε ένα σενάριο μέσω ολόκληρης της διαδικασίας:

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

### Μεμονωμένα βήματα oracle

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

### Δοκιμές μονάδας

```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` εκτελεί μόνο το PHPUnit. Η σουίτα `oracle` PHPUnit δείχνει στο
`tests/Oracle`, όπου υπάρχουν οι κλάσεις εκτέλεσης σεναρίων αλλά όχι περιπτώσεις δοκιμών, επομένως τα 32 σενάρια εκτελούνται μόνο μέσω του `composer test:oracle`, του `composer verify` ή του
`./bin/verify-all`.

Οι δοκιμές ενσωμάτωσης MySQL παραλείπονται, εκτός αν τα `MERQL_MYSQL_HOST`,
`MERQL_MYSQL_DATABASE` και `MERQL_MYSQL_USER` (και προαιρετικά τα
`MERQL_MYSQL_PASSWORD` και `MERQL_MYSQL_PORT`) δείχνουν σε μια δοκιμαστική βάση δεδομένων.

### Ποιότητα κώδικα

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

## Κατηγορίες σεναρίων

### clean (8 σενάρια)

Καθαρές συγχωνεύσεις χωρίς διενέξεις. Και οι δύο πλευρές μπορούν να αλλάξουν δεδομένα, αλλά ποτέ την ίδια στήλη της ίδιας γραμμής.

| Σενάριο | Δοκιμές |
|---|---|
| `insert-only-theirs` | Το theirs εισήγαγε γραμμές, το ours δεν άλλαξε. |
| `update-only-theirs` | Το theirs ενημέρωσε γραμμές, το ours δεν άλλαξε. |
| `delete-only-theirs` | Το theirs διέγραψε γραμμές, το ours δεν άλλαξε. |
| `insert-only-ours` | Το ours εισήγαγε γραμμές, το theirs δεν άλλαξε. |
| `mixed-no-overlap` | Και οι δύο πλευρές άλλαξαν διαφορετικούς πίνακες ή γραμμές. |
| `both-same-change` | Και οι δύο έκαναν ίδιες αλλαγές (χωρίς διένεξη). |
| `column-level-clean` | Και οι δύο άλλαξαν την ίδια γραμμή, σε διαφορετικές στήλες. |
| `multi-table` | Αλλαγές σε πολλούς πίνακες. |

### conflict (6 σενάρια)

Σενάρια που πρέπει να προκαλούν διενέξεις.

| Σενάριο | Δοκιμές |
|---|---|
| `both-update-same-column` | Και οι δύο άλλαξαν την ίδια στήλη σε διαφορετικές τιμές. |
| `update-vs-delete` | Η μία πλευρά ενημέρωσε τη γραμμή και η άλλη διέγραψε την ίδια γραμμή. |
| `delete-vs-update` | Αντίστροφη κατεύθυνση της ενημέρωσης έναντι της διαγραφής. |
| `both-insert-same-pk` | Και οι δύο εισήγαγαν γραμμή με το ίδιο πρωτεύον κλειδί. |
| `multiple-conflicts` | Πολλές διενέξεις σε μία συγχώνευση. |
| `partial-conflict` | Κάποιες στήλες έχουν διένεξη, ενώ άλλες συγχωνεύονται ομαλά. |

### identity (4 σενάρια)

Οριακές περιπτώσεις ταυτότητας γραμμών.

| Σενάριο | Δοκιμές |
|---|---|
| `auto-increment` | Οι νέες γραμμές έχουν διαφορετικά ID μεταξύ των κλάδων. |
| `natural-key` | Αντιστοίχιση βάσει μοναδικών στηλών αντί για PK. |
| `composite-key` | Πρωτεύον κλειδί πολλών στηλών. |
| `no-key` | Πίνακας χωρίς πρωτεύον κλειδί (εφεδρική λύση με κατακερματισμό περιεχομένου). |

### types (6 σενάρια)

Χειρισμός τύπων δεδομένων για όλους τους τύπους στηλών.

| Σενάριο | Δοκιμές |
|---|---|
| `text-columns` | Τιμές VARCHAR, TEXT, LONGTEXT. |
| `numeric-columns` | Τιμές INT, DECIMAL, FLOAT. |
| `date-columns` | Τιμές DATE, DATETIME, TIMESTAMP. |
| `json-columns` | Συγχώνευση στήλης JSON. |
| `blob-columns` | Δυαδικά δεδομένα. |
| `null-handling` | Μεταβάσεις από NULL σε τιμή και από τιμή σε NULL. |

### scale (4 σενάρια)

Απόδοση υπό φόρτο.

| Σενάριο | Δοκιμές |
|---|---|
| `1k-rows` | 1,000 γραμμές. |
| `10k-rows` | 10,000 γραμμές. |
| `100k-rows` | 100,000 γραμμές. |
| `wide-table` | Πίνακας με 50+ στήλες. |

### edge (4 σενάρια)

Οριακές περιπτώσεις και συνθήκες ορίων.

| Σενάριο | Δοκιμές |
|---|---|
| `empty-changeset` | Καμία αλλαγή στη μία ή και στις δύο πλευρές. |
| `schema-mismatch` | Προσθήκη ή αφαίρεση στήλης μεταξύ στιγμιότυπων. |
| `encoding` | UTF-8, emoji, ειδικοί χαρακτήρες. |
| `large-text` | Πολύ μεγάλες τιμές TEXT/LONGTEXT. |

## Δομή σεναρίου

Κάθε σενάριο βρίσκεται σε έναν κατάλογο κάτω από το `scenarios/<category>/<name>/` με διαμόρφωση `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"
    }
}
```

Για τα σενάρια διένεξης, οι αναμενόμενες διενέξεις καθορίζονται λεπτομερώς:

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

## Η διαδικασία ScenarioRunner

Το `ScenarioRunner::run()` ενορχηστρώνει ένα μεμονωμένο σενάριο:

1. Το `OracleCapture::compute()` δημιουργεί τρία στιγμιότυπα από τα δεδομένα JSON του σεναρίου και εκτελεί τη συγχώνευση.
2. Το `ScenarioComparator::compare()` ελέγχει το αποτέλεσμα συγχώνευσης με βάση τις αναμενόμενες τιμές στο `scenario.json`.
3. Επιστρέφει επιτυχία/αποτυχία μαζί με λίστα μηνυμάτων αποτυχίας.

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

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

Το `ScenarioRunner::runAll()` εκτελεί κάθε σενάριο διαδοχικά:

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

## Προσθήκη νέου σεναρίου

1. Δημιουργήστε έναν κατάλογο: `scenarios/<category>/<name>/`.
2. Δημιουργήστε το `scenario.json` με τα μεταδεδομένα και τις προσδοκίες του σεναρίου.
3. Ορίστε τις τρεις καταστάσεις της βάσης δεδομένων στη διαμόρφωση JSON: δεδομένα base, μεταβολές ours, μεταβολές theirs.
4. Εκτελέστε το σενάριο: `./bin/test-scenario <name>`.
5. Επαληθεύστε ολόκληρη τη σουίτα: `./bin/verify-all`.

## Πρότυπα ποιότητας κώδικα

Το merql επιβάλλει:

- **PHPStan επίπεδο 10** -- αυστηρή στατική ανάλυση χωρίς εξαιρέσεις βάσης.
- **PSR-12** -- έλεγχος προτύπων κώδικα από το `phpcs`.
- **Παλινδρόμηση oracle** -- πρέπει να περνούν και τα 32 σενάρια.
- **Δοκιμές μονάδας** -- απομονωμένες δοκιμές για κάθε στοιχείο.

Το σκριπτ `verify-all` εκτελεί και τους τέσσερις ελέγχους διαδοχικά. Δεν γίνεται αποδεκτή μερική έγκριση.

## Συνεχής ενσωμάτωση

Το CI εκτελεί ελέγχους προτύπων κώδικα, PHPStan, `composer test`, τη σουίτα ενσωμάτωσης με MySQL 8.4 μέσω mysqli, κάλυψη βασικών λειτουργιών συγχώνευσης και `composer audit` σε κάθε push και pull request προς το `main`. Δεν εκτελεί το σώμα δεδομένων παλινδρόμησης oracle ([#6](https://github.com/inline0/merql/issues/6)), επομένως εκτελέστε το `composer test:oracle` ή το `./bin/verify-all` τοπικά πριν βασιστείτε σε μια αλλαγή.
