3. Pirámide de pruebas
La estrategia sigue la pirámide clásica: muchas pruebas unitarias rápidas y deterministas en la base, una capa intermedia de integración con la base de datos real y una cima estrecha de pruebas E2E con navegador. El job de E2E en CI permanece deshabilitado hasta resolver el seed de admin determinista.
Última actualización: 2026-05-05
3.1 Conteo por capa
| Capa | Herramienta | Cantidad | Ubicación |
|---|---|---|---|
| Base · unitarias | Vitest | 11 archivos *.test.ts |
packages/validations/src/__tests__/, apps/web/src/lib/api/__tests__/cursor.test.ts |
| Medio · integración | Vitest + Postgres real | 5 suites | apps/web/src/__tests__/integration/ |
| Cima · E2E | Playwright | 7 tests | apps/web/e2e/ |
3.2 Base — pruebas unitarias (Vitest)
Pruebas puras, sin red ni base de datos. Validan schemas Zod, funciones de
serialización y utilidades. Estructura por archivo: controlados-positivos
(happy path), controlados-negativos (input usuario malformado),
no controlados (entrada adversarial).
Archivos:
| Archivo | Cobertura |
|---|---|
packages/validations/src/__tests__/auth.test.ts |
loginSchema, forgotPasswordSchema, resetPasswordSchema |
packages/validations/src/__tests__/common.test.ts |
uuidSchema, emailSchema, passwordSchema, phoneSchema, isoDateString, idParamSchema, paginationQuerySchema |
packages/validations/src/__tests__/mobile-orders.test.ts |
mobileCreateOrderSchema, mobileOrdersListQuerySchema |
packages/validations/src/__tests__/mobile-provider.test.ts |
mobileProviderJobsQuerySchema, mobileProviderJobStatusSchema, ESTATUS_ORDEN |
packages/validations/src/__tests__/mobile-register.test.ts |
mobileRegisterSchema |
apps/web/src/lib/api/__tests__/cursor.test.ts |
encode/decode de cursor keyset para paginación |
3.3 Medio — pruebas de integración
Cada suite invoca el handler real de Next.js (POST, GET, PATCH)
contra una base Postgres real. Fixtures con prefix por PID asegura que
tests concurrentes no se pisen. La limpieza ocurre en afterAll del
describe.
| Suite | Endpoints cubiertos | Dominio |
|---|---|---|
auth.test.ts |
POST /api/v1/auth/login, GET /api/v1/auth/me, POST /api/v1/auth/logout |
autenticación JWT, cookies, expiración |
orders.test.ts |
GET/POST /api/v1/mobile/orders, GET /api/v1/mobile/orders/[id] |
paginación, RBAC cliente, ownership |
provider-jobs.test.ts |
GET/PATCH /api/v1/mobile/provider/jobs/[id]/status |
RBAC prestador vs cliente, ownership de jobs |
register.test.ts |
POST /api/v1/mobile/register |
duplicados, normalización email, hash bcrypt |
services.test.ts |
GET /api/v1/mobile/services, GET /api/v1/mobile/services/[id] |
catálogo público, filtros, validación UUID |
Servicio Postgres en CI:
services:
postgres:
image: postgres:16
env:
POSTGRES_PASSWORD: test
POSTGRES_USER: test
POSTGRES_DB: expressmx_test
ports: ["5432:5432"]
Schema aplicado antes de los tests:
psql ... -f 00.sql
for f in migrations/*.sql; do psql ... -f "$f"; done
3.4 Cima — pruebas E2E (Playwright)
Configuración en apps/web/playwright.config.ts:
testDir: ./e2e,timeout: 30s,expect.timeout: 5s.chromiumDesktop Chrome,fullyParallel: true.retries: 1en CI,0en local.webServerarrancapnpm devsalvoE2E_NO_WEBSERVER=1.- Reporters
listyhtml.
| Spec | Tests | Notas |
|---|---|---|
e2e/api-health.spec.ts |
4 | GET /api/v1/auth/me sin token responde 401, GET /api/v1/mobile/services público 200, login con credenciales inválidas 401, login con payload inválido 422 |
e2e/login-admin.spec.ts |
2 | login admin con credenciales válidas y mensaje de error con credenciales inválidas |
e2e/dashboard-navigation.spec.ts |
1 | admin abre workspace operaciones y la cola se renderiza |
Los tests que necesitan admin sembrado se marcan con
test.skip(!ADMIN_AVAILABLE, ...) cuando E2E_ADMIN_AVAILABLE !== '1'.
Por eso el job CI e2e permanece bajo if: false hasta que pnpm db:seed
produzca un admin determinista.
3.5 Anotaciones por dominio
| Dominio | Capa principal | Notas |
|---|---|---|
| Autenticación JWT | integración + E2E API | suite auth y api-health validan happy path y boundaries |
| RBAC (rol cliente vs prestador) | integración (provider-jobs) | retorna 403 FORBIDDEN cuando un cliente accede a jobs |
| Paginación keyset (cursor) | unit + integración | cursor.test.ts cubre encode/decode; orders y provider validan flujo end-to-end |
| Catálogo público | integración + E2E API | services suite y api-health |
| Navegación dashboard | E2E browser | dashboard-navigation valida render de workspace |
3.6 Costo de mantenimiento
| Capa | Tiempo CI típico | Costo de mantenimiento |
|---|---|---|
| Unit | segundos | bajo |
| Integración | 30 a 90 segundos | medio (DB efímera, fixtures) |
| E2E | 1 a 3 minutos cuando esté activo | alto (web server, admin seed, browsers) |
3.7 Gaps conocidos
- No hay contract tests de OpenAPI ni gRPC; las shapes de respuesta se validan ad-hoc en los tests de integración.
- No hay pruebas de carga ni soak; los tests de paginación validan límites pero no comportamiento sostenido.
- No hay tests específicos de RLS Postgres; el RBAC se valida a nivel handler en integración, no a nivel kernel de la base de datos.
- E2E móvil (Expo) no está cubierto; la cobertura E2E es solo web admin.
- Job E2E en CI está deshabilitado (
if: false) hasta seed determinista.