Aller au contenu

Stratégie de test

merql utilise un modèle de test guidé par un oracle. Chaque scénario de test définit un ensemble connu d'entrées (trois états de base de données), un résultat attendu déterministe (l'oracle), puis compare le résultat réel de la fusion à cette attente. 32 scénarios couvrent les fusions sans conflit, les conflits, la résolution d'identité, les types de données, la charge et les cas limites.

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

Les commandes de cette page s'exécutent depuis une copie du dépôt merql après composer install. Les scripts de développement dans bin/, le corpus de scenarios/ et les tests ne font pas partie du paquet Composer. Les dépendances de développement incluent PHPUnit 13, qui nécessite PHP 8.4 ou une version ultérieure, alors que la bibliothèque elle-même fonctionne avec PHP 8.2.

Tests guidés par un oracle

Il n'existe pas d'outil de référence pour effectuer une fusion de bases de données à trois voies. L'oracle est donc calculé à partir d'entrées connues, plutôt que généré par un outil externe. Chaque scénario possède un résultat attendu déterministe, vérifiable par raisonnement :

  1. Préparation -- créer trois états de base de données (base, ours, theirs) avec un contenu connu.
  2. Oracle -- calculer le résultat de fusion attendu à partir des entrées connues.
  3. Résultat réel -- lancer la fusion de merql sur les trois états.
  4. Comparaison -- le résultat réel doit correspondre exactement au résultat attendu.
Code
Known inputs (base, ours, theirs)
         |                    |
    Deterministic          merql merge
    computation
         |                    |
    Expected result       Actual result
         |                    |
         -----> Compare <-----
                  |
            Pass or Fail

Exécution des tests

Vérification complète

Le script verify-all est le contrôle final. Il exécute l'analyse statique, la vérification des normes de code, les tests PHPUnit et la suite de régression de l'oracle :

Shell
./bin/verify-all

Cette vérification doit réussir avant que le travail puisse être considéré comme terminé.

Suite de régression de l'oracle

Exécutez les 32 scénarios :

Shell
./bin/test-regression

Exécutez une seule catégorie :

Shell
./bin/test-regression --category clean
./bin/test-regression --category conflict
./bin/test-regression --category identity

Les catégories correspondent aux noms des répertoires dans scenarios/ : clean, conflict, identity, types, scale et edge. Une catégorie inconnue affiche No scenarios found et se termine avec succès. Il est possible de répéter --category pour exécuter plusieurs catégories.

