blob: 47598437751a97556d9ea1615e851116a9722d13 [file] [log] [blame]
// ***************************************************************************************************************************
// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file *
// * distributed with this work for additional information regarding copyright ownership. The ASF licenses this file *
// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance *
// * with the License. You may obtain a copy of the License at *
// * *
// * http://www.apache.org/licenses/LICENSE-2.0 *
// * *
// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an *
// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the *
// * specific language governing permissions and limitations under the License. *
// ***************************************************************************************************************************
package org.apache.juneau.rest.mock2;
import static org.apache.juneau.internal.StringUtils.*;
import java.io.*;
import java.text.*;
import java.util.*;
import java.util.regex.*;
import javax.servlet.*;
import javax.servlet.http.*;
import org.apache.juneau.internal.*;
import org.apache.juneau.rest.util.*;
/**
* An implementation of {@link HttpServletResponse} for mocking purposes.
*
* <ul class='seealso'>
* <li class='link'>{@doc juneau-rest-mock.MockRest}
* </ul>
*/
public class MockServletResponse implements HttpServletResponse, MockHttpResponse {
private String characterEncoding = "UTF-8";
private ByteArrayOutputStream baos = new ByteArrayOutputStream();
private long contentLength = 0;
private int bufferSize = 0;
private Locale locale;
private int sc;
private String msg;
private Map<String,String[]> headerMap = new LinkedHashMap<>();
/**
* Creates a new servlet response.
*
* @return A new response.
*/
public static MockServletResponse create() {
return new MockServletResponse();
}
/**
* Returns the content length.
*
* @return The content length.
*/
public long getContentLength() {
return contentLength;
}
/**
* Returns the response message.
*
* @return The response message.
*/
@Override /* MockHttpResponse */
public String getMessage() {
return msg;
}
@Override /* HttpServletResponse */
public String getCharacterEncoding() {
return characterEncoding ;
}
@Override /* HttpServletResponse */
public String getContentType() {
return getHeader("Content-Type");
}
@Override /* HttpServletResponse */
public ServletOutputStream getOutputStream() throws IOException {
return new FinishableServletOutputStream(baos);
}
@Override /* HttpServletResponse */
public PrintWriter getWriter() throws IOException {
return new PrintWriter(new OutputStreamWriter(getOutputStream(), characterEncoding));
}
@Override /* HttpServletResponse */
public void setCharacterEncoding(String charset) {
this.characterEncoding = charset;
}
/**
* Fluent setter for {@link #setCharacterEncoding(String)}.
*
* @param value The new property value.
* @return This object (for method chaining).
*/
public MockServletResponse characterEncoding(String value) {
setCharacterEncoding(value);
return this;
}
@Override /* HttpServletResponse */
public void setContentLength(int len) {
this.contentLength = len;
}
/**
* Fluent setter for {@link #setContentLength(int)}.
*
* @param value The new property value.
* @return This object (for method chaining).
*/
public MockServletResponse contentLength(int value) {
setContentLength(value);
return this;
}
@Override /* HttpServletResponse */
public void setContentLengthLong(long len) {
this.contentLength = len;
}
@Override /* HttpServletResponse */
public void setContentType(String type) {
setHeader("Content-Type", type);
}
/**
* Fluent setter for {@link #setContentType(String)}.
*
* @param value The new property value.
* @return This object (for method chaining).
*/
public MockServletResponse contentType(String value) {
setContentType(value);
return this;
}
@Override /* HttpServletResponse */
public void setBufferSize(int size) {
this.bufferSize = size;
}
/**
* Fluent setter for {@link #bufferSize(int)}.
*
* @param value The new property value.
* @return This object (for method chaining).
*/
public MockServletResponse bufferSize(int value) {
setBufferSize(value);
return this;
}
@Override /* HttpServletResponse */
public int getBufferSize() {
return bufferSize;
}
@Override /* HttpServletResponse */
public void flushBuffer() throws IOException {
}
@Override /* HttpServletResponse */
public void resetBuffer() {
}
@Override /* HttpServletResponse */
public boolean isCommitted() {
return false;
}
@Override /* HttpServletResponse */
public void reset() {
}
@Override /* HttpServletResponse */
public void setLocale(Locale loc) {
this.locale = loc;
}
/**
* Fluent setter for {@link #setLocale(Locale)}.
*
* @param value The new property value.
* @return This object (for method chaining).
*/
public MockServletResponse locale(Locale value) {
setLocale(value);
return this;
}
@Override /* HttpServletResponse */
public Locale getLocale() {
return locale;
}
@Override /* HttpServletResponse */
public void addCookie(Cookie cookie) {
}
@Override /* HttpServletResponse */
public boolean containsHeader(String name) {
return getHeader(name) != null;
}
@Override /* HttpServletResponse */
public String encodeURL(String url) {
return null;
}
@Override /* HttpServletResponse */
public String encodeRedirectURL(String url) {
return null;
}
@Override /* HttpServletResponse */
public String encodeUrl(String url) {
return null;
}
@Override /* HttpServletResponse */
public String encodeRedirectUrl(String url) {
return null;
}
@Override /* HttpServletResponse */
public void sendError(int sc, String msg) throws IOException {
this.sc = sc;
this.msg = msg;
}
@Override /* HttpServletResponse */
public void sendError(int sc) throws IOException {
this.sc = sc;
}
@Override /* HttpServletResponse */
public void sendRedirect(String location) throws IOException {
this.sc = 302;
headerMap.put("Location", new String[] {location});
}
@Override /* HttpServletResponse */
public void setDateHeader(String name, long date) {
headerMap.put(name, new String[] {DateUtils.formatDate(new Date(date), DateUtils.PATTERN_RFC1123)});
}
@Override /* HttpServletResponse */
public void addDateHeader(String name, long date) {
headerMap.put(name, new String[] {DateUtils.formatDate(new Date(date), DateUtils.PATTERN_RFC1123)});
}
@Override /* HttpServletResponse */
public void setHeader(String name, String value) {
headerMap.put(name, new String[] {value});
}
@Override /* HttpServletResponse */
public void addHeader(String name, String value) {
headerMap.put(name, new String[] {value});
}
/**
* Fluent setter for {@link #setHeader(String,String)}.
*
* @param name The header name.
* @param value The new header value.
* @return This object (for method chaining).
*/
public MockServletResponse header(String name, String value) {
setHeader(name, value);
return this;
}
@Override /* HttpServletResponse */
public void setIntHeader(String name, int value) {
headerMap.put(name, new String[] {String.valueOf(value)});
}
@Override /* HttpServletResponse */
public void addIntHeader(String name, int value) {
headerMap.put(name, new String[] {String.valueOf(value)});
}
@Override /* HttpServletResponse */
public void setStatus(int sc) {
this.sc = sc;
}
/**
* Fluent setter for {@link #setStatus(int)}.
*
* @param value The new property value.
* @return This object (for method chaining).
*/
public MockServletResponse status(int value) {
setStatus(value);
return this;
}
@Override /* HttpServletResponse */
public void setStatus(int sc, String sm) {
this.sc = sc;
this.msg = sm;
}
@Override /* HttpServletResponse */
public int getStatus() {
return sc;
}
@Override /* HttpServletResponse */
public String getHeader(String name) {
String[] s = headerMap.get(name);
return s == null || s.length == 0 ? null : s[0];
}
@Override /* HttpServletResponse */
public Collection<String> getHeaders(String name) {
String[] s = headerMap.get(name);
return s == null ? Collections.emptyList() : Arrays.asList(s);
}
@Override /* HttpServletResponse */
public Collection<String> getHeaderNames() {
return headerMap.keySet();
}
/**
* Returns the body of the request as a string.
*
* @return The body of the request as a string.
*/
public String getBodyAsString() {
try {
return baos.toString("UTF-8");
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
}
/**
* Throws an {@link AssertionError} if the response status does not match the expected status.
*
* @param status The expected status.
* @return This object (for method chaining).
* @throws AssertionError Thrown if status does not match.
*/
public MockServletResponse assertStatus(int status) throws AssertionError {
if (getStatus() != status)
throw new MockAssertionError("Response did not have the expected status.\n\tExpected=[{0}]\n\tActual=[{1}]", status, getStatus());
return this;
}
/**
* Throws an {@link AssertionError} if the response body does not contain the expected text.
*
* @param text The expected text of the body.
* @return This object (for method chaining).
* @throws AssertionError Thrown if the body does not contain the expected text.
*/
public MockServletResponse assertBody(String text) throws AssertionError {
if (! StringUtils.isEquals(text, getBodyAsString()))
throw new MockAssertionError("Response did not have the expected text.\n\tExpected=[{0}]\n\tActual=[{1}]", text, getBodyAsString());
return this;
}
/**
* Throws an {@link AssertionError} if the response body does not contain all of the expected substrings.
*
* @param substrings The expected substrings.
* @return This object (for method chaining).
* @throws AssertionError Thrown if the body does not contain one or more of the expected substrings.
*/
public MockServletResponse assertBodyContains(String...substrings) throws AssertionError {
String text = getBodyAsString();
for (String substring : substrings)
if (! contains(text, substring))
throw new MockAssertionError("Response did not have the expected substring.\n\tExpected=[{0}]\n\tBody=[{1}]", substring, text);
return this;
}
/**
* Throws an {@link AssertionError} if the response body does not match the specified pattern.
*
* <p>
* A pattern is a simple string containing <js>"*"</js> to represent zero or more arbitrary characters.
*
* @param pattern The pattern to match against.
* @return This object (for method chaining).
* @throws AssertionError Thrown if the body does not match the specified pattern.
*/
public MockServletResponse assertBodyMatches(String pattern) throws AssertionError {
String text = getBodyAsString();
if (! getMatchPattern(pattern).matcher(text).matches())
throw new MockAssertionError("Response did not match expected pattern.\n\tPattern=[{0}]\n\tBody=[{1}]", pattern, text);
return this;
}
/**
* Throws an {@link AssertionError} if the response body does not match the specified regular expression.
*
* <p>
* A pattern is a simple string containing <js>"*"</js> to represent zero or more arbitrary characters.
*
* @param regExp The regular expression to match against.
* @return This object (for method chaining).
* @throws AssertionError Thrown if the body does not match the specified regular expression.
*/
public MockServletResponse assertBodyMatchesRE(String regExp) throws AssertionError {
String text = getBodyAsString();
if (! Pattern.compile(regExp).matcher(text).matches())
throw new MockAssertionError("Response did not match expected regular expression.\n\tRegExp=[{0}]\n\tBody=[{1}]", regExp, text);
return this;
}
/**
* Throws an {@link AssertionError} if the response does not contain the expected character encoding.
*
* @param value The expected character encoding.
* @return This object (for method chaining).
* @throws AssertionError Thrown if the response does not contain the expected character encoding.
*/
public MockServletResponse assertCharset(String value) {
if (! StringUtils.isEquals(value, getCharacterEncoding()))
throw new MockAssertionError("Response did not have the expected character encoding.\n\tExpected=[{0}]\n\tActual=[{1}]", value, getBodyAsString());
return this;
}
/**
* Throws an {@link AssertionError} if the response does not contain the expected header value.
*
* @param name The header name.
* @param value The expected header value.
* @return This object (for method chaining).
* @throws AssertionError Thrown if the response does not contain the expected header value.
*/
public MockServletResponse assertHeader(String name, String value) {
if (! StringUtils.isEquals(value, getHeader(name)))
throw new MockAssertionError("Response did not have the expected value for header {0}.\n\tExpected=[{1}]\n\tActual=[{2}]", name, value, getHeader(name));
return this;
}
/**
* Throws an {@link AssertionError} if the response header does not contain all of the expected substrings.
*
* @param name The header name.
* @param substrings The expected substrings.
* @return This object (for method chaining).
* @throws AssertionError Thrown if the header does not contain one or more of the expected substrings.
*/
public MockServletResponse assertHeaderContains(String name, String...substrings) {
String text = getHeader(name);
for (String substring : substrings)
if (! contains(text, substring))
throw new MockAssertionError("Response did not have the expected substring in header {0}.\n\tExpected=[{1}]\n\tHeader=[{2}]", name, substring, text);
return this;
}
/**
* Returns the body of the request.
*
* @return The body of the request.
*/
@Override /* MockHttpResponse */
public byte[] getBody() {
return baos.toByteArray();
}
@Override /* MockHttpResponse */
public Map<String,String[]> getHeaders() {
return headerMap;
}
private static class MockAssertionError extends AssertionError {
private static final long serialVersionUID = 1L;
MockAssertionError(String msg, Object...args) {
super(MessageFormat.format(msg, args));
System.err.println(getMessage()); // NOT DEBUG
}
}
}