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