24 KiB
RF-ADM-004: Auditoría Completa y Trazabilidad de Cambios
ID: RF-ADM-004 Módulo: MAI-013 - Administración & Seguridad Tipo: Requerimiento Funcional Prioridad: P0 (Crítica) Fecha de creación: 2025-11-20 Versión: 1.0
📋 Descripción
El sistema debe proporcionar auditoría completa y automática de todas las operaciones críticas, permitiendo:
- Registro automático de eventos de seguridad, cambios de datos y operaciones financieras
- Trazabilidad completa: Quién, qué, cuándo, dónde, por qué, cómo
- Consultas avanzadas con filtros múltiples (usuario, módulo, fecha, acción)
- Retención diferenciada: Logs operativos (90 días) vs críticos (5 años)
- Reportes de cumplimiento para auditorías internas/externas
- Alertas automáticas ante eventos sospechosos o críticos
La auditoría es fundamental para cumplimiento normativo (ISO 27001, SOC 2, LFPDPPP) y para investigación de incidentes de seguridad o errores operativos.
🎯 Objetivos
Objetivos de Negocio
- Cumplimiento normativo: ISO 27001, SOC 2, LFPDPPP, auditorías fiscales
- Investigación de incidentes: Rastrear errores, fraudes o accesos no autorizados
- Análisis forense: Reconstruir secuencia de eventos ante problemas
- Responsabilidad: Identificar quién realizó cada acción crítica
- Mejora de procesos: Analizar patrones de uso del sistema
Objetivos Técnicos
- Performance: Logging asíncrono (no bloquea operaciones)
- Integridad: Logs inmutables (append-only, no se pueden editar/borrar)
- Búsqueda rápida: Índices optimizados, queries < 500ms
- Almacenamiento eficiente: Compresión de logs antiguos, particionamiento
- Disponibilidad: Logs disponibles 24/7 para consulta
🔍 Categorías de Eventos Auditables
1. Autenticación y Autorización (Security Events)
| Evento | Severidad | Retención | Ejemplo |
|---|---|---|---|
| Login exitoso | Low | 1 año | Usuario juan@empresa.com login exitoso desde IP 192.168.1.10 |
| Login fallido | Medium | 1 año | Intento fallido para admin@empresa.com desde IP 203.0.113.5 |
| Bloqueo de cuenta | High | 5 años | Cuenta pedro@empresa.com bloqueada por 5 intentos fallidos |
| Cambio de contraseña | Medium | 5 años | Usuario maria@empresa.com cambió su contraseña |
| Cambio de rol | High | 5 años | Usuario carlos@empresa.com cambió de engineer a director |
| Acceso denegado | Medium | 90 días | Usuario jorge@empresa.com intentó acceder a /admin (403) |
| Logout | Low | 90 días | Usuario ana@empresa.com cerró sesión |
| Cambio de empresa | Low | 90 días | Usuario luis@empresa.com cambió de Empresa A a Empresa B |
2. Gestión de Usuarios (User Management Events)
| Evento | Severidad | Retención | Ejemplo |
|---|---|---|---|
| Creación de usuario | Medium | 5 años | Director creó usuario nuevo@empresa.com con rol engineer |
| Invitación enviada | Low | 1 año | Invitación enviada a invitado@empresa.com |
| Suspensión de usuario | High | 5 años | Director suspendió a empleado@empresa.com por 30 días |
| Reactivación | Medium | 5 años | Usuario empleado@empresa.com reactivado |
| Eliminación de usuario | Critical | 10 años | Super admin eliminó usuario antiguo@empresa.com |
| Modificación de permisos | High | 5 años | Director otorgó permiso budgets:approve a ingeniero@empresa.com |
3. Operaciones Críticas (Business Critical Events)
| Evento | Severidad | Retención | Condición | Ejemplo |
|---|---|---|---|---|
| Aprobación de estimación | Critical | 10 años | Monto > $50K | Director aprobó Estimación #125 por $2.5M |
| Aprobación de orden de compra | High | 5 años | Monto > $20K | Finanzas aprobó OC #452 por $150K |
| Modificación de presupuesto | Critical | 10 años | Cualquier cambio | Ingeniero modificó Presupuesto Obra A: $10M → $10.5M |
| Cierre de etapa | Critical | 10 años | Irreversible | Ingeniero cerró Etapa 1 de Obra Los Pinos |
| Eliminación de registro | High | 5 años | Soft delete | Admin eliminó Proyecto #45 |
| Cambio de estado de proyecto | Medium | 5 años | - | Director cambió Proyecto A de "Ejecución" a "Entregado" |
| Pago de estimación | Critical | 10 años | Monto > $10K | Finanzas registró pago de $1.2M para Estimación #130 |
| Modificación de contrato | High | 10 años | - | Ingeniero modificó monto de Contrato #22: $5M → $5.2M |
4. Administración del Sistema (System Administration Events)
| Evento | Severidad | Retención | Ejemplo |
|---|---|---|---|
| Cambio de configuración | High | 5 años | Admin cambió maxLoginAttempts de 5 a 3 |
| Creación de backup | Medium | 1 año | Sistema creó backup full backup-2025-11-20.tar.gz |
| Restauración de backup | Critical | 10 años | Admin restauró sistema desde backup del 2025-11-15 |
| Modificación de centro de costo | High | 5 años | Director creó centro 301 - Etapa 3 |
| Activación/desactivación de módulo | High | 5 años | Admin activó módulo INFONAVIT para Empresa A |
| Cambio de política de seguridad | Critical | 10 años | Admin cambió expiración de contraseñas: 90 → 60 días |
| Actualización de sistema | High | 5 años | Admin desplegó versión v2.1.5 → v2.2.0 |
📝 Modelo de Datos de Auditoría
Estructura Completa de AuditLog
interface AuditLog {
// Identificación
id: string; // UUID
timestamp: Date; // ISO 8601 con timezone
// Usuario (quien hizo la acción)
userId: string; // UUID
userName: string; // "Juan Pérez"
userEmail: string; // "juan@empresa.com"
userRole: ConstructionRole; // director, engineer, etc.
// Contexto empresarial
constructoraId: string; // UUID (multi-tenancy)
constructoraName: string; // "Constructora ABC"
projectId?: string; // Si la acción es sobre un proyecto específico
projectName?: string;
// Acción
action: AuditAction; // create, update, delete, approve, login, etc.
actionDescription: string; // "Aprobó estimación #125 por $2.5M"
module: string; // projects, budgets, estimations, auth, etc.
// Entidad afectada
entityType: string; // project, budget, estimation, user, etc.
entityId?: string; // UUID del registro afectado
entityName?: string; // "Proyecto Los Pinos"
// Cambios detallados (para updates)
changes?: AuditChange[];
// Contexto técnico
ipAddress: string; // "192.168.1.100"
userAgent: string; // "Mozilla/5.0 (Windows NT 10.0; Win64; x64)..."
sessionId: string; // UUID
requestId: string; // Para correlacionar requests relacionados
// Geolocalización (si disponible)
geolocation?: {
latitude: number;
longitude: number;
city?: string;
country?: string;
};
// Metadata adicional
severity: AuditSeverity; // low, medium, high, critical
category: AuditCategory; // authentication, user_management, business_critical, system_admin
success: boolean; // true = exitoso, false = fallido
errorMessage?: string; // Si success = false
duration?: number; // Milisegundos (para operaciones largas)
// Datos adicionales (JSON flexible)
metadata?: Record<string, any>;
// Control de retención
retentionPeriod: number; // Días (90, 365, 1825, 3650)
expiresAt: Date; // Fecha de eliminación automática
}
ENUMs
enum AuditAction {
// Autenticación
LOGIN = 'login',
LOGOUT = 'logout',
LOGIN_FAILED = 'login_failed',
PASSWORD_CHANGED = 'password_changed',
ACCOUNT_LOCKED = 'account_locked',
ACCOUNT_UNLOCKED = 'account_unlocked',
// CRUD
CREATE = 'create',
READ = 'read', // Solo para lecturas de datos sensibles
UPDATE = 'update',
DELETE = 'delete',
RESTORE = 'restore', // Restaurar soft delete
// Aprobaciones
APPROVE = 'approve',
REJECT = 'reject',
// Cambios de estado
STATUS_CHANGE = 'status_change',
// Administración
ROLE_CHANGE = 'role_change',
PERMISSION_GRANT = 'permission_grant',
PERMISSION_REVOKE = 'permission_revoke',
CONFIG_CHANGE = 'config_change',
BACKUP_CREATED = 'backup_created',
BACKUP_RESTORED = 'backup_restored',
// Accesos denegados
ACCESS_DENIED = 'access_denied',
}
enum AuditSeverity {
LOW = 'low', // Informativo (login exitoso, logout)
MEDIUM = 'medium', // Operaciones normales (crear proyecto, actualizar datos)
HIGH = 'high', // Operaciones críticas (aprobaciones >$20K, cambios de rol)
CRITICAL = 'critical' // Máxima criticidad (aprobaciones >$100K, eliminaciones, backups)
}
enum AuditCategory {
AUTHENTICATION = 'authentication',
USER_MANAGEMENT = 'user_management',
BUSINESS_CRITICAL = 'business_critical',
SYSTEM_ADMIN = 'system_admin'
}
Cambios Detallados
interface AuditChange {
field: string; // "totalAmount", "status", "role"
oldValue: any; // Valor anterior
newValue: any; // Valor nuevo
dataType: 'string' | 'number' | 'boolean' | 'date' | 'object' | 'array';
}
Ejemplo:
{
"id": "uuid-audit-123",
"timestamp": "2025-11-20T14:30:25.123Z",
"userId": "uuid-user-456",
"userName": "Juan Pérez",
"userEmail": "juan@empresa.com",
"userRole": "engineer",
"constructoraId": "uuid-empresa-a",
"constructoraName": "Constructora ABC",
"projectId": "uuid-proyecto-1",
"projectName": "Fraccionamiento Los Pinos",
"action": "update",
"actionDescription": "Modificó presupuesto maestro de Etapa 1",
"module": "budgets",
"entityType": "budget",
"entityId": "uuid-budget-789",
"entityName": "Presupuesto Etapa 1",
"changes": [
{
"field": "totalAmount",
"oldValue": 10000000,
"newValue": 10500000,
"dataType": "number"
},
{
"field": "notes",
"oldValue": "Presupuesto inicial",
"newValue": "Presupuesto ajustado por cambio de alcance",
"dataType": "string"
}
],
"ipAddress": "192.168.1.50",
"userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
"sessionId": "uuid-session-abc",
"requestId": "uuid-request-xyz",
"severity": "critical",
"category": "business_critical",
"success": true,
"retentionPeriod": 3650,
"expiresAt": "2035-11-20T14:30:25.123Z"
}
🔍 Consultas y Filtros de Auditoría
Filtros Disponibles
interface AuditLogFilters {
// Rango de fechas
startDate?: Date;
endDate?: Date;
// Usuario
userId?: string;
userEmail?: string;
userRole?: ConstructionRole;
// Acción
action?: AuditAction | AuditAction[];
module?: string | string[];
entityType?: string;
entityId?: string;
// Severidad y categoría
severity?: AuditSeverity | AuditSeverity[];
category?: AuditCategory | AuditCategory[];
// Éxito
success?: boolean; // true = solo exitosos, false = solo fallidos
// Búsqueda de texto libre
search?: string; // Busca en actionDescription, entityName, userName
// Proyecto/Empresa
constructoraId?: string;
projectId?: string;
// IP Address
ipAddress?: string;
// Paginación
page?: number;
limit?: number; // Max 100
sortBy?: 'timestamp' | 'severity';
sortOrder?: 'asc' | 'desc';
}
Queries Comunes
1. Todos los cambios a un proyecto específico
const logs = await getAuditLogs({
projectId: 'uuid-proyecto-1',
startDate: new Date('2025-11-01'),
endDate: new Date('2025-11-30'),
sortBy: 'timestamp',
sortOrder: 'desc'
});
2. Todas las aprobaciones financieras >$100K
const logs = await getAuditLogs({
action: 'approve',
module: ['estimations', 'purchases'],
severity: 'critical',
success: true
});
3. Intentos de login fallidos (últimas 24 horas)
const logs = await getAuditLogs({
action: 'login_failed',
startDate: subHours(new Date(), 24),
sortBy: 'timestamp',
sortOrder: 'desc'
});
4. Todas las acciones de un usuario específico
const logs = await getAuditLogs({
userEmail: 'juan@empresa.com',
startDate: new Date('2025-11-01'),
endDate: new Date('2025-11-30')
});
5. Eventos críticos (alertas de seguridad)
const logs = await getAuditLogs({
severity: 'critical',
category: ['authentication', 'user_management'],
success: false // Solo eventos fallidos
});
🔔 Alertas Automáticas
Reglas de Alertas
| Evento | Condición | Destinatarios | Canal |
|---|---|---|---|
| 5 logins fallidos en 10 min | Mismo usuario o IP | Admin de TI | Email + SMS |
| Acceso denegado repetido | >10 intentos en 1 hora | Admin de TI | |
| Aprobación >$100K | Cualquier estimación/OC | Director + CFO | |
| Cambio de rol a Director | Cualquier cambio | Super Admin | |
| Eliminación de registro | Cualquier entidad crítica | Admin | |
| Cambio de configuración | Sistema o seguridad | Admin de TI | |
| Backup fallido | Cualquier fallo | Admin de TI + Director | Email + SMS |
| Acceso fuera de horario | Login 22:00-06:00 | Admin de TI | Email (diario consolidado) |
Ejemplo de Alerta
Asunto: 🚨 Alerta de Seguridad: 5 Intentos de Login Fallidos
Estimado Administrador,
Se han detectado 5 intentos fallidos de login en los últimos 10 minutos:
Usuario: admin@empresa.com
IP: 203.0.113.45
Última intento: 2025-11-20 14:35:12
Ubicación: Culiacán, Sinaloa, México
Acciones tomadas:
- Cuenta bloqueada temporalmente (30 minutos)
- IP agregada a lista de vigilancia
Detalles completos:
https://app.ejemplo.com/admin/audit-logs?userId=uuid-123&action=login_failed
---
Sistema de Auditoría Automática
Constructora ABC
📊 Reportes de Auditoría
1. Reporte de Actividad de Usuario
Reporte de Actividad: juan@empresa.com
Periodo: 01-30 Noviembre 2025
┌─────────────────────────┬───────┬────────────────────────────┐
│ Acción │ Count │ Último evento │
├─────────────────────────┼───────┼────────────────────────────┤
│ Login │ 42 │ 2025-11-30 17:45 │
│ Logout │ 40 │ 2025-11-30 18:30 │
│ Crear proyecto │ 3 │ 2025-11-25 10:15 │
│ Actualizar presupuesto │ 18 │ 2025-11-29 14:20 │
│ Aprobar estimación │ 5 │ 2025-11-28 16:00 │
│ Modificar contrato │ 2 │ 2025-11-22 11:30 │
└─────────────────────────┴───────┴────────────────────────────┘
Eventos críticos: 5 aprobaciones de estimaciones (total $8.5M)
Eventos fallidos: 2 (acceso denegado a módulo Finanzas)
Proyectos modificados:
- Fraccionamiento Los Pinos (15 acciones)
- Torre Residencial Aura (8 acciones)
2. Reporte de Cumplimiento (Compliance)
Reporte de Auditoría para Cumplimiento ISO 27001
Constructora ABC S.A. de C.V.
Periodo: Q4 2025 (Oct-Nov-Dic)
1. AUTENTICACIÓN Y CONTROL DE ACCESO
- Total logins: 2,450 ✅
- Logins fallidos: 45 (1.8%) ✅
- Cuentas bloqueadas: 3 ✅
- Cambios de contraseña: 120 ✅
- Sesiones concurrentes promedio: 35 ✅
2. GESTIÓN DE USUARIOS
- Usuarios creados: 12 ✅
- Usuarios suspendidos: 2 ✅
- Usuarios eliminados: 0 ✅
- Cambios de rol: 5 (todos auditados) ✅
- Permisos modificados: 8 (todos auditados) ✅
3. OPERACIONES CRÍTICAS
- Aprobaciones financieras >$100K: 25 (todas auditadas) ✅
- Modificaciones de presupuestos: 45 (todas auditadas) ✅
- Eliminaciones de registros: 3 (todas auditadas) ✅
- Cambios de configuración: 2 (ambos auditados) ✅
4. RESPALDO Y RECUPERACIÓN
- Backups exitosos: 90/90 (100%) ✅
- Backups fallidos: 0 ✅
- Restauraciones: 0 ✅
5. INCIDENTES DE SEGURIDAD
- Accesos no autorizados: 0 ✅
- Brechas de datos: 0 ✅
- Alertas de seguridad: 15 (todas investigadas) ✅
CONCLUSIÓN: Sistema cumple 100% con requisitos ISO 27001
3. Timeline de Proyecto (Forense)
Timeline: Proyecto Fraccionamiento Los Pinos
Periodo: 01-30 Noviembre 2025
2025-11-01 09:00 | Director | Creó proyecto "Fraccionamiento Los Pinos"
2025-11-01 09:15 | Director | Asignó Ingeniero: Juan Pérez
2025-11-02 10:30 | Ingeniero | Creó presupuesto maestro: $50M
2025-11-05 14:20 | Ingeniero | Modificó presupuesto: $50M → $52M
2025-11-08 16:45 | Finanzas | Aprobó presupuesto ajustado
2025-11-10 11:00 | Compras | Creó orden de compra #1: $1.2M
2025-11-10 15:30 | Finanzas | Aprobó OC #1
2025-11-15 09:45 | Residente | Capturó avance Etapa 1: 15%
2025-11-20 10:00 | Ingeniero | Aprobó avance 15%
2025-11-25 14:00 | Finanzas | Creó estimación #1: $7.5M
2025-11-26 16:30 | Director | Aprobó estimación #1
2025-11-28 11:00 | Finanzas | Registró pago: $7.5M
2025-11-30 17:00 | Ingeniero | Cambió estado: "Ejecución" → "Entregado"
Total eventos: 145
Usuarios involucrados: 5 (Director, Ingeniero, Finanzas, Compras, Residente)
📋 Casos de Uso
Caso 1: Investigación de Error en Presupuesto
Contexto: Director detecta que presupuesto de Obra A cambió de $10M a $15M sin autorización.
Flujo:
- Director va a "Auditoría" → "Consultar Logs"
- Filtra:
- Entidad: Presupuesto Obra A
- Acción: Update
- Fecha: Últimos 30 días
- Sistema muestra:
2025-11-15 14:30 | Ingeniero Juan | UPDATE budget Campo: totalAmount Cambio: $10,000,000 → $15,000,000 IP: 192.168.1.50 Sesión: uuid-session-abc - Director click en evento → Ve detalles completos:
- Usuario: juan@empresa.com
- Razón: "Ajuste por cambio de alcance aprobado en minuta 25/10/2025"
- Archivos adjuntos: minuta-2025-10-25.pdf
- Director valida que cambio es legítimo (hay minuta)
- Cierra investigación
Resultado: Trazabilidad completa del cambio, investigación en 5 minutos.
Caso 2: Detección de Fraude (Intento)
Contexto: Alerta automática: "Usuario compras@empresa.com aprobó OC #455 de $500K fuera de horario (23:45)"
Flujo:
- Admin de TI recibe alerta por email a las 23:46
- Admin accede a sistema → "Auditoría" → Busca evento
- Ve detalles:
2025-11-20 23:45 | Comprador Pedro | APPROVE purchase_order OC #455: $500,000 (Proveedor: ABC Materiales S.A.) IP: 203.0.113.99 (externa, no es IP de oficina) Ubicación: Guadalajara, Jalisco (oficina está en Culiacán) - Admin llama a Pedro (Comprador):
- Pedro: "No estoy trabajando, estoy en casa"
- Admin: "¿Aprobaste OC #455?"
- Pedro: "No, yo no aprobé nada"
- Admin identifica: Sesión comprometida
- Acciones inmediatas:
- Bloquea cuenta de Pedro
- Revierte aprobación de OC #455
- Fuerza logout de todas las sesiones activas
- Cambia contraseña de Pedro
- Bloquea IP 203.0.113.99
- Contacta a Pedro para reactivar cuenta con nueva contraseña
Resultado: Fraude detectado y bloqueado en 15 minutos. Pérdida evitada: $500K.
Caso 3: Auditoría Externa (ISO 27001)
Contexto: Auditor externo solicita evidencia de controles de acceso.
Flujo:
- Auditor solicita: "Todos los cambios de rol en 2025"
- Admin va a "Reportes" → "Reporte de Auditoría de Cumplimiento"
- Selecciona:
- Tipo: Cambios de Rol
- Periodo: 2025-01-01 a 2025-12-31
- Sistema genera reporte PDF:
Reporte de Cambios de Rol - 2025 Constructora ABC S.A. de C.V. Total cambios: 12 # Fecha Usuario Rol Anterior Rol Nuevo Autorizado Por 1 2025-02-15 juan@empresa.com engineer director Super Admin 2 2025-03-20 maria@empresa.com resident engineer Director ... Conclusión: Todos los cambios fueron autorizados por Director o Super Admin. Cumplimiento: 100% - Admin exporta reporte y entrega a auditor
- Auditor valida que todos los cambios tienen autorización
Resultado: Auditoría externa aprobada en 1 día (vs 1 semana manual).
✅ Criterios de Aceptación
AC1: Registro Automático de Eventos
DADO un usuario que aprueba una estimación de $100K CUANDO completa la aprobación ENTONCES
- ✅ Sistema registra evento automáticamente (sin intervención del usuario)
- ✅ Log contiene: usuario, timestamp, acción, entidad, monto, IP, sesión
- ✅ Severidad =
critical(por monto >$100K) - ✅ Retención = 10 años
- ✅ Log es inmutable (no se puede editar ni borrar)
AC2: Consulta con Filtros Múltiples
DADO un admin que busca "aprobaciones financieras de Juan en noviembre" CUANDO aplica filtros:
{
userEmail: 'juan@empresa.com',
action: 'approve',
module: ['estimations', 'purchases'],
startDate: '2025-11-01',
endDate: '2025-11-30'
}
ENTONCES
- ✅ Sistema retorna solo registros que cumplen TODOS los filtros
- ✅ Resultados ordenados por timestamp desc (más reciente primero)
- ✅ Query ejecuta en < 500ms
- ✅ Paginación funciona correctamente
AC3: Alertas Automáticas
DADO 5 intentos de login fallidos en 10 minutos CUANDO ocurre el 5to intento ENTONCES
- ✅ Sistema envía alerta por email a Admin de TI
- ✅ Cuenta se bloquea automáticamente por 30 minutos
- ✅ IP se agrega a lista de vigilancia
- ✅ Alerta contiene: usuario, IP, ubicación, timestamp
AC4: Trazabilidad de Cambios
DADO un presupuesto que cambió de $10M a $15M CUANDO se consulta auditoría ENTONCES
- ✅ Log muestra cambio detallado:
{ "field": "totalAmount", "oldValue": 10000000, "newValue": 15000000 } - ✅ Log muestra quién lo cambió, cuándo, desde dónde
- ✅ Si hay justificación, se incluye en
metadata
AC5: Retención Diferenciada
DADO logs de diferentes severidades CUANDO se ejecuta proceso de limpieza automática ENTONCES
- ✅ Logs
lowseverity: eliminados tras 90 días - ✅ Logs
mediumseverity: eliminados tras 1 año - ✅ Logs
highseverity: eliminados tras 5 años - ✅ Logs
criticalseverity: eliminados tras 10 años - ✅ Proceso no elimina logs antes de
expiresAt
🧪 Escenarios de Prueba
Test 1: Logging Automático
describe('RF-ADM-004: Audit Logging', () => {
it('should automatically log critical operations', async () => {
const engineer = await loginAs('engineer');
// Aprobar estimación
const response = await api.patch('/estimations/123/approve', {}, {
headers: { Authorization: engineer.token }
});
expect(response.status).toBe(200);
// Verificar log
const logs = await getAuditLogs({
action: 'approve',
entityId: '123',
userId: engineer.id
});
expect(logs).toHaveLength(1);
expect(logs[0]).toMatchObject({
userId: engineer.id,
action: 'approve',
module: 'estimations',
entityId: '123',
severity: 'critical',
success: true
});
});
});
Test 2: Inmutabilidad de Logs
describe('RF-ADM-004: Log Immutability', () => {
it('should not allow editing or deleting logs', async () => {
const log = await createAuditLog({
action: 'login',
userId: 'user-123'
});
// Intentar modificar
const updateResponse = await api.patch(`/audit-logs/${log.id}`, {
action: 'logout'
});
expect(updateResponse.status).toBe(403);
expect(updateResponse.data.error).toBe('Audit logs are immutable');
// Intentar eliminar
const deleteResponse = await api.delete(`/audit-logs/${log.id}`);
expect(deleteResponse.status).toBe(403);
expect(deleteResponse.data.error).toBe('Audit logs cannot be deleted');
});
});
🔗 Referencias
- Especificación técnica: ET-ADM-003
- Historia de usuario: US-ADM-004
- RF relacionados: RF-ADM-001, RF-ADM-002
- Módulo: README.md
Generado: 2025-11-20 Versión: 1.0 Autor: Sistema de Documentación Técnica Estado: ✅ Completo