Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package school.hei.vola.endpoint.rest.controller;

import jakarta.servlet.http.HttpServletResponse;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import school.hei.vola.endpoint.rest.security.UnauthorizedException;

@ControllerAdvice
public class ErrorHandler {

@ExceptionHandler(NotFoundException.class)
public String handleNotFoundException(Model model, HttpServletResponse response) {
model.addAttribute("status", HttpServletResponse.SC_NOT_FOUND);
model.addAttribute("error", "Not Found");
model.addAttribute("errorMessage", "La ressource demandée est introuvable");
response.setStatus(HttpServletResponse.SC_NOT_FOUND);
return "error";
}

@ExceptionHandler(UnauthorizedException.class)
public String handleUnauthorizedException(Model model, HttpServletResponse response) {
model.addAttribute("status", HttpServletResponse.SC_UNAUTHORIZED);
model.addAttribute("error", "Unauthorized");
model.addAttribute("errorMessage", "Accès non autorisé");
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
return "error";
}

@ExceptionHandler(Exception.class)
public String handleException(Exception ex, Model model, HttpServletResponse response) {
var message = ex.getMessage() != null ? ex.getMessage() : ex.getClass().getSimpleName();
model.addAttribute("errorMessage", message);
response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
return "error";
}
}
55 changes: 55 additions & 0 deletions src/main/resources/templates/error.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
<!doctype html>
<html
xmlns:th="http://www.thymeleaf.org"
xmlns:sec="http://www.thymeleaf.org/extras/spring-security"
>
<head>
<title>Vola - Erreur</title>
<meta charset="utf-8" />
<meta content="width=device-width, initial-scale=1.0" name="viewport" />
<link
rel="stylesheet"
href="https://fonts.googleapis.com/css2?family=Poppins:wght@400;600;700&display=swap"
/>
<link
rel="stylesheet"
href="https://fonts.googleapis.com/icon?family=Material+Icons"
/>
<script src="https://cdn.tailwindcss.com"></script>
<link rel="stylesheet" href="/style/history.css" />
</head>
<body class="min-h-screen bg-[var(--color-page-bg)] font-[Poppins] text-gray-700">

<div class="container mx-auto px-4 sm:px-6 pt-6 sm:pt-10 pb-10 sm:pb-14">
<div class="max-w-lg mx-auto">
<div class="bg-white rounded-xl shadow-[0_4px_16px_rgba(0,0,0,0.08)] border border-[var(--color-border)] p-8 sm:p-10 text-center">
<div class="inline-flex items-center justify-center w-16 h-16 sm:w-20 sm:h-20 bg-[#800000] bg-opacity-10 rounded-full mb-4">
<span class="material-icons text-[var(--color-primary)] text-4xl sm:text-5xl">error_outline</span>
</div>

<h2 class="text-3xl font-bold text-[var(--color-primary)] mb-2">Loza! Oops...</h2>

<div th:if="${status}" class="mb-4">
<span class="inline-block bg-[#800000] bg-opacity-10 text-[var(--color-primary-dark)] font-semibold text-sm px-4 py-1.5 rounded-full">
Erreur <span th:text="${status}"></span> - <span th:text="${error}"></span>
</span>
</div>

<div th:if="${message}" class="bg-gray-50 border border-gray-200 rounded-lg p-4 mb-6">
<p class="text-sm text-gray-600 break-words" th:text="${message}"></p>
</div>

<div th:if="${errorMessage}" class="bg-gray-50 border border-gray-200 rounded-lg p-4 mb-6">
<p class="text-sm text-gray-600 break-words" th:text="${errorMessage}"></p>
</div>

<a href="/"
class="inline-flex items-center gap-2 bg-[var(--color-primary)] hover:bg-[var(--color-primary-dark)] text-white font-semibold py-3 px-8 rounded-full transition-all transform hover:scale-[1.02] text-sm">
<span class="material-icons text-base">arrow_back</span>
<span>Retour à l'accueil</span>
</a>
</div>
</div>
</div>
</body>
</html>
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package school.hei.vola.endpoint.rest.controller;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.*;

import jakarta.servlet.http.HttpServletResponse;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.ui.Model;

class ErrorHandlerTest {

private ErrorHandler errorHandler;
private Model model;
private HttpServletResponse response;

@BeforeEach
void setUp() {
errorHandler = new ErrorHandler();
model = mock(Model.class);
response = mock(HttpServletResponse.class);
}

@Test
void handleException_setsStatus500_andErrorMessage() {
var exception = new RuntimeException("Test error");

var result = errorHandler.handleException(exception, model, response);

assertEquals("error", result);
verify(response).setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
verify(model).addAttribute("errorMessage", "Test error");
}

@Test
void handleException_withNullMessage_usesClassName() {
var exception = new RuntimeException((String) null);

var result = errorHandler.handleException(exception, model, response);

assertEquals("error", result);
verify(response).setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
verify(model).addAttribute("errorMessage", "RuntimeException");
}

@Test
void handleNotFoundException_setsStatus404() {
var result = errorHandler.handleNotFoundException(model, response);

assertEquals("error", result);
verify(response).setStatus(HttpServletResponse.SC_NOT_FOUND);
verify(model).addAttribute("status", HttpServletResponse.SC_NOT_FOUND);
verify(model).addAttribute("error", "Not Found");
verify(model).addAttribute("errorMessage", "La ressource demandée est introuvable");
}

@Test
void handleUnauthorizedException_setsStatus401() {
var result = errorHandler.handleUnauthorizedException(model, response);

assertEquals("error", result);
verify(response).setStatus(HttpServletResponse.SC_UNAUTHORIZED);
verify(model).addAttribute("status", HttpServletResponse.SC_UNAUTHORIZED);
verify(model).addAttribute("error", "Unauthorized");
verify(model).addAttribute("errorMessage", "Accès non autorisé");
}
}
Loading