Estrategia de pruebas
merql utiliza un modelo de pruebas basado en un oráculo. Cada escenario de prueba define un conjunto conocido de entradas (tres estados de la base de datos), un resultado esperado determinista (el oráculo) y compara el resultado real de la fusión con esa expectativa. 32 escenarios cubren fusiones sin conflictos, conflictos, resolución de identidades, tipos de datos, escala y casos límite.
# Run the full verification suite.
./bin/verify-all
Los comandos de esta página se ejecutan desde una copia local del repositorio merql después de
composer install. Los scripts de desarrollo de bin/, el corpus de
scenarios/ y las pruebas no forman parte del paquete Composer. Las dependencias de
desarrollo incluyen PHPUnit 13, que requiere PHP 8.4 o posterior, aunque la
biblioteca funciona con PHP 8.2.
Pruebas basadas en un oráculo
No existe una herramienta de referencia que realice fusiones de bases de datos de tres vías, por lo que el oráculo se calcula a partir de entradas conocidas en lugar de generarse con una herramienta externa. Cada escenario tiene un resultado esperado determinista que puede verificarse mediante lógica:
- Configuración -- crear tres estados de la base de datos (base, nuestro y suyo) con contenido conocido.
- Oráculo -- calcular el resultado esperado de la fusión a partir de las entradas conocidas.
- Resultado real -- ejecutar la fusión de merql en los tres estados.
- Comparación -- el resultado real debe coincidir exactamente con el resultado esperado.
Known inputs (base, ours, theirs)
| |
Deterministic merql merge
computation
| |
Expected result Actual result
| |
-----> Compare <-----
|
Pass or Fail
Ejecutar las pruebas
Verificación completa
El script verify-all es la comprobación final. Ejecuta el análisis estático, las normas de código, las pruebas PHPUnit y la suite de regresión del oráculo:
./bin/verify-all
Debe superarse antes de considerar terminado cualquier trabajo.
Suite de regresión del oráculo
Ejecuta los 32 escenarios:
./bin/test-regression
Ejecuta una sola categoría:
./bin/test-regression --category clean
./bin/test-regression --category conflict
./bin/test-regression --category identity
Las categorías son los nombres de directorio bajo scenarios/: clean, conflict,
identity, types, scale y edge. Una categoría desconocida muestra
No scenarios found y termina correctamente. Se puede repetir --category para ejecutar
varias categorías.
El script también acepta --jobs, pero ignora su valor y ejecuta los escenarios
uno tras otro (#5).
Ejecuta con una salida mínima (solo aprobado/reprobado):
./bin/test-regression --fast
Escenario individual
Prueba un escenario mediante el flujo completo:
./bin/test-scenario column-level-clean
Pasos individuales del oráculo
./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
Pruebas unitarias
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 ejecuta solo PHPUnit. La suite oracle PHPUnit apunta a
tests/Oracle, que contiene las clases del ejecutor de escenarios, pero ningún caso de prueba, por lo que
los 32 escenarios solo se ejecutan mediante composer test:oracle, composer verify o
./bin/verify-all.
Las pruebas de integración MySQL se omiten salvo que MERQL_MYSQL_HOST,
MERQL_MYSQL_DATABASE y MERQL_MYSQL_USER (además de, opcionalmente,
MERQL_MYSQL_PASSWORD y MERQL_MYSQL_PORT) apunten a una base de datos de prueba.
Calidad del código
composer cs # PSR-12 coding standards check
composer cs:fix # Auto-fix coding standards
composer analyse # PHPStan level 10 static analysis
Categorías de escenarios
clean (8 escenarios)
Fusiones sin conflictos. Ambos lados pueden cambiar datos, pero nunca la misma columna de la misma fila.
| Escenario | Pruebas |
|---|---|
insert-only-theirs |
theirs insertó filas; ours no cambió. |
update-only-theirs |
theirs actualizó filas; ours no cambió. |
delete-only-theirs |
theirs eliminó filas; ours no cambió. |
insert-only-ours |
ours insertó filas; theirs no cambió. |
mixed-no-overlap |
Ambos cambiaron tablas o filas distintas. |
both-same-change |
Ambos hicieron los mismos cambios (sin conflicto). |
column-level-clean |
Ambos cambiaron la misma fila, pero distintas columnas. |
multi-table |
Cambios en varias tablas. |
conflict (6 escenarios)
Escenarios que deben generar conflictos.
| Escenario | Pruebas |
|---|---|
both-update-same-column |
Ambos cambiaron la misma columna a valores distintos. |
update-vs-delete |
Uno actualizó y el otro eliminó la misma fila. |
delete-vs-update |
Dirección inversa de actualización frente a eliminación. |
both-insert-same-pk |
Ambos insertaron una fila con la misma clave principal. |
multiple-conflicts |
Varios conflictos en una fusión. |
partial-conflict |
Algunas columnas entran en conflicto y otras se fusionan sin problemas. |
identity (4 escenarios)
Casos límite de identidad de filas.
| Escenario | Pruebas |
|---|---|
auto-increment |
Las filas nuevas tienen distintos ID entre ramas. |
natural-key |
Coincidencia mediante columnas únicas en lugar de la PK. |
composite-key |
Clave principal de varias columnas. |
no-key |
Tabla sin clave principal (alternativa de hash del contenido). |
types (6 escenarios)
Gestión de tipos de datos en todos los tipos de columna.
| Escenario | Pruebas |
|---|---|
text-columns |
Valores VARCHAR, TEXT y LONGTEXT. |
numeric-columns |
Valores INT, DECIMAL y FLOAT. |
date-columns |
Valores DATE, DATETIME y TIMESTAMP. |
json-columns |
Fusión de columnas JSON. |
blob-columns |
Datos binarios. |
null-handling |
Transiciones de NULL a un valor y de un valor a NULL. |
scale (4 escenarios)
Rendimiento con carga.
| Escenario | Pruebas |
|---|---|
1k-rows |
1,000 filas. |
10k-rows |
10,000 filas. |
100k-rows |
100,000 filas. |
wide-table |
Tabla con más de 50 columnas. |
edge (4 escenarios)
Casos límite y condiciones de borde.
| Escenario | Pruebas |
|---|---|
empty-changeset |
Sin cambios en uno o ambos lados. |
schema-mismatch |
Columna añadida o eliminada entre instantáneas. |
encoding |
UTF-8, emojis y caracteres especiales. |
large-text |
Valores TEXT/LONGTEXT muy grandes. |
Estructura de los escenarios
Cada escenario es un directorio bajo scenarios/<category>/<name>/ con una configuración 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"
}
}
En los escenarios con conflictos, los conflictos esperados se especifican en detalle:
{
"expectations": {
"conflicts": 1,
"conflict_details": [
{
"table": "test_posts",
"primary_key": {"id": 42},
"column": "title",
"ours_value": "Welcome",
"theirs_value": "Greetings"
}
]
}
}
Flujo de ScenarioRunner
ScenarioRunner::run() coordina un escenario individual:
OracleCapture::compute()crea tres instantáneas a partir de los datos JSON del escenario y ejecuta la fusión.ScenarioComparator::compare()comprueba el resultado de la fusión frente a los valores esperados enscenario.json.- Devuelve aprobado/reprobado junto con una lista de mensajes de error.
use Merql\Tests\Oracle\ScenarioRunner;
$result = ScenarioRunner::run($scenario);
// ['name' => 'column-level-clean', 'pass' => true, 'failures' => []]
ScenarioRunner::runAll() ejecuta todos los escenarios en secuencia:
$results = ScenarioRunner::runAll($scenarios);
Añadir un escenario nuevo
- Crea un directorio:
scenarios/<category>/<name>/. - Crea
scenario.jsoncon los metadatos y las expectativas del escenario. - Define los tres estados de la base de datos en la configuración JSON: datos base, modificaciones nuestras y modificaciones suyas.
- Ejecuta el escenario:
./bin/test-scenario <name>. - Verifica la suite completa:
./bin/verify-all.
Estándares de calidad del código
merql aplica:
- PHPStan nivel 10 -- análisis estático estricto sin excepciones de referencia.
- PSR-12 -- normas de código verificadas por
phpcs. - Regresión del oráculo -- deben superarse los 32 escenarios.
- Pruebas unitarias -- pruebas aisladas para cada componente.
El script verify-all ejecuta las cuatro comprobaciones en secuencia. No se acepta una aprobación parcial.
Integración continua
CI ejecuta las normas de código, PHPStan, composer test, la suite de integración
con MySQL 8.4 mediante mysqli, la cobertura de primitivas de fusión y
composer audit en cada envío y solicitud de incorporación a main. No ejecuta el
corpus de regresión del oráculo (#6), así que
antes de confiar en un cambio, ejecuta localmente composer test:oracle o ./bin/verify-all.