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