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.
# 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 :
- Préparation -- créer trois états de base de données (base, ours, theirs) avec un contenu connu.
- Oracle -- calculer le résultat de fusion attendu à partir des entrées connues.
- Résultat réel -- lancer la fusion de merql sur les trois états.
- Comparaison -- le résultat réel doit correspondre exactement au résultat attendu.
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 :
./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 :
./bin/test-regression
Exécutez une seule catégorie :
./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) :
./bin/test-regression --fast
Scénario unique
Testez un scénario avec le pipeline complet :
./bin/test-scenario column-level-clean
Étapes individuelles de l'oracle
./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
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
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 :
{
"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 :
{
"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 :
OracleCapture::compute()construit trois instantanés à partir des données JSON du scénario et lance la fusion.ScenarioComparator::compare()vérifie le résultat de la fusion par rapport aux valeurs attendues dansscenario.json.- Renvoie une réussite ou un échec, avec la liste des messages d'échec.
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 :
$results = ScenarioRunner::runAll($scenarios);
Ajouter un scénario
- Créez un répertoire :
scenarios/<category>/<name>/. - Créez
scenario.jsonavec les métadonnées et les résultats attendus du scénario. - 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.
- Lancez le scénario :
./bin/test-scenario <name>. - 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.