# Smoke Test — UDT-002: Logout + Refresh Token

**Branch**: feature/UDT-002  
**Fecha**: 2026-04-14  
**Prerequisito**: backend corriendo en `http://localhost:5212`, BD `SIGCM2` con migración V002 aplicada.

---

## Escenario 1 — Login y persistencia de tokens

- [ ] Abrir la app en `http://localhost:5173`
- [ ] Ingresar con credenciales válidas (admin / password)
- [ ] Verificar que el login redirige al home
- [ ] Abrir DevTools → Application → Local Storage → `auth-storage`
- [ ] Confirmar que el objeto contiene: `accessToken`, `refreshToken`, `expiresAt`, `user`
- [ ] Verificar que `expiresAt` es aproximadamente `Date.now() + 3600000` (1 hora)

---

## Escenario 2 — Refresh transparente en 401

**Opción A (esperar expiración natural — requiere token con TTL corto):**

- [ ] Modificar `Jwt:AccessTokenMinutes` a `1` en `appsettings.Development.json` y reiniciar el backend
- [ ] Hacer login
- [ ] Esperar 1 minuto para que el access token expire
- [ ] Realizar cualquier request autenticado (ej: navegar a una sección que llame a la API)
- [ ] Verificar que el request se completa sin error visible para el usuario
- [ ] Verificar en DevTools → Network que hubo una llamada a `POST /api/v1/auth/refresh` seguida del request original reenviado con un nuevo Bearer

**Opción B (manipulación manual del token):**

- [ ] Después del login, abrir DevTools → Application → Local Storage → `auth-storage`
- [ ] Editar el JSON y reemplazar `accessToken` con un valor inválido (ej: `"expired"`)
- [ ] Realizar cualquier request autenticado
- [ ] El interceptor de axiosClient recibe 401, llama a `/refresh` con el `refreshToken` real
- [ ] El request original se reintenta automáticamente con el nuevo `accessToken`
- [ ] El usuario no ve ningún error

---

## Escenario 3 — Refresh de 3 requests paralelos (singleton promise)

- [ ] Con el access token vencido (opción B del escenario 2)
- [ ] Abrir una página que dispare múltiples llamadas API simultáneas
- [ ] Verificar en DevTools → Network que hay exactamente **1** llamada a `POST /api/v1/auth/refresh`
- [ ] Verificar que todos los requests subsiguientes retornan con éxito

---

## Escenario 4 — Logout

- [ ] Con sesión activa, hacer click en el botón de logout
- [ ] Verificar que redirige a `/login`
- [ ] Verificar en DevTools → Network que se llamó a `POST /api/v1/auth/logout`
- [ ] Verificar en Local Storage que `auth-storage` tiene `user: null`, `accessToken: null`, `refreshToken: null`
- [ ] Intentar navegar a una ruta protegida — debería redirigir a login

---

## Escenario 5 — Reuso de refresh token después del logout (reuse detection)

- [ ] Hacer login y copiar el valor de `refreshToken` del Local Storage
- [ ] Hacer logout
- [ ] Intentar llamar manualmente al endpoint de refresh con el token anterior:
  ```bash
  curl -X POST http://localhost:5212/api/v1/auth/refresh \
    -H "Content-Type: application/json" \
    -d '{"accessToken": "<access-anterior>", "refreshToken": "<refresh-anterior>"}'
  ```
- [ ] Verificar que el backend responde `401` con `{ "error": "invalid_token" }`
- [ ] Verificar en la BD que todos los tokens de la familia fueron revocados:
  ```sql
  SELECT * FROM dbo.RefreshToken WHERE RevokedAt IS NOT NULL ORDER BY Id DESC;
  ```

---

## Escenario 6 — Refresh token expirado (7 días)

- [ ] Modificar `ExpiresAt` de un token en la BD `SIGCM2_Test` a una fecha pasada
- [ ] Intentar refresh con ese token — debería responder `401`
- [ ] Verificar que el frontend redirige a `/login` y limpia el Local Storage

---

## Escenario 7 — Refresh con access token de otro usuario (mismatch)

- [ ] Crear dos usuarios en la BD (o usar admin + otro)
- [ ] Hacer login con usuario A, guardar el `accessToken`
- [ ] Hacer login con usuario B, guardar el `refreshToken`
- [ ] Intentar refresh con accessToken de A + refreshToken de B:
  ```bash
  curl -X POST http://localhost:5212/api/v1/auth/refresh \
    -H "Content-Type: application/json" \
    -d '{"accessToken": "<access-usuario-A>", "refreshToken": "<refresh-usuario-B>"}'
  ```
- [ ] Verificar que el backend responde `401`

---

## Notas de verificación

| Check | Comando |
|-------|---------|
| Tokens en BD | `SELECT Id, UsuarioId, FamilyId, IssuedAt, ExpiresAt, RevokedAt FROM dbo.RefreshToken ORDER BY Id DESC` |
| Familias revocadas | `SELECT FamilyId, COUNT(*) as Total, SUM(CASE WHEN RevokedAt IS NOT NULL THEN 1 ELSE 0 END) as Revoked FROM dbo.RefreshToken GROUP BY FamilyId` |
| Usuario activo | `SELECT Id, Username, Activo FROM dbo.Usuario` |
