|
1 | 1 | package com.retailsvc.http; |
2 | 2 |
|
3 | 3 | import static org.assertj.core.api.Assertions.assertThat; |
4 | | -import static org.mockito.Mockito.mock; |
5 | 4 |
|
6 | 5 | import com.retailsvc.http.spec.HttpMethod; |
7 | 6 | import com.retailsvc.http.validate.ValidationError; |
8 | | -import com.sun.net.httpserver.Headers; |
9 | | -import com.sun.net.httpserver.HttpExchange; |
10 | | -import java.io.ByteArrayOutputStream; |
| 7 | +import java.nio.charset.StandardCharsets; |
| 8 | +import java.util.Map; |
11 | 9 | import java.util.Set; |
12 | 10 | import org.junit.jupiter.api.Test; |
13 | | -import org.mockito.Mockito; |
14 | 11 |
|
15 | 12 | class HandlersDefaultExceptionTest { |
16 | | - private HttpExchange newExchange(ByteArrayOutputStream sink) { |
17 | | - HttpExchange ex = mock(HttpExchange.class); |
18 | | - Mockito.when(ex.getResponseHeaders()).thenReturn(new Headers()); |
19 | | - Mockito.when(ex.getResponseBody()).thenReturn(sink); |
20 | | - return ex; |
| 13 | + |
| 14 | + private static final TypeMapper JSON = new GsonTypeMapper(); |
| 15 | + |
| 16 | + @Test |
| 17 | + void validationExceptionRendersProblemJson() { |
| 18 | + Response resp = |
| 19 | + Handlers.defaultExceptionHandler(JSON) |
| 20 | + .handle( |
| 21 | + new ValidationException( |
| 22 | + new ValidationError("/x", "type", "expected string", null))); |
| 23 | + |
| 24 | + assertThat(resp.status()).isEqualTo(400); |
| 25 | + assertThat(resp.contentType()).isEqualTo("application/problem+json"); |
| 26 | + byte[] bytes = (byte[]) resp.body(); |
| 27 | + String json = new String(bytes, StandardCharsets.UTF_8); |
| 28 | + @SuppressWarnings("unchecked") |
| 29 | + Map<String, Object> parsed = (Map<String, Object>) JSON.readFrom(bytes, "application/json"); |
| 30 | + assertThat(parsed).containsEntry("keyword", "type"); |
| 31 | + assertThat(((Number) parsed.get("status")).intValue()).isEqualTo(400); |
| 32 | + assertThat(json).contains("expected string"); |
21 | 33 | } |
22 | 34 |
|
23 | 35 | @Test |
24 | | - void validationExceptionRendersProblem() throws Exception { |
25 | | - ByteArrayOutputStream sink = new ByteArrayOutputStream(); |
26 | | - HttpExchange ex = newExchange(sink); |
27 | | - |
28 | | - Handlers.defaultExceptionHandler() |
29 | | - .handle( |
30 | | - ex, |
31 | | - new ValidationException(new ValidationError("/x", "type", "expected string", null))); |
32 | | - |
33 | | - Mockito.verify(ex).sendResponseHeaders(Mockito.eq(400), Mockito.anyLong()); |
34 | | - assertThat(ex.getResponseHeaders().getFirst("Content-Type")) |
35 | | - .isEqualTo("application/problem+json"); |
36 | | - assertThat(sink.toString()).contains("\"keyword\":\"type\""); |
| 36 | + void badRequestExceptionRendersProblemJsonWithCustomStatus() { |
| 37 | + Response resp = |
| 38 | + Handlers.defaultExceptionHandler(JSON) |
| 39 | + .handle(new BadRequestException(422, "email taken", "/email", "unique")); |
| 40 | + |
| 41 | + assertThat(resp.status()).isEqualTo(422); |
| 42 | + assertThat(resp.contentType()).isEqualTo("application/problem+json"); |
| 43 | + @SuppressWarnings("unchecked") |
| 44 | + Map<String, Object> parsed = |
| 45 | + (Map<String, Object>) JSON.readFrom((byte[]) resp.body(), "application/json"); |
| 46 | + assertThat(((Number) parsed.get("status")).intValue()).isEqualTo(422); |
| 47 | + assertThat(parsed) |
| 48 | + .containsEntry("title", "Unprocessable Content") |
| 49 | + .containsEntry("detail", "email taken") |
| 50 | + .containsEntry("pointer", "/email") |
| 51 | + .containsEntry("keyword", "unique"); |
37 | 52 | } |
38 | 53 |
|
39 | 54 | @Test |
40 | | - void notFoundReturns404() throws Exception { |
41 | | - HttpExchange ex = newExchange(new ByteArrayOutputStream()); |
42 | | - Handlers.defaultExceptionHandler().handle(ex, new NotFoundException("GET /x")); |
43 | | - Mockito.verify(ex).sendResponseHeaders(404, -1); |
| 55 | + void notFoundReturns404() { |
| 56 | + Response resp = Handlers.defaultExceptionHandler(JSON).handle(new NotFoundException("GET /x")); |
| 57 | + |
| 58 | + assertThat(resp.status()).isEqualTo(404); |
| 59 | + assertThat(resp.body()).isNull(); |
44 | 60 | } |
45 | 61 |
|
46 | 62 | @Test |
47 | | - void methodNotAllowedReturns405WithAllowHeader() throws Exception { |
48 | | - HttpExchange ex = newExchange(new ByteArrayOutputStream()); |
49 | | - Handlers.defaultExceptionHandler() |
50 | | - .handle(ex, new MethodNotAllowedException(Set.of(HttpMethod.GET, HttpMethod.POST))); |
51 | | - Mockito.verify(ex).sendResponseHeaders(405, -1); |
52 | | - assertThat(ex.getResponseHeaders().getFirst("Allow")).contains("GET").contains("POST"); |
| 63 | + void methodNotAllowedReturns405WithAllowHeader() { |
| 64 | + Response resp = |
| 65 | + Handlers.defaultExceptionHandler(JSON) |
| 66 | + .handle(new MethodNotAllowedException(Set.of(HttpMethod.GET, HttpMethod.POST))); |
| 67 | + |
| 68 | + assertThat(resp.status()).isEqualTo(405); |
| 69 | + assertThat(resp.headers()).containsKey("Allow"); |
| 70 | + assertThat(resp.headers().get("Allow")).contains("GET").contains("POST"); |
| 71 | + } |
| 72 | + |
| 73 | + @Test |
| 74 | + void unknownExceptionReturns500() { |
| 75 | + Response resp = Handlers.defaultExceptionHandler(JSON).handle(new RuntimeException("kaboom")); |
| 76 | + |
| 77 | + assertThat(resp.status()).isEqualTo(500); |
| 78 | + assertThat(resp.body()).isNull(); |
53 | 79 | } |
54 | 80 | } |
0 commit comments