Royal Clean CRM · Documentación
Calidad

Pruebas y control de calidad

Qué se prueba, con qué herramienta y qué revisar antes de dar algo por terminado.

Principio

Una función no está terminada porque compile. Está terminada cuando su comportamiento está demostrado, especialmente si toca dinero, tiempo, evidencia o permisos.

Niveles

NivelHerramientaContra qué correQué demuestra
UnitarioVitestNada externo; módulos purosQue las fórmulas son correctas
IntegraciónVitestPostgreSQL y almacenamiento realesQue las invariantes se sostienen
Extremo a extremoPlaywrightLa aplicación compiladaQue los recorridos completos funcionan
ManualNavegadorLa aplicación realQue la experiencia se siente bien
pnpm test              # unitarios
pnpm test:integration  # integración (requiere docker compose up -d)
pnpm test:e2e          # recorridos completos
pnpm verify            # typecheck + lint + unitarios

Tests unitarios

134 tests sobre los módulos puros. Sin mocks de Prisma: estos módulos no conocen la base de datos.

ArchivoCubre
modules/finance/money.test.tsConversión, parseo ambiguo de separadores, formato, margen
modules/finance/calculations.test.tsIngresos, costos, margen, cifras parciales
modules/time-tracking/calculations.test.tsSuma de sesiones, sesión activa, ventana operativa
modules/payroll/calculations.test.tsTarifas, redondeo, costo laboral, nómina por período
modules/services/status-machine.test.tsTransiciones válidas e inválidas, reglas por rol
modules/properties/sensitive.test.tsAcceso a códigos según estado y ventana de gracia
lib/datetime.test.tsDías de negocio, medianoche, formato costarricense
modules/audit/sanitize.test.tsRedacción de secretos y serialización de instancias

Casos que merecen mención

Coma flotante. Se comprueba explícitamente que la aritmética en enteros es exacta donde IEEE-754 falla:

expect(colonesToCentimos(0.1) + colonesToCentimos(0.2))
  .toBe(colonesToCentimos(0.3));

Medianoche en Costa Rica. Las 23:00 en Costa Rica son las 05:00 UTC del día siguiente. Un test verifica que ese instante pertenece al día de negocio anterior, que es como lo entiende el negocio:

expect(toBusinessDayKey(new Date("2026-09-02T05:00:00Z")))
  .toBe("2026-09-01");

Los tests corren con TZ=UTC a propósito: si algún cálculo dependiera implícitamente de la zona del proceso, fallarían.

Retomar el trabajo. 3 h + 20 min = 3 h 20 min, no 20 min.

Tests de integración

74 tests contra PostgreSQL y MinIO reales. No se simula Prisma: el objetivo es precisamente comprobar que las restricciones CHECK, los índices únicos y las transacciones se comportan como esperamos, y eso un mock no lo puede probar.

ArchivoCubre
service-lifecycle.test.tsCiclo completo del servicio, snapshots, reapertura, multi-empleado
authorization.test.tsAislamiento entre empleados, datos sensibles, invariantes de la base
evidence-storage.test.tsFirmar, subir, confirmar, borrar, expirar contra S3 real
payroll.test.tsCálculo, congelado del recibo, reversión
service-report.test.tsDatos del reporte, filtración financiera, paginación del PDF
supplies.test.tsAprobación obligatoria, totales exactos, costos ausentes

Preparación

docker compose up -d
docker compose exec db psql -U royalclean -d postgres -c "CREATE DATABASE royalclean_test OWNER royalclean;"

El arranque aplica las migraciones con prisma migrate deploy —el mismo comando que corre en producción— para que los tests validen exactamente el esquema real. Cada test parte de una base vacía.

Casos que merecen mención

El snapshot es inmutable. Se crea un servicio, se cambia el título en la plantilla y se verifica que el servicio conserva el título original.

El pago está congelado. Se registra un pago, luego se duplican las horas y se sube la tarifa, y se verifica que el recibo sigue diciendo lo mismo.

El aislamiento es indistinguible. Un empleado pidiendo el servicio de otro y pidiendo un id inexistente reciben el mismo mensaje de error.

Los códigos no viajan. Cuando el acceso expiró, se comprueba que la clave doorCode no existe en el objeto, no que esté vacía.

La URL firmada es estricta. Subir con un Content-Type distinto del autorizado es rechazado por el almacenamiento.

El reporte no filtra. Se buscan la tarifa y el costo interno en los datos serializados del reporte y no deben aparecer.

La base de datos defiende. Se intenta insertar dos sesiones abiertas del mismo empleado, una sesión que termina antes de empezar, un precio negativo y una hora fuera del día. Todas fallan a nivel de motor.

