- Updated `capitulos-esquema.md` to reflect the current state of models and integration testing setup. - Added `jest.mariadb.config.js` for MariaDB specific test configurations. - Introduced `mariadb.fixture.mock.ts` to handle isolated MariaDB connections for testing. - Created integration tests for quote chapters in `quoteChapters.mariadb.test.ts` to validate business logic against MariaDB. - Implemented `mariadbSafety.test.ts` to ensure safety checks and error handling during integration tests. - Added `useChapterCollapse.ts` hook for managing chapter collapse animations in the client. - Introduced `empty.tsx` components for consistent empty state UI across the application. - Updated `jest.config.js` to ignore MariaDB test files in the standard test run. - Enhanced `package.json` with a new script for running MariaDB tests.
11 KiB
Capítulos: cambio de esquema preparado para revisión
Estado: modelos implementados en fase 3; suite de integración MariaDB preparada. No se han aplicado cambios a una base de datos ni certificado el esquema con MariaDB: el servidor facilitado se identifica como MySQL 8.0.42 y falta un destino MariaDB o una decisión explícita de cambiar el motor validado. Este documento describe la estructura y su verificación aislada.
Cambios concretos
| Tabla | Cambio | Datos históricos |
|---|---|---|
quotes |
Añadir show_chapter_line_amounts, BOOLEAN, NOT NULL, DEFAULT TRUE |
Mantener importes individuales visibles por defecto |
quote_chapters |
Crear tabla sin timestamps: quote_id UUID, chapter_id UUID, position MEDIUMINT NOT NULL, description TEXT NOT NULL |
No insertar capítulos automáticamente |
quote_chapters |
PK compuesta (quote_id, chapter_id); índice quote_chapters_position_idx (quote_id, position) |
Sin transformación de detalles existentes |
quote_chapters |
FK de quote_id a quotes.id, borrado en cascada |
La asociación Sequelize declara esta relación |
quote_items |
Añadir chapter_id UUID nullable, DEFAULT NULL |
Todos los detalles antiguos permanecen libres |
quote_items |
Añadir índice quote_items_chapter_idx (quote_id, chapter_id) |
Sin alterar la posición de los detalles existentes |
Los modelos están en server/src/contexts/sales/infrastructure/sequelize/{quote,quoteItem,quoteChapter}.model.ts. El registro dinámico existente descubre el nuevo modelo y después configura las asociaciones. La declaración tipada dealer_id usa ForeignKey para representar la asociación ya existente; no introduce una columna adicional diferente.
Pertenencia y claves foráneas
El dominio valida que cada referencia de detalle exista entre los capítulos de su cotización. El mapper comprueba también la cotización propietaria de los capítulos al leer. No se declara una asociación Sequelize desde chapter_id de detalle hacia una clave de capítulo aislada: la identidad persistida del capítulo es compuesta y una FK simple no garantizaría la pertenencia a la misma cotización.
La FK compuesta adicional a revisar al aplicar el esquema tiene esta forma:
ALTER TABLE quote_items
ADD CONSTRAINT quote_items_chapter_fk
FOREIGN KEY (quote_id, chapter_id)
REFERENCES quote_chapters (quote_id, chapter_id)
ON DELETE RESTRICT ON UPDATE CASCADE;
Es una definición para revisión, no un script de migración ejecutado. Antes de aplicarla se deben contrastar los tipos físicos, collation, motor y restricciones existentes de las tablas reales. Con chapter_id = NULL, las líneas antiguas no requieren un capítulo. El repositorio elimina primero detalles y luego capítulos, por lo que el reemplazo completo respeta esta restricción.
Límite actual: el modelo Sequelize no representa ni instala esta FK compuesta; la garantía de pertenencia implementada ahora es de aplicación. No afirmar que la base de datos la impone hasta aplicarla y verificarla. El arranque existente ejecuta sync({ force: false, alter: true }); hay que verificar si conserva la FK manual y sus índices, también en un segundo arranque. No añadir DDL automático en hooks ni cambiar la estrategia global de sincronización dentro de esta fase.
Comprobaciones para una aplicación autorizada
- Trabajar con una copia aislada y recuperable del esquema existente; revisar las definiciones físicas de UUID, FK e índices antes de materializar el cambio.
- Verificar tabla nueva, columnas, defaults e índices, incluyendo el comportamiento del
sync({ alter: true })actual. Revisar por separado la FK compuesta anterior. - Leer, editar, guardar y duplicar una cotización anterior al cambio: cero capítulos, referencias null, mismo orden y mismos importes.
- Crear una cotización con dos capítulos y textos; guardar y recargar. Reordenar capítulos y trasladar detalles verificando sus posiciones.
- Inyectar un fallo al insertar capítulos y otro al insertar detalles. Comprobar en MariaDB que cabecera, capítulos y detalles permanecen en el estado previo, sin escrituras parciales.
- Lanzar dos actualizaciones concurrentes: una que introduce capítulos y otra de un cliente antiguo que los omite. La comprobación bajo bloqueo debe evitar una eliminación por omisión si la agrupación ya se ha confirmado.
- Probar la FK compuesta, si se aplica: rechazar pertenencias inexistentes o a otra cotización, permitir líneas libres null y permitir el reemplazo transaccional de estructura.
- Desplegar el backend compatible antes del editor. Una vez guardados capítulos, no volver a un backend que los ignore ni retirar columnas/tablas sin conservar esos datos.
Las pruebas de fase 3 usan metadatos Sequelize y dobles transaccionales, sin abrir conexiones. Verifican contrato, mapeo, orden de operaciones, propagación de transacción y errores; no certifican el DDL, el bloqueo concurrente ni el rollback real de MariaDB. Estas comprobaciones quedan para el entorno autorizado de integración.
Suite de integración preparada
Comando explícito desde server/: yarn test:mariadb. Usa jest.mariadb.config.js y src/contexts/sales/infrastructure/__integration__/quoteChapters.mariadb.test.ts. La suite habitual de Jest excluye los tests *.mariadb.test.ts y conserva las comprobaciones unitarias de seguridad del entorno de pruebas.
Conexión y aislamiento
La suite no importa la configuración de conexión de la aplicación ni lee stack.env o archivos .env. Requiere estas variables específicas:
| Variable | Uso |
|---|---|
UECKO_MARIADB_TEST_USER |
Usuario de pruebas obligatorio |
UECKO_MARIADB_TEST_PASSWORD |
Contraseña opcional; no se imprime |
UECKO_MARIADB_TEST_HOST |
Host, por defecto 127.0.0.1 |
UECKO_MARIADB_TEST_PORT |
Puerto, por defecto 3306 |
UECKO_MARIADB_TEST_SOCKET |
Socket local opcional en lugar de conexión TCP |
El comando establece UECKO_MARIADB_INTEGRATION=1 mediante cross-env ya instalado. No instala paquetes ni regenera lockfiles. Ejemplo sin contraseña en la línea de comandos, cuando las variables de acceso ya están disponibles en el entorno:
cd server
yarn test:mariadb
El usuario de pruebas necesita crear y eliminar esquemas temporales y sus tablas, índices, claves y triggers; para comprobar la espera real del bloqueo debe poder consultar information_schema.INNODB_LOCK_WAITS e INNODB_TRX (PROCESS). Usar una instancia de pruebas con permisos apropiados, no ampliar los permisos del usuario de producción para ejecutar esta suite.
Antes de cualquier DDL se exige que SELECT VERSION() identifique MariaDB; MySQL se rechaza expresamente. Se crea un nombre aleatorio uecko_chapters_it_<uuid> sin IF NOT EXISTS. Solo se elimina ese esquema si su creación en esta ejecución tuvo éxito. Se cierran los pools y se elimina el esquema en la limpieza de Jest, también si un test falla. Si se mata el proceso con SIGKILL, el esquema puede quedar para limpieza manual tras comprobar su nombre; no se hace limpieza por prefijos sobre otros esquemas.
Cobertura preparada (14 casos; pendientes de ejecución real)
- Registra los cinco modelos reales de usuarios, distribuidores, cotizaciones, capítulos y detalles, con las asociaciones y convenciones de persistencia del adaptador. No inicia la API, los seeds ni los otros servicios.
- Construye la forma anterior a capítulos retirando exclusivamente las tres incorporaciones nuevas del esquema temporal. Inserta usuario/distribuidor ficticios y una cotización libre con tres detalles antes de ejecutar
sync({ force: false, alter: true }). - Comprueba defaults, PK compuesta, índice, contenido histórico, orden y totales; edita y duplica la cotización histórica mediante los casos de uso reales.
- Crea y recarga capítulos con texto mediante casos de uso, repositorio, transacciones, mappers y modelos reales. Contrasta la salida del presentador API y el HTML del reporter con los datos leídos de MariaDB.
- Reordena capítulos, traslada detalles y desagrupa; verifica subtotales, posiciones canónicas y totales. Duplica y verifica identidades nuevas y pertenencias remapeadas.
- Cuatro fallos de escritura reales mediante triggers
SIGNAL: inserción de capítulos y detalles, tanto al crear como al actualizar. Compara las tres tablas tras rollback, incluyendo la cabecera y los timestamps. - Una actualización mantiene el bloqueo de la cotización mientras confirma capítulos. Una segunda actualización de cliente antiguo espera realmente en InnoDB; después del commit debe rechazar la omisión de capítulos sin borrarlos.
- Ejecuta un segundo
sync({ alter: true })con datos guardados; comprueba que no se pierdan datos ni asociaciones. - Instala la FK compuesta solo en el esquema temporal y comprueba pertenencias desconocidas, pertenencias cruzadas, null y reemplazo transaccional. El último caso comprueba un nuevo
synccon esa FK manual: su compatibilidad aún no se da por demostrada ni se traslada DDL a la aplicación.
Las seis comprobaciones unitarias del fixture verifican la autorización explícita, el usuario obligatorio, el rechazo de MySQL antes del DDL, el rechazo de un esquema ya existente y la limpieza del esquema creado. Estas comprobaciones usan dobles y no sustituyen los catorce casos contra MariaDB.
Estado de esta entrega
Se ha detectado MySQL 8.0.42 local, sin binarios MariaDB ni Docker. El usuario facilitó acceso a localhost:3306: la conexión funciona, pero la consulta de metadatos devuelve 8.0.42-0ubuntu0.20.04.1, producto Ubuntu/MySQL. La ejecución explícita yarn test:mariadb se detiene en la comprobación de motor antes de crear el esquema: no ejecuta DDL ni escribe en uecko. Los catorce casos no han sido verificados contra MariaDB y la fase permanece pendiente de un servidor MariaDB o de autorización para cambiar el motor de la validación. La incompatibilidad detectada es de entorno, no un resultado de las pruebas funcionales.
Validación sin base de datos: cd server && yarn test --runInBand src/contexts/sales (11 suites, 78 pruebas correctas), TypeScript con yarn tsc --noEmit --pretty false, lint acotado a los tres archivos de integración con --parser-options '{"project":null}' y git diff --check. Las seis pruebas nuevas son de aislamiento y limpieza; no certifican MariaDB. La suite real también ha sido comprobada por ts-jest antes de detenerse en la precondición de motor.
No se han instalado dependencias, arrancado el backend, aplicado cambios al esquema de la aplicación ni ejecutado operaciones Docker. La inspección de la interfaz con API autenticada y la generación PDF desde esa API permanecen fuera de esta ejecución de persistencia, aunque el contenido HTML sí está incluido en la suite preparada.