View Javadoc
1   /*
2    * SPDX-FileCopyrightText: Copyright (c) 2011-2025 Yegor Bugayenko
3    * SPDX-License-Identifier: MIT
4    */
5   package com.jcabi.http.response;
6   
7   import com.fasterxml.jackson.databind.node.ArrayNode;
8   import com.fasterxml.jackson.databind.node.ObjectNode;
9   import com.jcabi.http.request.FakeRequest;
10  import java.io.IOException;
11  import org.hamcrest.MatcherAssert;
12  import org.hamcrest.Matchers;
13  import org.junit.jupiter.api.Assertions;
14  import org.junit.jupiter.api.Test;
15  
16  /**
17   * Test case for {@link JacksonResponse}.
18   *
19   * @since 1.17
20   */
21  final class JacksonResponseTest {
22      /**
23       * JacksonResponse can read and return a JSON document.
24       *
25       * @throws IOException If anything goes wrong when parsing.
26       */
27      @Test
28      void canReadJsonDocument() throws IOException {
29          final JacksonResponse response = new FakeRequest()
30              .withBody("{\n\t\r\"foo-foo\":2,\n\"bar\":\"\u20ac\"}")
31              .fetch().as(JacksonResponse.class);
32          MatcherAssert.assertThat(
33              "should be 2",
34              response.json().read().path("foo-foo").asInt(),
35              Matchers.equalTo(2)
36          );
37          MatcherAssert.assertThat(
38              "should be '\u20ac'",
39              response.json().read().path("bar").asText(),
40              Matchers.equalTo("\u20ac")
41          );
42      }
43  
44      /**
45       * JacksonResponse can read control characters.
46       *
47       * @throws IOException If anything goes wrong when parsing.
48       */
49      @Test
50      void canParseUnquotedControlCharacters() throws IOException {
51          final JacksonResponse response = new FakeRequest()
52              .withBody("{\"test\":\n\"\u001Fblah\uFFFDcwhoa\u0000!\"}")
53              .fetch().as(JacksonResponse.class);
54          MatcherAssert.assertThat(
55              "should be '\u001Fblah\uFFFDcwhoa\u0000!'",
56              response.json().readObject().get("test").asText(),
57              Matchers.is("\u001Fblah\uFFFDcwhoa\u0000!")
58          );
59      }
60  
61      /**
62       * If there's a problem parsing the body as JSON the error handling is done
63       * by Jackson.
64       *
65       * @throws IOException If anything goes wrong.
66       */
67      @Test
68      void invalidJsonErrorHandlingIsLeftToJackson() throws IOException {
69          final String body = "{test:[]}";
70          final String err = "was expecting double-quote to start field name";
71          final JacksonResponse response = new FakeRequest()
72              .withBody(body).fetch().as(JacksonResponse.class);
73          MatcherAssert.assertThat(
74              "should contains error 'was expecting double-quote to start field name'",
75              Assertions.assertThrows(
76                  IOException.class,
77                  () -> response.json().read()
78              ),
79              Matchers.hasProperty(
80                  "message",
81                  Matchers.containsString(err)
82              )
83          );
84      }
85  
86      /**
87       * If there's a problem parsing the body as JSON the error handling is done
88       * by Jackson.
89       *
90       * @throws IOException If anything goes wrong.
91       */
92      @Test
93      void invalidJsonArrayErrorHandlingIsLeftToJackson()
94          throws IOException {
95          final JacksonResponse response = new FakeRequest()
96              .withBody("{\"anInvalidArrayTest\":[}")
97              .fetch().as(JacksonResponse.class);
98          MatcherAssert.assertThat(
99              "should contains error 'Unexpected close marker'",
100             Assertions.assertThrows(
101                 IOException.class,
102                 () -> response.json().readArray()
103             ),
104             Matchers.hasToString(
105                 Matchers.containsString("Unexpected close marker")
106             )
107         );
108     }
109 
110     /**
111      * If the parsed JSON is a valid one but an array an exception is raised.
112      *
113      * @throws IOException If anything goes wrong.
114      */
115     @Test
116     void cannotReadJsonAsArrayIfNotOne() throws IOException {
117         final JacksonResponse response = new FakeRequest()
118             .withBody("{\"objectIsNotArray\": \"It's not!\"}")
119             .fetch().as(JacksonResponse.class);
120         MatcherAssert.assertThat(
121             "should contains 'Cannot read as an array. The JSON is not a valid array.'",
122             Assertions.assertThrows(
123                 IOException.class,
124                 () -> response.json().readArray()
125             ),
126             Matchers.<IOException>hasToString(
127                 Matchers.containsString(
128                     "Cannot read as an array. The JSON is not a valid array."
129                 )
130             )
131         );
132     }
133 
134     /**
135      * Can retrieve the JSON as an array node if it's a valid one.
136      *
137      * @throws IOException If anything goes wrong.
138      */
139     @Test
140     void canReadAsArrayIfOne() throws IOException {
141         final JacksonResponse response = new FakeRequest()
142             .withBody("[\"one\", \"two\"]")
143             .fetch().as(JacksonResponse.class);
144         final ArrayNode array = response.json().readArray();
145         MatcherAssert.assertThat(
146             "should be 'one'", array.get(0).asText(), Matchers.is("one")
147         );
148         MatcherAssert.assertThat(
149             "should be 'one'", array.get(1).asText(), Matchers.is("two")
150         );
151     }
152 
153     /**
154      * If there's a problem parsing the body as JSON the error handling is done
155      * by Jackson.
156      *
157      * @throws IOException If anything goes wrong.
158      */
159     @Test
160     void invalidJsonObjectErrorIsLeftToJackson() throws IOException {
161         final JacksonResponse response = new FakeRequest()
162             .withBody("{\"anInvalidObjectTest\":{}")
163             .fetch().as(JacksonResponse.class);
164         MatcherAssert.assertThat(
165             "should contains error 'Unexpected end-of-input: expected close marker for Object",
166             Assertions.assertThrows(
167                 IOException.class,
168                 () -> response.json().readObject()
169             ),
170             Matchers.<IOException>hasToString(
171                 Matchers.containsString(
172                     "Unexpected end-of-input: expected close marker for Object"
173                 )
174             )
175         );
176     }
177 
178     /**
179      * If the parsed JSON is a valid one but an object an exception is raised.
180      *
181      * @throws IOException If anything goes wrong.
182      */
183     @Test
184     void cannotReadJsonAsObjectIfNotOne() throws IOException {
185         final String body = "[\"arrayIsNotObject\", \"It's not!\"]";
186         final JacksonResponse response = new FakeRequest()
187             .withBody(body).fetch().as(JacksonResponse.class);
188         MatcherAssert.assertThat(
189             "should contains error 'Cannot read as an object. The JSON is not a valid object.",
190             Assertions.assertThrows(
191                 IOException.class,
192                 () -> response.json().readObject()
193             ),
194             Matchers.<IOException>hasToString(
195                 Matchers.containsString(
196                     "Cannot read as an object. The JSON is not a valid object."
197                 )
198             )
199         );
200     }
201 
202     /**
203      * Can retrieve the JSON as an object node if it's a valid one.
204      *
205      * @throws IOException If anything goes wrong.
206      */
207     @Test
208     void canReadAsObjectIfOne() throws IOException {
209         final JacksonResponse response = new FakeRequest()
210             .withBody("{\"hooray\": \"Got milk?\"}")
211             .fetch().as(JacksonResponse.class);
212         final ObjectNode object = response.json().readObject();
213         MatcherAssert.assertThat(
214             "should contains 'Got milk?", object.get("hooray").asText(), Matchers.is("Got milk?")
215         );
216     }
217 }