1013 lines
27 KiB
Markdown
1013 lines
27 KiB
Markdown
# TEST PLAN - MGN-001: Fundamentos
|
|
|
|
**Módulo:** MGN-001 - Fundamentos
|
|
**Sprint:** Sprint 1-5 (Semanas 1-10)
|
|
**Story Points:** 68 SP
|
|
**User Stories:** 16 US
|
|
**Fecha:** 2025-11-24
|
|
**QA Owner:** TBD
|
|
**Estado:** Draft
|
|
|
|
---
|
|
|
|
## 1. RESUMEN DEL MÓDULO
|
|
|
|
### 1.1 Descripción
|
|
|
|
El módulo MGN-001 Fundamentos es la base crítica del ERP Genérico. Provee los servicios core de autenticación, autorización, gestión de usuarios, roles, permisos y multi-tenancy con aislamiento a nivel de schema.
|
|
|
|
Este es el módulo más crítico (P0) ya que todos los demás módulos dependen de él. Sin autenticación funcional, el sistema no puede operar.
|
|
|
|
### 1.2 Funcionalidades Principales
|
|
|
|
1. **Autenticación:** Login con email/contraseña, JWT tokens, refresh tokens, logout
|
|
2. **Autorización:** RBAC (Role-Based Access Control) con permisos granulares por modelo
|
|
3. **Gestión de Usuarios:** CRUD usuarios, perfiles, cambio de contraseña, reset password
|
|
4. **Multi-Tenancy:** Schema-level isolation, tenant context switching
|
|
5. **Gestión de Sesiones:** Sesiones activas, logout, expiración automática
|
|
6. **Row Level Security:** RLS policies en PostgreSQL, field-level security
|
|
7. **Registro de Usuarios:** Signup/auto-registro con validación de email
|
|
8. **Gestión de Roles:** CRUD roles, asignación de permisos CRUD por modelo
|
|
|
|
### 1.3 Dependencias
|
|
|
|
**Módulos requeridos:** Ninguno (es el primer módulo)
|
|
|
|
**Datos maestros necesarios:**
|
|
- Al menos 1 tenant seed con subdomain configurado
|
|
- Al menos 1 usuario administrador seed
|
|
- Roles base: admin, user, viewer
|
|
- Permisos base definidos en código
|
|
|
|
**Servicios externos:**
|
|
- PostgreSQL 16 con extensión pgcrypto
|
|
- Redis (para session storage y rate limiting)
|
|
- SMTP server (para reset password emails)
|
|
|
|
---
|
|
|
|
## 2. ALCANCE DEL TESTING
|
|
|
|
### 2.1 En Alcance
|
|
|
|
**Funcionalidades a testear:**
|
|
- ✅ Login con email y contraseña (happy path + error paths)
|
|
- ✅ Refresh token para renovar JWT
|
|
- ✅ Logout y cierre de sesión
|
|
- ✅ CRUD de roles y permisos
|
|
- ✅ Validación de permisos en runtime (guards/middleware)
|
|
- ✅ CRUD de usuarios
|
|
- ✅ Gestión de perfil propio y cambio de contraseña
|
|
- ✅ Reset de contraseña (forgot password flow)
|
|
- ✅ Signup/auto-registro de usuarios
|
|
- ✅ Creación y configuración de tenants
|
|
- ✅ Schema-level isolation (tenant no ve datos de otro tenant)
|
|
- ✅ Tenant context switching (multi-empresa)
|
|
- ✅ Gestión de sesiones activas
|
|
- ✅ RLS policies (Row Level Security)
|
|
- ✅ Field-level security (campos sensibles)
|
|
- ✅ Bloqueo de cuenta por intentos fallidos
|
|
- ✅ Rate limiting de endpoints de auth
|
|
|
|
**Testing no funcional:**
|
|
- ✅ Performance de login (<300ms p95)
|
|
- ✅ Security (OWASP Top 10, JWT validation, bcrypt)
|
|
- ✅ Tenant isolation (crítico)
|
|
- ✅ Concurrencia (múltiples logins simultáneos)
|
|
- ✅ Rate limiting (protección contra brute force)
|
|
|
|
### 2.2 Fuera de Alcance
|
|
|
|
**Exclusiones explícitas:**
|
|
- ❌ OAuth2/SAML (Fase 2 - no MVP)
|
|
- ❌ 2FA/MFA (Fase 2 - no MVP)
|
|
- ❌ CAPTCHA (Fase 2)
|
|
- ❌ Biometric authentication
|
|
- ❌ Login con redes sociales (Google, Facebook)
|
|
- ❌ LDAP/Active Directory integration
|
|
|
|
### 2.3 Assumptions
|
|
|
|
- PostgreSQL 16 está instalado y configurado correctamente
|
|
- Redis está disponible para session storage
|
|
- SMTP server está configurado para envío de emails
|
|
- Subdomain routing funciona correctamente (nginx/traefik)
|
|
- Environment variables están configuradas (JWT_SECRET, DATABASE_URL, etc.)
|
|
|
|
---
|
|
|
|
## 3. ESTRATEGIA DE TESTING
|
|
|
|
### 3.1 Tipos de Tests
|
|
|
|
#### Unit Tests
|
|
|
|
**Coverage objetivo:** >80% por archivo
|
|
|
|
**Herramientas:**
|
|
- Jest (backend)
|
|
- Vitest (frontend)
|
|
|
|
**Total estimado:** 96 unit tests
|
|
|
|
**Backend (60 tests):**
|
|
- AuthService: 20 tests
|
|
- login method (8 tests: success, invalid email, wrong password, user inactive, tenant inactive, locked account, rate limit, tenant isolation)
|
|
- validateUser method (5 tests)
|
|
- generateTokens method (3 tests)
|
|
- refreshToken method (4 tests)
|
|
- UsersService: 15 tests
|
|
- create, update, delete, findByEmail, changePassword
|
|
- RolesService: 10 tests
|
|
- CRUD roles, assign permissions
|
|
- PermissionsGuard: 8 tests
|
|
- @RequirePermissions decorator validation
|
|
- Utils/Helpers: 7 tests
|
|
- Password hashing, JWT signing/verification
|
|
|
|
**Frontend (36 tests):**
|
|
- LoginForm component: 8 tests
|
|
- Render form, submit with valid data, show errors, loading state
|
|
- useAuthStore (Zustand): 6 tests
|
|
- setUser, setTokens, logout, persist to localStorage
|
|
- authApi client: 8 tests
|
|
- login, refresh, logout API calls
|
|
- UserList component: 6 tests
|
|
- RoleList component: 6 tests
|
|
- ProtectedRoute component: 2 tests
|
|
|
|
#### Integration Tests
|
|
|
|
**Coverage:** Todos los endpoints API + componentes con API
|
|
|
|
**Herramientas:**
|
|
- Supertest (API testing)
|
|
- React Testing Library (component + API)
|
|
- TestContainers (PostgreSQL real instance)
|
|
|
|
**Total estimado:** 48 integration tests
|
|
|
|
**Test suites:**
|
|
|
|
1. **Auth API (16 tests):**
|
|
- POST /api/v1/auth/login (6 tests)
|
|
- POST /api/v1/auth/refresh (3 tests)
|
|
- POST /api/v1/auth/logout (2 tests)
|
|
- POST /api/v1/auth/forgot-password (3 tests)
|
|
- POST /api/v1/auth/reset-password (2 tests)
|
|
|
|
2. **Users API (12 tests):**
|
|
- GET /api/v1/users (2 tests)
|
|
- POST /api/v1/users (3 tests)
|
|
- PUT /api/v1/users/:id (3 tests)
|
|
- DELETE /api/v1/users/:id (2 tests)
|
|
- PATCH /api/v1/users/me/password (2 tests)
|
|
|
|
3. **Roles API (8 tests):**
|
|
- GET /api/v1/roles (2 tests)
|
|
- POST /api/v1/roles (3 tests)
|
|
- PUT /api/v1/roles/:id (2 tests)
|
|
- DELETE /api/v1/roles/:id (1 test)
|
|
|
|
4. **Tenants API (6 tests):**
|
|
- POST /api/v1/tenants (3 tests)
|
|
- PUT /api/v1/tenants/:id (2 tests)
|
|
- GET /api/v1/tenants/me (1 test)
|
|
|
|
5. **Database + RLS (6 tests):**
|
|
- Tenant isolation queries
|
|
- RLS policies enforcement
|
|
- Field-level security
|
|
|
|
#### E2E Tests
|
|
|
|
**Coverage:** Flujos críticos de usuario
|
|
|
|
**Herramienta:** Playwright
|
|
|
|
**Total estimado:** 16 E2E tests
|
|
|
|
**User journeys:**
|
|
|
|
1. **Authentication Flow (6 tests):**
|
|
- Login exitoso y redirect a dashboard
|
|
- Login con credenciales inválidas
|
|
- Logout y redirect a login page
|
|
- Reset password flow completo
|
|
- Cuenta bloqueada por intentos fallidos
|
|
- Tenant isolation (user de tenant A no accede a tenant B)
|
|
|
|
2. **User Management Flow (4 tests):**
|
|
- Admin crea usuario, asigna rol, usuario puede login
|
|
- Usuario cambia su propia contraseña
|
|
- Admin desactiva usuario, usuario no puede login
|
|
- Admin elimina usuario
|
|
|
|
3. **Role Management Flow (3 tests):**
|
|
- Admin crea rol con permisos, asigna a usuario, usuario accede solo a recursos permitidos
|
|
- Admin revoca permiso, usuario pierde acceso
|
|
- Usuario sin permiso recibe 403
|
|
|
|
4. **Multi-Tenancy Flow (3 tests):**
|
|
- Usuario accede a tenant A, crea registro, cambia a tenant B, no ve registro de tenant A
|
|
- Signup crea usuario en tenant correcto según subdomain
|
|
- Tenant context switch funciona correctamente
|
|
|
|
---
|
|
|
|
## 4. TEST CASES
|
|
|
|
### 4.1 Casos de Prueba Funcionales
|
|
|
|
#### TC-MGN-001-001: Login Exitoso con Credenciales Válidas
|
|
**US:** US-MGN-001-001-001
|
|
**Prioridad:** P0
|
|
**Tipo:** Functional
|
|
**Nivel:** E2E
|
|
|
|
**Precondiciones:**
|
|
- Tenant "acme-corp" existe y está activo
|
|
- Usuario "admin@acme.com" existe con password "Admin123!" y está activo
|
|
|
|
**Pasos:**
|
|
1. Navegar a https://acme-corp.erp.local/login
|
|
2. Ingresar email: "admin@acme.com"
|
|
3. Ingresar password: "Admin123!"
|
|
4. Click en botón "Iniciar Sesión"
|
|
|
|
**Resultado Esperado:**
|
|
- JWT token es retornado y almacenado en localStorage
|
|
- Usuario es redirigido a /dashboard
|
|
- failed_login_attempts se resetea a 0
|
|
- Registro se crea en auth.login_history con success=true
|
|
|
|
**Criterios de Aceptación Validados:**
|
|
- AC-001: Usuario con credenciales válidas puede autenticarse
|
|
- AC-002: JWT token contiene user_id, tenant_id, roles, permissions
|
|
|
|
---
|
|
|
|
#### TC-MGN-001-002: Login Fallido por Contraseña Incorrecta
|
|
**US:** US-MGN-001-001-001
|
|
**Prioridad:** P0
|
|
**Tipo:** Functional
|
|
**Nivel:** Integration
|
|
|
|
**Precondiciones:**
|
|
- Usuario "admin@acme.com" existe
|
|
|
|
**Pasos:**
|
|
1. POST /api/v1/auth/login con body: { email: "admin@acme.com", password: "WrongPassword" }
|
|
|
|
**Resultado Esperado:**
|
|
- Status code: 401 Unauthorized
|
|
- Error message: "Credenciales inválidas"
|
|
- failed_login_attempts incrementa en 1
|
|
- Registro en auth.login_history con success=false
|
|
|
|
**Criterios de Aceptación Validados:**
|
|
- AC-003: Sistema incrementa contador de intentos fallidos
|
|
|
|
---
|
|
|
|
#### TC-MGN-001-003: Cuenta Bloqueada por Intentos Fallidos
|
|
**US:** US-MGN-001-001-001
|
|
**Prioridad:** P0
|
|
**Tipo:** Functional
|
|
**Nivel:** Integration
|
|
|
|
**Precondiciones:**
|
|
- Usuario "admin@acme.com" tiene failed_login_attempts=5 y last_failed_login < 30 minutos
|
|
|
|
**Pasos:**
|
|
1. POST /api/v1/auth/login con credenciales correctas
|
|
|
|
**Resultado Esperado:**
|
|
- Status code: 429 Too Many Requests
|
|
- Error message: "Cuenta bloqueada temporalmente. Intente en X minutos"
|
|
- No valida contraseña (retorna inmediatamente)
|
|
|
|
**Criterios de Aceptación Validados:**
|
|
- AC-004: Sistema bloquea cuenta después de 5 intentos fallidos por 30 minutos
|
|
|
|
---
|
|
|
|
#### TC-MGN-001-004: Tenant Isolation (Usuario no Accede a Otro Tenant)
|
|
**US:** US-MGN-001-004-002
|
|
**Prioridad:** P0
|
|
**Tipo:** Security
|
|
**Nivel:** Integration
|
|
|
|
**Precondiciones:**
|
|
- Tenant A tiene usuario "user@tenant-a.com"
|
|
- Tenant B tiene 10 registros en tabla products
|
|
|
|
**Pasos:**
|
|
1. Login como "user@tenant-a.com" desde subdomain tenant-a
|
|
2. GET /api/v1/products (debería retornar productos de tenant A solamente)
|
|
3. Intentar GET /api/v1/products con header X-Tenant-ID: tenant-b
|
|
|
|
**Resultado Esperado:**
|
|
- GET /api/v1/products retorna solo productos de tenant A
|
|
- Intentar acceder con otro tenant_id resulta en 403 Forbidden o ignora el header
|
|
- Queries SQL automáticamente filtran por tenant_id
|
|
|
|
**Criterios de Aceptación Validados:**
|
|
- AC-005: Usuarios de diferentes tenants están completamente aislados
|
|
- AC-006: RLS policies previenen acceso cross-tenant
|
|
|
|
---
|
|
|
|
#### TC-MGN-001-005: Usuario Inactivo No Puede Login
|
|
**US:** US-MGN-001-001-001
|
|
**Prioridad:** P0
|
|
**Tipo:** Functional
|
|
**Nivel:** Integration
|
|
|
|
**Precondiciones:**
|
|
- Usuario "inactive@acme.com" tiene status='inactive'
|
|
|
|
**Pasos:**
|
|
1. POST /api/v1/auth/login con credenciales correctas de usuario inactivo
|
|
|
|
**Resultado Esperado:**
|
|
- Status code: 403 Forbidden
|
|
- Error message: "Cuenta inactiva. Contacte al administrador"
|
|
- No se genera JWT token
|
|
|
|
**Criterios de Aceptación Validados:**
|
|
- AC-007: Usuario inactivo no puede autenticarse
|
|
|
|
---
|
|
|
|
#### TC-MGN-001-006: Refresh Token Renueva JWT
|
|
**US:** US-MGN-001-001-002
|
|
**Prioridad:** P0
|
|
**Tipo:** Functional
|
|
**Nivel:** Integration
|
|
|
|
**Precondiciones:**
|
|
- Usuario autenticado con refresh token válido
|
|
|
|
**Pasos:**
|
|
1. POST /api/v1/auth/refresh con body: { refreshToken: "valid_refresh_token" }
|
|
|
|
**Resultado Esperado:**
|
|
- Status code: 200 OK
|
|
- Nuevo accessToken es retornado
|
|
- Nuevo refreshToken es retornado (rotation)
|
|
- Refresh token anterior es invalidado
|
|
|
|
**Criterios de Aceptación Validados:**
|
|
- AC-008: Refresh token permite renovar JWT sin re-autenticación
|
|
|
|
---
|
|
|
|
#### TC-MGN-001-007: Admin Crea Rol con Permisos
|
|
**US:** US-MGN-001-002-001
|
|
**Prioridad:** P0
|
|
**Tipo:** Functional
|
|
**Nivel:** E2E
|
|
|
|
**Precondiciones:**
|
|
- Usuario admin autenticado
|
|
|
|
**Pasos:**
|
|
1. Navegar a /roles
|
|
2. Click en "Crear Rol"
|
|
3. Ingresar nombre: "Vendedor"
|
|
4. Seleccionar permisos: products:read, sales:create, sales:read, sales:update
|
|
5. Click en "Guardar"
|
|
|
|
**Resultado Esperado:**
|
|
- Rol "Vendedor" se crea en auth.roles
|
|
- Permisos se guardan en auth.role_permissions
|
|
- Rol aparece en lista de roles
|
|
- Notificación de éxito se muestra
|
|
|
|
**Criterios de Aceptación Validados:**
|
|
- AC-009: Admin puede crear roles personalizados
|
|
- AC-010: Permisos CRUD se asignan por modelo
|
|
|
|
---
|
|
|
|
#### TC-MGN-001-008: Usuario Sin Permiso Recibe 403
|
|
**US:** US-MGN-001-002-003
|
|
**Prioridad:** P0
|
|
**Tipo:** Functional
|
|
**Nivel:** Integration
|
|
|
|
**Precondiciones:**
|
|
- Usuario "viewer@acme.com" tiene rol "Viewer" con solo permisos de lectura
|
|
|
|
**Pasos:**
|
|
1. Login como "viewer@acme.com"
|
|
2. POST /api/v1/products (intentar crear producto)
|
|
|
|
**Resultado Esperado:**
|
|
- Status code: 403 Forbidden
|
|
- Error message: "No tiene permiso para realizar esta acción"
|
|
- Producto no se crea
|
|
|
|
**Criterios de Aceptación Validados:**
|
|
- AC-011: Usuarios solo acceden a recursos según sus permisos
|
|
- AC-012: Guards validan permisos en runtime
|
|
|
|
---
|
|
|
|
#### TC-MGN-001-009: Reset Password Flow Completo
|
|
**US:** US-MGN-001-005-001
|
|
**Prioridad:** P0
|
|
**Tipo:** Functional
|
|
**Nivel:** E2E
|
|
|
|
**Precondiciones:**
|
|
- Usuario "user@acme.com" existe
|
|
- SMTP server configurado
|
|
|
|
**Pasos:**
|
|
1. Navegar a /login
|
|
2. Click en "¿Olvidaste tu contraseña?"
|
|
3. Ingresar email: "user@acme.com"
|
|
4. Click en "Enviar"
|
|
5. Abrir email recibido
|
|
6. Click en link de reset password
|
|
7. Ingresar nueva contraseña: "NewPass123!"
|
|
8. Confirmar contraseña: "NewPass123!"
|
|
9. Click en "Cambiar Contraseña"
|
|
10. Login con nueva contraseña
|
|
|
|
**Resultado Esperado:**
|
|
- Email se envía a user@acme.com con token de reset
|
|
- Link de reset es válido por 1 hora
|
|
- Password se actualiza en BD (hasheado)
|
|
- Login con nueva contraseña es exitoso
|
|
- Token de reset se invalida después de uso
|
|
|
|
**Criterios de Aceptación Validados:**
|
|
- AC-013: Usuario puede resetear contraseña olvidada vía email
|
|
- AC-014: Token de reset expira en 1 hora
|
|
- AC-015: Token solo puede usarse una vez
|
|
|
|
---
|
|
|
|
#### TC-MGN-001-010: Signup Crea Usuario en Tenant Correcto
|
|
**US:** US-MGN-001-006-001
|
|
**Prioridad:** P1
|
|
**Tipo:** Functional
|
|
**Nivel:** E2E
|
|
|
|
**Precondiciones:**
|
|
- Tenant "acme-corp" permite signup (allow_signup=true)
|
|
|
|
**Pasos:**
|
|
1. Navegar a https://acme-corp.erp.local/signup
|
|
2. Ingresar email: "newuser@example.com"
|
|
3. Ingresar password: "Secure123!"
|
|
4. Ingresar nombre: "Juan Pérez"
|
|
5. Click en "Registrarse"
|
|
|
|
**Resultado Esperado:**
|
|
- Usuario se crea en tenant "acme-corp"
|
|
- Email de verificación se envía
|
|
- Usuario puede login después de verificar email
|
|
- Usuario se crea con rol "User" por defecto
|
|
|
|
**Criterios de Aceptación Validados:**
|
|
- AC-016: Signup crea usuario en tenant correcto según subdomain
|
|
- AC-017: Email de verificación se envía
|
|
- AC-018: Usuario tiene rol "User" por defecto
|
|
|
|
---
|
|
|
|
### 4.2 Casos de Prueba No Funcionales
|
|
|
|
#### TC-MGN-001-PERF-001: Performance del Endpoint POST /auth/login
|
|
**Tipo:** Performance
|
|
**Herramienta:** k6
|
|
|
|
**Objetivo:** Response time <300ms (p95)
|
|
|
|
**Escenario:**
|
|
- 100 usuarios concurrentes
|
|
- 1000 login requests/min
|
|
- Duración: 5 minutos
|
|
- Mix: 90% credenciales válidas, 10% inválidas
|
|
|
|
**Script k6:**
|
|
```javascript
|
|
import http from 'k6/http';
|
|
import { check } from 'k6';
|
|
|
|
export let options = {
|
|
vus: 100,
|
|
duration: '5m',
|
|
thresholds: {
|
|
http_req_duration: ['p(95)<300'],
|
|
http_req_failed: ['rate<0.01'],
|
|
},
|
|
};
|
|
|
|
export default function () {
|
|
const payload = JSON.stringify({
|
|
email: 'admin@acme.com',
|
|
password: 'Admin123!',
|
|
});
|
|
|
|
const res = http.post('https://api.erp.local/v1/auth/login', payload, {
|
|
headers: { 'Content-Type': 'application/json' },
|
|
});
|
|
|
|
check(res, {
|
|
'status is 200': (r) => r.status === 200,
|
|
'has token': (r) => JSON.parse(r.body).accessToken !== undefined,
|
|
});
|
|
}
|
|
```
|
|
|
|
**Criterios de Éxito:**
|
|
- p50: <100ms
|
|
- p95: <300ms
|
|
- p99: <500ms
|
|
- Error rate: <1%
|
|
- Throughput: >1000 req/min
|
|
|
|
---
|
|
|
|
#### TC-MGN-001-SEC-001: JWT Token Security Validation
|
|
**Tipo:** Security
|
|
**Herramienta:** Manual + Jest
|
|
|
|
**Objetivo:** Validar seguridad de JWT tokens
|
|
|
|
**Test cases:**
|
|
1. JWT firmado con algoritmo seguro (RS256 o HS256)
|
|
2. JWT secret tiene mínimo 256 bits de entropía
|
|
3. JWT tiene claim exp (expiration) configurado
|
|
4. JWT no contiene información sensible (password, etc.)
|
|
5. JWT no puede ser alterado sin invalidar firma
|
|
6. Refresh token es diferente de access token
|
|
7. Refresh token se almacena hasheado en BD
|
|
8. Refresh token rotation implementado
|
|
|
|
**Resultado Esperado:**
|
|
- Todos los test cases pasan
|
|
- Security scan (Snyk, OWASP ZAP) no reporta vulnerabilidades críticas en auth
|
|
|
|
---
|
|
|
|
#### TC-MGN-001-SEC-002: Protection Against Brute Force Attacks
|
|
**Tipo:** Security
|
|
**Herramienta:** Manual + k6
|
|
|
|
**Objetivo:** Validar rate limiting y bloqueo de cuenta
|
|
|
|
**Test cases:**
|
|
1. Más de 10 login attempts en 1 minuto desde misma IP resulta en 429 (rate limit)
|
|
2. 5 failed login attempts bloquea cuenta por 30 minutos
|
|
3. CAPTCHA aparece después de 3 failed attempts (Fase 2)
|
|
4. Logs de failed attempts se registran para detección de ataques
|
|
|
|
**Resultado Esperado:**
|
|
- Rate limiting funciona correctamente
|
|
- Cuenta se bloquea después de 5 intentos
|
|
- Logs permiten identificar patrones de ataque
|
|
|
|
---
|
|
|
|
#### TC-MGN-001-SEC-003: RLS Policy Enforcement
|
|
**Tipo:** Security
|
|
**Herramienta:** Integration test + Manual DB queries
|
|
|
|
**Objetivo:** Validar que RLS policies previenen acceso cross-tenant
|
|
|
|
**Test cases:**
|
|
1. SELECT sin filtro tenant_id retorna solo datos del tenant actual
|
|
2. INSERT sin tenant_id falla o usa tenant_id del contexto
|
|
3. UPDATE de registro de otro tenant falla
|
|
4. DELETE de registro de otro tenant falla
|
|
5. RLS bypass solo funciona con service role
|
|
|
|
**Script SQL:**
|
|
```sql
|
|
-- Como usuario de tenant A
|
|
SET app.tenant_id = 'tenant-a';
|
|
SELECT * FROM products; -- Solo productos de tenant A
|
|
|
|
-- Intentar acceder a tenant B
|
|
SET app.tenant_id = 'tenant-b';
|
|
SELECT * FROM products; -- Debería fallar o retornar vacío
|
|
```
|
|
|
|
**Resultado Esperado:**
|
|
- RLS policies previenen acceso cross-tenant en 100% de casos
|
|
- Service role puede bypass RLS para migraciones/backups
|
|
- Performance de queries con RLS es aceptable (<10% overhead)
|
|
|
|
---
|
|
|
|
## 5. DATOS DE PRUEBA
|
|
|
|
### 5.1 Test Data Requirements
|
|
|
|
**Tenants (3):**
|
|
```json
|
|
{
|
|
"tenant-a": {
|
|
"subdomain": "tenant-a",
|
|
"name": "Acme Corp",
|
|
"status": "active",
|
|
"plan": "enterprise",
|
|
"allow_signup": true
|
|
},
|
|
"tenant-b": {
|
|
"subdomain": "tenant-b",
|
|
"name": "Beta Inc",
|
|
"status": "active",
|
|
"plan": "pro",
|
|
"allow_signup": false
|
|
},
|
|
"tenant-c": {
|
|
"subdomain": "tenant-c",
|
|
"name": "Gamma LLC",
|
|
"status": "inactive",
|
|
"plan": "trial",
|
|
"allow_signup": false
|
|
}
|
|
}
|
|
```
|
|
|
|
**Usuarios por Tenant (10):**
|
|
```json
|
|
{
|
|
"admin@tenant-a.com": {
|
|
"password": "Admin123!",
|
|
"role": "admin",
|
|
"status": "active",
|
|
"tenant_id": "tenant-a"
|
|
},
|
|
"contador@tenant-a.com": {
|
|
"password": "Contador123!",
|
|
"role": "accountant",
|
|
"status": "active",
|
|
"tenant_id": "tenant-a"
|
|
},
|
|
"vendedor@tenant-a.com": {
|
|
"password": "Vendedor123!",
|
|
"role": "sales",
|
|
"status": "active",
|
|
"tenant_id": "tenant-a"
|
|
},
|
|
"viewer@tenant-a.com": {
|
|
"password": "Viewer123!",
|
|
"role": "viewer",
|
|
"status": "active",
|
|
"tenant_id": "tenant-a"
|
|
},
|
|
"inactive@tenant-a.com": {
|
|
"password": "Inactive123!",
|
|
"role": "user",
|
|
"status": "inactive",
|
|
"tenant_id": "tenant-a"
|
|
}
|
|
}
|
|
```
|
|
|
|
**Roles Base (5):**
|
|
- admin: Todos los permisos
|
|
- accountant: Permisos de módulo financiero
|
|
- sales: Permisos de ventas, productos, clientes
|
|
- user: Permisos básicos de lectura
|
|
- viewer: Solo lectura, sin escritura
|
|
|
|
**Permisos (ejemplos):**
|
|
- users:create, users:read, users:update, users:delete
|
|
- roles:create, roles:read, roles:update, roles:delete
|
|
- products:create, products:read, products:update, products:delete
|
|
|
|
### 5.2 Data Setup
|
|
|
|
**Script de seed:**
|
|
```bash
|
|
npm run seed:test
|
|
```
|
|
|
|
**Contenido de seed script:**
|
|
```typescript
|
|
// test/seeds/auth.seed.ts
|
|
export async function seedAuthData(prisma: PrismaClient) {
|
|
// 1. Crear tenants
|
|
const tenants = await createTenants(prisma);
|
|
|
|
// 2. Crear roles
|
|
const roles = await createRoles(prisma, tenants);
|
|
|
|
// 3. Crear permisos
|
|
const permissions = await createPermissions(prisma);
|
|
|
|
// 4. Asignar permisos a roles
|
|
await assignPermissionsToRoles(prisma, roles, permissions);
|
|
|
|
// 5. Crear usuarios
|
|
await createUsers(prisma, tenants, roles);
|
|
|
|
console.log('✅ Auth test data seeded successfully');
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 6. AMBIENTE DE TESTING
|
|
|
|
### 6.1 Configuración
|
|
|
|
**Base de datos:** PostgreSQL 16 (test DB)
|
|
- Database: `erp_test`
|
|
- Schema: `auth`, `public`
|
|
- Extensions: `pgcrypto`, `uuid-ossp`
|
|
- RLS enabled en todas las tablas
|
|
|
|
**Backend:** NestJS
|
|
- Port: 3000
|
|
- Mode: test
|
|
- JWT_SECRET: test_secret_key_256bits
|
|
- DATABASE_URL: postgresql://test:test@localhost:5432/erp_test
|
|
|
|
**Frontend:** React + Vite
|
|
- Port: 5173
|
|
- API_URL: http://localhost:3000/api/v1
|
|
- Mode: test
|
|
|
|
**Redis:** Session storage
|
|
- Port: 6379
|
|
- DB: 1 (test database)
|
|
|
|
**SMTP:** Mailhog (test email server)
|
|
- Port: 1025 (SMTP)
|
|
- Port: 8025 (Web UI)
|
|
|
|
### 6.2 URLs
|
|
|
|
- **API:** http://localhost:3000/api/v1
|
|
- **Frontend:** http://localhost:5173
|
|
- **Swagger:** http://localhost:3000/api/docs
|
|
- **Mailhog UI:** http://localhost:8025
|
|
|
|
### 6.3 Environment Variables
|
|
|
|
```env
|
|
# Database
|
|
DATABASE_URL=postgresql://test:test@localhost:5432/erp_test
|
|
|
|
# JWT
|
|
JWT_SECRET=test_secret_key_must_be_at_least_256_bits_long
|
|
JWT_EXPIRES_IN=8h
|
|
REFRESH_TOKEN_EXPIRES_IN=30d
|
|
|
|
# Redis
|
|
REDIS_URL=redis://localhost:6379/1
|
|
|
|
# Email
|
|
SMTP_HOST=localhost
|
|
SMTP_PORT=1025
|
|
SMTP_USER=test
|
|
SMTP_PASSWORD=test
|
|
|
|
# App
|
|
APP_ENV=test
|
|
APP_URL=http://localhost:5173
|
|
```
|
|
|
|
---
|
|
|
|
## 7. SCHEDULE
|
|
|
|
### 7.1 Timeline
|
|
|
|
| Sprint | Actividad | Duración | Responsable | Fecha Estimada |
|
|
|--------|-----------|----------|-------------|----------------|
|
|
| Sprint 1 | RF-001, RF-002 (Login, RBAC) | 2 sem | Dev + QA | Semana 2 |
|
|
| Sprint 2 | RF-003, RF-005, RF-007 (Users, Reset, Sessions) | 2 sem | Dev + QA | Semana 4 |
|
|
| Sprint 3 | RF-004 (Multi-tenancy) | 2 sem | Dev + QA | Semana 6 |
|
|
| Sprint 4 | RF-006 (Signup) | 2 sem | Dev + QA | Semana 8 |
|
|
| Sprint 5 | RF-008 (RLS) | 2 sem | Dev + QA | Semana 10 |
|
|
|
|
### 7.2 Actividades por Sprint
|
|
|
|
**Sprint 1 (Login + RBAC):**
|
|
- Día 1-3: Desarrollo de US-001-001-001, US-001-001-002, US-001-002-001, US-001-002-002
|
|
- Día 4-5: Code review + Integration tests
|
|
- Día 6-7: QA testing + E2E tests (TC-001 a TC-003)
|
|
- Día 8: Bug fixing
|
|
- Día 9: Regression testing
|
|
- Día 10: Sprint review + retrospective
|
|
|
|
**Sprint 2 (Users + Sessions):**
|
|
- Similar estructura, enfocado en US-001-003-001, US-001-003-002, US-001-005-001, etc.
|
|
|
|
---
|
|
|
|
## 8. ENTRY/EXIT CRITERIA
|
|
|
|
### Entry Criteria
|
|
|
|
- [ ] User Stories del sprint escritas y aprobadas (DoR)
|
|
- [ ] Test cases escritos en este Test Plan
|
|
- [ ] Ambiente de QA configurado (DB, Redis, SMTP)
|
|
- [ ] Test data seed scripts creados
|
|
- [ ] Código implementado y mergeado a develop
|
|
- [ ] Unit tests escritos y pasando (>80%)
|
|
- [ ] Integration tests escritos y pasando
|
|
- [ ] Code review completado
|
|
|
|
### Exit Criteria
|
|
|
|
- [ ] Todos los test cases ejecutados (160 tests)
|
|
- [ ] Unit test coverage >80% en módulo MGN-001
|
|
- [ ] Integration tests 100% pasando (48 tests)
|
|
- [ ] E2E tests críticos 100% pasando (16 tests)
|
|
- [ ] Bugs P0/P1 resueltos (100%)
|
|
- [ ] Bugs P2 documentados en backlog
|
|
- [ ] Performance tests pasando (p95 <300ms)
|
|
- [ ] Security tests pasando (JWT, RLS, rate limiting)
|
|
- [ ] Tenant isolation verificado (100% casos)
|
|
- [ ] Criterios de aceptación validados por PO
|
|
- [ ] Documentación Swagger actualizada
|
|
- [ ] Sprint review completado
|
|
|
|
---
|
|
|
|
## 9. DEFECT MANAGEMENT
|
|
|
|
### 9.1 Severidad de Bugs
|
|
|
|
**P0 - Blocker:**
|
|
- Sistema de autenticación no funciona (nadie puede login)
|
|
- Tenant isolation roto (usuarios ven datos de otros tenants)
|
|
- JWT tokens no se generan o son inválidos
|
|
- RLS policies no funcionan
|
|
- **SLA:** Fix en 24 horas
|
|
|
|
**P1 - Critical:**
|
|
- Refresh token no funciona (usuarios deben re-autenticarse cada 8h)
|
|
- Reset password no envía email
|
|
- Bloqueo de cuenta no funciona (vulnerability)
|
|
- Rate limiting no funciona
|
|
- **SLA:** Fix en 3 días
|
|
|
|
**P2 - Major:**
|
|
- UI de login tiene issues (pero login funciona)
|
|
- Mensajes de error no son claros
|
|
- Performance de login es lenta (>500ms)
|
|
- **SLA:** Fix en 1 sprint
|
|
|
|
**P3 - Minor:**
|
|
- Typos en mensajes
|
|
- Estilos CSS incorrectos
|
|
- Logs no tienen suficiente detalle
|
|
- **SLA:** Backlog (no bloqueante)
|
|
|
|
### 9.2 Bug Report Template
|
|
|
|
```markdown
|
|
**Bug ID:** BUG-MGN-001-001
|
|
**Title:** Login falla con credenciales válidas en tenant-b
|
|
**Severity:** P0
|
|
**Found in:** Sprint 1
|
|
**Environment:** QA
|
|
**Module:** MGN-001 - Fundamentos
|
|
|
|
**Steps to Reproduce:**
|
|
1. Navegar a https://tenant-b.erp.local/login
|
|
2. Ingresar email: "admin@tenant-b.com"
|
|
3. Ingresar password: "Admin123!"
|
|
4. Click en "Iniciar Sesión"
|
|
|
|
**Expected Result:**
|
|
- JWT token retornado
|
|
- Redirect a /dashboard
|
|
|
|
**Actual Result:**
|
|
- Error 500 Internal Server Error
|
|
- Log: "tenant_id not found in context"
|
|
|
|
**Screenshots/Logs:**
|
|
[Attach screenshot + server logs]
|
|
|
|
**Root Cause:**
|
|
Tenant context middleware no se ejecuta antes de AuthController
|
|
|
|
**Assigned to:** Developer 1
|
|
**Status:** In Progress
|
|
**Fixed in:** PR #123
|
|
**Verified by:** QA Engineer
|
|
**Closed date:** 2025-11-25
|
|
```
|
|
|
|
---
|
|
|
|
## 10. RIESGOS ESPECÍFICOS DEL MÓDULO
|
|
|
|
| Riesgo | Probabilidad | Impacto | Mitigación |
|
|
|--------|--------------|---------|------------|
|
|
| **Tenant isolation roto** | Media | Crítico | Test suite dedicada con 10+ test cases. Manual audit de RLS policies. Penetration testing. |
|
|
| **JWT secret expuesto en logs** | Baja | Crítico | Code review strict. Secrets en env vars, nunca en código. Logging sanitizado. |
|
|
| **Bcrypt cost factor muy alto** | Alta | Alto | Benchmarking de login performance. Cost factor 12 es óptimo (300-500ms). |
|
|
| **Refresh token leakage** | Media | Alto | Almacenar hasheado en BD. Rotation implementado. HttpOnly cookies. |
|
|
| **Rate limiting bypass** | Media | Alto | Rate limit por IP + por user_id. Redis cluster para HA. Monitoring de spikes. |
|
|
| **RLS policies tienen bugs** | Media | Crítico | Extensive testing con queries adversariales. DBA review. PostgreSQL audit logs. |
|
|
| **Password reset tokens no expiran** | Baja | Alto | Expiration en 1 hora implementado. Test case específico (TC-009). |
|
|
| **Session hijacking** | Media | Alto | JWT con short expiry (8h). Refresh token rotation. Device fingerprinting (Fase 2). |
|
|
|
|
---
|
|
|
|
## 11. MÉTRICAS
|
|
|
|
### 11.1 Test Execution Metrics
|
|
|
|
**Total test cases:** 160
|
|
- Unit: 96
|
|
- Integration: 48
|
|
- E2E: 16
|
|
|
|
**Executed:** 0/160 (0%)
|
|
**Passed:** 0/160 (0%)
|
|
**Failed:** 0/160 (0%)
|
|
**Blocked:** 0/160 (0%)
|
|
**Pass rate:** 0% (objetivo: >95%)
|
|
|
|
### 11.2 Defect Metrics
|
|
|
|
**Total bugs found:** 0
|
|
- P0: 0
|
|
- P1: 0
|
|
- P2: 0
|
|
- P3: 0
|
|
|
|
**Defect density:** 0 bugs/100 LOC (objetivo: <1)
|
|
|
|
### 11.3 Coverage Metrics
|
|
|
|
**Unit test coverage:** 0% (objetivo: >80%)
|
|
- Backend: 0%
|
|
- Frontend: 0%
|
|
|
|
**API coverage:** 0/20 endpoints (objetivo: 100%)
|
|
|
|
**E2E coverage:** 0/16 journeys (objetivo: 100%)
|
|
|
|
---
|
|
|
|
## 12. SIGN-OFF
|
|
|
|
### 12.1 Test Completion
|
|
|
|
- [ ] All planned tests executed (160/160)
|
|
- [ ] Exit criteria met
|
|
- [ ] Test report generated
|
|
- [ ] Defects documented
|
|
- [ ] Lessons learned captured
|
|
|
|
### 12.2 Approvals
|
|
|
|
**QA Engineer:**
|
|
Nombre: _______________
|
|
Firma: _______________
|
|
Fecha: _______________
|
|
|
|
**Tech Lead:**
|
|
Nombre: _______________
|
|
Firma: _______________
|
|
Fecha: _______________
|
|
|
|
**Product Owner:**
|
|
Nombre: _______________
|
|
Firma: _______________
|
|
Fecha: _______________
|
|
|
|
---
|
|
|
|
## 13. REFERENCIAS
|
|
|
|
**User Stories:**
|
|
- [US-MGN-001-001-001: Login con Email y Contraseña](../../03-user-stories/mgn-001/US-MGN-001-001-001-login-con-email-password.md)
|
|
- [US-MGN-001-001-002: Renovar Token JWT](../../03-user-stories/mgn-001/US-MGN-001-001-002-renovar-token-jwt.md)
|
|
- [US-MGN-001-002-001: Crear y Gestionar Roles](../../03-user-stories/mgn-001/US-MGN-001-002-001-crear-y-gestionar-roles.md)
|
|
- [Ver todas las US de MGN-001](../../03-user-stories/mgn-001/)
|
|
|
|
**Especificaciones Técnicas:**
|
|
- [ET Backend MGN-001](../../02-modelado/especificaciones-tecnicas/backend/mgn-001/)
|
|
- [ET Frontend MGN-001](../../02-modelado/especificaciones-tecnicas/frontend/mgn-001/)
|
|
|
|
**Referencias:**
|
|
- [RF MGN-001](../../02-modelado/requerimientos-funcionales/mgn-001/)
|
|
- [Traceability MGN-001](../../02-modelado/trazabilidad/TRACEABILITY-MGN-001.yaml)
|
|
- [Master Test Plan](./MASTER-TEST-PLAN.md)
|
|
|
|
---
|
|
|
|
**Versión:** 1.0
|
|
**Última actualización:** 2025-11-24
|
|
**Estado:** Draft - Pendiente de aprobación
|
|
**Próxima revisión:** Sprint 1 Kickoff
|