COMPLETADO: Fase 2 de la migración a Clean Architecture + POO ## DatabaseMigrator - ✓ Clase DatabaseMigrator con estrategia completa de migración - ✓ Creación de tablas v2 con nueva estructura (config_group) - ✓ Migración de datos con transformación automática - ✓ Validación de integridad de datos migrados - ✓ Swap seguro de tablas (legacy → _backup, v2 → producción) - ✓ Rollback automático en caso de error - ✓ Logging detallado de todas las operaciones ## Transformaciones de BD - ✓ Nueva columna config_group (visibility, content, styles, general) - ✓ Renombrado: version → schema_version - ✓ UNIQUE KEY actualizada: (component_name, config_group, config_key) - ✓ Nuevos índices: idx_group, idx_schema_version - ✓ Timestamps con DEFAULT CURRENT_TIMESTAMP ## MigrationCommand (WP-CLI) - ✓ Comando: wp roi-theme migrate - ✓ Opción --dry-run para simulación segura - ✓ Comando: wp roi-theme cleanup-backup - ✓ Output formateado y detallado - ✓ Confirmación para operaciones destructivas - ✓ Estadísticas de migración completas ## Tests de Integración - ✓ 6 tests de integración implementados - ✓ Test: Creación de tablas v2 - ✓ Test: Preservación de cantidad de registros - ✓ Test: Inferencia correcta de grupos - ✓ Test: Creación de backup - ✓ Test: Rollback en error - ✓ Test: Cleanup de backup ## Heurística de Inferencia de Grupos - enabled, visible_* → visibility - message_*, cta_*, title_* → content - *_color, *_height, *_width, *_size, *_font → styles - Resto → general ## Integración - ✓ Comando WP-CLI registrado en functions.php - ✓ Autoloader actualizado - ✓ Strict types en todos los archivos - ✓ PHPDoc completo ## Validación - ✓ Script validate-phase-2.php (26/26 checks pasados) - ✓ Sintaxis PHP válida en todos los archivos - ✓ 100% de validaciones exitosas ## Seguridad - ✓ Backup automático de tablas legacy (_backup) - ✓ Rollback automático si falla validación - ✓ Validación de integridad antes de swap - ✓ Logging completo para auditoría IMPORTANTE: La migración está lista pero NO ejecutada. Ejecutar con: 1. wp db export backup-antes-migracion.sql 2. wp roi-theme migrate --dry-run 3. wp roi-theme migrate 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
366 lines
11 KiB
PHP
366 lines
11 KiB
PHP
<?php
|
|
declare(strict_types=1);
|
|
|
|
namespace ROITheme\Tests\Integration\Infrastructure;
|
|
|
|
use PHPUnit\Framework\TestCase;
|
|
use ROITheme\Infrastructure\Persistence\WordPress\DatabaseMigrator;
|
|
|
|
/**
|
|
* Tests de Integración para DatabaseMigrator
|
|
*
|
|
* IMPORTANTE: Estos tests modifican la base de datos
|
|
* Solo ejecutar en entorno de testing
|
|
*/
|
|
class DatabaseMigratorTest extends TestCase
|
|
{
|
|
/**
|
|
* @var \wpdb
|
|
*/
|
|
private \wpdb $wpdb;
|
|
|
|
/**
|
|
* @var DatabaseMigrator
|
|
*/
|
|
private DatabaseMigrator $migrator;
|
|
|
|
/**
|
|
* @var string
|
|
*/
|
|
private string $prefix;
|
|
|
|
/**
|
|
* Setup antes de cada test
|
|
*/
|
|
protected function setUp(): void
|
|
{
|
|
if (!defined('ABSPATH')) {
|
|
define('ABSPATH', dirname(__DIR__, 5) . '/');
|
|
}
|
|
|
|
if (!function_exists('update_option')) {
|
|
function update_option($option, $value) { return true; }
|
|
}
|
|
|
|
if (!function_exists('delete_option')) {
|
|
function delete_option($option) { return true; }
|
|
}
|
|
|
|
if (!function_exists('current_time')) {
|
|
function current_time($type) { return date('Y-m-d H:i:s'); }
|
|
}
|
|
|
|
global $wpdb;
|
|
|
|
if (!isset($wpdb)) {
|
|
$this->markTestSkipped('WordPress not loaded - skipping integration tests');
|
|
return;
|
|
}
|
|
|
|
$this->wpdb = $wpdb;
|
|
$this->prefix = $wpdb->prefix;
|
|
$this->migrator = new DatabaseMigrator($wpdb);
|
|
|
|
// Limpiar tablas anteriores
|
|
$this->cleanupTables();
|
|
|
|
// Crear tablas legacy con datos de prueba
|
|
$this->createLegacyTables();
|
|
$this->seedLegacyData();
|
|
}
|
|
|
|
/**
|
|
* Teardown después de cada test
|
|
*/
|
|
protected function tearDown(): void
|
|
{
|
|
$this->cleanupTables();
|
|
}
|
|
|
|
/**
|
|
* Test: La migración crea tablas v2 correctamente
|
|
*
|
|
* @test
|
|
*/
|
|
public function it_creates_v2_tables(): void
|
|
{
|
|
$result = $this->migrator->migrate();
|
|
|
|
$this->assertTrue($result['success'], $result['message']);
|
|
|
|
// Verificar que tabla components existe
|
|
$components_table = $this->prefix . 'roi_theme_components';
|
|
$table_exists = $this->wpdb->get_var(
|
|
"SHOW TABLES LIKE '{$components_table}'"
|
|
) === $components_table;
|
|
|
|
$this->assertTrue($table_exists, 'Tabla components no existe después de migración');
|
|
|
|
// Verificar que tabla defaults existe
|
|
$defaults_table = $this->prefix . 'roi_theme_components_defaults';
|
|
$table_exists = $this->wpdb->get_var(
|
|
"SHOW TABLES LIKE '{$defaults_table}'"
|
|
) === $defaults_table;
|
|
|
|
$this->assertTrue($table_exists, 'Tabla defaults no existe después de migración');
|
|
}
|
|
|
|
/**
|
|
* Test: La migración preserva la cantidad de registros
|
|
*
|
|
* @test
|
|
*/
|
|
public function it_preserves_record_count(): void
|
|
{
|
|
// Contar antes de migración
|
|
$legacy_count = $this->wpdb->get_var(
|
|
"SELECT COUNT(*) FROM {$this->prefix}roi_theme_components"
|
|
);
|
|
|
|
// Ejecutar migración
|
|
$result = $this->migrator->migrate();
|
|
$this->assertTrue($result['success']);
|
|
|
|
// Contar después de migración
|
|
$v2_count = $this->wpdb->get_var(
|
|
"SELECT COUNT(*) FROM {$this->prefix}roi_theme_components"
|
|
);
|
|
|
|
$this->assertEquals(
|
|
$legacy_count,
|
|
$v2_count,
|
|
"Cantidad de registros no coincide: legacy={$legacy_count}, v2={$v2_count}"
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Test: La migración infiere grupos correctamente
|
|
*
|
|
* @test
|
|
*/
|
|
public function it_infers_config_groups_correctly(): void
|
|
{
|
|
$result = $this->migrator->migrate();
|
|
$this->assertTrue($result['success']);
|
|
|
|
// Verificar que "enabled" se migró a grupo "visibility"
|
|
$group = $this->wpdb->get_var(
|
|
"SELECT config_group FROM {$this->prefix}roi_theme_components
|
|
WHERE config_key = 'enabled' LIMIT 1"
|
|
);
|
|
|
|
$this->assertEquals('visibility', $group);
|
|
|
|
// Verificar que "message_text" se migró a grupo "content"
|
|
$group = $this->wpdb->get_var(
|
|
"SELECT config_group FROM {$this->prefix}roi_theme_components
|
|
WHERE config_key = 'message_text' LIMIT 1"
|
|
);
|
|
|
|
$this->assertEquals('content', $group);
|
|
|
|
// Verificar que "background_color" se migró a grupo "styles"
|
|
$group = $this->wpdb->get_var(
|
|
"SELECT config_group FROM {$this->prefix}roi_theme_components
|
|
WHERE config_key = 'background_color' LIMIT 1"
|
|
);
|
|
|
|
$this->assertEquals('styles', $group);
|
|
}
|
|
|
|
/**
|
|
* Test: La migración crea backup de tablas legacy
|
|
*
|
|
* @test
|
|
*/
|
|
public function it_creates_backup_tables(): void
|
|
{
|
|
$result = $this->migrator->migrate();
|
|
$this->assertTrue($result['success']);
|
|
|
|
// Verificar que tabla backup existe
|
|
$backup_table = $this->prefix . 'roi_theme_components_backup';
|
|
$table_exists = $this->wpdb->get_var(
|
|
"SHOW TABLES LIKE '{$backup_table}'"
|
|
) === $backup_table;
|
|
|
|
$this->assertTrue($table_exists, 'Tabla backup no fue creada');
|
|
|
|
// Verificar que backup tiene datos
|
|
$backup_count = $this->wpdb->get_var(
|
|
"SELECT COUNT(*) FROM {$backup_table}"
|
|
);
|
|
|
|
$this->assertGreaterThan(0, $backup_count, 'Tabla backup está vacía');
|
|
}
|
|
|
|
/**
|
|
* Test: Rollback elimina tablas v2 en caso de error
|
|
*
|
|
* @test
|
|
*/
|
|
public function it_rolls_back_on_error(): void
|
|
{
|
|
// Crear escenario de error: tabla legacy vacía después de crear v2
|
|
$this->wpdb->query("DELETE FROM {$this->prefix}roi_theme_components");
|
|
|
|
$result = $this->migrator->migrate();
|
|
|
|
// La migración debe fallar por mismatch de conteo
|
|
$this->assertFalse($result['success']);
|
|
|
|
// Verificar que tablas v2 fueron eliminadas
|
|
$v2_table = $this->prefix . 'roi_theme_components_v2';
|
|
$table_exists = $this->wpdb->get_var(
|
|
"SHOW TABLES LIKE '{$v2_table}'"
|
|
) === $v2_table;
|
|
|
|
$this->assertFalse($table_exists, 'Tabla v2 no fue eliminada en rollback');
|
|
}
|
|
|
|
/**
|
|
* Test: cleanup_backup elimina tablas de respaldo
|
|
*
|
|
* @test
|
|
*/
|
|
public function it_removes_backup_tables(): void
|
|
{
|
|
// Ejecutar migración
|
|
$result = $this->migrator->migrate();
|
|
$this->assertTrue($result['success']);
|
|
|
|
// Verificar que backup existe
|
|
$backup_table = $this->prefix . 'roi_theme_components_backup';
|
|
$table_exists = $this->wpdb->get_var(
|
|
"SHOW TABLES LIKE '{$backup_table}'"
|
|
) === $backup_table;
|
|
$this->assertTrue($table_exists);
|
|
|
|
// Ejecutar cleanup
|
|
$this->migrator->cleanupBackup();
|
|
|
|
// Verificar que backup fue eliminado
|
|
$table_exists = $this->wpdb->get_var(
|
|
"SHOW TABLES LIKE '{$backup_table}'"
|
|
) === $backup_table;
|
|
|
|
$this->assertFalse($table_exists, 'Tabla backup no fue eliminada');
|
|
}
|
|
|
|
/**
|
|
* Helper: Crear tablas legacy para testing
|
|
*/
|
|
private function createLegacyTables(): void
|
|
{
|
|
if (!defined('ABSPATH')) {
|
|
return;
|
|
}
|
|
|
|
require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
|
|
|
|
$charset_collate = $this->wpdb->get_charset_collate();
|
|
|
|
$components_sql = "CREATE TABLE {$this->prefix}roi_theme_components (
|
|
id BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
|
|
component_name VARCHAR(50) NOT NULL,
|
|
config_key VARCHAR(100) NOT NULL,
|
|
config_value TEXT NOT NULL,
|
|
data_type VARCHAR(20) NOT NULL DEFAULT 'string',
|
|
version VARCHAR(10) NOT NULL DEFAULT '1.0.0',
|
|
created_at DATETIME NOT NULL,
|
|
updated_at DATETIME NOT NULL,
|
|
PRIMARY KEY (id),
|
|
UNIQUE KEY component_config (component_name, config_key)
|
|
) {$charset_collate};";
|
|
|
|
$defaults_sql = "CREATE TABLE {$this->prefix}roi_theme_components_defaults (
|
|
id BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
|
|
component_name VARCHAR(50) NOT NULL,
|
|
config_key VARCHAR(100) NOT NULL,
|
|
config_value TEXT NOT NULL,
|
|
data_type VARCHAR(20) NOT NULL DEFAULT 'string',
|
|
version VARCHAR(10) NOT NULL DEFAULT '1.0.0',
|
|
created_at DATETIME NOT NULL,
|
|
updated_at DATETIME NOT NULL,
|
|
PRIMARY KEY (id),
|
|
UNIQUE KEY component_config (component_name, config_key)
|
|
) {$charset_collate};";
|
|
|
|
dbDelta($components_sql);
|
|
dbDelta($defaults_sql);
|
|
}
|
|
|
|
/**
|
|
* Helper: Insertar datos de prueba en tablas legacy
|
|
*/
|
|
private function seedLegacyData(): void
|
|
{
|
|
$timestamp = current_time('mysql');
|
|
|
|
// Components
|
|
$components_data = [
|
|
['top_bar', 'enabled', '1', 'boolean'],
|
|
['top_bar', 'message_text', 'Welcome!', 'string'],
|
|
['top_bar', 'background_color', '#000000', 'string'],
|
|
['footer', 'enabled', '1', 'boolean'],
|
|
['footer', 'cta_url', 'https://example.com', 'string'],
|
|
['footer', 'cta_text', 'Click here', 'string'],
|
|
];
|
|
|
|
foreach ($components_data as $data) {
|
|
$this->wpdb->insert(
|
|
$this->prefix . 'roi_theme_components',
|
|
[
|
|
'component_name' => $data[0],
|
|
'config_key' => $data[1],
|
|
'config_value' => $data[2],
|
|
'data_type' => $data[3],
|
|
'version' => '1.0.0',
|
|
'created_at' => $timestamp,
|
|
'updated_at' => $timestamp
|
|
],
|
|
['%s', '%s', '%s', '%s', '%s', '%s', '%s']
|
|
);
|
|
}
|
|
|
|
// Defaults (misma estructura)
|
|
$this->wpdb->insert(
|
|
$this->prefix . 'roi_theme_components_defaults',
|
|
[
|
|
'component_name' => 'top_bar',
|
|
'config_key' => 'enabled',
|
|
'config_value' => '1',
|
|
'data_type' => 'boolean',
|
|
'version' => '1.0.0',
|
|
'created_at' => $timestamp,
|
|
'updated_at' => $timestamp
|
|
],
|
|
['%s', '%s', '%s', '%s', '%s', '%s', '%s']
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Helper: Limpiar todas las tablas del test
|
|
*/
|
|
private function cleanupTables(): void
|
|
{
|
|
$tables = [
|
|
'roi_theme_components',
|
|
'roi_theme_components_defaults',
|
|
'roi_theme_components_v2',
|
|
'roi_theme_components_defaults_v2',
|
|
'roi_theme_components_backup',
|
|
'roi_theme_components_defaults_backup'
|
|
];
|
|
|
|
foreach ($tables as $table) {
|
|
$this->wpdb->query("DROP TABLE IF EXISTS {$this->prefix}{$table}");
|
|
}
|
|
|
|
// Limpiar opciones
|
|
delete_option('roi_theme_migration_date');
|
|
delete_option('roi_theme_migration_backup_date');
|
|
delete_option('roi_theme_migration_stats');
|
|
}
|
|
}
|