From 8b5bdc0043d2895787f7b36c613ff0d2ae23f2d1 Mon Sep 17 00:00:00 2001 From: Sebastiaan de Schaetzen Date: Fri, 27 Feb 2026 08:04:48 +0100 Subject: [PATCH] Add generic error page with stack trace Add a @ControllerAdvice that catches all exceptions and renders an error page showing status, error type, message, and full stack trace in a
 block. Uses table-based layout consistent with
the rest of the UI.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---
 .../webgit/controller/ErrorController.java    | 29 ++++++++++++++++
 src/main/resources/templates/error.html       | 27 +++++++++++++++
 .../webgit/controller/ErrorPageTest.java      | 33 +++++++++++++++++++
 3 files changed, 89 insertions(+)
 create mode 100644 src/main/java/be/seeseepuff/webgit/controller/ErrorController.java
 create mode 100644 src/main/resources/templates/error.html
 create mode 100644 src/test/java/be/seeseepuff/webgit/controller/ErrorPageTest.java

diff --git a/src/main/java/be/seeseepuff/webgit/controller/ErrorController.java b/src/main/java/be/seeseepuff/webgit/controller/ErrorController.java
new file mode 100644
index 0000000..31013bb
--- /dev/null
+++ b/src/main/java/be/seeseepuff/webgit/controller/ErrorController.java
@@ -0,0 +1,29 @@
+package be.seeseepuff.webgit.controller;
+
+import jakarta.servlet.http.HttpServletRequest;
+import org.springframework.http.HttpStatus;
+import org.springframework.web.bind.annotation.ControllerAdvice;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.servlet.ModelAndView;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+
+@ControllerAdvice
+public class ErrorController
+{
+	@ExceptionHandler(Exception.class)
+	public ModelAndView handleException(HttpServletRequest request, Exception ex)
+	{
+		StringWriter sw = new StringWriter();
+		ex.printStackTrace(new PrintWriter(sw));
+
+		ModelAndView mav = new ModelAndView("error");
+		mav.addObject("status", HttpStatus.INTERNAL_SERVER_ERROR.value());
+		mav.addObject("error", HttpStatus.INTERNAL_SERVER_ERROR.getReasonPhrase());
+		mav.addObject("message", ex.getMessage());
+		mav.addObject("trace", sw.toString());
+		mav.setStatus(HttpStatus.INTERNAL_SERVER_ERROR);
+		return mav;
+	}
+}
diff --git a/src/main/resources/templates/error.html b/src/main/resources/templates/error.html
new file mode 100644
index 0000000..382d2fa
--- /dev/null
+++ b/src/main/resources/templates/error.html
@@ -0,0 +1,27 @@
+
+
+
+    Error
+
+
+

Error

+ + + + + + + + + + + + + +
Status500
ErrorInternal Server Error
MessageSomething went wrong
+

Stack Trace

+
No stack trace available.
+
+Back to home + + diff --git a/src/test/java/be/seeseepuff/webgit/controller/ErrorPageTest.java b/src/test/java/be/seeseepuff/webgit/controller/ErrorPageTest.java new file mode 100644 index 0000000..e18b784 --- /dev/null +++ b/src/test/java/be/seeseepuff/webgit/controller/ErrorPageTest.java @@ -0,0 +1,33 @@ +package be.seeseepuff.webgit.controller; + +import be.seeseepuff.webgit.service.GitService; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.webmvc.test.autoconfigure.WebMvcTest; +import org.springframework.test.context.bean.override.mockito.MockitoBean; +import org.springframework.test.web.servlet.MockMvc; + +import static org.mockito.Mockito.doThrow; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + +@WebMvcTest +class ErrorPageTest +{ + @Autowired + private MockMvc mockMvc; + + @MockitoBean + private GitService gitService; + + @Test + void errorPageShowsStackTrace() throws Exception + { + doThrow(new RuntimeException("Something broke")).when(gitService).push("badrepo"); + + mockMvc.perform(post("/repo/badrepo/push")) + .andExpect(status().is5xxServerError()) + .andExpect(content().string(org.hamcrest.Matchers.containsString("Error"))) + .andExpect(content().string(org.hamcrest.Matchers.containsString("Stack Trace"))); + } +}