From a510d309bc48c21c6e83a097a5718e4b8b86f386 Mon Sep 17 00:00:00 2001 From: enlineawork Date: Tue, 8 Sep 2026 10:30:53 -0300 Subject: [PATCH] test(f4): prove legacy field rollback preserves data --- .../unit/f4-inspection-legacy-fields.test.ts | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/api-v3/test/unit/f4-inspection-legacy-fields.test.ts b/api-v3/test/unit/f4-inspection-legacy-fields.test.ts index 54027ac..683764d 100644 --- a/api-v3/test/unit/f4-inspection-legacy-fields.test.ts +++ b/api-v3/test/unit/f4-inspection-legacy-fields.test.ts @@ -55,7 +55,7 @@ test('F4: el contrato WEB de Inspecciones no reintroduce title, visitTitle ni pl assert.doesNotMatch(inspectionContract, /\bplannedEndAt\b/); }); -test('F4: la migración retira las columnas legacy y conserva índice de inicio', async () => { +test('F4: la migración respalda datos antes de retirar columnas legacy', async () => { const statements: string[] = []; const queryRunner = { query: async (sql: string) => { @@ -67,14 +67,21 @@ test('F4: la migración retira las columnas legacy y conserva índice de inicio' await new F4RemoveInspectionLegacyFields1790000500000().up(queryRunner); const sql = statements.join('\n'); + assert.match(sql, /CREATE TABLE IF NOT EXISTS f4_inspection_visit_legacy_fields_backup/i); + assert.match(sql, /INSERT INTO f4_inspection_visit_legacy_fields_backup/i); + assert.match(sql, /SELECT id, title, planned_end_at, CURRENT_TIMESTAMP FROM inspection_visits/i); assert.match(sql, /DROP COLUMN IF EXISTS planned_end_at/i); assert.match(sql, /DROP COLUMN IF EXISTS title/i); assert.match(sql, /DROP CONSTRAINT IF EXISTS chk_inspection_visits_planned_dates/i); assert.match(sql, /CREATE INDEX IF NOT EXISTS idx_inspection_visits_planned_start_at/i); assert.doesNotMatch(sql, /CREATE INDEX IF NOT EXISTS idx_inspection_visits_planned_dates/i); + + const backupAt = sql.indexOf('INSERT INTO f4_inspection_visit_legacy_fields_backup'); + const dropAt = sql.indexOf('DROP COLUMN IF EXISTS planned_end_at'); + assert.ok(backupAt >= 0 && dropAt > backupAt, 'el respaldo debe ocurrir antes de eliminar columnas'); }); -test('F4: el rollback reconstruye la forma histórica de D1', async () => { +test('F4: el rollback restaura valores históricos exactos y cubre filas nuevas', async () => { const statements: string[] = []; const queryRunner = { query: async (sql: string) => { @@ -87,9 +94,15 @@ test('F4: el rollback reconstruye la forma histórica de D1', async () => { const sql = statements.join('\n'); assert.match(sql, /ADD COLUMN IF NOT EXISTS title varchar\(200\)/i); + assert.match(sql, /ADD COLUMN IF NOT EXISTS planned_end_at timestamptz/i); + assert.match(sql, /UPDATE inspection_visits visit SET title = backup\.title, planned_end_at = backup\.planned_end_at FROM f4_inspection_visit_legacy_fields_backup backup WHERE backup\.visit_id = visit\.id/i); assert.match(sql, /UPDATE inspection_visits SET title = code WHERE title IS NULL/i); assert.match(sql, /ALTER COLUMN title SET NOT NULL/i); - assert.match(sql, /ADD COLUMN IF NOT EXISTS planned_end_at timestamptz/i); assert.match(sql, /ADD CONSTRAINT chk_inspection_visits_planned_dates CHECK/i); assert.match(sql, /CREATE INDEX IF NOT EXISTS idx_inspection_visits_planned_dates/i); + assert.match(sql, /DROP TABLE IF EXISTS f4_inspection_visit_legacy_fields_backup/i); + + const restoreAt = sql.indexOf('UPDATE inspection_visits visit SET title = backup.title'); + const backupDropAt = sql.indexOf('DROP TABLE IF EXISTS f4_inspection_visit_legacy_fields_backup'); + assert.ok(restoreAt >= 0 && backupDropAt > restoreAt, 'el respaldo sólo se elimina después de restaurar los datos'); });