En proyectos reales, el equipo QA enfrenta tres escenarios recurrentes con los datos de prueba:
| Escenario | Situación | Riesgo | Solución Shift-Left |
|---|---|---|---|
| Sin datos | Dev no provee datos. QA genera sintéticos ad-hoc. | No se detectan problemas de calidad de datos reales. | Golden Dataset versionado + Data Contract en DoR. |
| DB sin control | Se da acceso a una BD de test controlada por dev. Puede cambiar sin aviso. | Tests inestables. La culpa "cae" sobre QA cuando rompe. | BD propia de QA. Schema validado contra contrato. Seed reproducible. |
| Datos reales | Cliente autoriza uso de datos de producción para pruebas. | Violación GDPR/LGPD si no hay anonimización. Riesgo legal. | DPA firmado + pipeline de anonimización (Privacy by Design — GDPR Art. 25). |
El Data Contract es el artefacto formal que define qué datos existen, cómo se estructuran y qué reglas deben cumplir. Es el DoR de la capa de datos: sin él, no se puede ejecutar ninguna prueba de datos válida.
| Elemento | Descripción | Ejemplo |
|---|---|---|
| Entidades y tablas | Listado de tablas/colecciones relevantes para la HU. | users, orders, payments, audit_log |
| Tipos y formatos | Tipo de dato esperado por columna. | email: VARCHAR(255), NOT NULL, UNIQUE |
| Constraints de negocio | Reglas que el dato debe cumplir más allá del DDL. | password_hash NUNCA en texto plano · age >= 18 |
| Campos PII | Identificación de datos personales (GDPR/LGPD). | email, full_name, phone, ip_address |
| Relaciones | FK y relaciones entre entidades. | orders.user_id → users.id (FK obligatoria) |
| Estados válidos | Enumeración de valores permitidos. | status IN ('pending','active','cancelled') |
| Volumen de referencia | Orden de magnitud de registros en producción. | users: ~50.000 · orders: ~250.000 |
El schema es la primera línea de defensa de la calidad del dato. Un campo sin constraint a nivel BD es un defecto que la aplicación puede ocultar temporalmente pero que eventualmente produce datos corruptos.
| Check | Qué verificar | Comando SQL de validación | Estado |
|---|---|---|---|
| PK definida | Toda tabla tiene PRIMARY KEY explícita. | SELECT table_name FROM information_schema.tables WHERE table_name NOT IN (SELECT table_name FROM information_schema.table_constraints WHERE constraint_type='PRIMARY KEY') |
☐ OK / ☐ Defecto |
| FK con índice | Toda FK tiene índice en la columna referenciada. | SHOW INDEX FROM tabla |
☐ OK / ☐ Defecto |
| NOT NULL críticos | Columnas del Data Contract marcadas como NOT NULL lo son en DDL. | SELECT column_name, is_nullable FROM information_schema.columns WHERE table_name='users' |
☐ OK / ☐ Defecto |
| UNIQUE constraints | Campos identificados como únicos tienen constraint UNIQUE. | SELECT constraint_name FROM information_schema.table_constraints WHERE constraint_type='UNIQUE' |
☐ OK / ☐ Defecto |
| Longitudes de VARCHAR | Longitudes del DDL coinciden con el Data Contract. | SELECT column_name, character_maximum_length FROM information_schema.columns WHERE table_name='users' |
☐ OK / ☐ Defecto |
| ENUM/CHECK constraints | Estados y enumeraciones tienen CHECK constraint o tipo ENUM. | SELECT check_clause FROM information_schema.check_constraints |
☐ OK / ☐ Defecto |
| Sin columnas huérfanas | No existen columnas en BD que no estén en el Data Contract. | Comparar information_schema.columns contra el contrato. |
☐ OK / ☐ Defecto |
El Golden Dataset es un conjunto de datos sintéticos, versionado en Git, que siembra la BD de QA antes de cada ejecución del pipeline. QA lo controla 100%.
| Principio | Descripción |
|---|---|
| Representatividad | Los datos cubren todos los escenarios del Data Contract: valores límite, formatos extremos, estados válidos e inválidos. |
| Reproducibilidad | El mismo seed produce exactamente los mismos datos. El pipeline es determinista. |
| Versionado | El seed script vive en Git junto al código. Cambia con el Data Contract. |
| Aislamiento | Cada ejecución del pipeline restaura la BD desde el seed. Los tests no se contaminan entre sí. |
| Técnica de enmascaramiento | Aplica a | Ejemplo |
|---|---|---|
| Sustitución | Nombres, emails, teléfonos | juan.perez@empresa.com → user_4829@qasl.test |
| Shuffling | Datos numéricos, fechas | Redistribuir valores del mismo campo entre filas |
| Nulling | Campos no necesarios para el test | phone_number → NULL |
| Tokenización | Números de tarjeta, DNI | 4111111111111111 → TOK_a83f9e2c |
| Perturbación | Montos, edades, coordenadas | Añadir ruido gaussiano ±5% |
Después de que Playwright ejecuta un flujo E2E completo, las DB assertions verifican que el estado de la base de datos sea exactamente el esperado. Lo que el UI muestra puede diferir de lo que realmente quedó persistido.
| Tipo | Qué valida | Ejemplo de query | Caso de prueba |
|---|---|---|---|
| Existencia | El registro fue creado (o eliminado). | SELECT COUNT(*) FROM users WHERE email='nuevo@test.com' |
TC-001: Registro exitoso → usuario existe en BD |
| Valores | Los campos tienen los valores esperados post-operación. | SELECT status FROM users WHERE email='...' |
TC-002: Estado correcto post-registro |
| No-existencia | Registros inválidos NO fueron creados. | SELECT COUNT(*) FROM users WHERE email='inválido' |
TC-003: Registro fallido → NO existe en BD (BUG-001 type) |
| Integridad referencial | FK correctamente vinculadas post-operación. | SELECT o.id FROM orders o LEFT JOIN users u ON o.user_id=u.id WHERE u.id IS NULL |
TC-004: Sin órdenes huérfanas |
| Seguridad del dato | PII no almacenada en texto plano. | SELECT COUNT(*) FROM users WHERE LENGTH(password_hash) < 60 |
TC-005: Password hasheado — alinea a OWASP A02 |
| Auditoría | Registro de auditoría creado para operaciones críticas. | SELECT COUNT(*) FROM audit_log WHERE entity='user' AND action='CREATE' |
TC-006: Log de creación de usuario existe |
Las propiedades ACID (Atomicity, Consistency, Isolation, Durability) son los contratos que la BD hace con la aplicación. Cuando se violan, los datos quedan en estados inconsistentes que ningún test de UI detecta.
| Propiedad | Qué significa | Cómo testearla | Escenario de falla |
|---|---|---|---|
| Atomicity | La transacción completa o no hace nada. | Simular falla en el paso N de una transacción multi-step. Verificar rollback completo. | Pago registrado en tabla payments pero orden sin actualizar en orders. |
| Consistency | La BD pasa de un estado válido a otro válido. | Verificar constraints post-transacción. Ninguna constraint debe violarse. | Balance de cuenta negativo tras débito sin validación. |
| Isolation | Transacciones concurrentes no interfieren entre sí. | Ejecutar 2+ transacciones en paralelo (K6 con 2 VUs) sobre el mismo recurso. | Dos usuarios registran el mismo email simultáneamente. Uno debería fallar. |
| Durability | Los datos confirmados persisten ante fallos. | Verificar que datos existen en BD después de reiniciar el servicio. | Registro exitoso en UI pero dato perdido después de reinicio del servidor. |
Una query lenta en producción es un defecto de calidad del dato igual que un valor nulo inesperado. Se detecta antes del release, no cuando el DBA recibe la alerta a las 3 AM.
| Check | Herramienta | Umbral de alerta | Acción |
|---|---|---|---|
| Full table scan | EXPLAIN ANALYZE | type = ALL en queries frecuentes | Agregar índice en la columna filtrada. |
| Query time p95 | K6 + slow query log | > 500 ms en operaciones CRUD básicas | Revisar índices, N+1, joins innecesarios. |
| N+1 queries | Query log / ORM profiler | Más de 1 query por entidad en listados | Usar JOINs o eager loading. |
| Lock contention | pg_locks / SHOW STATUS | Waits > 100ms bajo 2 VUs | Revisar isolation level y orden de locks. |
La capa de BD es el objetivo final de los ataques de inyección y la última línea de defensa para los datos sensibles. Alineado a OWASP Top 10 A02 (Cryptographic Failures) y A03 (Injection).
| Riesgo OWASP | Check en capa BD | Query de validación |
|---|---|---|
| A02 · Cryptographic Failures | Passwords nunca en texto plano. Datos sensibles encriptados. | SELECT COUNT(*) FROM users WHERE LENGTH(password_hash) < 60 OR password_hash NOT LIKE '$2%' |
| A02 · PII en logs | Logs de BD no contienen PII (email, nombre, DNI). | Revisar slow query log / general log por patrones de email. |
| A03 · SQL Injection | Inputs del usuario parametrizados antes de llegar a BD. | Intentar '; DROP TABLE users; -- vía API. Verificar que BD no ejecuta el DROP. |
| Excessive data exposure | Endpoints no devuelven columnas que no deben (password_hash, etc.). | Interceptar respuesta API. Verificar que password_hash no aparece en ningún response. |
| Permisos de BD | Usuario de la app tiene mínimos privilegios (no DROP, no GRANT). | SHOW GRANTS FOR 'app_user'@'%' |
El DB testing no es un paso separado — se integra al pipeline existente del QASL Framework como una capa adicional que se ejecuta en paralelo o post-E2E.
El DQFI (Data Quality Forensic Index) es el índice forense de la capa de datos. Convierte los resultados de las assertions en un score 0–100 con grado A→F, siguiendo el mismo patrón que CFQI (AppSec) y AFQI (AI/LLM) del ecosistema QASL.
Calidad del schema y las constraints. Mide la salud de la BD en reposo.
Herramientas: information_schema queries · sqlfluff · schemaspy
Calidad del contenido persistido. Mide lo que está mal en los datos reales.
Herramientas: Great Expectations · assertions SQL custom · dbt tests
Comportamiento bajo operaciones concurrentes y adversas. Mide ACID bajo presión.
Herramientas: K6 + DB assertions · pg_locks · slow query log
| Score | Grado | Interpretación | Acción recomendada |
|---|---|---|---|
| 90–100 | A | Capa de datos en excelente estado. Release autorizado. | Mantener. Monitorear en producción. |
| 75–89 | B | Pequeñas deudas de calidad. No bloquea release. | Documentar. Resolver en próximo sprint. |
| 60–74 | C | Problemas de calidad de dato notables. Riesgo moderado. | Resolver antes del release. Mínimo aceptable. |
| 40–59 | D | Problemas serios de integridad o consistencia. | Bloquear release hasta resolver críticos. |
| 0–39 | F | Capa de datos no apta. Posible breach o corrupción activa. | Detener pipeline. Investigar inmediatamente. |
| Métrica | Fórmula / Fuente | Umbral objetivo | Dashboard |
|---|---|---|---|
| DQFI Score | D1×0.35 + D2×0.35 + D3×0.30 − Λ | ≥ 75 (grado B) | Grafana · panel DQFI gauge |
| Schema Assertions Pass Rate | Assertions OK / Total assertions × 100 | 100% (bloqueante) | Grafana · panel D1 |
| Integrity Assertions Pass Rate | Assertions OK / Total assertions × 100 | ≥ 95% | Grafana · panel D2 |
| Query p95 | Percentil 95 de latencia de queries críticas | < 500ms | Grafana · panel D3 |
| PII Exposure Rate | Registros con PII en texto plano / Total | 0% (bloqueante Λ) | Grafana · panel seguridad |
| Seed Reproducibility | Hash del estado de BD post-seed debe ser idéntico en cada run | 100% idéntico | CI log |
| Anti-pattern | Por qué es peligroso | Solución |
|---|---|---|
| Tests que comparten estado de BD | Un test contamina los datos del siguiente. Resultados no reproducibles. | Seed antes de cada ejecución. Transacciones con rollback en tests unitarios de BD. |
| Probar solo via UI | El UI puede mostrar éxito mientras el dato quedó corrupto en BD. | Siempre combinar: assertion UI + assertion BD para flujos críticos. |
| Usar BD de producción para tests | Riesgo de borrado/corrupción de datos reales. Violación GDPR. | BD propia de QA, aislada, controlada. Golden Dataset o datos enmascarados. |
| Ignorar el schema hasta que falla | Un campo sin NOT NULL produce datos corruptos que tardan meses en detectarse. | Schema assertions en Fase 2, antes del primer commit de dev. |
| Data Contract verbal | "El equipo sabe cómo son los datos" — hasta que rotan personas o cambia el equipo. | Data Contract escrito, versionado en Git, firmado como DoR. |
| No testear ACID bajo concurrencia | Los bugs de isolation aparecen en producción bajo carga real, no en tests seriales. | Usar K6 con 2+ VUs sobre los mismos recursos. Testear race conditions. |
Esta guía es la extensión de capa de datos del framework. Se integra como sigue: