mirror of https://github.com/apache/jclouds.git
Issue 799:simplify http test creation: take 1
This commit is contained in:
parent
9c97f2b6a3
commit
08355a92a1
|
@ -22,6 +22,7 @@ import java.util.concurrent.Future;
|
||||||
|
|
||||||
import com.google.common.base.Function;
|
import com.google.common.base.Function;
|
||||||
import com.google.common.util.concurrent.ListenableFuture;
|
import com.google.common.util.concurrent.ListenableFuture;
|
||||||
|
import com.google.inject.ImplementedBy;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Executor which will invoke and transform the response of an {@code EndpointCommand} into generic
|
* Executor which will invoke and transform the response of an {@code EndpointCommand} into generic
|
||||||
|
@ -29,6 +30,7 @@ import com.google.common.util.concurrent.ListenableFuture;
|
||||||
*
|
*
|
||||||
* @author Adrian Cole
|
* @author Adrian Cole
|
||||||
*/
|
*/
|
||||||
|
@ImplementedBy(TransformingHttpCommandExecutorServiceImpl.class)
|
||||||
public interface TransformingHttpCommandExecutorService {
|
public interface TransformingHttpCommandExecutorService {
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
|
|
|
@ -29,6 +29,7 @@ import org.jclouds.logging.Logger;
|
||||||
import org.jclouds.rest.internal.GeneratedHttpRequest;
|
import org.jclouds.rest.internal.GeneratedHttpRequest;
|
||||||
|
|
||||||
import com.google.common.base.Function;
|
import com.google.common.base.Function;
|
||||||
|
import com.google.common.base.Objects;
|
||||||
import com.google.common.util.concurrent.ListenableFuture;
|
import com.google.common.util.concurrent.ListenableFuture;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -144,6 +145,20 @@ public class TransformingHttpCommandImpl<T> implements TransformingHttpCommand<T
|
||||||
this.request = request;
|
this.request = request;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hashCode(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object that) {
|
||||||
|
if (that == null)
|
||||||
|
return false;
|
||||||
|
if (!(that instanceof HttpCommand))
|
||||||
|
return false;
|
||||||
|
return Objects.equal(this.request, HttpCommand.class.cast(that).getCurrentRequest());
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
if (request instanceof GeneratedHttpRequest<?>)
|
if (request instanceof GeneratedHttpRequest<?>)
|
||||||
|
|
|
@ -71,20 +71,13 @@ public class RestContextSpec<S, A> {
|
||||||
(Class) RestContextBuilder.class, EMPTY_LIST);
|
(Class) RestContextBuilder.class, EMPTY_LIST);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* this uses the inefficient {@link Objects} implementation as the object count will be
|
|
||||||
* relatively small and therefore efficiency is not a concern.
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
return Objects.hashCode(provider, endpoint, apiVersion, iso3166Codes, identity, credential, sync, async,
|
return Objects.hashCode(provider, endpoint, apiVersion, iso3166Codes, identity, credential, sync, async,
|
||||||
propertiesBuilderClass, contextBuilderClass, modules);
|
propertiesBuilderClass, contextBuilderClass, modules);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* this uses the inefficient {@link Objects} implementation as the object count will be
|
|
||||||
* relatively small and therefore efficiency is not a concern.
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object that) {
|
public boolean equals(Object that) {
|
||||||
if (that == null)
|
if (that == null)
|
||||||
|
@ -92,10 +85,6 @@ public class RestContextSpec<S, A> {
|
||||||
return Objects.equal(this.toString(), that.toString());
|
return Objects.equal(this.toString(), that.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* this uses the inefficient {@link Objects} implementation as the object count will be
|
|
||||||
* relatively small and therefore efficiency is not a concern.
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return Objects.toStringHelper(this).add("provider", provider).add("endpoint", endpoint).add("apiVersion",
|
return Objects.toStringHelper(this).add("provider", provider).add("endpoint", endpoint).add("apiVersion",
|
||||||
|
|
|
@ -80,12 +80,15 @@ public class Throwables2 {
|
||||||
return (Exception) throwable;
|
return (Exception) throwable;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (Class<Exception> propagatableExceptionType : new Class[] { IllegalStateException.class,
|
for (Class<Exception> propagatableExceptionType : new Class[] { IllegalStateException.class, AssertionError.class,
|
||||||
UnsupportedOperationException.class, IllegalArgumentException.class, AuthorizationException.class,
|
UnsupportedOperationException.class, IllegalArgumentException.class, AuthorizationException.class,
|
||||||
ResourceNotFoundException.class, InsufficientResourcesException.class, HttpResponseException.class }) {
|
ResourceNotFoundException.class, InsufficientResourcesException.class, HttpResponseException.class }) {
|
||||||
Throwable throwable = getFirstThrowableOfType(exception, propagatableExceptionType);
|
Throwable throwable = getFirstThrowableOfType(exception, propagatableExceptionType);
|
||||||
if (throwable != null) {
|
if (throwable != null) {
|
||||||
throw (Exception) throwable;
|
if (throwable instanceof AssertionError)
|
||||||
|
throw (AssertionError) throwable;
|
||||||
|
else
|
||||||
|
throw (Exception) throwable;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Throwables.propagateIfPossible(exception.getCause(), Exception.class);
|
Throwables.propagateIfPossible(exception.getCause(), Exception.class);
|
||||||
|
|
|
@ -48,6 +48,7 @@ import org.jclouds.rest.annotations.XMLResponseParser;
|
||||||
import org.jclouds.rest.binders.BindMapToMatrixParams;
|
import org.jclouds.rest.binders.BindMapToMatrixParams;
|
||||||
import org.jclouds.rest.binders.BindToJsonPayload;
|
import org.jclouds.rest.binders.BindToJsonPayload;
|
||||||
import org.jclouds.rest.binders.BindToStringPayload;
|
import org.jclouds.rest.binders.BindToStringPayload;
|
||||||
|
import org.jclouds.rest.functions.ReturnFalseOnNotFoundOr404;
|
||||||
import org.jclouds.util.Strings2;
|
import org.jclouds.util.Strings2;
|
||||||
|
|
||||||
import com.google.common.base.Function;
|
import com.google.common.base.Function;
|
||||||
|
@ -73,6 +74,7 @@ public interface IntegrationTestAsyncClient {
|
||||||
|
|
||||||
@HEAD
|
@HEAD
|
||||||
@Path("/objects/{id}")
|
@Path("/objects/{id}")
|
||||||
|
@ExceptionParser(ReturnFalseOnNotFoundOr404.class)
|
||||||
ListenableFuture<Boolean> exists(@PathParam("id") String path);
|
ListenableFuture<Boolean> exists(@PathParam("id") String path);
|
||||||
|
|
||||||
@GET
|
@GET
|
||||||
|
|
|
@ -0,0 +1,57 @@
|
||||||
|
/**
|
||||||
|
* Licensed to jclouds, Inc. (jclouds) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. jclouds 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.jclouds.http;
|
||||||
|
|
||||||
|
import static org.testng.Assert.assertEquals;
|
||||||
|
|
||||||
|
import java.net.URI;
|
||||||
|
|
||||||
|
import org.jclouds.rest.BaseRestClientExpectTest;
|
||||||
|
import org.jclouds.rest.BaseRestClientExpectTest.RegisterContext;
|
||||||
|
import org.testng.annotations.Test;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Allows us to test a client via its side effects.
|
||||||
|
*
|
||||||
|
* @author Adrian Cole
|
||||||
|
*/
|
||||||
|
@Test(groups = "unit", testName = "IntegrationTestClientExpectTest")
|
||||||
|
// only needed as IntegrationTestClient is not registered in rest.properties
|
||||||
|
@RegisterContext(sync = IntegrationTestClient.class, async = IntegrationTestAsyncClient.class)
|
||||||
|
public class IntegrationTestClientExpectTest extends BaseRestClientExpectTest<IntegrationTestClient> {
|
||||||
|
|
||||||
|
public void testWhenResponseIs2xxExistsReturnsTrue() {
|
||||||
|
|
||||||
|
IntegrationTestClient client = requestSendsResponse(HttpRequest.builder().method("HEAD").endpoint(
|
||||||
|
URI.create("http://mock/objects/rabbit")).build(), HttpResponse.builder().statusCode(200).build());
|
||||||
|
|
||||||
|
assertEquals(client.exists("rabbit"), true);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testWhenResponseIs404ExistsReturnsFalse() {
|
||||||
|
|
||||||
|
IntegrationTestClient client = requestSendsResponse(HttpRequest.builder().method("HEAD").endpoint(
|
||||||
|
URI.create("http://mock/objects/rabbit")).build(), HttpResponse.builder().statusCode(404).build());
|
||||||
|
|
||||||
|
assertEquals(client.exists("rabbit"), false);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,232 @@
|
||||||
|
/**
|
||||||
|
* Licensed to jclouds, Inc. (jclouds) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. jclouds licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except fn 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 fn 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.jclouds.rest;
|
||||||
|
|
||||||
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
|
import static java.lang.annotation.ElementType.TYPE;
|
||||||
|
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||||
|
import static org.jclouds.rest.RestContextFactory.contextSpec;
|
||||||
|
import static org.jclouds.rest.RestContextFactory.createContext;
|
||||||
|
import static org.testng.Assert.assertEquals;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
import java.util.Properties;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import javax.inject.Named;
|
||||||
|
import javax.inject.Singleton;
|
||||||
|
|
||||||
|
import org.jclouds.Constants;
|
||||||
|
import org.jclouds.concurrent.MoreExecutors;
|
||||||
|
import org.jclouds.concurrent.SingleThreaded;
|
||||||
|
import org.jclouds.concurrent.config.ConfiguresExecutorService;
|
||||||
|
import org.jclouds.http.HttpCommandExecutorService;
|
||||||
|
import org.jclouds.http.HttpRequest;
|
||||||
|
import org.jclouds.http.HttpResponse;
|
||||||
|
import org.jclouds.http.HttpUtils;
|
||||||
|
import org.jclouds.http.IOExceptionRetryHandler;
|
||||||
|
import org.jclouds.http.config.ConfiguresHttpCommandExecutorService;
|
||||||
|
import org.jclouds.http.handlers.DelegatingErrorHandler;
|
||||||
|
import org.jclouds.http.handlers.DelegatingRetryHandler;
|
||||||
|
import org.jclouds.http.internal.BaseHttpCommandExecutorService;
|
||||||
|
import org.jclouds.http.internal.HttpWire;
|
||||||
|
import org.jclouds.io.Payload;
|
||||||
|
import org.jclouds.io.Payloads;
|
||||||
|
import org.jclouds.logging.config.NullLoggingModule;
|
||||||
|
import org.jclouds.util.Strings2;
|
||||||
|
import org.testng.annotations.Test;
|
||||||
|
|
||||||
|
import com.google.common.annotations.Beta;
|
||||||
|
import com.google.common.base.Function;
|
||||||
|
import com.google.common.base.Throwables;
|
||||||
|
import com.google.common.collect.ImmutableSet;
|
||||||
|
import com.google.inject.AbstractModule;
|
||||||
|
import com.google.inject.Binder;
|
||||||
|
import com.google.inject.Module;
|
||||||
|
import com.google.inject.TypeLiteral;
|
||||||
|
import com.google.inject.name.Names;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Allows us to test a client via its side effects.
|
||||||
|
*
|
||||||
|
* @author Adrian Cole
|
||||||
|
*/
|
||||||
|
@Test(groups = "unit")
|
||||||
|
@Beta
|
||||||
|
public abstract class BaseRestClientExpectTest<S> {
|
||||||
|
/**
|
||||||
|
* only needed when the client is simple and not registered fn rest.properties
|
||||||
|
*/
|
||||||
|
@Target(TYPE)
|
||||||
|
@Retention(RUNTIME)
|
||||||
|
public static @interface RegisterContext {
|
||||||
|
Class<?> sync();
|
||||||
|
|
||||||
|
Class<?> async();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected String provider = "mock";
|
||||||
|
|
||||||
|
protected Module createModule() {
|
||||||
|
return new Module() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void configure(Binder binder) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Payload payloadFromResource(String resource) {
|
||||||
|
return Payloads.newInputStreamPayload(getClass().getResourceAsStream(resource));
|
||||||
|
}
|
||||||
|
|
||||||
|
@SingleThreaded
|
||||||
|
@Singleton
|
||||||
|
public static class ExpectHttpCommandExecutorService extends BaseHttpCommandExecutorService<HttpRequest> {
|
||||||
|
|
||||||
|
private final Function<HttpRequest, HttpResponse> fn;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public ExpectHttpCommandExecutorService(Function<HttpRequest, HttpResponse> fn, HttpUtils utils,
|
||||||
|
@Named(Constants.PROPERTY_IO_WORKER_THREADS) ExecutorService ioExecutor,
|
||||||
|
IOExceptionRetryHandler ioRetryHandler, DelegatingRetryHandler retryHandler,
|
||||||
|
DelegatingErrorHandler errorHandler, HttpWire wire) {
|
||||||
|
super(utils, ioExecutor, retryHandler, ioRetryHandler, errorHandler, wire);
|
||||||
|
this.fn = checkNotNull(fn, "fn");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void cleanup(HttpRequest nativeResponse) {
|
||||||
|
if (nativeResponse.getPayload() != null)
|
||||||
|
nativeResponse.getPayload().release();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected HttpRequest convert(HttpRequest request) throws IOException, InterruptedException {
|
||||||
|
return request;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected HttpResponse invoke(HttpRequest nativeRequest) throws IOException, InterruptedException {
|
||||||
|
return fn.apply(nativeRequest);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ConfiguresHttpCommandExecutorService
|
||||||
|
@ConfiguresExecutorService
|
||||||
|
public static class ExpectModule extends AbstractModule {
|
||||||
|
private final Function<HttpRequest, HttpResponse> fn;
|
||||||
|
|
||||||
|
public ExpectModule(Function<HttpRequest, HttpResponse> fn) {
|
||||||
|
this.fn = checkNotNull(fn, "fn");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void configure() {
|
||||||
|
bind(ExecutorService.class).annotatedWith(Names.named(Constants.PROPERTY_USER_THREADS)).toInstance(
|
||||||
|
MoreExecutors.sameThreadExecutor());
|
||||||
|
bind(ExecutorService.class).annotatedWith(Names.named(Constants.PROPERTY_IO_WORKER_THREADS)).toInstance(
|
||||||
|
MoreExecutors.sameThreadExecutor());
|
||||||
|
bind(new TypeLiteral<Function<HttpRequest, HttpResponse>>() {
|
||||||
|
}).toInstance(fn);
|
||||||
|
bind(HttpCommandExecutorService.class).to(ExpectHttpCommandExecutorService.class);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected S requestSendsResponse(final HttpRequest fn, final HttpResponse out) {
|
||||||
|
return createClient(new Function<HttpRequest, HttpResponse>() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public HttpResponse apply(HttpRequest command) {
|
||||||
|
assertEquals(renderRequest(command), renderRequest(fn));
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private String renderRequest(HttpRequest request) {
|
||||||
|
StringBuilder builder = new StringBuilder().append(request.getRequestLine()).append('\n');
|
||||||
|
for (Entry<String, String> header : request.getHeaders().entries()) {
|
||||||
|
builder.append(header.getKey()).append(": ").append(header.getValue()).append('\n');
|
||||||
|
}
|
||||||
|
if (request.getPayload() != null) {
|
||||||
|
for (Entry<String, String> header : HttpUtils.getContentHeadersFromMetadata(
|
||||||
|
request.getPayload().getContentMetadata()).entries()) {
|
||||||
|
builder.append(header.getKey()).append(": ").append(header.getValue()).append('\n');
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
builder.append('\n').append(Strings2.toStringAndClose(request.getPayload().getInput()));
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw Throwables.propagate(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
builder.append('\n');
|
||||||
|
}
|
||||||
|
return builder.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected S createClient(Function<HttpRequest, HttpResponse> fn) {
|
||||||
|
return createClient(fn, createModule(), setupProperties());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected S createClient(Function<HttpRequest, HttpResponse> fn, Module module) {
|
||||||
|
return createClient(fn, module, setupProperties());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
protected S createClient(Function<HttpRequest, HttpResponse> fn, Properties props) {
|
||||||
|
return createClient(fn, createModule(), props);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
protected S createClient(Function<HttpRequest, HttpResponse> fn, Module module, Properties props) {
|
||||||
|
RestContextSpec<S, ?> contextSpec = makeContextSpec();
|
||||||
|
|
||||||
|
return createContext(contextSpec,
|
||||||
|
ImmutableSet.<Module> of(new ExpectModule(fn), new NullLoggingModule(), module), props).getApi();
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private RestContextSpec<S, ?> makeContextSpec() {
|
||||||
|
if (getClass().isAnnotationPresent(RegisterContext.class))
|
||||||
|
return (RestContextSpec<S, ?>) contextSpec(provider, "http://mock", "1", "", "userfoo", null, getClass()
|
||||||
|
.getAnnotation(RegisterContext.class).sync(),
|
||||||
|
getClass().getAnnotation(RegisterContext.class).async(), ImmutableSet.<Module> of());
|
||||||
|
else
|
||||||
|
return new RestContextFactory(setupRestProperties()).createContextSpec(provider, "identity", "credential",
|
||||||
|
new Properties());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
protected Properties setupRestProperties() {
|
||||||
|
return RestContextFactory.getPropertiesFromResource("/rest.properties");
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Properties setupProperties() {
|
||||||
|
return new Properties();
|
||||||
|
}
|
||||||
|
}
|
|
@ -35,8 +35,8 @@ import org.jclouds.concurrent.MoreExecutors;
|
||||||
import org.jclouds.concurrent.config.ConfiguresExecutorService;
|
import org.jclouds.concurrent.config.ConfiguresExecutorService;
|
||||||
import org.jclouds.crypto.Crypto;
|
import org.jclouds.crypto.Crypto;
|
||||||
import org.jclouds.crypto.CryptoStreams;
|
import org.jclouds.crypto.CryptoStreams;
|
||||||
|
import org.jclouds.http.HttpCommandExecutorService;
|
||||||
import org.jclouds.http.HttpRequest;
|
import org.jclouds.http.HttpRequest;
|
||||||
import org.jclouds.http.TransformingHttpCommandExecutorService;
|
|
||||||
import org.jclouds.http.config.ConfiguresHttpCommandExecutorService;
|
import org.jclouds.http.config.ConfiguresHttpCommandExecutorService;
|
||||||
import org.jclouds.http.functions.ParseSax;
|
import org.jclouds.http.functions.ParseSax;
|
||||||
import org.jclouds.io.MutableContentMetadata;
|
import org.jclouds.io.MutableContentMetadata;
|
||||||
|
@ -65,13 +65,13 @@ public abstract class BaseRestClientTest {
|
||||||
@ConfiguresHttpCommandExecutorService
|
@ConfiguresHttpCommandExecutorService
|
||||||
@ConfiguresExecutorService
|
@ConfiguresExecutorService
|
||||||
public static class MockModule extends AbstractModule {
|
public static class MockModule extends AbstractModule {
|
||||||
private final TransformingHttpCommandExecutorService mock;
|
private final HttpCommandExecutorService mock;
|
||||||
|
|
||||||
public MockModule() {
|
public MockModule() {
|
||||||
this(createMock(TransformingHttpCommandExecutorService.class));
|
this(createMock(HttpCommandExecutorService.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
public MockModule(TransformingHttpCommandExecutorService mock) {
|
public MockModule(HttpCommandExecutorService mock) {
|
||||||
this.mock = mock;
|
this.mock = mock;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -81,7 +81,7 @@ public abstract class BaseRestClientTest {
|
||||||
MoreExecutors.sameThreadExecutor());
|
MoreExecutors.sameThreadExecutor());
|
||||||
bind(ExecutorService.class).annotatedWith(Names.named(Constants.PROPERTY_IO_WORKER_THREADS)).toInstance(
|
bind(ExecutorService.class).annotatedWith(Names.named(Constants.PROPERTY_IO_WORKER_THREADS)).toInstance(
|
||||||
MoreExecutors.sameThreadExecutor());
|
MoreExecutors.sameThreadExecutor());
|
||||||
bind(TransformingHttpCommandExecutorService.class).toInstance(mock);
|
bind(HttpCommandExecutorService.class).toInstance(mock);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -65,6 +65,7 @@ public abstract class RestClientTest<T> extends BaseRestClientTest {
|
||||||
@BeforeClass
|
@BeforeClass
|
||||||
protected void setupFactory() throws IOException {
|
protected void setupFactory() throws IOException {
|
||||||
RestContextSpec<?, ?> contextSpec = createContextSpec();
|
RestContextSpec<?, ?> contextSpec = createContextSpec();
|
||||||
|
|
||||||
injector = createContextBuilder(contextSpec,
|
injector = createContextBuilder(contextSpec,
|
||||||
ImmutableSet.of(new MockModule(), new NullLoggingModule(), createModule()), getProperties())
|
ImmutableSet.of(new MockModule(), new NullLoggingModule(), createModule()), getProperties())
|
||||||
.buildInjector();
|
.buildInjector();
|
||||||
|
|
|
@ -20,12 +20,6 @@ package org.jclouds.rest.internal;
|
||||||
|
|
||||||
import static com.google.common.base.Charsets.UTF_8;
|
import static com.google.common.base.Charsets.UTF_8;
|
||||||
import static com.google.common.base.Preconditions.checkNotNull;
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
import static org.easymock.EasyMock.eq;
|
|
||||||
import static org.easymock.EasyMock.expect;
|
|
||||||
import static org.easymock.EasyMock.reportMatcher;
|
|
||||||
import static org.easymock.classextension.EasyMock.createNiceMock;
|
|
||||||
import static org.easymock.classextension.EasyMock.replay;
|
|
||||||
import static org.easymock.classextension.EasyMock.verify;
|
|
||||||
import static org.jclouds.io.Payloads.calculateMD5;
|
import static org.jclouds.io.Payloads.calculateMD5;
|
||||||
import static org.jclouds.io.Payloads.newInputStreamPayload;
|
import static org.jclouds.io.Payloads.newInputStreamPayload;
|
||||||
import static org.jclouds.io.Payloads.newStringPayload;
|
import static org.jclouds.io.Payloads.newStringPayload;
|
||||||
|
@ -53,6 +47,7 @@ import java.util.Date;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.ExecutionException;
|
import java.util.concurrent.ExecutionException;
|
||||||
|
import java.util.concurrent.Future;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
import javax.inject.Named;
|
import javax.inject.Named;
|
||||||
|
@ -75,26 +70,24 @@ import javax.ws.rs.core.MediaType;
|
||||||
import javax.ws.rs.core.UriBuilder;
|
import javax.ws.rs.core.UriBuilder;
|
||||||
import javax.xml.bind.annotation.XmlRootElement;
|
import javax.xml.bind.annotation.XmlRootElement;
|
||||||
|
|
||||||
import org.easymock.IArgumentMatcher;
|
|
||||||
import org.eclipse.jetty.http.HttpHeaders;
|
import org.eclipse.jetty.http.HttpHeaders;
|
||||||
import org.jclouds.concurrent.Timeout;
|
import org.jclouds.concurrent.Timeout;
|
||||||
import org.jclouds.crypto.Crypto;
|
import org.jclouds.crypto.Crypto;
|
||||||
import org.jclouds.date.DateService;
|
import org.jclouds.date.DateService;
|
||||||
import org.jclouds.date.internal.SimpleDateFormatDateService;
|
import org.jclouds.date.internal.SimpleDateFormatDateService;
|
||||||
import org.jclouds.http.HttpCommand;
|
import org.jclouds.http.HttpCommand;
|
||||||
|
import org.jclouds.http.HttpCommandExecutorService;
|
||||||
import org.jclouds.http.HttpException;
|
import org.jclouds.http.HttpException;
|
||||||
import org.jclouds.http.HttpRequest;
|
import org.jclouds.http.HttpRequest;
|
||||||
import org.jclouds.http.HttpRequestFilter;
|
import org.jclouds.http.HttpRequestFilter;
|
||||||
import org.jclouds.http.HttpResponse;
|
import org.jclouds.http.HttpResponse;
|
||||||
import org.jclouds.http.IOExceptionRetryHandler;
|
import org.jclouds.http.IOExceptionRetryHandler;
|
||||||
import org.jclouds.http.RequiresHttp;
|
import org.jclouds.http.RequiresHttp;
|
||||||
import org.jclouds.http.TransformingHttpCommandExecutorService;
|
|
||||||
import org.jclouds.http.functions.ParseFirstJsonValueNamed;
|
import org.jclouds.http.functions.ParseFirstJsonValueNamed;
|
||||||
import org.jclouds.http.functions.ParseJson;
|
import org.jclouds.http.functions.ParseJson;
|
||||||
import org.jclouds.http.functions.ParseSax;
|
import org.jclouds.http.functions.ParseSax;
|
||||||
import org.jclouds.http.functions.ParseURIFromListOrLocationHeaderIf20x;
|
import org.jclouds.http.functions.ParseURIFromListOrLocationHeaderIf20x;
|
||||||
import org.jclouds.http.functions.ParseXMLWithJAXB;
|
import org.jclouds.http.functions.ParseXMLWithJAXB;
|
||||||
import org.jclouds.http.functions.ReleasePayloadAndReturn;
|
|
||||||
import org.jclouds.http.functions.ReturnInputStream;
|
import org.jclouds.http.functions.ReturnInputStream;
|
||||||
import org.jclouds.http.functions.ReturnStringIf2xx;
|
import org.jclouds.http.functions.ReturnStringIf2xx;
|
||||||
import org.jclouds.http.functions.ReturnTrueIf2xx;
|
import org.jclouds.http.functions.ReturnTrueIf2xx;
|
||||||
|
@ -180,8 +173,8 @@ public class RestAnnotationProcessorTest extends BaseRestClientTest {
|
||||||
|
|
||||||
@RequiresHttp
|
@RequiresHttp
|
||||||
@ConfiguresRestClient
|
@ConfiguresRestClient
|
||||||
protected static class CallerCalleeModule extends RestClientModule<Caller, AsyncCaller> {
|
protected static class CallerModule extends RestClientModule<Caller, AsyncCaller> {
|
||||||
CallerCalleeModule() {
|
CallerModule() {
|
||||||
super(Caller.class, AsyncCaller.class, ImmutableMap.<Class<?>, Class<?>> of(Callee.class, AsyncCallee.class));
|
super(Caller.class, AsyncCaller.class, ImmutableMap.<Class<?>, Class<?>> of(Callee.class, AsyncCallee.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -227,13 +220,17 @@ public class RestAnnotationProcessorTest extends BaseRestClientTest {
|
||||||
public AsyncCallee getCallee(@EndpointParam URI endpoint);
|
public AsyncCallee getCallee(@EndpointParam URI endpoint);
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
public void testAsyncDelegateIsLazyLoadedAndRequestIncludesVersionAndPath() throws InterruptedException, ExecutionException {
|
||||||
public void testDelegateAsyncIncludesVersion() throws SecurityException, NoSuchMethodException,
|
Injector child = injectorForCaller(new HttpCommandExecutorService() {
|
||||||
InterruptedException, ExecutionException {
|
|
||||||
Injector child = injectorForClient();
|
|
||||||
TransformingHttpCommandExecutorService mock = child.getInstance(TransformingHttpCommandExecutorService.class);
|
|
||||||
|
|
||||||
ReleasePayloadAndReturn function = child.getInstance(ReleasePayloadAndReturn.class);
|
@Override
|
||||||
|
public Future<HttpResponse> submit(HttpCommand command) {
|
||||||
|
assertEquals(command.getCurrentRequest().getRequestLine(),
|
||||||
|
"GET http://localhost:9999/client/1/foo HTTP/1.1");
|
||||||
|
return Futures.immediateFuture(HttpResponse.builder().build());
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
child.getInstance(AsyncCallee.class);
|
child.getInstance(AsyncCallee.class);
|
||||||
|
@ -242,42 +239,21 @@ public class RestAnnotationProcessorTest extends BaseRestClientTest {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
AsyncCaller caller = child.getInstance(AsyncCaller.class);
|
child.getInstance(AsyncCaller.class).getCallee().onePath("foo").get();
|
||||||
expect(mock.submit(requestLineEquals("GET http://localhost:9999/client/1/foo HTTP/1.1"), eq(function)))
|
|
||||||
.andReturn(createNiceMock(ListenableFuture.class)).atLeastOnce();
|
|
||||||
replay(mock);
|
|
||||||
|
|
||||||
caller.getCallee().onePath("foo");
|
|
||||||
|
|
||||||
verify(mock);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static HttpCommand requestLineEquals(final String requestLine) {
|
public void testDelegateIsLazyLoadedAndRequestIncludesVersionAndPath() throws InterruptedException, ExecutionException {
|
||||||
reportMatcher(new IArgumentMatcher() {
|
Injector child = injectorForCaller(new HttpCommandExecutorService() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void appendTo(StringBuffer buffer) {
|
public Future<HttpResponse> submit(HttpCommand command) {
|
||||||
buffer.append("requestLineEquals(");
|
assertEquals(command.getCurrentRequest().getRequestLine(),
|
||||||
buffer.append(requestLine);
|
"GET http://localhost:1111/client/1/foo HTTP/1.1");
|
||||||
buffer.append(")");
|
return Futures.immediateFuture(HttpResponse.builder().build());
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean matches(Object arg) {
|
|
||||||
return ((HttpCommand) arg).getCurrentRequest().getRequestLine().equals(requestLine);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testDelegateWithOverridingEndpoint() throws SecurityException, NoSuchMethodException,
|
|
||||||
InterruptedException, ExecutionException {
|
|
||||||
Injector child = injectorForClient();
|
|
||||||
TransformingHttpCommandExecutorService mock = child.getInstance(TransformingHttpCommandExecutorService.class);
|
|
||||||
|
|
||||||
ReleasePayloadAndReturn function = child.getInstance(ReleasePayloadAndReturn.class);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
child.getInstance(Callee.class);
|
child.getInstance(Callee.class);
|
||||||
|
@ -286,23 +262,45 @@ public class RestAnnotationProcessorTest extends BaseRestClientTest {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Caller caller = child.getInstance(Caller.class);
|
child.getInstance(Caller.class).getCallee().onePath("foo");
|
||||||
expect(mock.submit(requestLineEquals("GET http://localhost:1111/client/1/foo HTTP/1.1"), eq(function)))
|
|
||||||
.andReturn(Futures.<Void> immediateFuture(null)).atLeastOnce();
|
|
||||||
replay(mock);
|
|
||||||
|
|
||||||
caller.getCallee().onePath("foo");
|
|
||||||
|
|
||||||
verify(mock);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testDelegateWithOverridingEndpointOnMethod() throws SecurityException, NoSuchMethodException,
|
|
||||||
InterruptedException, ExecutionException {
|
|
||||||
Injector child = injectorForClient();
|
|
||||||
TransformingHttpCommandExecutorService mock = child.getInstance(TransformingHttpCommandExecutorService.class);
|
|
||||||
|
|
||||||
ReleasePayloadAndReturn function = child.getInstance(ReleasePayloadAndReturn.class);
|
public void testAsyncDelegateIsLazyLoadedAndRequestIncludesEndpointVersionAndPath() throws InterruptedException, ExecutionException {
|
||||||
|
Injector child = injectorForCaller(new HttpCommandExecutorService() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Future<HttpResponse> submit(HttpCommand command) {
|
||||||
|
assertEquals(command.getCurrentRequest().getRequestLine(),
|
||||||
|
"GET http://howdyboys/client/1/foo HTTP/1.1");
|
||||||
|
return Futures.immediateFuture(HttpResponse.builder().build());
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
child.getInstance(AsyncCallee.class);
|
||||||
|
assert false : "Callee shouldn't be bound yet";
|
||||||
|
} catch (ConfigurationException e) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
child.getInstance(AsyncCaller.class).getCallee(URI.create("http://howdyboys")).onePath("foo").get();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testDelegateIsLazyLoadedAndRequestIncludesEndpointVersionAndPath() throws InterruptedException, ExecutionException {
|
||||||
|
Injector child = injectorForCaller(new HttpCommandExecutorService() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Future<HttpResponse> submit(HttpCommand command) {
|
||||||
|
assertEquals(command.getCurrentRequest().getRequestLine(),
|
||||||
|
"GET http://howdyboys/client/1/foo HTTP/1.1");
|
||||||
|
return Futures.immediateFuture(HttpResponse.builder().build());
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
child.getInstance(Callee.class);
|
child.getInstance(Callee.class);
|
||||||
|
@ -311,22 +309,15 @@ public class RestAnnotationProcessorTest extends BaseRestClientTest {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Caller caller = child.getInstance(Caller.class);
|
child.getInstance(Caller.class).getCallee(URI.create("http://howdyboys")).onePath("foo");
|
||||||
expect(mock.submit(requestLineEquals("GET http://howdyboys/client/1/foo HTTP/1.1"), eq(function))).andReturn(
|
|
||||||
Futures.<Void> immediateFuture(null)).atLeastOnce();
|
|
||||||
replay(mock);
|
|
||||||
|
|
||||||
caller.getCallee(URI.create("http://howdyboys")).onePath("foo");
|
|
||||||
|
|
||||||
verify(mock);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Injector injectorForClient() {
|
private Injector injectorForCaller(HttpCommandExecutorService service) {
|
||||||
|
|
||||||
RestContextSpec<Caller, AsyncCaller> contextSpec = contextSpec("test", "http://localhost:9999", "1", "",
|
RestContextSpec<Caller, AsyncCaller> contextSpec = contextSpec("test", "http://localhost:9999", "1", "",
|
||||||
"userfoo", null, Caller.class, AsyncCaller.class,
|
"userfoo", null, Caller.class, AsyncCaller.class,
|
||||||
ImmutableSet.<Module> of(new MockModule(), new NullLoggingModule(), new CallerCalleeModule()));
|
ImmutableSet.<Module> of(new MockModule(service), new NullLoggingModule(), new CallerModule()));
|
||||||
|
|
||||||
return createContextBuilder(contextSpec).buildInjector();
|
return createContextBuilder(contextSpec).buildInjector();
|
||||||
|
|
||||||
|
|
|
@ -116,6 +116,12 @@ public class Throwables2Test {
|
||||||
returnFirstExceptionIfInListOrThrowStandardExceptionOrCause(new Class[] {}, new RuntimeException(e));
|
returnFirstExceptionIfInListOrThrowStandardExceptionOrCause(new Class[] {}, new RuntimeException(e));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test(expectedExceptions = AssertionError.class)
|
||||||
|
public void testPropagateStandardExceptionAssertionError() throws Exception {
|
||||||
|
AssertionError e = new AssertionError();
|
||||||
|
returnFirstExceptionIfInListOrThrowStandardExceptionOrCause(new Class[] {}, new RuntimeException(e));
|
||||||
|
}
|
||||||
|
|
||||||
@Test(expectedExceptions = AuthorizationException.class)
|
@Test(expectedExceptions = AuthorizationException.class)
|
||||||
public void testPropagateStandardExceptionAuthorizationException() throws Exception {
|
public void testPropagateStandardExceptionAuthorizationException() throws Exception {
|
||||||
Exception e = new AuthorizationException();
|
Exception e = new AuthorizationException();
|
||||||
|
|
Loading…
Reference in New Issue