001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.juneau.rest.assertions;
018
019import java.io.*;
020import java.lang.reflect.*;
021import java.util.*;
022import java.util.function.*;
023
024import org.apache.juneau.assertions.*;
025import org.apache.juneau.http.response.*;
026import org.apache.juneau.rest.httppart.*;
027import org.apache.juneau.serializer.*;
028
029/**
030 * Used for fluent assertion calls against {@link RequestContent} objects.
031 *
032 * <h5 class='topic'>Test Methods</h5>
033 * <p>
034 * <ul class='javatree'>
035 *    <li class='jc'>{@link FluentRequestContentAssertion}
036 *    <ul class='javatreec'>
037 *       <li class='jm'>{@link FluentRequestContentAssertion#is(String) is(String)}
038 *       <li class='jm'>{@link FluentRequestContentAssertion#isContains(String...) isContains(String...)}
039 *       <li class='jm'>{@link FluentRequestContentAssertion#isNotContains(String...) isNotContains(String...)}
040 *       <li class='jm'>{@link FluentRequestContentAssertion#isEmpty() isEmpty()}
041 *       <li class='jm'>{@link FluentRequestContentAssertion#isNotEmpty() isNotEmpty()}
042 *    </ul>
043 *    <li class='jc'>{@link FluentObjectAssertion}
044 *    <ul class='javatreec'>
045 *       <li class='jm'>{@link FluentObjectAssertion#isExists() isExists()}
046 *       <li class='jm'>{@link FluentObjectAssertion#is(Object) is(Object)}
047 *       <li class='jm'>{@link FluentObjectAssertion#is(Predicate) is(Predicate)}
048 *       <li class='jm'>{@link FluentObjectAssertion#isNot(Object) isNot(Object)}
049 *       <li class='jm'>{@link FluentObjectAssertion#isAny(Object...) isAny(Object...)}
050 *       <li class='jm'>{@link FluentObjectAssertion#isNotAny(Object...) isNotAny(Object...)}
051 *       <li class='jm'>{@link FluentObjectAssertion#isNull() isNull()}
052 *       <li class='jm'>{@link FluentObjectAssertion#isNotNull() isNotNull()}
053 *       <li class='jm'>{@link FluentObjectAssertion#isString(String) isString(String)}
054 *       <li class='jm'>{@link FluentObjectAssertion#isJson(String) isJson(String)}
055 *       <li class='jm'>{@link FluentObjectAssertion#isSame(Object) isSame(Object)}
056 *       <li class='jm'>{@link FluentObjectAssertion#isSameJsonAs(Object) isSameJsonAs(Object)}
057 *       <li class='jm'>{@link FluentObjectAssertion#isSameSortedJsonAs(Object) isSameSortedJsonAs(Object)}
058 *       <li class='jm'>{@link FluentObjectAssertion#isSameSerializedAs(Object, WriterSerializer) isSameSerializedAs(Object, WriterSerializer)}
059 *       <li class='jm'>{@link FluentObjectAssertion#isType(Class) isType(Class)}
060 *       <li class='jm'>{@link FluentObjectAssertion#isExactType(Class) isExactType(Class)}
061 *    </ul>
062 * </ul>
063 *
064 * <h5 class='topic'>Transform Methods</h5>
065 * <p>
066 * <ul class='javatree'>
067 *    <li class='jc'>{@link FluentRequestContentAssertion}
068 *    <ul class='javatreec'>
069 *       <li class='jm'>{@link FluentRequestContentAssertion#asBytes() asBytes()}
070 *       <li class='jm'>{@link FluentRequestContentAssertion#as(Class) as(Class)}
071 *       <li class='jm'>{@link FluentRequestContentAssertion#as(Type,Type...) as(Type,Type...)}
072 *    </ul>
073 *    <li class='jc'>{@link FluentObjectAssertion}
074 *    <ul class='javatreec'>
075 *       <li class='jm'>{@link FluentObjectAssertion#asString() asString()}
076 *       <li class='jm'>{@link FluentObjectAssertion#asString(WriterSerializer) asString(WriterSerializer)}
077 *       <li class='jm'>{@link FluentObjectAssertion#asString(Function) asString(Function)}
078 *       <li class='jm'>{@link FluentObjectAssertion#asJson() asJson()}
079 *       <li class='jm'>{@link FluentObjectAssertion#asJsonSorted() asJsonSorted()}
080 *       <li class='jm'>{@link FluentObjectAssertion#asTransformed(Function) asApplied(Function)}
081 *       <li class='jm'>{@link FluentObjectAssertion#asAny() asAny()}
082 * </ul>
083 * </ul>
084 *
085 * <h5 class='topic'>Configuration Methods</h5>
086 * <p>
087 * <ul class='javatree'>
088 *    <li class='jc'>{@link Assertion}
089 *    <ul class='javatreec'>
090 *       <li class='jm'>{@link Assertion#setMsg(String, Object...) setMsg(String, Object...)}
091 *       <li class='jm'>{@link Assertion#setOut(PrintStream) setOut(PrintStream)}
092 *       <li class='jm'>{@link Assertion#setSilent() setSilent()}
093 *       <li class='jm'>{@link Assertion#setStdOut() setStdOut()}
094 *       <li class='jm'>{@link Assertion#setThrowable(Class) setThrowable(Class)}
095 *    </ul>
096 * </ul>
097 *
098 * <h5 class='section'>See Also:</h5><ul>
099 *    <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/JuneauEcosystemOverview">Juneau Ecosystem Overview</a>
100 * </ul>
101 *
102 * @param <R> The return type.
103 */
104public class FluentRequestContentAssertion<R> extends FluentObjectAssertion<RequestContent,R> {
105   /**
106    * Chained constructor.
107    *
108    * <p>
109    * Used when transforming one assertion into another so that the assertion config can be used by the new assertion.
110    *
111    * @param creator
112    *    The assertion that created this assertion.
113    *    <br>Should be <jk>null</jk> if this is the top-level assertion.
114    * @param value
115    *    The object being tested.
116    *    <br>Can be <jk>null</jk>.
117    * @param returns
118    *    The object to return after a test method is called.
119    *    <br>If <jk>null</jk>, the test method returns this object allowing multiple test method calls to be
120    * used on the same assertion.
121    */
122   public FluentRequestContentAssertion(Assertion creator, RequestContent value, R returns) {
123      super(creator, value, returns);
124      setThrowable(BadRequest.class);
125   }
126
127   /**
128    * Constructor.
129    *
130    * @param value
131    *    The object being tested.
132    *    <br>Can be <jk>null</jk>.
133    * @param returns
134    *    The object to return after a test method is called.
135    *    <br>If <jk>null</jk>, the test method returns this object allowing multiple test method calls to be
136    * used on the same assertion.
137    */
138   public FluentRequestContentAssertion(RequestContent value, R returns) {
139      this(null, value, returns);
140   }
141
142   /**
143    * Converts the content to a type using {@link RequestContent#as(Class)} and then returns the value as an object assertion.
144    *
145    * <h5 class='section'>Examples:</h5>
146    * <p class='bjava'>
147    *    <jc>// Validates the request content bean is the expected value.</jc>
148    *    <jv>request</jv>
149    *       .assertContent()
150    *       .as(MyBean.<jk>class</jk>)
151    *          .asJson().is(<js>"{foo:'bar'}"</js>);
152    * </p>
153    *
154    * <h5 class='section'>Notes:</h5><ul>
155    *    <li class='note'>
156    *       If no charset was found on the <code>Content-Type</code> request header, <js>"UTF-8"</js> is assumed.
157    *  <li class='note'>
158    *    When using this method, the content is automatically cached by calling the {@link RequestContent#cache()}.
159    *    <li class='note'>
160    *       The input stream is automatically closed after this call.
161    * </ul>
162    *
163    * <p>
164    * See <a class="doclink" href="https://juneau.apache.org/docs/topics/ComplexDataTypes">Complex Data Types</a> for information on defining complex generic types of {@link Map Maps} and {@link Collection Collections}.
165    *
166    * @param <T> The object type to create.
167    * @param type The object type to create.
168    * @return A new fluent assertion object.
169    */
170   public <T> FluentObjectAssertion<T,R> as(Class<T> type) {
171      return new FluentObjectAssertion<>(valueAsType(type), returns());
172   }
173
174   /**
175    * Converts the content to a type using {@link RequestContent#as(Type,Type...)} and then returns the value as an object assertion.
176    *
177    * <h5 class='section'>Examples:</h5>
178    * <p class='bjava'>
179    *    <jc>// Validates the request content bean is the expected value.</jc>
180    *    <jv>request</jv>
181    *       .assertContent()
182    *       .as(Map.<jk>class</jk>,String.<jk>class</jk>,Integer.<jk>class</jk>)
183    *          .asJson().is(<js>"{foo:123}"</js>);
184    * </p>
185    *
186    * <h5 class='section'>Notes:</h5><ul>
187    *    <li class='note'>
188    *       If no charset was found on the <code>Content-Type</code> request header, <js>"UTF-8"</js> is assumed.
189    *  <li class='note'>
190    *    When using this method, the content is automatically cached by calling the {@link RequestContent#cache()}.
191    *    <li class='note'>
192    *       The input stream is automatically closed after this call.
193    * </ul>
194    *
195    * <p>
196    * See <a class="doclink" href="https://juneau.apache.org/docs/topics/ComplexDataTypes">Complex Data Types</a> for information on defining complex generic types of {@link Map Maps} and {@link Collection Collections}.
197    *
198    * @param <T> The type to create.
199    * @param type The object type to create.
200    * @param args Optional type arguments.
201    * @return A new fluent assertion object.
202    */
203   public <T> FluentObjectAssertion<T,R> as(Type type, Type...args) {
204      return new FluentObjectAssertion<>(valueAsType(type, args), returns());
205   }
206
207   /**
208    * Provides the ability to perform fluent-style assertions on the bytes of the request content.
209    *
210    * <h5 class='section'>Examples:</h5>
211    * <p class='bjava'>
212    *    <jc>// Validates the request content equals the text "foo".</jc>
213    *    <jv>request</jv>
214    *       .assertContent().asBytes().asHex().is(<js>"666F6F"</js>);
215    * </p>
216    *
217    * <h5 class='section'>Notes:</h5><ul>
218    *    <li class='note'>
219    *       If no charset was found on the <code>Content-Type</code> request header, <js>"UTF-8"</js> is assumed.
220    *  <li class='note'>
221    *    When using this method, the content is automatically cached by calling the {@link RequestContent#cache()}.
222    *    <li class='note'>
223    *       The input stream is automatically closed after this call.
224    * </ul>
225    *
226    * @return A new fluent assertion object.
227    */
228   public FluentByteArrayAssertion<R> asBytes() {
229      return new FluentByteArrayAssertion<>(valueAsBytes(), returns());
230   }
231
232   /**
233    * Asserts that the content contains the specified value.
234    *
235    * @param values The value to check against.
236    * @return This object.
237    * @throws AssertionError If assertion failed.
238    */
239   public R is(String values) throws AssertionError {
240      return asString().is(values);
241   }
242
243   /**
244    * Asserts that the text contains all of the specified substrings.
245    *
246    * @param values The values to check against.
247    * @return This object.
248    * @throws AssertionError If assertion failed.
249    */
250   public R isContains(String...values) throws AssertionError {
251      return asString().isContains(values);
252   }
253
254   /**
255    * Asserts that the content is empty.
256    *
257    * @return This object.
258    * @throws AssertionError If assertion failed.
259    */
260   public R isEmpty() { return asString().isEmpty(); }
261
262   /**
263    * Asserts that the content doesn't contain any of the specified substrings.
264    *
265    * @param values The values to check against.
266    * @return This object.
267    * @throws AssertionError If assertion failed.
268    */
269   public R isNotContains(String...values) throws AssertionError {
270      return asString().isNotContains(values);
271   }
272
273   /**
274    * Asserts that the content is not empty.
275    *
276    * @return This object.
277    * @throws AssertionError If assertion failed.
278    */
279   public R isNotEmpty() { return asString().isNotEmpty(); }
280
281   @Override /* Overridden from Assertion */
282   public FluentRequestContentAssertion<R> setMsg(String msg, Object...args) {
283      super.setMsg(msg, args);
284      return this;
285   }
286
287   @Override /* Overridden from Assertion */
288   public FluentRequestContentAssertion<R> setOut(PrintStream value) {
289      super.setOut(value);
290      return this;
291   }
292
293   @Override /* Overridden from Assertion */
294   public FluentRequestContentAssertion<R> setSilent() {
295      super.setSilent();
296      return this;
297   }
298
299   @Override /* Overridden from Assertion */
300   public FluentRequestContentAssertion<R> setStdOut() {
301      super.setStdOut();
302      return this;
303   }
304
305   @Override /* Overridden from Assertion */
306   public FluentRequestContentAssertion<R> setThrowable(Class<? extends java.lang.RuntimeException> value) {
307      super.setThrowable(value);
308      return this;
309   }
310
311   private byte[] valueAsBytes() throws AssertionError {
312      try {
313         return value().cache().asBytes();
314      } catch (IOException e) {
315         throw error(e, "Exception occurred during call.");
316      }
317   }
318
319   private <T> T valueAsType(Class<T> c) throws AssertionError {
320      try {
321         return value().cache().as(c);
322      } catch (IOException e) {
323         throw error(e, "Exception occurred during call.");
324      }
325   }
326
327   private <T> T valueAsType(Type c, Type...args) throws AssertionError {
328      try {
329         return value().cache().as(c, args);
330      } catch (IOException e) {
331         throw error(e, "Exception occurred during call.");
332      }
333   }
334
335   @Override
336   protected String valueAsString() throws AssertionError {
337      try {
338         return value().cache().asString();
339      } catch (IOException e) {
340         throw error(e, "Exception occurred during call.");
341      }
342   }
343}