Le script accepte également --jobs, mais ignore sa valeur et exécute les scénarios l'un après l'autre (#5).

Exécutez avec une sortie minimale (réussite/échec uniquement) :

Shell
./bin/test-regression --fast

Scénario unique

Testez un scénario avec le pipeline complet :

Shell
./bin/test-scenario column-level-clean

Étapes individuelles de l'oracle

Shell
./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

Tests unitaires

Shell
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 exécute uniquement PHPUnit. La suite oracle PHPUnit pointe vers tests/Oracle, qui contient les classes du lanceur de scénarios, mais aucun cas de test ; les 32 scénarios s'exécutent donc uniquement via composer test:oracle, composer verify ou ./bin/verify-all.

Les tests d'intégration MySQL sont ignorés, sauf si MERQL_MYSQL_HOST, MERQL_MYSQL_DATABASE et MERQL_MYSQL_USER (ainsi que, facultativement, MERQL_MYSQL_PASSWORD et MERQL_MYSQL_PORT) pointent vers une base de données de test.

Qualité du code

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

Catégories de scénarios

clean (8 scénarios)

Fusions sans conflit. Les deux côtés peuvent modifier des données, mais jamais la même colonne de la même ligne.

Scénario Tests
insert-only-theirs theirs a inséré des lignes, ours n'a rien modifié.
update-only-theirs theirs a mis à jour des lignes, ours n'a rien modifié.
delete-only-theirs theirs a supprimé des lignes, ours n'a rien modifié.
insert-only-ours ours a inséré des lignes, theirs n'a rien modifié.
mixed-no-overlap Les deux ont modifié des tables ou des lignes différentes.
both-same-change Les deux ont effectué des modifications identiques (sans conflit).
column-level-clean Les deux ont modifié la même ligne, mais des colonnes différentes.
multi-table Modifications réparties sur plusieurs tables.

conflict (6 scénarios)

Scénarios devant produire des conflits.

Scénario Tests
both-update-same-column Les deux ont modifié la même colonne avec des valeurs différentes.
update-vs-delete L'un a mis à jour la ligne, l'autre l'a supprimée.
delete-vs-update Cas inverse de mise à jour contre suppression.
both-insert-same-pk Les deux ont inséré une ligne avec la même clé primaire.
multiple-conflicts Plusieurs conflits dans une seule fusion.
partial-conflict Certaines colonnes sont en conflit, tandis que d'autres sont fusionnées sans conflit.

identity (4 scénarios)

Cas limites liés à l'identité des lignes.

Scénario Tests
auto-increment Les nouvelles lignes ont des identifiants différents selon les branches.
natural-key Correspondance par colonnes uniques plutôt que par clé primaire.
composite-key Clé primaire composée de plusieurs colonnes.
no-key Table sans clé primaire (repli sur le hachage du contenu).

types (6 scénarios)

Gestion de tous les types de données de colonne.

Scénario Tests
text-columns Valeurs VARCHAR, TEXT, LONGTEXT.
numeric-columns Valeurs INT, DECIMAL, FLOAT.
date-columns Valeurs DATE, DATETIME, TIMESTAMP.
json-columns Fusion de colonnes JSON.
blob-columns Données binaires.
null-handling Transitions de NULL vers une valeur et d'une valeur vers NULL.

scale (4 scénarios)

Performances en charge.

Scénario Tests
1k-rows 1,000 lignes.
10k-rows 10,000 lignes.
100k-rows 100,000 lignes.
wide-table Table de plus de 50 colonnes.

edge (4 scénarios)

Cas limites et conditions aux frontières.

Scénario Tests
empty-changeset Aucun changement d'un côté ou des deux.
schema-mismatch Colonne ajoutée ou supprimée entre deux instantanés.
encoding UTF-8, emoji, caractères spéciaux.
large-text Valeurs TEXT/LONGTEXT très volumineuses.

Structure des scénarios

Chaque scénario est un répertoire dans scenarios/<category>/<name>/, contenant une configuration 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"
    }
}

Pour les scénarios avec conflit, les conflits attendus sont spécifiés en détail :

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

Pipeline du ScenarioRunner

ScenarioRunner::run() orchestre un scénario unique :

  1. OracleCapture::compute() construit trois instantanés à partir des données JSON du scénario et lance la fusion.
  2. ScenarioComparator::compare() vérifie le résultat de la fusion par rapport aux valeurs attendues dans scenario.json.
  3. Renvoie une réussite ou un échec, avec la liste des messages d'échec.
PHP
use Merql\Tests\Oracle\ScenarioRunner;

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

ScenarioRunner::runAll() exécute chaque scénario à la suite :

PHP
$results = ScenarioRunner::runAll($scenarios);

Ajouter un scénario

  1. Créez un répertoire : scenarios/<category>/<name>/.
  2. Créez scenario.json avec les métadonnées et les résultats attendus du scénario.
  3. Définissez les trois états de base de données dans la configuration JSON : données de base, modifications de ours et modifications de theirs.
  4. Lancez le scénario : ./bin/test-scenario <name>.
  5. Vérifiez la suite complète : ./bin/verify-all.

Normes de qualité du code

merql impose :

  • Niveau 10 de PHPStan -- analyse statique stricte, sans exception de référence de base.
  • PSR-12 -- normes de codage vérifiées par phpcs.
  • Régression de l'oracle -- les 32 scénarios doivent réussir.
  • Tests unitaires -- tests isolés pour chaque composant.

Le script verify-all exécute les quatre vérifications à la suite. Aucune validation partielle n'est acceptée.

Intégration continue

L'intégration continue exécute les vérifications des normes de codage, PHPStan, composer test, la suite d'intégration avec MySQL 8.4 via mysqli, les tests de couverture des opérations de fusion primitives et composer audit à chaque envoi et demande d'intégration vers main. Elle n'exécute pas le corpus de régression de l'oracle (#6) ; exécutez donc composer test:oracle ou ./bin/verify-all localement avant de vous fier à une modification.