Skip to content

Commit e2c1c83

Browse files
committed
Add integration test for ErrorResponse, GlobalExceptionHandler, NotFoundException, Updates GlobalExceptionHandler to handel invalidParameterException
1 parent f8b0704 commit e2c1c83

File tree

5 files changed

+529
-0
lines changed

5 files changed

+529
-0
lines changed

src/main/java/no/entur/mummu/resources/GlobalExceptionHandler.java

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
import org.slf4j.LoggerFactory;
55
import org.springframework.http.HttpStatus;
66
import org.springframework.http.ResponseEntity;
7+
import org.springframework.validation.FieldError;
8+
import org.springframework.web.bind.MethodArgumentNotValidException;
79
import org.springframework.web.bind.annotation.ExceptionHandler;
810
import org.springframework.web.bind.annotation.RestControllerAdvice;
911
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
@@ -34,6 +36,23 @@ public ResponseEntity<ErrorResponse> handleTypeMismatch(MethodArgumentTypeMismat
3436
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(error);
3537
}
3638

39+
@ExceptionHandler(MethodArgumentNotValidException.class)
40+
public ResponseEntity<ErrorResponse> handleValidationException(MethodArgumentNotValidException ex) {
41+
FieldError fieldError = ex.getBindingResult().getFieldError();
42+
if (fieldError != null) {
43+
String parameterName = fieldError.getField();
44+
Object rejectedValue = fieldError.getRejectedValue();
45+
ErrorResponse error = new ErrorResponse(
46+
"INVALID_PARAMETER",
47+
String.format("Invalid value '%s' for parameter '%s'", rejectedValue, parameterName),
48+
Map.of("parameter", parameterName, "providedValue", String.valueOf(rejectedValue))
49+
);
50+
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(error);
51+
}
52+
ErrorResponse error = new ErrorResponse("INVALID_PARAMETER", "Validation failed");
53+
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(error);
54+
}
55+
3756
@ExceptionHandler(IllegalArgumentException.class)
3857
public ResponseEntity<ErrorResponse> handleIllegalArgument(IllegalArgumentException ex) {
3958
ErrorResponse error = new ErrorResponse("INVALID_PARAMETER", ex.getMessage());

src/test/java/no/entur/mummu/RestResourceIntegrationTest.java

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,81 @@ void testGetUnknownStopPlaceReturnsNotFound() throws Exception {
271271
.andExpect(status().isNotFound());
272272
}
273273

274+
@Test
275+
void testGetUnknownStopPlaceReturnsErrorResponse() throws Exception {
276+
mvc.perform(get("/stop-places/FOO:StopPlace:1234"))
277+
.andExpect(status().isNotFound())
278+
.andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON))
279+
.andExpect(jsonPath("$.errorCode").value("RESOURCE_NOT_FOUND"))
280+
.andExpect(jsonPath("$.message").exists());
281+
}
282+
283+
@Test
284+
void testGetUnknownQuayReturnsErrorResponse() throws Exception {
285+
mvc.perform(get("/quays/FOO:Quay:9999"))
286+
.andExpect(status().isNotFound())
287+
.andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON))
288+
.andExpect(jsonPath("$.errorCode").value("RESOURCE_NOT_FOUND"))
289+
.andExpect(jsonPath("$.message").exists());
290+
}
291+
292+
@Test
293+
void testGetUnknownGroupOfStopPlacesReturnsErrorResponse() throws Exception {
294+
mvc.perform(get("/groups-of-stop-places/FOO:GroupOfStopPlaces:9999"))
295+
.andExpect(status().isNotFound())
296+
.andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON))
297+
.andExpect(jsonPath("$.errorCode").value("RESOURCE_NOT_FOUND"))
298+
.andExpect(jsonPath("$.message").exists());
299+
}
300+
301+
@Test
302+
void testGetUnknownTopographicPlaceReturnsErrorResponse() throws Exception {
303+
mvc.perform(get("/topographic-places/FOO:TopographicPlace:9999"))
304+
.andExpect(status().isNotFound())
305+
.andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON))
306+
.andExpect(jsonPath("$.errorCode").value("RESOURCE_NOT_FOUND"))
307+
.andExpect(jsonPath("$.message").exists());
308+
}
309+
310+
@Test
311+
void testGetUnknownTariffZoneReturnsErrorResponse() throws Exception {
312+
mvc.perform(get("/tariff-zones/FOO:TariffZone:9999"))
313+
.andExpect(status().isNotFound())
314+
.andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON))
315+
.andExpect(jsonPath("$.errorCode").value("RESOURCE_NOT_FOUND"))
316+
.andExpect(jsonPath("$.message").exists());
317+
}
318+
319+
@Test
320+
void testGetUnknownParkingReturnsErrorResponse() throws Exception {
321+
mvc.perform(get("/parkings/FOO:Parking:9999"))
322+
.andExpect(status().isNotFound())
323+
.andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON))
324+
.andExpect(jsonPath("$.errorCode").value("RESOURCE_NOT_FOUND"))
325+
.andExpect(jsonPath("$.message").exists());
326+
}
327+
328+
@Test
329+
void testInvalidParameterTypeReturnsErrorResponse() throws Exception {
330+
mvc.perform(get("/stop-places?count=invalid"))
331+
.andExpect(status().isBadRequest())
332+
.andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON))
333+
.andExpect(jsonPath("$.errorCode").value("INVALID_PARAMETER"))
334+
.andExpect(jsonPath("$.message").exists())
335+
.andExpect(jsonPath("$.details.parameter").value("count"))
336+
.andExpect(jsonPath("$.details.providedValue").value("invalid"));
337+
}
338+
339+
@Test
340+
void testInvalidSkipParameterReturnsErrorResponse() throws Exception {
341+
mvc.perform(get("/stop-places?skip=notanumber"))
342+
.andExpect(status().isBadRequest())
343+
.andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON))
344+
.andExpect(jsonPath("$.errorCode").value("INVALID_PARAMETER"))
345+
.andExpect(jsonPath("$.message").exists())
346+
.andExpect(jsonPath("$.details.parameter").value("skip"));
347+
}
348+
274349
@Test
275350
void testXMLOutputCanBeMarshalled() throws Exception {
276351
ResultActions resultActions = mvc.perform(get("/stop-places/NSR:StopPlace:4004")
Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
package no.entur.mummu.resources;
2+
3+
import com.fasterxml.jackson.databind.ObjectMapper;
4+
import org.junit.jupiter.api.Test;
5+
6+
import java.util.Map;
7+
8+
import static org.junit.jupiter.api.Assertions.*;
9+
10+
class ErrorResponseTest {
11+
12+
private final ObjectMapper objectMapper = new ObjectMapper();
13+
14+
@Test
15+
void testDefaultConstructor() {
16+
// When
17+
ErrorResponse errorResponse = new ErrorResponse();
18+
19+
// Then
20+
assertNotNull(errorResponse);
21+
assertNull(errorResponse.getErrorCode());
22+
assertNull(errorResponse.getMessage());
23+
assertNull(errorResponse.getDetails());
24+
}
25+
26+
@Test
27+
void testConstructorWithCodeAndMessage() {
28+
// Given
29+
String errorCode = "TEST_ERROR";
30+
String message = "Test error message";
31+
32+
// When
33+
ErrorResponse errorResponse = new ErrorResponse(errorCode, message);
34+
35+
// Then
36+
assertNotNull(errorResponse);
37+
assertEquals(errorCode, errorResponse.getErrorCode());
38+
assertEquals(message, errorResponse.getMessage());
39+
assertNull(errorResponse.getDetails());
40+
}
41+
42+
@Test
43+
void testConstructorWithAllFields() {
44+
// Given
45+
String errorCode = "TEST_ERROR";
46+
String message = "Test error message";
47+
Map<String, Object> details = Map.of(
48+
"field", "testField",
49+
"value", "testValue",
50+
"count", 42
51+
);
52+
53+
// When
54+
ErrorResponse errorResponse = new ErrorResponse(errorCode, message, details);
55+
56+
// Then
57+
assertNotNull(errorResponse);
58+
assertEquals(errorCode, errorResponse.getErrorCode());
59+
assertEquals(message, errorResponse.getMessage());
60+
assertNotNull(errorResponse.getDetails());
61+
assertEquals(3, errorResponse.getDetails().size());
62+
assertEquals("testField", errorResponse.getDetails().get("field"));
63+
assertEquals("testValue", errorResponse.getDetails().get("value"));
64+
assertEquals(42, errorResponse.getDetails().get("count"));
65+
}
66+
67+
@Test
68+
void testSetters() {
69+
// Given
70+
ErrorResponse errorResponse = new ErrorResponse();
71+
String errorCode = "UPDATED_ERROR";
72+
String message = "Updated message";
73+
Map<String, Object> details = Map.of("key", "value");
74+
75+
// When
76+
errorResponse.setErrorCode(errorCode);
77+
errorResponse.setMessage(message);
78+
errorResponse.setDetails(details);
79+
80+
// Then
81+
assertEquals(errorCode, errorResponse.getErrorCode());
82+
assertEquals(message, errorResponse.getMessage());
83+
assertEquals(details, errorResponse.getDetails());
84+
}
85+
86+
@Test
87+
void testJsonSerialization_withoutDetails() throws Exception {
88+
// Given
89+
ErrorResponse errorResponse = new ErrorResponse("NOT_FOUND", "Resource not found");
90+
91+
// When
92+
String json = objectMapper.writeValueAsString(errorResponse);
93+
94+
// Then
95+
assertNotNull(json);
96+
assertTrue(json.contains("\"errorCode\":\"NOT_FOUND\""));
97+
assertTrue(json.contains("\"message\":\"Resource not found\""));
98+
assertFalse(json.contains("\"details\""));
99+
}
100+
101+
@Test
102+
void testJsonSerialization_withDetails() throws Exception {
103+
// Given
104+
Map<String, Object> details = Map.of("id", "123", "type", "StopPlace");
105+
ErrorResponse errorResponse = new ErrorResponse("NOT_FOUND", "Resource not found", details);
106+
107+
// When
108+
String json = objectMapper.writeValueAsString(errorResponse);
109+
110+
// Then
111+
assertNotNull(json);
112+
assertTrue(json.contains("\"errorCode\":\"NOT_FOUND\""));
113+
assertTrue(json.contains("\"message\":\"Resource not found\""));
114+
assertTrue(json.contains("\"details\""));
115+
assertTrue(json.contains("\"id\":\"123\""));
116+
assertTrue(json.contains("\"type\":\"StopPlace\""));
117+
}
118+
119+
@Test
120+
void testJsonDeserialization_withoutDetails() throws Exception {
121+
// Given
122+
String json = "{\"errorCode\":\"NOT_FOUND\",\"message\":\"Resource not found\"}";
123+
124+
// When
125+
ErrorResponse errorResponse = objectMapper.readValue(json, ErrorResponse.class);
126+
127+
// Then
128+
assertNotNull(errorResponse);
129+
assertEquals("NOT_FOUND", errorResponse.getErrorCode());
130+
assertEquals("Resource not found", errorResponse.getMessage());
131+
assertNull(errorResponse.getDetails());
132+
}
133+
134+
@Test
135+
void testJsonDeserialization_withDetails() throws Exception {
136+
// Given
137+
String json = "{\"errorCode\":\"INVALID_PARAMETER\",\"message\":\"Invalid value\",\"details\":{\"parameter\":\"count\",\"value\":\"abc\"}}";
138+
139+
// When
140+
ErrorResponse errorResponse = objectMapper.readValue(json, ErrorResponse.class);
141+
142+
// Then
143+
assertNotNull(errorResponse);
144+
assertEquals("INVALID_PARAMETER", errorResponse.getErrorCode());
145+
assertEquals("Invalid value", errorResponse.getMessage());
146+
assertNotNull(errorResponse.getDetails());
147+
assertEquals("count", errorResponse.getDetails().get("parameter"));
148+
assertEquals("abc", errorResponse.getDetails().get("value"));
149+
}
150+
151+
@Test
152+
void testJsonIncludeNonNull() throws Exception {
153+
// Given
154+
ErrorResponse errorResponse = new ErrorResponse();
155+
errorResponse.setErrorCode("ERROR");
156+
errorResponse.setMessage("Message");
157+
// details is null
158+
159+
// When
160+
String json = objectMapper.writeValueAsString(errorResponse);
161+
162+
// Then
163+
assertNotNull(json);
164+
assertFalse(json.contains("\"details\""), "Null details should not be included in JSON");
165+
}
166+
}
167+

0 commit comments

Comments
 (0)