
nl.hsac.fitnesse.fixture.slim.HttpTest Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of hsac-fitnesse-fixtures Show documentation
Show all versions of hsac-fitnesse-fixtures Show documentation
Fixtures to assist in testing via FitNesse
package nl.hsac.fitnesse.fixture.slim;
import freemarker.template.Template;
import nl.hsac.fitnesse.fixture.util.HttpResponse;
import org.apache.http.client.CookieStore;
import org.apache.http.cookie.Cookie;
import org.apache.http.impl.client.BasicCookieStore;
import org.apache.http.impl.cookie.BasicClientCookie;
import java.io.File;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Fixture to make HTTP requests using Slim scripts and/or scenarios.
*/
public class HttpTest extends SlimFixtureWithMap {
/** Default content type for posts and puts. */
public final static String DEFAULT_POST_CONTENT_TYPE = "application/x-www-form-urlencoded; charset=UTF-8";
private final Map headerValues = new LinkedHashMap<>();
private boolean storeCookies = false;
private HttpResponse response = createResponse();
private String template;
private boolean explicitContentTypeSet = false;
private String contentType = DEFAULT_POST_CONTENT_TYPE;
private String lastUrl = null;
private String lastMethod = null;
/**
* Sets template to use.
* @param aTemplate name of template file (relative to 'templates' directory on classpath)
* @return true if template could be found.
*/
public boolean template(String aTemplate) {
boolean result = false;
Template t = getEnvironment().getTemplate(aTemplate);
if (t != null) {
template = aTemplate;
result = true;
}
return result;
}
/**
* Stores value to be passed as headers.
* @param value value to be passed.
* @param name name to use this value for.
*/
public void setValueForHeader(Object value, String name) {
getMapHelper().setValueForIn(value, name, headerValues);
}
/**
* Clears a header value previously set.
* @param name value to remove.
* @return true if value was present.
*/
public boolean clearHeaderValue(String name) {
String cleanName = cleanupValue(name);
boolean result = headerValues.containsKey(cleanName);
headerValues.remove(cleanName);
return result;
}
/**
* Clears all header values previously set.
*/
public void clearHeaderValues() {
headerValues.clear();
}
//// methods to support usage in dynamic decision tables
/**
* Called before next row is executed. (Clears all current and header values.)
*/
@Override
public void reset() {
clearValues();
clearHeaderValues();
}
private static final Pattern HEADER_KEY_PATTERN = Pattern.compile("header:\\s*(\\.+)");
/**
* Sets a value.
* @param key (possibly nested) key to set value for.
* @param value value to be stored.
*/
public void set(String key, Object value) {
Matcher m = HEADER_KEY_PATTERN.matcher(key);
if (m.matches()) {
String headerKey = m.group(1);
setValueForHeader(value, headerKey);
} else {
super.set(key, value);
}
}
//// end: methods to support usage in dynamic decision tables
/**
* Sends HTTP POST template with current values to service endpoint.
* @param serviceUrl service endpoint to send request to.
* @return true if call could be made and response did not indicate error.
*/
public boolean postTemplateTo(String serviceUrl) {
return postTemplateTo(serviceUrl, getContentType());
}
/**
* Sends HTTP POST template with current values to service endpoint.
* @param serviceUrl service endpoint to send request to.
* @param aContentType content type to use for post.
* @return true if call could be made and response did not indicate error.
*/
public boolean postTemplateTo(String serviceUrl, String aContentType) {
boolean result;
resetResponse();
if (template == null) {
throw new StopTestException("No template available to use in post");
} else {
String url = getUrl(serviceUrl);
try {
storeLastCall("POST", serviceUrl);
getEnvironment().doHttpPost(url, template, getCurrentValues(), response, headerValues, aContentType);
} catch (Throwable t) {
throw new StopTestException("Unable to get response from POST to: " + url, t);
}
result = postProcessResponse();
}
return result;
}
/**
* Sends HTTP POST body to service endpoint.
* @param body content to post
* @param serviceUrl service endpoint to send body to.
* @return true if call could be made and response did not indicate error.
*/
public boolean postTo(String body, String serviceUrl) {
String cleanedBody = cleanupBody(body);
return postToImpl(cleanedBody, serviceUrl);
}
/**
* Sends a file by HTTP POST body to service endpoint.
* @param fileName fileName to post
* @param serviceUrl service endpoint to send body to.
* @return true if call could be made and response did not indicate error.
*/
public boolean postFileTo(String fileName, String serviceUrl) {
return postFileToImpl(fileName, serviceUrl);
}
/**
* Sends all values (url encoded) using post.
* @param serviceUrl service endpoint to send values to.
* @return true if call could be made and response did not indicate error.
*/
public boolean postValuesTo(String serviceUrl) {
String body = urlEncodeCurrentValues();
return postToImpl(body, serviceUrl);
}
protected boolean postToImpl(String body, String serviceUrl) {
return postToImpl(body, serviceUrl, getContentType());
}
protected boolean postToImpl(String body, String serviceUrl, String aContentType) {
boolean result;
resetResponse();
response.setRequest(body);
String url = getUrl(serviceUrl);
try {
storeLastCall("POST", serviceUrl);
getEnvironment().doHttpPost(url, response, headerValues, aContentType);
} catch (Throwable t) {
throw new StopTestException("Unable to get response from POST to: " + url, t);
}
result = postProcessResponse();
return result;
}
protected boolean postFileToImpl(String fileName, String serviceUrl) {
boolean result;
resetResponse();
String url = getUrl(serviceUrl);
String filePath = getFilePathFromWikiUrl(fileName);
File file = new File(filePath);
if (!file.exists()) {
throw new StopTestException(false, "File " + filePath + " not found.");
}
try {
response.setRequest(fileName);
storeLastCall("POST_FILE", serviceUrl);
getEnvironment().doHttpFilePost(url, response, headerValues, file);
} catch (Throwable t) {
throw new StopTestException("Unable to get response from POST to: " + url, t);
}
result = postProcessResponse();
return result;
}
/**
* Sends HTTP PUT template with current values to service endpoint.
* @param serviceUrl service endpoint to send request to.
* @return true if call could be made and response did not indicate error.
*/
public boolean putTemplateTo(String serviceUrl) {
return putTemplateTo(serviceUrl, getContentType());
}
/**
* Sends HTTP PUT template with current values to service endpoint.
* @param serviceUrl service endpoint to send request to.
* @param aContentType content type to use for post.
* @return true if call could be made and response did not indicate error.
*/
public boolean putTemplateTo(String serviceUrl, String aContentType) {
boolean result;
resetResponse();
if (template == null) {
throw new StopTestException("No template available to use in put");
} else {
String url = getUrl(serviceUrl);
try {
storeLastCall("PUT", serviceUrl);
getEnvironment().doHttpPut(url, template, getCurrentValues(), response, headerValues, aContentType);
} catch (Throwable t) {
throw new StopTestException("Unable to get response from PUT to: " + url, t);
}
result = postProcessResponse();
}
return result;
}
/**
* Sends HTTP PUT body to service endpoint.
* @param body content to put
* @param serviceUrl service endpoint to send body to.
* @return true if call could be made and response did not indicate error.
*/
public boolean putTo(String body, String serviceUrl) {
String cleanedBody = cleanupBody(body);
return putToImpl(cleanedBody, serviceUrl);
}
/**
* Sends all values (url encoded) using put.
* @param serviceUrl service endpoint to send values to.
* @return true if call could be made and response did not indicate error.
*/
public boolean putValuesTo(String serviceUrl) {
String body = urlEncodeCurrentValues();
return putToImpl(body, serviceUrl);
}
protected boolean putToImpl(String body, String serviceUrl) {
return putToImpl(body, serviceUrl, getContentType());
}
protected boolean putToImpl(String body, String serviceUrl, String aContentType) {
boolean result;
resetResponse();
response.setRequest(body);
String url = getUrl(serviceUrl);
try {
storeLastCall("PUT", serviceUrl);
getEnvironment().doHttpPut(url, response, headerValues, aContentType);
} catch (Throwable t) {
throw new StopTestException("Unable to get response from PUT to: " + url, t);
}
result = postProcessResponse();
return result;
}
protected String cleanupBody(String body) {
return getEnvironment().getHtmlCleaner().cleanupPreFormatted(body);
}
/**
* Sends HTTP GET to service endpoint to retrieve content.
* @param serviceUrl service endpoint to get content from.
* @return true if call could be made and response did not indicate error.
*/
public boolean getFrom(String serviceUrl) {
return getImpl(serviceUrl, true);
}
/**
* Sends HTTP GET to service endpoint to retrieve content, not following a redirect if sent.
* @param serviceUrl service endpoint to get content from.
* @return true if call could be made and response did not indicate error.
*/
public boolean getFromNoRedirect(String serviceUrl) {
return getImpl(serviceUrl, false);
}
protected boolean getImpl(String serviceUrl, boolean followRedirect) {
boolean result;
resetResponse();
String url = createUrlWithParams(serviceUrl);
try {
String method;
if (followRedirect) {
method = "GET";
} else {
method = "GET_NO_REDIRECT";
}
storeLastCall(method, serviceUrl);
getEnvironment().doGet(url, response, headerValues, followRedirect);
} catch (Throwable t) {
throw new StopTestException("Unable to GET response from: " + url, t);
}
result = postProcessResponse();
return result;
}
/**
* Sends HTTP DELETE to service endpoint.
* @param serviceUrl service endpoint to delete.
* @return true if call could be made and response did not indicate error.
*/
public boolean delete(String serviceUrl) {
boolean result;
resetResponse();
String url = createUrlWithParams(serviceUrl);
try {
storeLastCall("DELETE", serviceUrl);
getEnvironment().doDelete(url, response, headerValues);
} catch (Throwable t) {
throw new StopTestException("Unable to DELETE: " + url, t);
}
result = postProcessResponse();
return result;
}
protected void resetResponse() {
CookieStore cookieStore = null;
if (storeCookies) {
cookieStore = getResponse().getCookieStore();
if (cookieStore == null) {
cookieStore = new BasicCookieStore();
}
}
response = createResponse();
if (storeCookies) {
response.setCookieStore(cookieStore);
}
}
String createUrlWithParams(String serviceUrl) {
String baseUrl = getUrl(serviceUrl);
if (!getCurrentValues().isEmpty()) {
if (baseUrl.contains("?") && !baseUrl.endsWith("?")) {
baseUrl += "&";
}
if (!baseUrl.contains("?")) {
baseUrl += "?";
}
baseUrl += urlEncodeCurrentValues();
}
return baseUrl;
}
protected String urlEncodeCurrentValues() {
StringBuilder sb = new StringBuilder();
addUrlEncodedKeyValues(sb, "", getCurrentValues());
return sb.toString();
}
private void addUrlEncodedKeyValues(StringBuilder sb, String prefix, Map map) {
for (Map.Entry entry : map.entrySet()) {
String key = prefix + entry.getKey();
Object value = entry.getValue();
if (value instanceof List) {
List values = (List) value;
for (Object v : values) {
addEncodedKeyValue(sb, key, v);
}
} else if (value instanceof Map) {
addUrlEncodedKeyValues(sb, key + ".", (Map) value);
} else {
addEncodedKeyValue(sb, key, value);
}
}
}
private void addEncodedKeyValue(StringBuilder sb, String key, Object value) {
if (sb.length() != 0) {
sb.append("&");
}
sb.append(urlEncode(key));
if (value != null) {
sb.append("=");
sb.append(urlEncode(value.toString()));
}
}
protected String urlEncode(String str) {
try {
return URLEncoder.encode(str, "utf-8");
} catch (UnsupportedEncodingException e) {
throw new Error(e);
}
}
/**
* Performs any post processing directly after retrieving response.
* @return true if all is well, false otherwise.
*/
protected boolean postProcessResponse() {
return responseIsValid();
}
/**
* @return true if response does not indicate an error.
*/
public boolean responseIsValid() {
boolean result;
try {
response.validResponse();
result = true;
} catch (RuntimeException e) {
result = false;
}
return result;
}
/**
* @return request sent last time.
*/
public String request() {
return safeFormatValue(response.getRequest());
}
/**
* @return response received last time postTo(), delete() or getFrom() was called.
*/
public String response() {
return safeFormatValue(response.getResponse());
}
/**
* Internal method to format a value, which will just return the 'raw' value if there is a problem formatting.
* @param value value to format
* @return formatted value
*/
protected final String safeFormatValue(String value) {
String result;
try {
result = formatValue(value);
} catch (Exception e) {
result = value;
}
return result;
}
/**
* Method that will take care of formatting a value, which may be overridden in subclasses.
* This implementation just returns value.
* @param value value to format
* @return formatted value
*/
protected String formatValue(String value) {
return value;
}
/**
* @return HTML response received last time postTo() or get() was called.
*/
public String htmlResponse() {
String content = response.getResponse();
content = "" + content + "";
return content;
}
/**
* @return response time in ms for call to service.
*/
public long responseTime() {
return getResponse().getResponseTime();
}
/**
* @return http status received in response to last request.
*/
public int responseStatus() {
return response.getStatusCode();
}
/**
* @return headers received with response to last request.
*/
public Map responseHeaders() {
return response.getResponseHeaders();
}
/**
* @param headerName name of response header.
* @return value of header in last response (may be a list if the saame header name was sent multiple times
* (e.g. Set-Cookie).
*/
public Object responseHeader(String headerName) {
return responseHeaders().get(headerName);
}
public void setStoreCookies(boolean storeCookies) {
this.storeCookies = storeCookies;
}
/**
* Adds all current Selenium cookies to this fixture's cookie store.
* This will also ensure this class will store cookies (otherwise copying the cookies has no purpose).
*/
public void copyBrowserCookies() {
setStoreCookies(true);
getEnvironment().addSeleniumCookies(getResponse());
}
/**
* @return name->value of cookies in the cookie store.
*/
public Map cookieValues() {
Map result = null;
CookieStore cookies = getResponse().getCookieStore();
if (cookies != null) {
result = new LinkedHashMap<>();
for (Cookie cookie : cookies.getCookies()) {
result.put(cookie.getName(), cookie.getValue());
}
}
return result;
}
/**
* @param cookieName name of cookie.
* @return value of cookie in the cookie store.
*/
public String cookieValue(String cookieName) {
String result = null;
Cookie cookie = getCookie(cookieName);
if (cookie != null) {
result = cookie.getValue();
}
return result;
}
/**
* @param cookieName name of cookie.
* @return domain of cookie in the cookie store.
*/
public String cookieDomain(String cookieName) {
String result = null;
Cookie cookie = getCookie(cookieName);
if (cookie != null) {
result = cookie.getDomain();
}
return result;
}
/**
* @param cookieName name of cookie.
* @return path of cookie in the cookie store.
*/
public String cookiePath(String cookieName) {
String result = null;
Cookie cookie = getCookie(cookieName);
if (cookie != null) {
result = cookie.getPath();
}
return result;
}
/**
* @param cookieName name of cookie.
* @return whether cookie in the cookie store is persistent.
*/
public Boolean cookieIsPersistent(String cookieName) {
Boolean result = null;
Cookie cookie = getCookie(cookieName);
if (cookie != null) {
result = cookie.isPersistent();
}
return result;
}
/**
* @param cookieName name of cookie.
* @return whether cookie in the cookie store requires a secure connection.
*/
public Boolean cookieIsSecure(String cookieName) {
Boolean result = null;
Cookie cookie = getCookie(cookieName);
if (cookie != null) {
result = cookie.isSecure();
}
return result;
}
/**
* @param cookieName name of cookie.
* @return whether cookie in the cookie store is http-only (not accessible to Javascript).
*/
public Boolean cookieIsHttpOnly(String cookieName) {
return cookieAttribute(cookieName, "httponly") != null;
}
/**
* @param cookieName name of cookie.
* @param attributeName name of attribute.
* @return value of attribute for cookie.
*/
public String cookieAttribute(String cookieName, String attributeName) {
String result = null;
Cookie cookie = getCookie(cookieName);
if (cookie instanceof BasicClientCookie) {
result = ((BasicClientCookie) cookie).getAttribute(attributeName.toLowerCase(Locale.ENGLISH));
}
return result;
}
private Cookie getCookie(String cookieName) {
return getResponse().getCookieNamed(cookieName);
}
/**
* Removes all cookies from the cookie store.
*/
public void clearCookies() {
getResponse().getCookieStore().clear();
}
protected HttpResponse getResponse() {
return response;
}
protected HttpResponse createResponse() {
return new HttpResponse();
}
public String getContentType() {
return contentType;
}
public void setContentType(String aContentType) {
explicitContentTypeSet = true;
contentType = aContentType;
}
public boolean isExplicitContentTypeSet() {
return explicitContentTypeSet;
}
// Polling
public boolean repeatUntilResponseStatusIs(final int expectedStatus) {
return repeatUntil(
new RepeatLastCall() {
@Override
public boolean isFinished() {
return responseStatus() == expectedStatus;
}
});
}
public boolean repeatUntilResponseIs(final String expectedResponse) {
RepeatCompletion completion;
if (expectedResponse == null) {
completion = new RepeatLastCall() {
@Override
public boolean isFinished() {
return response() == null;
}
};
} else {
completion = new RepeatLastCall() {
@Override
public boolean isFinished() {
Object actual = response();
return compareActualToExpected(expectedResponse, actual);
}
};
}
return repeatUntil(completion);
}
public boolean repeatUntilHeaderIs(final String header, final Object expectedValue) {
RepeatCompletion completion;
if (expectedValue == null) {
completion = new RepeatLastCall() {
@Override
public boolean isFinished() {
return responseHeader(header) == null;
}
};
} else {
completion = new RepeatLastCall() {
@Override
public boolean isFinished() {
Object actual = responseHeader(header);
return compareActualToExpected(expectedValue, actual);
}
};
}
return repeatUntil(completion);
}
protected void repeatLastCall() {
if (lastMethod == null) {
throw new SlimFixtureException(false, "First make a call before trying to repeat one.");
}
switch (lastMethod) {
case "GET":
getImpl(lastUrl, true);
break;
case "POST":
postToImpl(response.getRequest(), lastUrl);
break;
case "PUT":
putToImpl(response.getRequest(), lastUrl);
break;
case "DELETE":
delete(lastUrl);
break;
case "GET_NO_REDIRECT":
getImpl(lastUrl, false);
break;
case "POST_FILE":
postFileToImpl(response.getRequest(), lastUrl);
break;
default:
throw new SlimFixtureException(false, "Repeat of method: " + lastMethod + " not configured.");
}
}
protected void storeLastCall(String method, String url) {
lastMethod = method;
lastUrl = url;
}
protected abstract class RepeatLastCall implements RepeatCompletion {
protected boolean compareActualToExpected(Object expected, Object actual) {
boolean result;
if (actual == null) {
result = expected.equals("null");
} else {
result = expected.equals(actual)
|| expected.toString().equals(actual.toString());
}
return result;
}
@Override
public void repeat() {
repeatLastCall();
}
}
// Polling
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy