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