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.server.assertions; 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.response.*; 026import org.apache.juneau.rest.server.httppart.*; 027import org.apache.juneau.bct.assertions.*; 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#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 FluentRequestContentAssertion} 064 * <ul class='javatreec'> 065 * <li class='jm'>{@link FluentRequestContentAssertion#asBytes() asBytes()} 066 * <li class='jm'>{@link FluentRequestContentAssertion#as(Class) as(Class)} 067 * <li class='jm'>{@link FluentRequestContentAssertion#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 * </ul> 094 * 095 * @param <R> The return type. 096 */ 097@SuppressWarnings({ 098 "java:S115", // Constants use UPPER_snakeCase convention (e.g., MSG_exceptionDuringCall) 099 "java:S1192" // Duplicated literals read more clearly inline than as constants. 100}) 101public class FluentRequestContentAssertion<R> extends FluentObjectAssertion<RequestContent,R> { 102 103 /** 104 * Chained constructor. 105 * 106 * <p> 107 * Used when transforming one assertion into another so that the assertion config can be used by the new assertion. 108 * 109 * @param creator 110 * The assertion that created this assertion. 111 * <br>Should be <jk>null</jk> if this is the top-level assertion. 112 * @param value 113 * The object being tested. 114 * <br>Can be <jk>null</jk> (assertions such as {@code isNull()} then operate on the absence of a value). 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 FluentRequestContentAssertion(Assertion creator, RequestContent value, R returns) { 121 super(creator, value, returns); 122 setThrowable(BadRequest.class); 123 } 124 125 /** 126 * Constructor. 127 * 128 * @param value 129 * The object being tested. 130 * <br>Can be <jk>null</jk> (assertions such as {@code isNull()} then operate on the absence of a value). 131 * @param returns 132 * The object to return after a test method is called. 133 * <br>If <jk>null</jk>, the test method returns this object allowing multiple test method calls to be 134 * used on the same assertion. 135 */ 136 public FluentRequestContentAssertion(RequestContent value, R returns) { 137 this(null, value, returns); 138 } 139 140 /** 141 * Converts the content to a type using {@link RequestContent#as(Class)} and then returns the value as an object assertion. 142 * 143 * <h5 class='section'>Examples:</h5> 144 * <p class='bjava'> 145 * <jc>// Validates the request content bean is the expected value.</jc> 146 * <jv>request</jv> 147 * .assertContent() 148 * .as(MyBean.<jk>class</jk>) 149 * .asJson().is(<js>"{foo:'bar'}"</js>); 150 * </p> 151 * 152 * <h5 class='section'>Notes:</h5><ul> 153 * <li class='note'> 154 * If no charset was found on the <code>Content-Type</code> request header, <js>"UTF-8"</js> is assumed. 155 * <li class='note'> 156 * When using this method, the content is automatically cached by calling the {@link RequestContent#cache()}. 157 * <li class='note'> 158 * The input stream is automatically closed after this call. 159 * </ul> 160 * 161 * <p> 162 * 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}. 163 * 164 * @param <T> The object type to create. 165 * @param type The object type to create. 166 * @return A new fluent assertion object. 167 */ 168 public <T> FluentObjectAssertion<T,R> as(Class<T> type) { 169 return new FluentObjectAssertion<>(valueAsType(type), returns()); 170 } 171 172 /** 173 * Converts the content to a type using {@link RequestContent#as(Type,Type...)} and then returns the value as an object assertion. 174 * 175 * <h5 class='section'>Examples:</h5> 176 * <p class='bjava'> 177 * <jc>// Validates the request content bean is the expected value.</jc> 178 * <jv>request</jv> 179 * .assertContent() 180 * .as(Map.<jk>class</jk>,String.<jk>class</jk>,Integer.<jk>class</jk>) 181 * .asJson().is(<js>"{foo:123}"</js>); 182 * </p> 183 * 184 * <h5 class='section'>Notes:</h5><ul> 185 * <li class='note'> 186 * If no charset was found on the <code>Content-Type</code> request header, <js>"UTF-8"</js> is assumed. 187 * <li class='note'> 188 * When using this method, the content is automatically cached by calling the {@link RequestContent#cache()}. 189 * <li class='note'> 190 * The input stream is automatically closed after this call. 191 * </ul> 192 * 193 * <p> 194 * 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}. 195 * 196 * @param <T> The type to create. 197 * @param type The object type to create. 198 * @param args Optional type arguments. 199 * @return A new fluent assertion object. 200 */ 201 public <T> FluentObjectAssertion<T,R> as(Type type, Type...args) { 202 return new FluentObjectAssertion<>(valueAsType(type, args), returns()); 203 } 204 205 /** 206 * Provides the ability to perform fluent-style assertions on the bytes of the request content. 207 * 208 * <h5 class='section'>Examples:</h5> 209 * <p class='bjava'> 210 * <jc>// Validates the request content equals the text "foo".</jc> 211 * <jv>request</jv> 212 * .assertContent().asBytes().asHex().is(<js>"666F6F"</js>); 213 * </p> 214 * 215 * <h5 class='section'>Notes:</h5><ul> 216 * <li class='note'> 217 * If no charset was found on the <code>Content-Type</code> request header, <js>"UTF-8"</js> is assumed. 218 * <li class='note'> 219 * When using this method, the content is automatically cached by calling the {@link RequestContent#cache()}. 220 * <li class='note'> 221 * The input stream is automatically closed after this call. 222 * </ul> 223 * 224 * @return A new fluent assertion object. 225 */ 226 public FluentByteArrayAssertion<R> asBytes() { 227 return new FluentByteArrayAssertion<>(valueAsBytes(), returns()); 228 } 229 230 /** 231 * Asserts that the content contains the specified value. 232 * 233 * @param values The value to check against. 234 * @return This object. 235 * @throws AssertionError If assertion failed. 236 */ 237 public R is(String values) throws AssertionError { 238 return asString().is(values); 239 } 240 241 /** 242 * Asserts that the text contains all of the specified substrings. 243 * 244 * @param values The values to check against. 245 * @return This object. 246 * @throws AssertionError If assertion failed. 247 */ 248 public R isContains(String...values) throws AssertionError { 249 return asString().isContains(values); 250 } 251 252 /** 253 * Asserts that the content is empty. 254 * 255 * @return This object. 256 * @throws AssertionError If assertion failed. 257 */ 258 public R isEmpty() { return asString().isEmpty(); } 259 260 /** 261 * Asserts that the content doesn't contain any of the specified substrings. 262 * 263 * @param values The values to check against. 264 * @return This object. 265 * @throws AssertionError If assertion failed. 266 */ 267 public R isNotContains(String...values) throws AssertionError { 268 return asString().isNotContains(values); 269 } 270 271 /** 272 * Asserts that the content is not empty. 273 * 274 * @return This object. 275 * @throws AssertionError If assertion failed. 276 */ 277 public R isNotEmpty() { return asString().isNotEmpty(); } 278 279 @Override /* Overridden from Assertion */ 280 public FluentRequestContentAssertion<R> setMsg(String msg, Object...args) { 281 super.setMsg(msg, args); 282 return this; 283 } 284 285 @Override /* Overridden from Assertion */ 286 public FluentRequestContentAssertion<R> setOut(PrintStream value) { 287 super.setOut(value); 288 return this; 289 } 290 291 @Override /* Overridden from Assertion */ 292 public FluentRequestContentAssertion<R> setSilent() { 293 super.setSilent(); 294 return this; 295 } 296 297 @Override /* Overridden from Assertion */ 298 public FluentRequestContentAssertion<R> setStdOut() { 299 super.setStdOut(); 300 return this; 301 } 302 303 @Override /* Overridden from Assertion */ 304 public FluentRequestContentAssertion<R> setThrowable(Class<? extends RuntimeException> value) { 305 super.setThrowable(value); 306 return this; 307 } 308 309 private byte[] valueAsBytes() throws AssertionError { 310 try { 311 return value().cache().asBytes(); 312 } catch (IOException e) { 313 throw error(e, "Exception occurred during call."); 314 } 315 } 316 317 private <T> T valueAsType(Class<T> c) throws AssertionError { 318 try { 319 return value().cache().as(c); 320 } catch (IOException e) { 321 throw error(e, "Exception occurred during call."); 322 } 323 } 324 325 private <T> T valueAsType(Type c, Type...args) throws AssertionError { 326 try { 327 return value().cache().as(c, args); 328 } catch (IOException e) { 329 throw error(e, "Exception occurred during call."); 330 } 331 } 332 333 @Override 334 protected String valueAsString() throws AssertionError { 335 try { 336 return value().cache().asString(); 337 } catch (IOException e) { 338 throw error(e, "Exception occurred during call."); 339 } 340 } 341}