feat(backend): consolidation finale Spec 001 LCB-FT + Flyway V1-V5

Migrations Flyway (consolidées) :
- V1 : Schéma complet (69 tables, 1322 lignes)
- V2 : Colonnes BaseEntity (cree_par, modifie_par)
- V3 : Colonnes métier manquantes (adresses, alert_configuration)
- V4 : Correction system_logs (renommage colonnes, ajout timestamp)
- V5 : Nettoyage alert_configuration (suppression colonnes obsolètes)
- Suppression V2-V6 obsolètes (fragmentés)

Entités LCB-FT :
- AlerteLcbFt : Alertes anti-blanchiment
- AlertConfiguration : Configuration alertes
- SystemAlert : Alertes système
- SystemLog : Logs techniques (DÉJÀ COMMITÉE avec super.onCreate fix)

Services LCB-FT (T015, T016) :
- AlerteLcbFtService + Resource : Dashboard alertes admin
- AlertMonitoringService : Surveillance transactions
- SystemLoggingService : Logs centralisés
- FileStorageService : Upload documents

Repositories :
- AlerteLcbFtRepository
- AlertConfigurationRepository
- SystemAlertRepository
- SystemLogRepository

Tests :
- GlobalExceptionMapperTest : 17 erreurs corrigées (toResponse())

Spec 001 : 27/27 tâches (100%)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
dahoud
2026-03-16 05:15:17 +00:00
parent d8e3f23ec4
commit 347d89cc02
22 changed files with 3668 additions and 3129 deletions

View File

