View Javadoc
1   /*
2    * Copyright (c) 2011-2022, jcabi.com
3    * All rights reserved.
4    *
5    * Redistribution and use in source and binary forms, with or without
6    * modification, are permitted provided that the following conditions
7    * are met: 1) Redistributions of source code must retain the above
8    * copyright notice, this list of conditions and the following
9    * disclaimer. 2) Redistributions in binary form must reproduce the above
10   * copyright notice, this list of conditions and the following
11   * disclaimer in the documentation and/or other materials provided
12   * with the distribution. 3) Neither the name of the jcabi.com nor
13   * the names of its contributors may be used to endorse or promote
14   * products derived from this software without specific prior written
15   * permission.
16   *
17   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18   * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT
19   * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
20   * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
21   * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
22   * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23   * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24   * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25   * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
26   * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
28   * OF THE POSSIBILITY OF SUCH DAMAGE.
29   */
30  package com.jcabi.http.mock;
31  
32  import com.jcabi.http.Request;
33  import com.jcabi.http.request.JdkRequest;
34  import com.jcabi.http.response.RestResponse;
35  import com.jcabi.http.wire.VerboseWire;
36  import jakarta.ws.rs.HttpMethod;
37  import jakarta.ws.rs.core.MediaType;
38  import java.net.HttpURLConnection;
39  import java.util.NoSuchElementException;
40  import org.hamcrest.MatcherAssert;
41  import org.hamcrest.Matchers;
42  import org.hamcrest.core.IsAnything;
43  import org.junit.jupiter.api.Assertions;
44  import org.junit.jupiter.api.Test;
45  import org.junit.jupiter.api.function.Executable;
46  
47  /**
48   * Test case for {@link MkContainer}.
49   * @since 1.0
50   */
51  final class MkContainerTest {
52  
53      /**
54       * MkContainer can return required answers.
55       * @throws Exception If something goes wrong inside
56       */
57      @Test
58      void worksAsServletContainer() throws Exception {
59          try (MkContainer container = new MkGrizzlyContainer()) {
60              container.next(
61                  new MkAnswer.Simple(HttpURLConnection.HTTP_OK, "works fine!")
62              ).start();
63              new JdkRequest(container.home())
64                  .fetch().as(RestResponse.class)
65                  .assertStatus(HttpURLConnection.HTTP_OK)
66                  .assertBody(Matchers.startsWith("works"));
67              final MkQuery query = container.take();
68              MatcherAssert.assertThat(
69                  query.method(),
70                  Matchers.equalTo(Request.GET)
71              );
72          }
73      }
74  
75      /**
76       * MkContainer can understand duplicate headers.
77       * @throws Exception If something goes wrong inside
78       */
79      @Test
80      void understandsDuplicateHeaders() throws Exception {
81          try (MkContainer container = new MkGrizzlyContainer()) {
82              container.next(new MkAnswer.Simple("")).start();
83              final String header = "X-Something";
84              new JdkRequest(container.home())
85                  .through(VerboseWire.class)
86                  .header(header, MediaType.TEXT_HTML)
87                  .header(header, MediaType.TEXT_XML)
88                  .fetch().as(RestResponse.class)
89                  .assertStatus(HttpURLConnection.HTTP_OK);
90              final MkQuery query = container.take();
91              MatcherAssert.assertThat(
92                  query.headers().get(header),
93                  Matchers.hasSize(2)
94              );
95          }
96      }
97  
98      /**
99       * MkContainer can return certain answers for matching conditions.
100      * @throws Exception If something goes wrong inside.
101      */
102     @Test
103     void answersConditionally() throws Exception {
104         final String match = "matching";
105         final String mismatch = "not matching";
106         try (MkContainer container = new MkGrizzlyContainer()) {
107             container.next(
108                 new MkAnswer.Simple(mismatch),
109                 Matchers.not(new IsAnything<MkQuery>())
110             ).next(new MkAnswer.Simple(match), new IsAnything<MkQuery>())
111                 .start();
112             new JdkRequest(container.home())
113                 .through(VerboseWire.class)
114                 .fetch().as(RestResponse.class)
115                 .assertStatus(HttpURLConnection.HTTP_OK)
116                 .assertBody(
117                     Matchers.allOf(
118                         Matchers.is(match),
119                         Matchers.not(mismatch)
120                     )
121                 );
122         }
123     }
124 
125     /**
126      * MkContainer can return a correct binary answers.
127      * @throws Exception If something goes wrong inside.
128      */
129     @Test
130     void answersBinary() throws Exception {
131         final byte[] body = {0x00, 0x01, 0x45, 0x21, (byte) 0xFF};
132         try (MkContainer container = new MkGrizzlyContainer()) {
133             container.next(
134                 new MkAnswer.Simple(HttpURLConnection.HTTP_OK)
135                     .withBody(body)
136             ).start();
137             new JdkRequest(container.home())
138                 .through(VerboseWire.class)
139                 .fetch().as(RestResponse.class)
140                 .assertStatus(HttpURLConnection.HTTP_OK)
141                 .assertBinary(Matchers.is(body));
142         }
143     }
144 
145     /**
146      * MkContainer returns HTTP 500 if no answers match.
147      */
148     @Test
149     void returnsErrorIfNoMatches() {
150         Assertions.assertThrows(
151             NoSuchElementException.class,
152             new Executable() {
153                 @Override
154                 public void execute() throws Throwable {
155                     try (MkContainer container = new MkGrizzlyContainer()) {
156                         container.next(
157                             new MkAnswer.Simple("not supposed to match"),
158                             Matchers.not(new IsAnything<MkQuery>())
159                         ).start();
160                         new JdkRequest(container.home())
161                             .through(VerboseWire.class)
162                             .fetch()
163                             .as(RestResponse.class)
164                             .assertStatus(
165                                 HttpURLConnection.HTTP_INTERNAL_ERROR
166                             );
167                         container.take();
168                     }
169                 }
170             }
171         );
172     }
173 
174     /**
175      * MkContainer can answer multiple times for matching requests.
176      * @throws Exception If something goes wrong inside
177      */
178     @Test
179     void canAnswerMultipleTimes() throws Exception {
180         final String body = "multiple";
181         final int times = 5;
182         try (MkContainer container = new MkGrizzlyContainer()) {
183             container.next(
184                 new MkAnswer.Simple(body),
185                 new IsAnything<MkQuery>(),
186                 times
187             ).start();
188             final Request req = new JdkRequest(container.home())
189                 .through(VerboseWire.class);
190             for (int idx = 0; idx < times; idx += 1) {
191                 req.fetch().as(RestResponse.class)
192                     .assertStatus(HttpURLConnection.HTTP_OK)
193                     .assertBody(Matchers.is(body));
194             }
195         }
196     }
197 
198     /**
199      * MkContainer can prioritize multiple matching answers by using the
200      * first matching request.
201      * @throws Exception If something goes wrong inside.
202      */
203     @Test
204     void prioritizesMatchingAnswers() throws Exception {
205         final String first = "first";
206         final String second = "second";
207         try (MkContainer container = new MkGrizzlyContainer()) {
208             container
209                 .next(new MkAnswer.Simple(first), new IsAnything<MkQuery>())
210                 .next(new MkAnswer.Simple(second), new IsAnything<MkQuery>())
211                 .start();
212             new JdkRequest(container.home())
213                 .through(VerboseWire.class)
214                 .fetch().as(RestResponse.class)
215                 .assertStatus(HttpURLConnection.HTTP_OK)
216                 .assertBody(
217                     Matchers.allOf(
218                         Matchers.is(first),
219                         Matchers.not(second)
220                     )
221                 );
222         }
223     }
224 
225     /**
226      * MkContainer can return the query that matched a certain response.
227      * @throws Exception If something goes wrong inside
228      */
229     @Test
230     void takesMatchingQuery() throws Exception {
231         final String request = "reqBodyMatches";
232         final String response = "respBodyMatches";
233         try (MkContainer container = new MkGrizzlyContainer()) {
234             container
235                 .next(new MkAnswer.Simple(response))
236                 .next(new MkAnswer.Simple("bleh"))
237                 .start();
238             new JdkRequest(container.home())
239                 .through(VerboseWire.class)
240                 .method(HttpMethod.POST)
241                 .body().set(request).back()
242                 .fetch().as(RestResponse.class)
243                 .assertStatus(HttpURLConnection.HTTP_OK);
244             new JdkRequest(container.home())
245                 .through(VerboseWire.class)
246                 .method(HttpMethod.POST)
247                 .body().set("reqBodyMismatches").back()
248                 .fetch().as(RestResponse.class)
249                 .assertStatus(HttpURLConnection.HTTP_OK);
250             MatcherAssert.assertThat(
251                 container.take(MkAnswerMatchers.hasBody(Matchers.is(response))),
252                 MkQueryMatchers.hasBody(Matchers.is(request))
253             );
254         }
255     }
256 
257     /**
258      * MkContainer can return all queries that matched a certain response.
259      * @throws Exception If something goes wrong inside
260      */
261     @Test
262     @SuppressWarnings("unchecked")
263     void takesAllMatchingQueries() throws Exception {
264         final String match = "multipleRequestMatches";
265         final String mismatch = "multipleRequestNotMatching";
266         final String response = "multipleResponseMatches";
267         try (MkContainer container = new MkGrizzlyContainer()) {
268             container.next(
269                 new MkAnswer.Simple(response),
270                 MkQueryMatchers.hasBody(Matchers.is(match)),
271                 2
272             ).next(new MkAnswer.Simple("blaa")).start();
273             new JdkRequest(container.home())
274                 .through(VerboseWire.class)
275                 .method(HttpMethod.POST)
276                 .body().set(match).back()
277                 .fetch().as(RestResponse.class)
278                 .assertStatus(HttpURLConnection.HTTP_OK)
279                 .back()
280                 .fetch().as(RestResponse.class)
281                 .assertStatus(HttpURLConnection.HTTP_OK);
282             new JdkRequest(container.home())
283                 .through(VerboseWire.class)
284                 .method(HttpMethod.POST)
285                 .body().set(mismatch).back()
286                 .fetch().as(RestResponse.class)
287                 .assertStatus(HttpURLConnection.HTTP_OK);
288             MatcherAssert.assertThat(
289                 container.takeAll(
290                     MkAnswerMatchers.hasBody(Matchers.is(response))
291                 ),
292                 Matchers.allOf(
293                     Matchers.<MkQuery>iterableWithSize(2),
294                     Matchers.hasItems(
295                         MkQueryMatchers.hasBody(Matchers.is(match))
296                     ),
297                     Matchers.not(
298                         Matchers.hasItems(
299                             MkQueryMatchers.hasBody(Matchers.is(mismatch))
300                         )
301                     )
302                 )
303             );
304         }
305     }
306 }