Pruebas de extremo a extremo

26 tests con Playwright sobre la aplicación compilada, no sobre el servidor de desarrollo: éste compila cada ruta la primera vez que se pide y bajo la carga de una suite completa llega a caerse, produciendo fallos que no son del código. Además, así los recorridos se prueban contra el mismo artefacto que se despliega.

Dos proyectos, cada uno con el dispositivo que corresponde:

ProyectoDispositivoCubre
escritorio1440x900Recorridos de administración
movilPixel 7Recorridos del personal de campo
ArchivoFlujo del §99
admin/directorio.spec.ts1 - alta de cliente, propiedad y servicio
admin/revision.spec.ts3 y 5 - revisión y nómina
empleado/servicio.spec.ts2 y 4 - trabajo de campo
empleado/seguridad.spec.ts6 - intentos de acceso indebido

Preparación:

docker compose up -d
pnpm db:seed
pnpm test:e2e

El servidor se compila y se levanta solo, en el puerto 3101 para no chocar con el de desarrollo.

Casos que merecen mención

El personal no ve importes. Dos tests leen el texto completo de las pantallas de historial y perfil y comprueban que no contienen el símbolo del colón. Es una comprobación tosca a propósito: no depende de saber dónde podría aparecer un importe.

Los códigos llegan tapados. Se verifica que el código real no está en la página hasta pulsar «Mostrar códigos», y que vuelve a desaparecer al ocultarlo.

El 404 es indistinguible. Un servicio ajeno y un identificador inventado producen exactamente la misma pantalla y el mismo código de estado.

El login no delata cuentas. Se comparan los mensajes de error de un usuario inexistente y de uno real con contraseña incorrecta: deben ser idénticos.

Verificación manual

Lo que sigue teniendo sentido comprobar a mano:

Flujo del personal (móvil)

  1. Iniciar sesión, ver el servicio del día.
  2. Abrir el servicio: propiedad, dirección, botón de Google Maps.
  3. Pulsar «Mostrar códigos» y comprobar que aparecen enmascarados antes.
  4. Iniciar el servicio; el cronómetro corre.
  5. Abrir una tarea, tomar una foto, ver la barra de progreso.
  6. Intentar completar sin foto «después»: el botón está deshabilitado y explica por qué.
  7. Completar todas las tareas y finalizar.
  8. Retomar el trabajo y comprobar que el tiempo suma.

Flujo de administración

  1. Crear cliente, propiedad y plantilla de tareas.
  2. Programar un servicio y asignar personal.
  3. Revisar el servicio completado: tiempos, evidencia, incidentes, cifras.
  4. Corregir un horario con motivo y verlo en la auditoría.
  5. Registrar un pago y abrir el recibo.
  6. Generar el PDF y revisar que no contiene datos internos.

Tamaños a comprobar

DispositivoAnchoQué mirar
iPhone SE375 pxQue nada se desborde
iPhone actual393 pxZonas táctiles cómodas
Android medio412 pxBarra inferior sobre el área segura
Tablet768 pxTransición de tarjetas a tablas
Portátil1366 pxBarra lateral y contenido
Escritorio1920 pxQue el contenido no se estire sin límite

En ningún tamaño debe existir scroll horizontal del cuerpo de la página.

PWA

  • El manifiesto carga sin errores.
  • Se puede instalar en Android e iOS.
  • Se abre en modo standalone, sin barra del navegador.
  • Los atajos llevan a «Hoy» e «Historial».

Aceptación de seguridad

Cubierta por tests de integración:

  • Un empleado pidiendo un servicio ajeno recibe 404.
  • Un empleado no ve códigos de acceso fuera de su servicio activo.
  • Un empleado no puede registrar pagos.
  • Una URL de R2 sin firma es rechazada.

Lista completa en SECURITY.md.

Definición de terminado

Antes de dar por buena una función:

  • pnpm verify en verde
  • pnpm test:integration en verde si toca la base de datos
  • pnpm test:e2e en verde si cambia un recorrido de la interfaz
  • Tests para el camino feliz y para el caso que debe fallar
  • Si toca dinero o tiempo, test del cálculo
  • Si toca permisos, test del acceso indebido
  • Verificación manual en móvil si afecta al personal de campo
  • Textos visibles en español
  • Estados de carga y de vacío resueltos
  • Documentación actualizada si cambia una regla de negocio

Qué falta

Ver IMPLEMENTATION_STATUS.md. Los recorridos cubren los seis flujos críticos del §99; lo que queda son casos de menor riesgo, como subir una fotografía de verdad desde el navegador, que hoy se verifica a nivel de integración contra el almacenamiento real.

On this page