blob: d314c3e29d4aa7a0b667423125746f441884db60 [file] [log] [blame]
/*=========================================================================
* Copyright (c) 2010-2014 Pivotal Software, Inc. All Rights Reserved.
* This product is protected by U.S. and international copyright
* and intellectual property laws. Pivotal products are covered by
* one or more patents listed at http://www.pivotal.io/patents.
*=========================================================================
*/
package com.gemstone.gemfire.management.internal.web.shell;
import static org.junit.Assert.*;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import com.gemstone.gemfire.internal.lang.StringUtils;
import com.gemstone.gemfire.management.internal.cli.CommandRequest;
import com.gemstone.gemfire.management.internal.web.AbstractWebTestCase;
import com.gemstone.gemfire.management.internal.web.domain.Link;
import com.gemstone.gemfire.management.internal.web.domain.LinkIndex;
import com.gemstone.gemfire.management.internal.web.http.ClientHttpRequest;
import com.gemstone.gemfire.management.internal.web.http.HttpMethod;
import com.gemstone.gemfire.test.junit.categories.UnitTest;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.experimental.categories.Category;
import org.springframework.core.io.Resource;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.client.ResourceAccessException;
/**
* The RestHttpOperationInvokerJUnitTest class is a test suite of test cases testing the contract and functionality of the
* RestHttpOperationInvoker class.
* <p/>
* @author John Blum
* @see java.net.URI
* @see com.gemstone.gemfire.management.internal.cli.CommandRequest
* @see com.gemstone.gemfire.management.internal.web.http.HttpMethod
* @see com.gemstone.gemfire.management.internal.web.domain.Link
* @see com.gemstone.gemfire.management.internal.web.domain.LinkIndex
* @see com.gemstone.gemfire.management.internal.web.shell.RestHttpOperationInvoker
* @see org.junit.Assert
* @see org.junit.Test
* @since 8.0
*/
@SuppressWarnings("unused")
@Category(UnitTest.class)
public class RestHttpOperationInvokerJUnitTest extends AbstractWebTestCase {
private LinkIndex linkIndex;
private RestHttpOperationInvoker operationInvoker;
@Before
public void setUp() throws Exception {
final Link listLibraries = new Link("list-libraries", toUri("http://host.domain.com/service/v1/libraries"));
final Link getLibrary = new Link("get-library", toUri("http://host.domain.com/service/v1/libraries/{name}"));
final Link listBooks = new Link("list-books", toUri("http://host.domain.com/service/v1/libraries/{name}/books"));
final Link listBooksByAuthor = new Link("list-books", toUri("http://host.domain.com/service/v1/libraries/{name}/books/{author}"));
final Link listBooksByAuthorAndCategory = new Link("list-books", toUri("http://host.domain.com/service/v1/libraries/{name}/books/{author}/{category}"));
final Link listBooksByAuthorAndYear = new Link("list-books", toUri("http://host.domain.com/service/v1/libraries/{name}/books/{author}/{year}"));
final Link listBooksByAuthorCategoryAndYear = new Link("list-books", toUri("http://host.domain.com/service/v1/libraries/{name}/books/{author}/{category}/{year}"));
final Link addBook = new Link("add-book", toUri("http://host.domain.com/service/v1/libraries/{name}/books"), HttpMethod.POST);
final Link getBookByIsbn = new Link("get-book", toUri("http://host.domain.com/service/v1/libraries/{name}/books/{isbn}"));
final Link getBookByTitle = new Link("get-book", toUri("http://host.domain.com/service/v1/libraries/{name}/books/{title}"));
final Link removeBook = new Link("remove-book", toUri("http://host.domain.com/service/v1/libraries/{name}/books/{isbn}"), HttpMethod.DELETE);
linkIndex = new LinkIndex();
linkIndex.addAll(
listLibraries,
getLibrary,
listBooks,
listBooksByAuthor,
listBooksByAuthorAndCategory,
listBooksByAuthorAndYear,
listBooksByAuthorCategoryAndYear,
addBook,
getBookByIsbn,
getBookByTitle,
removeBook
);
assertEquals(11, linkIndex.size());
operationInvoker = new RestHttpOperationInvoker(linkIndex);
assertSame(linkIndex, operationInvoker.getLinkIndex());
}
@After
public void tearDown() {
operationInvoker.stop();
operationInvoker = null;
}
protected CommandRequest createCommandRequest(final String command, final Map<String, String> options) {
return new TestCommandRequest(command, options, Collections.<String, String>emptyMap(), null);
}
protected CommandRequest createCommandRequest(final String command, final Map<String, String> options, final Map<String, String> environment) {
return new TestCommandRequest(command, options, environment, null);
}
protected CommandRequest createCommandRequest(final String command, final Map<String, String> options, final byte[][] fileData) {
return new TestCommandRequest(command, options, Collections.<String, String>emptyMap(), fileData);
}
protected CommandRequest createCommandRequest(final String command, final Map<String, String> options, final Map<String, String> environment, final byte[][] fileData) {
return new TestCommandRequest(command, options, environment, fileData);
}
protected LinkIndex getLinkIndex() {
assertTrue("The LinkIndex was not property initialized!", linkIndex != null);
return linkIndex;
}
protected RestHttpOperationInvoker getOperationInvoker() {
assertTrue("The RestHttpOperationInvoker was not properly initialized!", operationInvoker != null);
return operationInvoker;
}
@Test
public void testCreateHttpRequest() {
final Map<String, String> commandOptions = new HashMap<String, String>();
commandOptions.put("author", "Adams");
commandOptions.put("blankOption", " ");
commandOptions.put("category", "sci-fi");
commandOptions.put("emptyOption", StringUtils.EMPTY_STRING);
commandOptions.put("isbn", "0-123456789");
commandOptions.put("nullOption", null);
commandOptions.put("title", "Hitch Hiker's Guide to the Galaxy");
commandOptions.put("year", "1983");
final CommandRequest command = createCommandRequest("add-book", commandOptions);
final ClientHttpRequest request = getOperationInvoker().createHttpRequest(command);
assertNotNull(request);
assertEquals("POST http://host.domain.com/service/v1/libraries/{name}/books", request.getLink().toHttpRequestLine());
assertEquals("Adams", request.getParameterValue("author"));
assertEquals("sci-fi", request.getParameterValue("category"));
assertEquals("0-123456789", request.getParameterValue("isbn"));
assertEquals("Hitch Hiker's Guide to the Galaxy", request.getParameterValue("title"));
assertEquals("1983", request.getParameterValue("year"));
assertTrue(request.getParameters().containsKey("blankOption"));
assertTrue(request.getParameters().containsKey("emptyOption"));
assertFalse(request.getParameters().containsKey("nullOption"));
for (String requestParameter : request.getParameters().keySet()) {
assertFalse(requestParameter.startsWith(RestHttpOperationInvoker.ENVIRONMENT_VARIABLE_REQUEST_PARAMETER_PREFIX));
}
assertNull(request.getParameterValue(RestHttpOperationInvoker.RESOURCES_REQUEST_PARAMETER));
}
@Test
public void testCreateHttpRequestWithEnvironmentVariables() {
final Map<String, String> commandOptions = new HashMap<String, String>(2);
commandOptions.put("name", "ElLibreDeCongress");
commandOptions.put("isbn", "${ISBN}");
final Map<String, String> environment = new HashMap<String, String>(2);
environment.put("ISBN", "0-987654321");
environment.put("VAR", "test");
final CommandRequest command = createCommandRequest("get-book", commandOptions, environment);
final ClientHttpRequest request = getOperationInvoker().createHttpRequest(command);
assertNotNull(request);
assertEquals("GET http://host.domain.com/service/v1/libraries/{name}/books/{isbn}", request.getLink()
.toHttpRequestLine());
assertEquals("${ISBN}", request.getParameterValue("isbn"));
assertFalse(request.getParameters().containsKey("ISBN"));
assertEquals("0-987654321", request.getParameterValue(
RestHttpOperationInvoker.ENVIRONMENT_VARIABLE_REQUEST_PARAMETER_PREFIX + "ISBN"));
assertFalse(request.getParameters().containsKey("VAR"));
assertEquals("test", request.getParameterValue(
RestHttpOperationInvoker.ENVIRONMENT_VARIABLE_REQUEST_PARAMETER_PREFIX + "VAR"));
}
@Test
public void testCreatHttpRequestWithFileData() {
final Map<String, String> commandOptions = Collections.singletonMap("isbn", "0-123456789");
final byte[][] fileData = {
"/path/to/book/content.txt".getBytes(),
"Once upon a time in a galaxy far, far away...".getBytes()
};
final CommandRequest command = createCommandRequest("add-book", commandOptions, fileData);
final ClientHttpRequest request = getOperationInvoker().createHttpRequest(command);
assertNotNull(request);
assertEquals("POST http://host.domain.com/service/v1/libraries/{name}/books", request.getLink().toHttpRequestLine());
assertEquals("0-123456789", request.getParameterValue("isbn"));
assertTrue(request.getParameters().containsKey(RestHttpOperationInvoker.RESOURCES_REQUEST_PARAMETER));
assertTrue(request.getParameterValue(RestHttpOperationInvoker.RESOURCES_REQUEST_PARAMETER) instanceof Resource);
final List<Object> resources = request.getParameterValues(RestHttpOperationInvoker.RESOURCES_REQUEST_PARAMETER);
assertNotNull(resources);
assertFalse(resources.isEmpty());
assertEquals(1, resources.size());
}
@Test
public void testFindAndResolveLink() throws Exception {
final Map<String, String> commandOptions = new HashMap<String, String>();
commandOptions.put("name", "BarnesN'Noble");
Link link = getOperationInvoker().findLink(createCommandRequest("list-libraries", commandOptions));
assertNotNull(link);
assertEquals("http://host.domain.com/service/v1/libraries", toString(link.getHref()));
link = getOperationInvoker().findLink(createCommandRequest("get-library", commandOptions));
assertNotNull(link);
assertEquals("http://host.domain.com/service/v1/libraries/{name}", toString(link.getHref()));
commandOptions.put("author", "J.K.Rowlings");
link = getOperationInvoker().findLink(createCommandRequest("list-books", commandOptions));
assertNotNull(link);
assertEquals("http://host.domain.com/service/v1/libraries/{name}/books/{author}", toString(link.getHref()));
commandOptions.put("category", "sci-fi");
commandOptions.put("year", "1998");
commandOptions.put("bogus", "data");
link = getOperationInvoker().findLink(createCommandRequest("list-books", commandOptions));
assertNotNull(link);
assertEquals("http://host.domain.com/service/v1/libraries/{name}/books/{author}/{category}/{year}",
toString(link.getHref()));
commandOptions.remove("category");
link = getOperationInvoker().findLink(createCommandRequest("list-books", commandOptions));
assertNotNull(link);
assertEquals("http://host.domain.com/service/v1/libraries/{name}/books/{author}/{year}",
toString(link.getHref()));
commandOptions.put("category", "fantasy");
commandOptions.put("isbn", "0-123456789");
commandOptions.put("title", "Harry Potter");
link = getOperationInvoker().findLink(createCommandRequest("add-book", commandOptions));
assertNotNull(link);
assertEquals("http://host.domain.com/service/v1/libraries/{name}/books", toString(link.getHref()));
commandOptions.remove("isbn");
link = getOperationInvoker().findLink(createCommandRequest("get-book", commandOptions));
assertNotNull(link);
assertEquals("http://host.domain.com/service/v1/libraries/{name}/books/{title}", toString(link.getHref()));
link = getOperationInvoker().findLink(createCommandRequest("remove-book", commandOptions));
assertNotNull(link);
assertEquals("http://host.domain.com/service/v1/libraries/{name}/books/{isbn}", toString(link.getHref()));
}
@Test
public void testProcessCommand() {
final String expectedResult = "{\"libraries\":[{\"library-of\":\"Congress\"}]"; // JSON
final RestHttpOperationInvoker operationInvoker = new RestHttpOperationInvoker(getLinkIndex()) {
@Override
public boolean isConnected() {
return true;
}
@Override
@SuppressWarnings("unchecked")
protected <T> ResponseEntity<T> send(final ClientHttpRequest request, final Class<T> responseType, final Map<String, ?> uriVariables) {
return new ResponseEntity(expectedResult, HttpStatus.OK);
}
};
final String actualResult = operationInvoker.processCommand(createCommandRequest("list-libraries",
Collections.<String, String>emptyMap()));
assertEquals(expectedResult, actualResult);
}
@Test
public void testProcessCommandDelegatesToSimpleProcessCommand() {
final String expectedResult = "<resources>test</resources>";
final RestHttpOperationInvoker operationInvoker = new RestHttpOperationInvoker(getLinkIndex()) {
@Override
public boolean isConnected() {
return true;
}
@Override
protected HttpOperationInvoker getHttpOperationInvoker() {
return new AbstractHttpOperationInvoker(AbstractHttpOperationInvoker.REST_API_URL) {
@Override public Object processCommand(final CommandRequest command) {
return expectedResult;
}
};
}
@Override
protected void printWarning(final String message, final Object... args) {
}
};
final String actualResult = operationInvoker.processCommand(createCommandRequest("get resource",
Collections.<String, String>emptyMap()));
assertEquals(expectedResult, actualResult);
}
@Test
public void testProcessCommandHandlesResourceAccessException() {
final RestHttpOperationInvoker operationInvoker = new RestHttpOperationInvoker(getLinkIndex()) {
private boolean connected = true;
@Override
public boolean isConnected() {
return connected;
}
@Override
protected void printWarning(final String message, final Object... args) {
}
@Override
protected <T> ResponseEntity<T> send(final ClientHttpRequest request, final Class<T> responseType, final Map<String, ?> uriVariables) {
throw new ResourceAccessException("test");
}
@Override
public void stop() {
this.connected = false;
}
};
assertTrue(operationInvoker.isConnected());
final String expectedResult = String.format(
"The connection to the GemFire Manager's HTTP service @ %1$s failed with: %2$s. "
+ "Please try reconnecting or see the GemFire Manager's log file for further details.",
operationInvoker.getBaseUrl(), "test");
final String actualResult = operationInvoker.processCommand(createCommandRequest("list-libraries",
Collections.<String, String>emptyMap()));
assertFalse(operationInvoker.isConnected());
assertEquals(expectedResult, actualResult);
}
@Test(expected = RestApiCallForCommandNotFoundException.class)
public void testProcessCommandThrowsRestApiCallForCommandNotFoundException() {
final RestHttpOperationInvoker operationInvoker = new RestHttpOperationInvoker(getLinkIndex()) {
@Override
public boolean isConnected() {
return true;
}
@Override
protected HttpOperationInvoker getHttpOperationInvoker() {
return null;
}
};
try {
operationInvoker.processCommand(createCommandRequest("get resource", Collections.<String, String>emptyMap()));
}
catch (RestApiCallForCommandNotFoundException e) {
assertEquals("No REST API call for command (get resource) was found!", e.getMessage());
throw e;
}
}
@Test(expected = IllegalStateException.class)
public void testProcessCommandWhenNotConnected() {
try {
getOperationInvoker().processCommand(createCommandRequest("get-book", Collections.<String, String>emptyMap()));
}
catch (IllegalStateException e) {
assertEquals("Gfsh must be connected to the GemFire Manager in order to process commands remotely!",
e.getMessage());
throw e;
}
}
protected static final class TestCommandRequest extends CommandRequest {
private final Map<String, String> commandParameters = new TreeMap<String, String>();
private final String command;
protected TestCommandRequest(final String command,
final Map<String, String> commandParameters,
final Map<String, String> environment,
final byte[][] fileData)
{
super(environment, fileData);
assert command != null : "The command cannot be null!";
this.command = command;
if (commandParameters != null) {
this.commandParameters.putAll(commandParameters);
}
}
@Override
public String getInput() {
return command;
}
@Override
public String getName() {
return command;
}
@Override
public Map<String, String> getParameters() {
return Collections.unmodifiableMap(commandParameters);
}
}
}