View Javadoc
1   /*
2   * Copyright 2019-2026 the original author or authors.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package org.bremersee.exception;
18  
19  import static org.assertj.core.api.Assertions.assertThat;
20  
21  //import com.fasterxml.jackson.databind.ObjectMapper;
22  //import com.fasterxml.jackson.dataformat.xml.XmlMapper;
23  import java.nio.charset.Charset;
24  import java.nio.charset.StandardCharsets;
25  import java.time.OffsetDateTime;
26  import org.assertj.core.api.SoftAssertions;
27  import org.assertj.core.api.junit.jupiter.SoftAssertionsExtension;
28  import org.bremersee.exception.model.RestApiException;
29  import org.junit.jupiter.api.Test;
30  import org.junit.jupiter.api.extension.ExtendWith;
31  import org.springframework.http.HttpHeaders;
32  import org.springframework.http.HttpStatus;
33  import org.springframework.http.MediaType;
34  import tools.jackson.databind.json.JsonMapper;
35  import tools.jackson.dataformat.xml.XmlMapper;
36  //import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
37  
38  /**
39   * The rest api exception parser impl test.
40   *
41   * @author Christian Bremer
42   */
43  @ExtendWith(SoftAssertionsExtension.class)
44  class RestApiExceptionParserImplTest {
45  
46    private final RestApiExceptionParserImpl target = new RestApiExceptionParserImpl(
47        StandardCharsets.US_ASCII);
48  
49    private final RestApiExceptionParserImpl targetWithDefaults = new RestApiExceptionParserImpl();
50  
51    /**
52     * Gets default charset.
53     */
54    @Test
55    void getDefaultCharset() {
56      assertThat(target.getDefaultCharset())
57          .isEqualTo(StandardCharsets.US_ASCII);
58    }
59  
60    /**
61     * Gets object mapper.
62     *
63     * @param softly the softly
64     */
65    @Test
66    void getObjectMapper(SoftAssertions softly) {
67      softly.assertThat(targetWithDefaults.getObjectMapper(RestApiResponseType.JSON))
68          .isPresent()
69          .get()
70          .isInstanceOf(JsonMapper.class);
71      softly.assertThat(targetWithDefaults.getObjectMapper(RestApiResponseType.XML))
72          .isPresent()
73          .get()
74          .isInstanceOf(XmlMapper.class);
75      softly.assertThat(targetWithDefaults.getObjectMapper(RestApiResponseType.HEADER))
76          .isEmpty();
77    }
78  
79    /**
80     * Parse exception.
81     *
82     * @param softly the softly
83     * @throws Exception the exception
84     */
85    @Test
86    void parseException(SoftAssertions softly) throws Exception {
87      HttpStatus httpStatus = HttpStatus.FORBIDDEN;
88      HttpHeaders httpHeaders = new HttpHeaders();
89  
90      RestApiException actual = targetWithDefaults.parseException(
91          (byte[]) null, httpStatus, httpHeaders);
92      RestApiException expected = RestApiException.builder()
93          .status(HttpStatus.FORBIDDEN.value())
94          .error(HttpStatus.FORBIDDEN.getReasonPhrase())
95          .build();
96      softly.assertThat(actual)
97          .isEqualTo(expected);
98  
99      actual = targetWithDefaults.parseException(
100         "Something failed".getBytes(StandardCharsets.US_ASCII), httpStatus, httpHeaders);
101     expected = expected.toBuilder()
102         .message("Something failed")
103         .build();
104     softly.assertThat(actual)
105         .isEqualTo(expected);
106 
107     httpHeaders.add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);
108 
109     actual = targetWithDefaults.parseException(
110         "Something failed", httpStatus, httpHeaders);
111     softly.assertThat(actual)
112         .isEqualTo(expected);
113 
114     String response = JsonMapper.builder().build().writeValueAsString(expected);
115     actual = targetWithDefaults.parseException(
116         response, httpStatus, httpHeaders);
117     softly.assertThat(actual)
118         .isEqualTo(expected);
119   }
120 
121   /**
122    * Gets rest api exception from headers.
123    *
124    * @param softly the softly
125    */
126   @Test
127   void getRestApiExceptionFromHeaders(SoftAssertions softly) {
128     HttpStatus httpStatus = HttpStatus.FORBIDDEN;
129     HttpHeaders httpHeaders = new HttpHeaders();
130     httpHeaders.add(RestApiExceptionConstants.ID_HEADER_NAME, "1234");
131     httpHeaders.add(
132         RestApiExceptionConstants.TIMESTAMP_HEADER_NAME,
133         OffsetDateTime.parse("2022-02-28T02:31:22Z")
134             .format(RestApiExceptionConstants.TIMESTAMP_FORMATTER));
135     httpHeaders.add(RestApiExceptionConstants.CODE_HEADER_NAME, "5005");
136     httpHeaders.add(RestApiExceptionConstants.CODE_INHERITED_HEADER_NAME, "true");
137     httpHeaders.add(RestApiExceptionConstants.EXCEPTION_HEADER_NAME, "exc");
138     httpHeaders.add(RestApiExceptionConstants.APPLICATION_HEADER_NAME, "app");
139     httpHeaders.add(RestApiExceptionConstants.PATH_HEADER_NAME, "/path");
140 
141     RestApiException actual = target.getRestApiExceptionFromHeaders(
142         "no message in headers",
143         httpStatus,
144         httpHeaders);
145 
146     RestApiException expected = RestApiException.builder()
147         .status(HttpStatus.FORBIDDEN.value())
148         .error(HttpStatus.FORBIDDEN.getReasonPhrase())
149         .id("1234")
150         .timestamp(OffsetDateTime.parse("2022-02-28T02:31:22Z"))
151         .errorCode("5005")
152         .errorCodeInherited(true)
153         .exception("exc")
154         .application("app")
155         .path("/path")
156         .message("no message in headers")
157         .build();
158 
159     softly.assertThat(actual)
160         .isEqualTo(expected);
161 
162     httpHeaders.add(RestApiExceptionConstants.MESSAGE_HEADER_NAME, "given");
163     actual = target.getRestApiExceptionFromHeaders(
164         "no message in headers",
165         httpStatus,
166         httpHeaders);
167     expected = expected.toBuilder()
168         .message("given")
169         .build();
170     softly.assertThat(actual)
171         .isEqualTo(expected);
172   }
173 
174   /**
175    * Gets content type charset.
176    *
177    * @param softly the softly
178    */
179   @Test
180   void getContentTypeCharset(SoftAssertions softly) {
181     Charset actual = target.getContentTypeCharset(null);
182     softly.assertThat(actual)
183         .isEqualTo(target.getDefaultCharset());
184 
185     actual = target.getContentTypeCharset(MediaType.APPLICATION_JSON);
186     softly.assertThat(actual)
187         .isEqualTo(StandardCharsets.UTF_8);
188 
189     actual = target.getContentTypeCharset(MediaType.APPLICATION_XML);
190     softly.assertThat(actual)
191         .isEqualTo(StandardCharsets.UTF_8);
192 
193     MediaType mediaType = MediaType.parseMediaType(
194         MediaType.APPLICATION_JSON_VALUE + ";charset=" + StandardCharsets.ISO_8859_1.name());
195     actual = target.getContentTypeCharset(mediaType);
196     softly.assertThat(actual)
197         .isEqualTo(StandardCharsets.UTF_8);
198 
199     mediaType = MediaType.parseMediaType(
200         MediaType.APPLICATION_XML_VALUE + ";charset=" + StandardCharsets.ISO_8859_1.name());
201     actual = target.getContentTypeCharset(mediaType);
202     softly.assertThat(actual)
203         .isEqualTo(StandardCharsets.ISO_8859_1);
204 
205     mediaType = MediaType.parseMediaType(
206         MediaType.TEXT_PLAIN_VALUE + ";charset=" + StandardCharsets.ISO_8859_1.name());
207     actual = target.getContentTypeCharset(mediaType);
208     softly.assertThat(actual)
209         .isEqualTo(StandardCharsets.ISO_8859_1);
210   }
211 
212   /**
213    * Parse error timestamp.
214    *
215    * @param softly the softly
216    */
217   @Test
218   void parseErrorTimestamp(SoftAssertions softly) {
219     OffsetDateTime actual = target.parseErrorTimestamp(null);
220     softly.assertThat(actual)
221         .isNull();
222 
223     actual = target.parseErrorTimestamp("illegal");
224     softly.assertThat(actual)
225         .isNull();
226 
227     OffsetDateTime expected = OffsetDateTime.parse("2022-02-28T02:31:22Z");
228     actual = target.parseErrorTimestamp(expected
229         .format(RestApiExceptionConstants.TIMESTAMP_FORMATTER));
230     softly.assertThat(actual)
231         .isEqualTo(expected);
232   }
233 }