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.request;
31  
32  import com.jcabi.http.Request;
33  import com.jcabi.http.Wire;
34  import com.jcabi.http.mock.MkAnswer;
35  import com.jcabi.http.mock.MkContainer;
36  import com.jcabi.http.mock.MkGrizzlyContainer;
37  import com.jcabi.immutable.ArrayMap;
38  import jakarta.json.Json;
39  import jakarta.ws.rs.core.HttpHeaders;
40  import jakarta.ws.rs.core.MediaType;
41  import java.io.IOException;
42  import org.hamcrest.MatcherAssert;
43  import org.hamcrest.Matchers;
44  import org.junit.jupiter.api.Assertions;
45  import org.junit.jupiter.api.Test;
46  import org.junit.jupiter.api.function.Executable;
47  import org.mockito.Mockito;
48  
49  /**
50   * Test case for {@link BaseRequest}.
51   *
52   * @since 1.0
53   */
54  final class BaseRequestTest {
55  
56      /**
57       * Property name of Exception.
58       */
59      private static final String MESSAGE = "message";
60  
61      /**
62       * BaseRequest can build the right destination URI.
63       */
64      @Test
65      void buildsDestinationUri() {
66          final Wire wire = Mockito.mock(Wire.class);
67          MatcherAssert.assertThat(
68              new BaseRequest(wire, "http://localhost:88/t/f")
69                  .uri().path("/bar").queryParam("u1", "\u20ac")
70                  .queryParams(new ArrayMap<String, String>().with("u2", ""))
71                  .userInfo("hey:\u20ac").back().uri().get(),
72              Matchers.hasToString(
73                  "http://hey:%E2%82%AC@localhost:88/t/f/bar?u1=%E2%82%AC&u2="
74              )
75          );
76      }
77  
78      /**
79       * BaseRequest can set body to JSON.
80       */
81      @Test
82      void printsJsonInBody() {
83          final Wire wire = Mockito.mock(Wire.class);
84          MatcherAssert.assertThat(
85              new BaseRequest(wire, "http://localhost:88/x").body().set(
86                  Json.createObjectBuilder().add("foo", "test 1").build()
87              ).get(),
88              Matchers.equalTo("{\"foo\":\"test 1\"}")
89          );
90      }
91  
92      /**
93       * BaseRequest can include the port number.
94       */
95      @Test
96      void includesPort() {
97          final Wire wire = Mockito.mock(Wire.class);
98          MatcherAssert.assertThat(
99              // @checkstyle MagicNumber (2 lines)
100             new BaseRequest(wire, "http://localhost")
101                 .uri().port(8080).back().uri().get(),
102             Matchers.hasToString("http://localhost:8080/")
103         );
104     }
105 
106     /**
107      * FakeRequest can identify itself uniquely.
108      */
109     @Test
110     void identifiesUniquely() {
111         final Wire wire = Mockito.mock(Wire.class);
112         MatcherAssert.assertThat(
113             new BaseRequest(wire, "").header("header-1", "value-1"),
114             Matchers.not(
115                 Matchers.equalTo(
116                     new BaseRequest(wire, "").header("header-2", "value-2")
117                 )
118             )
119         );
120         MatcherAssert.assertThat(
121             new BaseRequest(wire, ""),
122             Matchers.equalTo(new BaseRequest(wire, ""))
123         );
124     }
125 
126     /**
127      * Throws exception when using formParam on multipart-body without
128      * content-type defined.
129      */
130     @Test
131     void exceptionWhenMissingContentType() {
132         final Wire wire = Mockito.mock(Wire.class);
133         MatcherAssert.assertThat(
134             Assertions.assertThrows(
135                 IllegalStateException.class,
136                 new Executable() {
137                     @Override
138                     public void execute() throws Throwable {
139                         new BaseRequest(wire, "")
140                             .multipartBody()
141                             .formParam("a", "value")
142                             .back();
143                     }
144                 }
145             ),
146             Matchers.hasProperty(
147                 BaseRequestTest.MESSAGE,
148                 Matchers.is(BaseRequestTest.boundaryErrorMesg())
149             )
150         );
151     }
152 
153     /**
154      * Throws exception when using formParam on multipartbody without boundary
155      * provided in content-type defined.
156      */
157     @Test
158     void exceptionWhenMissingBoundary() {
159         final Wire wire = Mockito.mock(Wire.class);
160         MatcherAssert.assertThat(
161             Assertions.assertThrows(
162                 IllegalStateException.class,
163                 new Executable() {
164                     @Override
165                     public void execute() throws Throwable {
166                         new BaseRequest(wire, "")
167                             .header(
168                                 HttpHeaders.CONTENT_TYPE,
169                                 MediaType.MULTIPART_FORM_DATA
170                             )
171                             .multipartBody().formParam("b", "val").back();
172                     }
173                 }
174             ),
175             Matchers.hasProperty(
176                 BaseRequestTest.MESSAGE,
177                 Matchers.is(BaseRequestTest.boundaryErrorMesg())
178             )
179         );
180     }
181 
182     @Test
183     void shouldHaveCorrectFormParameters() throws IOException {
184         final MkContainer srv = new MkGrizzlyContainer()
185             .next(new MkAnswer.Simple("OK")).start();
186         new JdkRequest(srv.home())
187             .body()
188             .formParam("foo1", "bar1")
189             .back()
190             .body()
191             .formParam("foo2", "bar2")
192             .back()
193             .body()
194             .formParam("foo3", "bar3")
195             .formParam("foo4", "bar4")
196             .back()
197             .method(Request.POST)
198             .fetch();
199         MatcherAssert.assertThat(
200             srv.take().body(),
201             Matchers.is("foo1=bar1&foo2=bar2&foo3=bar3&foo4=bar4")
202         );
203     }
204 
205     /**
206      * Boundary error message.
207      *
208      * @return Message error as String.
209      */
210     private static String boundaryErrorMesg() {
211         return "Content-Type: multipart/form-data requires boundary";
212     }
213 }