View Javadoc
1   /*
2    * SPDX-FileCopyrightText: Copyright (c) 2011-2025 Yegor Bugayenko
3    * SPDX-License-Identifier: MIT
4    */
5   package com.jcabi.http.mock;
6   
7   import com.jcabi.http.Request;
8   import com.jcabi.http.request.JdkRequest;
9   import com.jcabi.http.response.RestResponse;
10  import com.jcabi.http.wire.VerboseWire;
11  import jakarta.ws.rs.HttpMethod;
12  import jakarta.ws.rs.core.MediaType;
13  import java.net.HttpURLConnection;
14  import java.util.NoSuchElementException;
15  import org.hamcrest.MatcherAssert;
16  import org.hamcrest.Matchers;
17  import org.hamcrest.core.IsAnything;
18  import org.junit.jupiter.api.Assertions;
19  import org.junit.jupiter.api.Test;
20  
21  /**
22   * Test case for {@link MkContainer}.
23   * @since 1.0
24   */
25  final class MkContainerTest {
26  
27      /**
28       * MkContainer can return required answers.
29       * @throws Exception If something goes wrong inside
30       */
31      @Test
32      void worksAsServletContainer() throws Exception {
33          try (MkContainer container = new MkGrizzlyContainer()) {
34              container.next(
35                  new MkAnswer.Simple(HttpURLConnection.HTTP_OK, "works fine!")
36              ).start();
37              new JdkRequest(container.home())
38                  .fetch().as(RestResponse.class)
39                  .assertStatus(HttpURLConnection.HTTP_OK)
40                  .assertBody(Matchers.startsWith("works"));
41              final MkQuery query = container.take();
42              MatcherAssert.assertThat(
43                  "should be GET method",
44                  query.method(),
45                  Matchers.equalTo(Request.GET)
46              );
47          }
48      }
49  
50      /**
51       * MkContainer can understand duplicate headers.
52       * @throws Exception If something goes wrong inside
53       */
54      @Test
55      void understandsDuplicateHeaders() throws Exception {
56          try (MkContainer container = new MkGrizzlyContainer()) {
57              container.next(new MkAnswer.Simple("")).start();
58              final String header = "X-Something";
59              new JdkRequest(container.home())
60                  .through(VerboseWire.class)
61                  .header(header, MediaType.TEXT_HTML)
62                  .header(header, MediaType.TEXT_XML)
63                  .fetch().as(RestResponse.class)
64                  .assertStatus(HttpURLConnection.HTTP_OK);
65              final MkQuery query = container.take();
66              MatcherAssert.assertThat(
67                  "should has size 2",
68                  query.headers().get(header),
69                  Matchers.hasSize(2)
70              );
71          }
72      }
73  
74      /**
75       * MkContainer can return certain answers for matching conditions.
76       * @throws Exception If something goes wrong inside.
77       */
78      @Test
79      void answersConditionally() throws Exception {
80          final String match = "matching";
81          final String mismatch = "not matching";
82          try (MkContainer container = new MkGrizzlyContainer()) {
83              container.next(
84                  new MkAnswer.Simple(mismatch),
85                  Matchers.not(new IsAnything<MkQuery>())
86              ).next(new MkAnswer.Simple(match), new IsAnything<MkQuery>())
87                  .start();
88              new JdkRequest(container.home())
89                  .through(VerboseWire.class)
90                  .fetch().as(RestResponse.class)
91                  .assertStatus(HttpURLConnection.HTTP_OK)
92                  .assertBody(
93                      Matchers.allOf(
94                          Matchers.is(match),
95                          Matchers.not(mismatch)
96                      )
97                  );
98          }
99      }
100 
101     /**
102      * MkContainer can return a correct binary answers.
103      * @throws Exception If something goes wrong inside.
104      */
105     @Test
106     void answersBinary() throws Exception {
107         final byte[] body = {0x00, 0x01, 0x45, 0x21, (byte) 0xFF};
108         try (MkContainer container = new MkGrizzlyContainer()) {
109             container.next(
110                 new MkAnswer.Simple(HttpURLConnection.HTTP_OK)
111                     .withBody(body)
112             ).start();
113             new JdkRequest(container.home())
114                 .through(VerboseWire.class)
115                 .fetch().as(RestResponse.class)
116                 .assertStatus(HttpURLConnection.HTTP_OK)
117                 .assertBinary(Matchers.is(body));
118         }
119     }
120 
121     /**
122      * MkContainer returns HTTP 500 if no answers match.
123      */
124     @Test
125     void returnsErrorIfNoMatches() {
126         Assertions.assertThrows(
127             NoSuchElementException.class,
128             () -> {
129                 try (MkContainer container = new MkGrizzlyContainer()) {
130                     container.next(
131                         new MkAnswer.Simple("not supposed to match"),
132                         Matchers.not(new IsAnything<MkQuery>())
133                     ).start();
134                     new JdkRequest(container.home())
135                         .through(VerboseWire.class)
136                         .fetch()
137                         .as(RestResponse.class)
138                         .assertStatus(
139                             HttpURLConnection.HTTP_INTERNAL_ERROR
140                         );
141                     container.take();
142                 }
143             }
144         );
145     }
146 
147     /**
148      * MkContainer can answer multiple times for matching requests.
149      * @throws Exception If something goes wrong inside
150      */
151     @Test
152     void canAnswerMultipleTimes() throws Exception {
153         final String body = "multiple";
154         final int times = 5;
155         try (MkContainer container = new MkGrizzlyContainer()) {
156             container.next(
157                 new MkAnswer.Simple(body),
158                 new IsAnything<MkQuery>(),
159                 times
160             ).start();
161             final Request req = new JdkRequest(container.home())
162                 .through(VerboseWire.class);
163             for (int idx = 0; idx < times; idx += 1) {
164                 req.fetch().as(RestResponse.class)
165                     .assertStatus(HttpURLConnection.HTTP_OK)
166                     .assertBody(Matchers.is(body));
167             }
168         }
169     }
170 
171     /**
172      * MkContainer can prioritize multiple matching answers by using the
173      * first matching request.
174      * @throws Exception If something goes wrong inside.
175      */
176     @Test
177     void prioritizesMatchingAnswers() throws Exception {
178         final String first = "first";
179         final String second = "second";
180         try (MkContainer container = new MkGrizzlyContainer()) {
181             container
182                 .next(new MkAnswer.Simple(first), new IsAnything<MkQuery>())
183                 .next(new MkAnswer.Simple(second), new IsAnything<MkQuery>())
184                 .start();
185             new JdkRequest(container.home())
186                 .through(VerboseWire.class)
187                 .fetch().as(RestResponse.class)
188                 .assertStatus(HttpURLConnection.HTTP_OK)
189                 .assertBody(
190                     Matchers.allOf(
191                         Matchers.is(first),
192                         Matchers.not(second)
193                     )
194                 );
195         }
196     }
197 
198     /**
199      * MkContainer can return the query that matched a certain response.
200      * @throws Exception If something goes wrong inside
201      */
202     @Test
203     void takesMatchingQuery() throws Exception {
204         final String request = "reqBodyMatches";
205         final String response = "respBodyMatches";
206         try (MkContainer container = new MkGrizzlyContainer()) {
207             container
208                 .next(new MkAnswer.Simple(response))
209                 .next(new MkAnswer.Simple("bleh"))
210                 .start();
211             new JdkRequest(container.home())
212                 .through(VerboseWire.class)
213                 .method(HttpMethod.POST)
214                 .body().set(request).back()
215                 .fetch().as(RestResponse.class)
216                 .assertStatus(HttpURLConnection.HTTP_OK);
217             new JdkRequest(container.home())
218                 .through(VerboseWire.class)
219                 .method(HttpMethod.POST)
220                 .body().set("reqBodyMismatches").back()
221                 .fetch().as(RestResponse.class)
222                 .assertStatus(HttpURLConnection.HTTP_OK);
223             MatcherAssert.assertThat(
224                 "should match the answer body",
225                 container.take(MkAnswerMatchers.hasBody(Matchers.is(response))),
226                 MkQueryMatchers.hasBody(Matchers.is(request))
227             );
228         }
229     }
230 
231     /**
232      * MkContainer can return all queries that matched a certain response.
233      * @throws Exception If something goes wrong inside
234      */
235     @Test
236     @SuppressWarnings("unchecked")
237     void takesAllMatchingQueries() throws Exception {
238         final String match = "multipleRequestMatches";
239         final String mismatch = "multipleRequestNotMatching";
240         final String response = "multipleResponseMatches";
241         try (MkContainer container = new MkGrizzlyContainer()) {
242             container.next(
243                 new MkAnswer.Simple(response),
244                 MkQueryMatchers.hasBody(Matchers.is(match)),
245                 2
246             ).next(new MkAnswer.Simple("blaa")).start();
247             new JdkRequest(container.home())
248                 .through(VerboseWire.class)
249                 .method(HttpMethod.POST)
250                 .body().set(match).back()
251                 .fetch().as(RestResponse.class)
252                 .assertStatus(HttpURLConnection.HTTP_OK)
253                 .back()
254                 .fetch().as(RestResponse.class)
255                 .assertStatus(HttpURLConnection.HTTP_OK);
256             new JdkRequest(container.home())
257                 .through(VerboseWire.class)
258                 .method(HttpMethod.POST)
259                 .body().set(mismatch).back()
260                 .fetch().as(RestResponse.class)
261                 .assertStatus(HttpURLConnection.HTTP_OK);
262             MatcherAssert.assertThat(
263                 "should match all bodies",
264                 container.takeAll(
265                     MkAnswerMatchers.hasBody(Matchers.is(response))
266                 ),
267                 Matchers.allOf(
268                     Matchers.<MkQuery>iterableWithSize(2),
269                     Matchers.hasItems(
270                         MkQueryMatchers.hasBody(Matchers.is(match))
271                     ),
272                     Matchers.not(
273                         Matchers.hasItems(
274                             MkQueryMatchers.hasBody(Matchers.is(mismatch))
275                         )
276                     )
277                 )
278             );
279         }
280     }
281 }