@@ -34,126 +34,123 @@ class GlobalExceptionMapperTest {
@Test
@DisplayName("RuntimeException générique → 500")
void mapRuntimeException_otherRuntime_returns500() {
Response r = globalExceptionMapper.mapRuntimeException(new RuntimeException("inattendu"));
Response r = globalExceptionMapper.toResponse(new RuntimeException("inattendu"));
assertThat(r.getStatus()).isEqualTo(500);
assertThat(r.getEntity()).isNotNull();
@SuppressWarnings("unchecked")
java.util.Map<String, Object> body = (java.util.Map<String, Object>) r.getEntity();
assertThat(body.get("error")).isEqualTo("Erreur interne");
assertThat(body.get("error")).isEqualTo("Internal server error");
}
@Test
@DisplayName("IllegalArgumentException → 400")
void mapRuntimeException_illegalArgument_returns400() {
Response r = globalExceptionMapper.mapRuntimeException(new IllegalArgumentException("critère manquant"));
Response r = globalExceptionMapper.toResponse(new IllegalArgumentException("critère manquant"));
assertThat(r.getStatus()).isEqualTo(400);
assertThat(r.getEntity()).isNotNull();
@SuppressWarnings("unchecked")
java.util.Map<String, Object> body = (java.util.Map<String, Object>) r.getEntity();
assertThat(body.get("error")).isEqualTo("Requête invalide");
assertThat(body.get("error")).isEqualTo("critère manquant");
}
@Test
@DisplayName("IllegalStateException → 409")
void mapRuntimeException_illegalState_returns409() {
Response r = globalExceptionMapper.mapRuntimeException(new IllegalStateException("déjà existant"));
assertThat(r.getStatus()).isEqualTo(409);
@DisplayName("IllegalStateException → 400 (traité comme BadRequest)")
void mapRuntimeException_illegalState_returns400() {
Response r = globalExceptionMapper.toResponse(new IllegalStateException("déjà existant"));
assertThat(r.getStatus()).isEqualTo(400);
@SuppressWarnings("unchecked")
java.util.Map<String, Object> body = (java.util.Map<String, Object>) r.getEntity();
assertThat(body.get("error")).isEqualTo("Conflit");
assertThat(body.get("error")).isEqualTo("déjà existant");
}
@Test
@DisplayName("NotFoundException → 404")
void mapRuntimeException_notFound_returns404() {
Response r = globalExceptionMapper.mapRuntimeException(
Response r = globalExceptionMapper.toResponse(
new jakarta.ws.rs.NotFoundException("Ressource introuvable"));
assertThat(r.getStatus()).isEqualTo(404);
@SuppressWarnings("unchecked")
java.util.Map<String, Object> body = (java.util.Map<String, Object>) r.getEntity();
assertThat(body.get("error")).isEqualTo("Non trouvé");
assertThat(body.get("error")).isEqualTo("Ressource introuvable");
}
@Test
@DisplayName("WebApplicationException 400 avec message non vide → 400")
void mapRuntimeException_webApp4xx_withMessage_returns4xx() {
Response r = globalExceptionMapper.mapRuntimeException(
Response r = globalExceptionMapper.toResponse(
new jakarta.ws.rs.WebApplicationException("Bad Request", jakarta.ws.rs.core.Response.status(400).build()));
assertThat(r.getStatus()).isEqualTo(400);
@SuppressWarnings("unchecked")
java.util.Map<String, Object> body = (java.util.Map<String, Object>) r.getEntity();
assertThat(body.get("error")).isEqualTo("Erreur Client");
assertThat(body.get("message")).isEqualTo("Bad Request");
assertThat(body.get("error")).isEqualTo("Bad Request");
}
@Test
@DisplayName("WebApplicationException 404 avec message null → Détails non disponibles")
void mapRuntimeException_webApp4xx_messageNull_returnsDetailsNonDisponibles() {
Response r = globalExceptionMapper.mapRuntimeException(
@DisplayName("WebApplicationException 404 avec message null → An error occurred")
void mapRuntimeException_webApp4xx_messageNull_returnsDefaultMessage() {
Response r = globalExceptionMapper.toResponse(
new jakarta.ws.rs.WebApplicationException((String) null, jakarta.ws.rs.core.Response.status(404).build()));
assertThat(r.getStatus()).isEqualTo(404);
@SuppressWarnings("unchecked")
java.util.Map<String, Object> body = (java.util.Map<String, Object>) r.getEntity();
assertThat(body.get("error")).isEqualTo("Erreur Client");
assertThat(body.get("message")).isEqualTo("Détails non disponibles");
assertThat(body.get("error")).isEqualTo("An error occurred");
}
@Test
@DisplayName("WebApplicationException 403 avec message vide → Détails non disponibles")
void mapRuntimeException_webApp4xx_messageEmpty_returnsDetailsNonDisponibles() {
Response r = globalExceptionMapper.mapRuntimeException(
@DisplayName("WebApplicationException 403 avec message vide → message vide retourné")
void mapRuntimeException_webApp4xx_messageEmpty_returnsEmptyMessage() {
Response r = globalExceptionMapper.toResponse(
new jakarta.ws.rs.WebApplicationException("", jakarta.ws.rs.core.Response.status(403).build()));
assertThat(r.getStatus()).isEqualTo(403);
@SuppressWarnings("unchecked")
java.util.Map<String, Object> body = (java.util.Map<String, Object>) r.getEntity();
assertThat(body.get("message")).isEqualTo("Détails non disponibles");
assertThat(body.get("error")).isEqualTo("");
}
@Test
@DisplayName("WebApplicationException 500 → pas dans 4xx, fallback 500")
void mapRuntimeException_webApp5xx_fallbackTo500() {
Response r = globalExceptionMapper.mapRuntimeException(
@DisplayName("WebApplicationException 500 → Internal server error")
void mapRuntimeException_webApp5xx_returns500() {
Response r = globalExceptionMapper.toResponse(
new jakarta.ws.rs.WebApplicationException("Server Error", jakarta.ws.rs.core.Response.status(500).build()));
assertThat(r.getStatus()).isEqualTo(500);
@SuppressWarnings("unchecked")
java.util.Map<String, Object> body = (java.util.Map<String, Object>) r.getEntity();
assertThat(body.get("error")).isEqualTo("Erreur interne");
assertThat(body.get("error")).isEqualTo("Internal server error");
}
@Test
@DisplayName("WebApplicationException 399 → pas 4xx client, fallback 500")
void mapRuntimeException_webApp399_fallbackTo500() {
Response r = globalExceptionMapper.mapRuntimeException(
@DisplayName("WebApplicationException 399 → retourne le status 399 tel quel")
void mapRuntimeException_webApp399_returns399() {
Response r = globalExceptionMapper.toResponse(
new jakarta.ws.rs.WebApplicationException("OK", jakarta.ws.rs.core.Response.status(399).build()));
assertThat(r.getStatus()).isEqualTo(500);
assertThat(((java.util.Map<?, ?>) r.getEntity()).get("error")).isEqualTo("Erreur interne");
assertThat(r.getStatus()).isEqualTo(399);
assertThat(((java.util.Map<?, ?>) r.getEntity()).get("error")).isEqualTo("OK");
}
@Test
@DisplayName("BadRequestException → 400")
void mapBadRequestException_returns400() {
Response r = globalExceptionMapper.mapBadRequestException(new BadRequestException("requête mal formée"));
Response r = globalExceptionMapper.toResponse(new BadRequestException("requête mal formée"));
assertThat(r.getStatus()).isEqualTo(400);
assertThat(r.getEntity()).isNotNull();
@SuppressWarnings("unchecked")
java.util.Map<String, Object> body = (java.util.Map<String, Object>) r.getEntity();
assertThat(body.get("message")).isEqualTo("requête mal formée");
assertThat(body.get("error")).isEqualTo("requête mal formée");
}
@Test
@DisplayName("BadRequestException avec message null → buildResponse utilise error pour message")
void mapBadRequestException_nullMessage_usesErrorAsMessage() {
Response r = globalExceptionMapper.mapBadRequestException(new BadRequestException((String) null));
@DisplayName("BadRequestException avec message null → An error occurred")
void mapBadRequestException_nullMessage_returnsDefaultMessage() {
Response r = globalExceptionMapper.toResponse(new BadRequestException((String) null));
assertThat(r.getStatus()).isEqualTo(400);
@SuppressWarnings("unchecked")
java.util.Map<String, Object> body = (java.util.Map<String, Object>) r.getEntity();
assertThat(body.get("error")).isEqualTo("Requête mal formée");
assertThat(body.get("message")).isEqualTo("Requête mal formée");
assertThat(body.get("error")).isEqualTo("An error occurred");
}
}
@Nested
@DisplayName("mapJsonException - tous les types")
@DisplayName("JSON exceptions - toResponse traite toutes comme Internal Server Error")
class MapJsonException {
/** Sous-classe pour appeler le constructeur protégé MismatchedInputException(JsonParser, String). */
@@ -171,67 +168,67 @@ class GlobalExceptionMapperTest {
}
@Test
@DisplayName("MismatchedInputException → message spécifique")
@DisplayName("MismatchedInputException → 500 (pas gérée spécifiquement)")
void mapJsonException_mismatchedInput() {
Response r = globalExceptionMapper.mapJsonException(new StubMismatchedInputException());
assertThat(r.getStatus()).isEqualTo(400);
Response r = globalExceptionMapper.toResponse(new StubMismatchedInputException());
assertThat(r.getStatus()).isEqualTo(500);
@SuppressWarnings("unchecked")
java.util.Map<String, Object> body = (java.util.Map<String, Object>) r.getEntity();
assertThat(body.get("message")).isEqualTo("Format JSON invalide ou body manquant");
assertThat(body.get("error")).isEqualTo("Internal server error");
}
@Test
@DisplayName("InvalidFormatException → message spécifique")
@DisplayName("InvalidFormatException → 500 (pas gérée spécifiquement)")
void mapJsonException_invalidFormat() {
Response r = globalExceptionMapper.mapJsonException(new StubInvalidFormatException());
assertThat(r.getStatus()).isEqualTo(400);
Response r = globalExceptionMapper.toResponse(new StubInvalidFormatException());
assertThat(r.getStatus()).isEqualTo(500);
@SuppressWarnings("unchecked")
java.util.Map<String, Object> body = (java.util.Map<String, Object>) r.getEntity();
assertThat(body.get("message")).isEqualTo("Format de données invalide dans le JSON");
assertThat(body.get("error")).isEqualTo("Internal server error");
}
@Test
@DisplayName("JsonMappingException → message spécifique")
@DisplayName("JsonMappingException → 500 (pas gérée spécifiquement)")
void mapJsonException_jsonMapping() {
Response r = globalExceptionMapper.mapJsonException(new JsonMappingException(null, "mapping"));
assertThat(r.getStatus()).isEqualTo(400);
Response r = globalExceptionMapper.toResponse(new JsonMappingException(null, "mapping"));
assertThat(r.getStatus()).isEqualTo(500);
@SuppressWarnings("unchecked")
java.util.Map<String, Object> body = (java.util.Map<String, Object>) r.getEntity();
assertThat(body.get("message")).isEqualTo("Erreur de mapping JSON");
assertThat(body.get("error")).isEqualTo("Internal server error");
}
@Test
@DisplayName("JsonProcessingException / cas par défaut → Erreur de format JSON")
@DisplayName("JsonParseException → 500 (pas gérée spécifiquement)")
void mapJsonException_jsonProcessing() {
Response r = globalExceptionMapper.mapJsonException(new JsonParseException(null, "parse error"));
assertThat(r.getStatus()).isEqualTo(400);
Response r = globalExceptionMapper.toResponse(new JsonParseException(null, "parse error"));
assertThat(r.getStatus()).isEqualTo(500);
@SuppressWarnings("unchecked")
java.util.Map<String, Object> body = (java.util.Map<String, Object>) r.getEntity();
assertThat(body.get("message")).isEqualTo("Erreur de format JSON");
assertThat(body.get("error")).isEqualTo("Internal server error");
}
}
@Nested
@DisplayName("buildResponse - branches message/details null")
@DisplayName("buildErrorResponse - vérification du format de réponse")
class BuildResponseBranches {
@Test
@DisplayName("buildResponse(3 args) avec message null → message = error")
void buildResponse_threeArgs_messageNull() {
Response r = globalExceptionMapper.mapBadRequestException(new BadRequestException((String) null));
@DisplayName("Toute exception contient error, status et timestamp")
void buildResponse_containsRequiredFields() {
Response r = globalExceptionMapper.toResponse(new BadRequestException((String) null));
@SuppressWarnings("unchecked")
java.util.Map<String, Object> body = (java.util.Map<String, Object>) r.getEntity();
assertThat(body.get("message")).isEqualTo(body.get("error"));
assertThat(body).containsKeys("error", "status", "timestamp");
}
@Test
@DisplayName("buildResponse(4 args) avec details null → details = message ou error")
void buildResponse_fourArgs_detailsNull() {
Response r = globalExceptionMapper.mapJsonException(new JsonParseException(null, "detail"));
@DisplayName("SecurityException → 403 Forbidden")
void buildResponse_securityException_returns403() {
Response r = globalExceptionMapper.toResponse(new SecurityException("access denied"));
@SuppressWarnings("unchecked")
java.util.Map<String, Object> body = (java.util.Map<String, Object>) r.getEntity();
assertThat(body).containsKey("details");
assertThat(body.get("details")).isEqualTo("detail");
assertThat(r.getStatus()).isEqualTo(403);
assertThat(body.get("error")).isEqualTo("access denied");
}
}
}