Bucle de Verificación Quarkus
Ejecutar antes de PRs, después de cambios importantes y previo al despliegue.
Cuándo Activar
- Antes de abrir un pull request para un servicio Quarkus
- Después de una refactorización importante o actualización de dependencias
- Verificación previa al despliegue en staging o producción
- Ejecutar el pipeline completo: compilación → lint → prueba → escaneo de seguridad → compilación nativa
- Validar que la cobertura de pruebas cumpla con los umbrales (80%+)
- Probar la compatibilidad con imagen nativa
Fase 1: Compilación
# Maven
mvn clean verify -DskipTests
# Gradle
./gradlew clean assemble -x test
Si la compilación falla, detenerse y corregir los errores de compilación.
Fase 2: Análisis Estático
Checkstyle, PMD, SpotBugs (Maven)
mvn checkstyle:check pmd:check spotbugs:check
SonarQube (si está configurado)
mvn sonar:sonar \
-Dsonar.projectKey=my-quarkus-project \
-Dsonar.host.url=http://localhost:9000 \
-Dsonar.login=${SONAR_TOKEN}
Problemas Comunes a Abordar
- Importaciones o variables no utilizadas
- Métodos complejos (alta complejidad ciclomática)
- Posibles desreferencias de puntero nulo
- Problemas de seguridad señalados por SpotBugs
Fase 3: Pruebas + Cobertura
# Ejecutar todas las pruebas
mvn clean test
# Generar informe de cobertura
mvn jacoco:report
# Aplicar umbral de cobertura (80%)
mvn jacoco:check
# O con Gradle
./gradlew test jacocoTestReport jacocoTestCoverageVerification
Categorías de Pruebas
Pruebas Unitarias
Prueba la lógica del servicio con dependencias simuladas:
@ExtendWith(MockitoExtension.class)
class UserServiceTest {
@Mock UserRepository userRepository;
@InjectMocks UserService userService;
@Test
void createUser_validInput_returnsUser() {
var dto = new CreateUserDto("Alice", "[email protected]");
// Panache persist() es void — usar doNothing + verify
doNothing().when(userRepository).persist(any(User.class));
User result = userService.create(dto);
assertThat(result.name).isEqualTo("Alice");
verify(userRepository).persist(any(User.class));
}
}
Pruebas de Integración
Prueba con base de datos real (Testcontainers):
@QuarkusTest
@QuarkusTestResource(PostgresTestResource.class)
class UserRepositoryIntegrationTest {
@Inject
UserRepository userRepository;
@Test
@Transactional
void findByEmail_existingUser_returnsUser() {
User user = new User();
user.name = "Alice";
user.email = "[email protected]";
userRepository.persist(user);
Optional<User> found = userRepository.findByEmail("[email protected]");
assertThat(found).isPresent();
assertThat(found.get().name).isEqualTo("Alice");
}
}
Pruebas de API
Prueba los endpoints REST con REST Assured:
@QuarkusTest
class UserResourceTest {
@Test
void createUser_validInput_returns201() {
given()
.contentType(ContentType.JSON)
.body("""
{"name": "Alice", "email": "[email protected]"}
""")
.when().post("/api/users")
.then()
.statusCode(201)
.body("name", equalTo("Alice"));
}
@Test
void createUser_invalidEmail_returns400() {
given()
.contentType(ContentType.JSON)
.body("""
{"name": "Alice", "email": "invalid"}
""")
.when().post("/api/users")
.then()
.statusCode(400);
}
}
Informe de Cobertura
Consulte target/site/jacoco/index.html para obtener cobertura detallada:
- Cobertura de líneas general (objetivo: 80%+)
- Cobertura de ramas (objetivo: 70%+)
- Identificar rutas críticas no cubiertas
Fase 4: Escaneo de Seguridad
Vulnerabilidades de Dependencias (Maven)
mvn org.owasp:dependency-check-maven:check
Revise target/dependency-check-report.html para conocer los CVE.
Auditoría de Seguridad de Quarkus
# Verificar extensiones vulnerables
mvn quarkus:audit
# Listar todas las extensiones
mvn quarkus:list-extensions
OWASP ZAP (Pruebas de Seguridad de API)
docker run -t ghcr.io/zaproxy/zaproxy:stable zap-api-scan.py \
-t http://localhost:8080/q/openapi \
-f openapi
Verificaciones de Seguridad Comunes
- Todos los secretos en variables de entorno (no en el código)
- Validación de entrada en todos los endpoints
- Autenticación/autorización configurada
- CORS configurado correctamente
- Cabeceras de seguridad configuradas
- Contraseñas hasheadas con BCrypt
- Protección contra inyección SQL (consultas parametrizadas)
- Limitación de tasa en endpoints públicos
Fase 5: Compilación Nativa
Prueba la compatibilidad con la imagen nativa de GraalVM:
# Compilar ejecutable nativo
mvn package -Dnative
# O con contenedor
mvn package -Dnative -Dquarkus.native.container-build=true
# Probar ejecutable nativo
./target/*-runner
# Ejecutar pruebas de humo básicas
curl http://localhost:8080/q/health/live
curl http://localhost:8080/q/health/ready
Solución de Problemas de Imagen Nativa
Problemas comunes:
- Reflexión: Agregar configuración de reflexión para clases dinámicas
- Recursos: Incluir recursos con
quarkus.native.resources.includes - JNI: Registrar clases JNI si se utilizan bibliotecas nativas
Ejemplo de configuración de reflexión:
@RegisterForReflection(targets = {MyDynamicClass.class})
public class ReflectionConfiguration {}
Fase 6: Pruebas de Rendimiento
Pruebas de Carga con K6
// load-test.js
import http from 'k6/http';
import { check } from 'k6';
export const options = {
stages: [
{ duration: '30s', target: 50 },
{ duration: '1m', target: 100 },
{ duration: '30s', target: 0 },
],
};
export default function () {
const res = http.get('http://localhost:8080/api/markets');
check(res, {
'status is 200': (r) => r.status === 200,
'response time < 200ms': (r) => r.timings.duration < 200,
});
}
Ejecutar:
k6 run load-test.js
Métricas a Monitorear
- Tiempo de respuesta (p50, p95, p99)
- Rendimiento (solicitudes/segundo)
- Tasa de error
- Uso de memoria
- Uso de CPU
Fase 7: Verificaciones de Salud
# Liveness
curl http://localhost:8080/q/health/live
# Readiness
curl http://localhost:8080/q/health/ready
# Todas las verificaciones de salud
curl http://localhost:8080/q/health
# Métricas (si están habilitadas)
curl http://localhost:8080/q/metrics
Respuestas esperadas:
{
"status": "UP",
"checks": [
{
"name": "Conexión a base de datos",
"status": "UP"
}
]
}
Fase 8: Compilación de Imagen de Contenedor
# Compilar imagen de contenedor
mvn package -Dquarkus.container-image.build=true
# O con registro específico
mvn package \
-Dquarkus.container-image.build=true \
-Dquarkus.container-image.registry=docker.io \
-Dquarkus.container-image.group=myorg \
-Dquarkus.container-image.tag=1.0.0
# Probar contenedor
docker run -p 8080:8080 myorg/my-quarkus-app:1.0.0
Escaneo de Seguridad del Contenedor
# Trivy
trivy image myorg/my-quarkus-app:1.0.0
# Grype
grype myorg/my-quarkus-app:1.0.0
Fase 9: Validación de Configuración
# Verificar todas las propiedades de configuración
mvn quarkus:info
# Listar todas las fuentes de configuración
curl http://localhost:8080/q/dev/io.quarkus.quarkus-vertx-http/config
Verificaciones Específicas del Entorno
- URLs de base de datos configuradas por entorno
- Secretos externalizados (Vault, variables de entorno)
- Niveles de registro adecuados
- Orígenes CORS configurados correctamente
- Limitación de tasa configurada
- Monitoreo/rastreo habilitado
Fase 10: Revisión de Documentación
- Documentación OpenAPI/Swagger actualizada (
/q/swagger-ui) - README con instrucciones de configuración
- Cambios en la API documentados
- Guía de migración para cambios disruptivos
- Propiedades de configuración documentadas
Generar especificación OpenAPI:
curl http://localhost:8080/q/openapi -o openapi.json
Lista de Verificación
Calidad del Código
- La compilación pasa sin advertencias
- Análisis estático limpio (sin problemas altos/medios)
- El código sigue las convenciones del equipo
- Sin código comentado ni TODOs en el PR
Pruebas
- Todas las pruebas pasan
- Cobertura de código ≥ 80%
- Pruebas de integración con base de datos real
- Pruebas de seguridad pasan
- Rendimiento dentro de límites aceptables
Seguridad
- Sin vulnerabilidades de dependencias
- Autenticación/autorización probadas
- Validación de entrada completa
- Secretos no en código fuente
- Cabeceras de seguridad configuradas
Despliegue
- Compilación nativa exitosa
- La imagen de contenedor se compila
- Las verificaciones de salud responden correctamente
- Configuración válida para el entorno objetivo
Imagen Nativa
- El ejecutable nativo se compila
- Las pruebas nativas pasan
- Tiempo de inicio < 100 ms
- Huella de memoria aceptable
Script de Verificación Automatizada
#!/bin/bash
set -e
echo "=== Fase 1: Compilación ==="
mvn clean verify -DskipTests
echo "=== Fase 2: Análisis Estático ==="
mvn checkstyle:check pmd:check spotbugs:check
echo "=== Fase 3: Pruebas + Cobertura ==="
mvn test jacoco:report jacoco:check
echo "=== Fase 4: Escaneo de Seguridad ==="
mvn org.owasp:dependency-check-maven:check
echo "=== Fase 5: Compilación Nativa ==="
mvn package -Dnative -Dquarkus.native.container-build=true
echo "=== Todas las Fases Completadas ==="
echo "Revisar informes:"
echo " - Cobertura: target/site/jacoco/index.html"
echo " - Seguridad: target/dependency-check-report.html"
echo " - Nativo: target/*-runner"
Integración en CI/CD
Ejemplo de GitHub Actions
name: Verificación
on: [push, pull_request]
jobs:
verify:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- name: Configurar JDK 21
uses: actions/setup-java@v5
with:
java-version: '21'
distribution: 'temurin'
- name: Cachear paquetes Maven
uses: actions/cache@v6
with:
path: ~/.m2
key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }}
- name: Compilar
run: mvn clean verify -DskipTests
- name: Pruebas con Cobertura
run: mvn test jacoco:report jacoco:check
- name: Escaneo de Seguridad
run: mvn org.owasp:dependency-check-maven:check
- name: Subir Cobertura
uses: codecov/codecov-action@v7
with:
token: ${{ secrets.CODECOV_TOKEN }}
files: target/site/jacoco/jacoco.xml
Mejores Prácticas
- Ejecutar el bucle de verificación antes de cada PR
- Automatizar en el pipeline de CI/CD
- Corregir los problemas inmediatamente; no acumular deuda
- Mantener la cobertura por encima del 80%
- Actualizar las dependencias regularmente
- Probar la compilación nativa periódicamente
- Monitorear las tendencias de rendimiento
- Documentar los cambios disruptivos
- Revisar los resultados de los escaneos de seguridad
- Validar la configuración para cada entorno