Test: cobertura del hallazgo #2 (P0 + P1) — spring-security-test, JwtRequestFilter y @WebMvcTest de los 23 controllers
Qué
Cierra el hallazgo #2 (Alto) de la auditoría de backend (2026-08-27):
antes de esta rama, controller tenía 0% de cobertura de tests y
security apenas 1%, pese a ser las capas que deciden si una request
con datos confidenciales de estudiantes pasa o no.
Esta MR cubre tanto el P0 (autorización real end-to-end) como el P1
(contrato HTTP) de ese hallazgo. Los 23 controllers del proyecto quedan
con al menos un test.
P0 — Autorización real (spring-security-test + JwtRequestFilter + comentarios confidenciales)
-
pom.xml: agregaspring-security-test. -
JwtRequestFilterTest(9 casos, 100% de cobertura de instrucciones y
ramas del filtro): tokens reales firmados con JJWT, no mockeados —
sin header, malformado, firma manipulada, vencido, usuario no
encontrado, credenciales no vigentes (password cambiada después de
emitido el token), válido, y el guard de "ya autenticado". -
ComentarioConfidencialEstudianteControllerTest/
ComentarioConfidencialInstanciaControllerTest: los dos endpoints
con datos más sensibles del sistema. Deliberadamente no usan
@WebMvcTest— en este proyecto@PreAuthorizevive en el service,
no en el controller, y@WebMvcTestreemplaza el service por un
mock que nunca ejecutaría esa autorización. Usan@SpringBootTest
con el contexto completo (service real, proxy de seguridad real) y
solo los repositorios mockeados, para que la request atraviese de
verdad:JwtRequestFilter→@PreAuthorizereal →
GlobalExceptionHandler. Cubren, por endpoint de listar/crear: sin
token (401), sin permiso (403), con permiso (200/201), y contenido
en blanco (400, Bean Validation real).
P1 — Contrato HTTP del resto de los controllers
Para los 20 controllers restantes, el objetivo no es la autorización
(ya cubierta en el P0) sino el contrato de entrada/salida: @Valid
→ 400, 404/409/422 propagados desde el service, forma del JSON de
respuesta. Por eso acá sí se usa @WebMvcTest clásico con el service
mockeado — es la herramienta correcta para esto.
AbstractControllerTest (clase base nueva) evita repetir en cada
uno de los 20 controllers el mismo boilerplate de infraestructura:
- Mocks de
CustomUsuarioDetailsServiceyJwtTokenUtil:
JwtRequestFilterse carga en cualquier@WebMvcTest(es un
Filter) y depende de ambos en su constructor. - Mock de
CacheManager:@EnableCachingvive enSienepApplication, la@SpringBootConfigurationque usa cualquier@WebMvcTestcomo
base, sin importar si el controller bajo test cachea algo o no. -
@WithMockUsera nivel de clase. - Un
ObjectMapperpropio con.findAndRegisterModules()— sin esto,
Jackson falla con "Java 8 date/time type not supported by default"
al (de)serializarLocalDate/LocalDateTime, muy comunes en los
DTOs de este proyecto.
Controllers cubiertos: Auditoria, Auth, Carrera, CategoriaInstancia,
CategoriaRecordatorio, ComentarioNormalEstudiante,
ComentarioNormalInstancia, Estudiante, Frecuencia, Funcionario, Grupo,
Incidencia, InformeAdjunto, InstanciaComun, Involucrado, Itr, Permiso,
Recordatorio, Reporte, Rol, Telefono.
Testing
- Suite completa: 695 tests, 0 failures (516 base + 21 del P0 + 158
nuevos del P1). - Cobertura JaCoCo:
-
JwtRequestFilter: 0% → 100% instrucciones y ramas. - Paquete
controllercompleto: 0% → 63% instrucciones, 44% ramas
— las 23 clases con al menos un test.
-
Qué queda del hallazgo #2 (fuera de esta MR)
- Ampliar cobertura a endpoints menos comunes por controller
(obtenerPorId/editar/eliminar donde solo se cubrió crear/listar). -
@DataJpaTestpara los repositories con@Querycustom de mayor
riesgo (la otra mitad del P1 de la sección 5 de la auditoría). - Test que fije cuál
UserDetailsServicese usa en cada flujo de
autenticación (warning de arranque "Found 2 UserDetailsService beans").