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