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 static org.apache.juneau.commons.utils.Shorts.*; 020 021import java.io.*; 022 023import org.apache.juneau.commons.utils.*; 024import org.apache.juneau.rest.client.*; 025 026/** 027 * A fluent assertion class for HTTP response bodies. 028 * 029 * <p> 030 * Obtain instances via {@link ResponseAssertion#body()}. 031 * 032 * <h5 class='section'>See Also:</h5><ul> 033 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/NextGenRestClient">Next-Generation REST Client</a> 034 * </ul> 035 * 036 * @since 9.2.1 037 */ 038@SuppressWarnings({ 039 "resource" // Eclipse resource analysis: response is borrowed; caller closes it 040}) 041public final class ResponseBodyAssertion { 042 043 private final RestResponse response; 044 private final ResponseAssertion parent; 045 046 ResponseBodyAssertion(RestResponse response, ResponseAssertion parent) { 047 this.response = response; 048 this.parent = parent; 049 } 050 051 /** 052 * Asserts that the response body (as a UTF-8 string) equals the expected value. 053 * 054 * @param expected The expected body string. May be <jk>null</jk> to assert an empty/absent body. 055 * @return This object (for chaining). 056 * @throws AssertionError If the body does not match. 057 * @throws UncheckedIOException If reading the body throws {@link IOException}. 058 */ 059 @SuppressWarnings({ 060 "java:S1201" // 'equals(String)' is an intentional fluent-assertion DSL method (asserts body equality), not an override of Object.equals(Object); renaming would break this public API. 061 }) 062 public ResponseBodyAssertion equals(String expected) { 063 var actual = readBodyAsString(); 064 if (expected == null ? actual != null : neq(expected, actual)) 065 throw new AssertionError("Expected body '" + expected + "' but got '" + actual + "'"); 066 return this; 067 } 068 069 /** 070 * Asserts that the response body (as a UTF-8 string) contains the given substring. 071 * 072 * @param substring The substring to look for. Must not be <jk>null</jk>. 073 * @return This object (for chaining). 074 * @throws AssertionError If the body is absent or does not contain the substring. 075 * @throws UncheckedIOException If reading the body throws {@link IOException}. 076 */ 077 public ResponseBodyAssertion contains(String substring) { 078 var actual = readBodyAsString(); 079 if (actual == null || !actual.contains(substring)) 080 throw new AssertionError("Body '" + actual + "' does not contain '" + substring + "'"); 081 return this; 082 } 083 084 /** 085 * Asserts that the response body is empty (<jk>null</jk> or zero-length). 086 * 087 * @return This object (for chaining). 088 * @throws AssertionError If the body is non-empty. 089 * @throws UncheckedIOException If reading the body throws {@link IOException}. 090 */ 091 public ResponseBodyAssertion isEmpty() { 092 var actual = readBodyAsString(); 093 if (StringUtils.isNotEmpty(actual)) 094 throw new AssertionError("Expected empty body but got '" + actual + "'"); 095 return this; 096 } 097 098 /** 099 * Asserts that the response body is present and non-empty. 100 * 101 * @return This object (for chaining). 102 * @throws AssertionError If the body is <jk>null</jk> or empty. 103 * @throws UncheckedIOException If reading the body throws {@link IOException}. 104 */ 105 public ResponseBodyAssertion isNotEmpty() { 106 var actual = readBodyAsString(); 107 if (StringUtils.isEmpty(actual)) 108 throw new AssertionError("Expected non-empty body but body was absent or empty"); 109 return this; 110 } 111 112 /** 113 * Asserts that the response body starts with the given prefix. 114 * 115 * @param prefix The expected prefix. Must not be <jk>null</jk>. 116 * @return This object (for chaining). 117 * @throws AssertionError If the body is absent or does not start with the prefix. 118 * @throws UncheckedIOException If reading the body throws {@link IOException}. 119 */ 120 public ResponseBodyAssertion startsWith(String prefix) { 121 var actual = readBodyAsString(); 122 if (actual == null || !actual.startsWith(prefix)) 123 throw new AssertionError("Body '" + actual + "' does not start with '" + prefix + "'"); 124 return this; 125 } 126 127 /** 128 * Returns the parent {@link ResponseAssertion} to continue assertion chaining. 129 * 130 * @return The parent assertion. Never <jk>null</jk>. 131 */ 132 public ResponseAssertion and() { 133 return parent; 134 } 135 136 private String readBodyAsString() { 137 try { 138 return response.getBodyAsString(); 139 } catch (IOException e) { 140 throw new UncheckedIOException(e); 141 } 142 } 143}