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.classic.assertion; 018 019import java.io.*; 020import java.lang.reflect.*; 021import java.util.*; 022import java.util.function.*; 023 024import org.apache.juneau.commons.function.*; 025import org.apache.juneau.http.classic.response.*; 026import org.apache.juneau.rest.client.classic.*; 027import org.apache.juneau.bct.assertions.*; 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#isSame(Object) isSame(Object)} 055 * <li class='jm'>{@link FluentObjectAssertion#isType(Class) isType(Class)} 056 * <li class='jm'>{@link FluentObjectAssertion#isExactType(Class) isExactType(Class)} 057 * </ul> 058 * </ul> 059 * 060 * <h5 class='topic'>Transform Methods</h5> 061 * <p> 062 * <ul class='javatree'> 063 * <li class='jc'>{@link FluentResponseBodyAssertion} 064 * <ul class='javatreec'> 065 * <li class='jm'>{@link FluentResponseBodyAssertion#asBytes() asBytes()} 066 * <li class='jm'>{@link FluentResponseBodyAssertion#as(Class) as(Class)} 067 * <li class='jm'>{@link FluentResponseBodyAssertion#as(Type,Type...) as(Type,Type...)} 068 * </ul> 069 * <li class='jc'>{@link FluentObjectAssertion} 070 * <ul class='javatreec'> 071 * <li class='jm'>{@link FluentObjectAssertion#asString() asString()} 072 * <li class='jm'>{@link FluentObjectAssertion#asString(ThrowingFunction) asString(ThrowingFunction)} 073 * <li class='jm'>{@link FluentObjectAssertion#asTransformed(UnaryOperator) asTransformed(UnaryOperator)} 074 * <li class='jm'>{@link FluentObjectAssertion#asAny() asAny()} 075 * </ul> 076 * </ul> 077 * 078 * <h5 class='topic'>Configuration Methods</h5> 079 * <p> 080 * <ul class='javatree'> 081 * <li class='jc'>{@link Assertion} 082 * <ul class='javatreec'> 083 * <li class='jm'>{@link Assertion#setMsg(String, Object...) setMsg(String, Object...)} 084 * <li class='jm'>{@link Assertion#setOut(PrintStream) setOut(PrintStream)} 085 * <li class='jm'>{@link Assertion#setSilent() setSilent()} 086 * <li class='jm'>{@link Assertion#setStdOut() setStdOut()} 087 * <li class='jm'>{@link Assertion#setThrowable(Class) setThrowable(Class)} 088 * </ul> 089 * </ul> 090 * 091 * <h5 class='section'>See Also:</h5><ul> 092 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/JuneauEcosystemOverview">Juneau Ecosystem</a> 093 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/JuneauRestClient">juneau-rest-client-classic</a> 094 * </ul> 095 * 096 * @param <R> The return type. 097 */ 098@SuppressWarnings({ 099 "java:S115", // Constants use UPPER_snakeCase convention (e.g., MSG_exceptionDuringCall) 100 "java:S1192" // Duplicated literals read more clearly inline than as constants. 101}) 102public class FluentResponseBodyAssertion<R> extends FluentObjectAssertion<ResponseContent,R> { 103 104 /** 105 * Chained constructor. 106 * 107 * <p> 108 * Used when transforming one assertion into another so that the assertion config can be used by the new assertion. 109 * 110 * @param creator 111 * The assertion that created this assertion. 112 * <br>Should be <jk>null</jk> if this is the top-level assertion. 113 * @param value 114 * The object being tested. 115 * <br>Can be <jk>null</jk> (assertions such as {@code isNull()} then operate on the absence of a value). 116 * @param returns 117 * The object to return after a test method is called. 118 * <br>If <jk>null</jk>, the test method returns this object allowing multiple test method calls to be 119 * used on the same assertion. 120 */ 121 public FluentResponseBodyAssertion(Assertion creator, ResponseContent value, R returns) { 122 super(creator, value, returns); 123 setThrowable(BadRequest.class); 124 } 125 126 /** 127 * Constructor. 128 * 129 * @param value 130 * The object being tested. 131 * <br>Can be <jk>null</jk> (assertions such as {@code isNull()} then operate on the absence of a value). 132 * @param returns 133 * The object to return after a test method is called. 134 * <br>If <jk>null</jk>, the test method returns this object allowing multiple test method calls to be 135 * used on the same assertion. 136 */ 137 public FluentResponseBodyAssertion(ResponseContent value, R returns) { 138 this(null, value, returns); 139 } 140 141 /** 142 * Converts the body to a type using {@link ResponseContent#as(Class)} and then returns the value as an object assertion. 143 * 144 * <h5 class='section'>Examples:</h5> 145 * <p class='bjava'> 146 * <jc>// Validates the response body as a list of strings and validates the length.</jc> 147 * <jv>client</jv> 148 * .get(<js>"/myBean"</js>) 149 * .run() 150 * .assertContent().as(List.<jk>class</jk>, String.<jk>class</jk>).is(<jv>x</jv> -> <jv>x</jv>.size() > 0); 151 * </p> 152 * 153 * @param <T> The object type to create. 154 * @param type The object type to create. 155 * @return A new fluent assertion object. 156 */ 157 public <T> FluentAnyAssertion<T,R> as(Class<T> type) { 158 return new FluentAnyAssertion<>(valueAsType(type), returns()); 159 } 160 161 /** 162 * Converts the body to a type using {@link ResponseContent#as(Type,Type...)} and then returns the value as an object assertion. 163 * 164 * <h5 class='section'>Examples:</h5> 165 * <p class='bjava'> 166 * <jc>// Validates the response body as a list of strings and validates the length.</jc> 167 * <jv>client</jv> 168 * .get(<js>"/myBean"</js>) 169 * .run() 170 * .assertContent().as(List.<jk>class</jk>, String.<jk>class</jk>).is(<jv>x</jv> -> <jv>x</jv>.size() > 0); 171 * </p> 172 * 173 * <p> 174 * 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}. 175 * 176 * @param type The object type to create. 177 * @param args Optional type arguments. 178 * @return A new fluent assertion object. 179 */ 180 public FluentAnyAssertion<Object,R> as(Type type, Type...args) { 181 return new FluentAnyAssertion<>(valueAsType(type, args), returns()); 182 } 183 184 /** 185 * Provides the ability to perform fluent-style assertions on the bytes of the response body. 186 * 187 * <h5 class='section'>Examples:</h5> 188 * <p class='bjava'> 189 * <jc>// Validates the response body equals the text "foo".</jc> 190 * <jv>client</jv> 191 * .get(<jsf>URI</jsf>) 192 * .run() 193 * .assertContent().asBytes().asHex().is(<js>"666F6F"</js>); 194 * </p> 195 * 196 * <h5 class='section'>Notes:</h5><ul> 197 * <li class='note'> 198 * If no charset was found on the <code>Content-Type</code> response header, <js>"UTF-8"</js> is assumed. 199 * <li class='note'> 200 * When using this method, the body is automatically cached by calling the {@link ResponseContent#cache()}. 201 * <li class='note'> 202 * The input stream is automatically closed after this call. 203 * </ul> 204 * 205 * @return A new fluent assertion object. 206 */ 207 public FluentByteArrayAssertion<R> asBytes() { 208 return new FluentByteArrayAssertion<>(valueAsBytes(), returns()); 209 } 210 211 /** 212 * Provides the ability to perform fluent-style assertions on this response body. 213 * 214 * <h5 class='section'>Examples:</h5> 215 * <p class='bjava'> 216 * <jc>// Validates the response body equals the text "OK".</jc> 217 * <jv>client</jv> 218 * .get(<jsf>URI</jsf>) 219 * .run() 220 * .assertContent().is(<js>"OK"</js>); 221 * 222 * <jc>// Validates the response body contains the text "OK".</jc> 223 * <jv>client</jv> 224 * .get(<jsf>URI</jsf>) 225 * .run() 226 * .assertContent().isContains(<js>"OK"</js>); 227 * 228 * <jc>// Validates the response body passes a predicate test.</jc> 229 * <jv>client</jv> 230 * .get(<jsf>URI</jsf>) 231 * .run() 232 * .assertContent().is(<jv>x</jv> -> <jv>x</jv>.contains(<js>"OK"</js>)); 233 * 234 * <jc>// Validates the response body matches a regular expression.</jc> 235 * <jv>client</jv> 236 * .get(<jsf>URI</jsf>) 237 * .run() 238 * .assertContent().isPattern(<js>".*OK.*"</js>); 239 * 240 * <jc>// Validates the response body matches a regular expression using regex flags.</jc> 241 * <jv>client</jv> 242 * .get(<jsf>URI</jsf>) 243 * .run() 244 * .assertContent().isPattern(<js>".*OK.*"</js>, <jsf>MULTILINE</jsf> & <jsf>CASE_INSENSITIVE</jsf>); 245 * 246 * <jc>// Validates the response body matches a regular expression in the form of an existing Pattern.</jc> 247 * Pattern <jv>pattern</jv> = Pattern.<jsm>compile</jsm>(<js>".*OK.*"</js>); 248 * <jv>client</jv> 249 * .get(<jsf>URI</jsf>) 250 * .run() 251 * .assertContent().isPattern(<jv>pattern</jv>); 252 * </p> 253 * 254 * <p> 255 * The assertion test returns the original response object allowing you to chain multiple requests like so: 256 * <p class='bjava'> 257 * <jc>// Validates the response body matches a regular expression.</jc> 258 * MyBean <jv>bean</jv> = <jv>client</jv> 259 * .get(<jsf>URI</jsf>) 260 * .run() 261 * .assertContent().isPattern(<js>".*OK.*"</js>); 262 * .assertContent().isNotPattern(<js>".*ERROR.*"</js>) 263 * .getContent().as(MyBean.<jk>class</jk>); 264 * </p> 265 * 266 * <h5 class='section'>Notes:</h5><ul> 267 * <li class='note'> 268 * If no charset was found on the <code>Content-Type</code> response header, <js>"UTF-8"</js> is assumed. 269 * <li class='note'> 270 * When using this method, the body is automatically cached by calling the {@link ResponseContent#cache()}. 271 * <li class='note'> 272 * The input stream is automatically closed after this call. 273 * </ul> 274 * 275 * @return A new fluent assertion object. 276 */ 277 @Override 278 public FluentStringAssertion<R> asString() { 279 return new FluentStringAssertion<>(valueAsString(), returns()); 280 } 281 282 /** 283 * Asserts that the body contains the specified value. 284 * 285 * @param value The value to check against. 286 * @return This object. 287 * @throws AssertionError If assertion failed. 288 */ 289 public R is(String value) throws AssertionError { 290 return asString().is(value); 291 } 292 293 /** 294 * Asserts that the text contains all of the specified substrings. 295 * 296 * @param values The values to check against. 297 * @return This object. 298 * @throws AssertionError If assertion failed. 299 */ 300 public R isContains(String...values) throws AssertionError { 301 return asString().isContains(values); 302 } 303 304 /** 305 * Asserts that the body is empty. 306 * 307 * @return This object. 308 * @throws AssertionError If assertion failed. 309 */ 310 public R isEmpty() { return asString().isEmpty(); } 311 312 /** 313 * Asserts that the body doesn't contain any of the specified substrings. 314 * 315 * @param values The values to check against. 316 * @return This object. 317 * @throws AssertionError If assertion failed. 318 */ 319 public R isNotContains(String...values) throws AssertionError { 320 return asString().isNotContains(values); 321 } 322 323 /** 324 * Asserts that the body is not empty. 325 * 326 * @return This object. 327 * @throws AssertionError If assertion failed. 328 */ 329 public R isNotEmpty() { return asString().isNotEmpty(); } 330 331 @Override /* Overridden from Assertion */ 332 public FluentResponseBodyAssertion<R> setMsg(String msg, Object...args) { 333 super.setMsg(msg, args); 334 return this; 335 } 336 337 @Override /* Overridden from Assertion */ 338 public FluentResponseBodyAssertion<R> setOut(PrintStream value) { 339 super.setOut(value); 340 return this; 341 } 342 343 @Override /* Overridden from Assertion */ 344 public FluentResponseBodyAssertion<R> setSilent() { 345 super.setSilent(); 346 return this; 347 } 348 349 @Override /* Overridden from Assertion */ 350 public FluentResponseBodyAssertion<R> setStdOut() { 351 super.setStdOut(); 352 return this; 353 } 354 355 @Override /* Overridden from Assertion */ 356 public FluentResponseBodyAssertion<R> setThrowable(Class<? extends RuntimeException> value) { 357 super.setThrowable(value); 358 return this; 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 @Override 378 protected String valueAsString() throws AssertionError { 379 try { 380 return value().cache().asString(); 381 } catch (RestCallException e) { 382 throw error(e, "Exception occurred during call."); 383 } 384 } 385}