blob: 7dc110481cea7c7223062a16f005dc97121b6e16 [file] [log] [blame]
/*
* Copyright 2017 The Mifos Initiative.
*
* Licensed 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 io.mifos.core.api.util;
import feign.Feign;
import feign.FeignException;
import feign.Response;
import io.mifos.core.api.annotation.ThrowsException;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
/**
* @author Myrle Krantz
*/
@RunWith(Parameterized.class)
public class AnnotatedErrorDecoderTest {
private final TestCase testCase;
public AnnotatedErrorDecoderTest(final TestCase testCase) {
this.testCase = testCase;
}
@Parameterized.Parameters
public static Collection testCases() throws NoSuchMethodException {
final Collection<TestCase> ret = new ArrayList<>();
final Response emptyInternalServerErrorResponse = Response.builder()
.status(HttpStatus.INTERNAL_SERVER_ERROR.value())
.reason("blah")
.headers(Collections.emptyMap())
.build();
final Response emptyBadRequestResponse = Response.builder()
.status(HttpStatus.BAD_REQUEST.value())
.reason("blah")
.headers(Collections.emptyMap())
.build();
final Response emptyConflictResponse = Response.builder()
.status(HttpStatus.CONFLICT.value())
.reason("blah")
.headers(Collections.emptyMap())
.build();
final Response emptyIAmATeapotResponse = Response.builder()
.status(HttpStatus.I_AM_A_TEAPOT.value())
.reason("blah")
.headers(Collections.emptyMap())
.build();
final Response emptyUnauthorizedResponse = Response.builder()
.status(HttpStatus.UNAUTHORIZED.value())
.reason("blah")
.headers(Collections.emptyMap())
.build();
final Response emptyForbiddenResponse = Response.builder()
.status(HttpStatus.FORBIDDEN.value())
.reason("blah")
.headers(Collections.emptyMap())
.build();
final String madeUpMethodKey = "x";
final String annotationlessMethodKey =
Feign.configKey(AnnotationlessInterface.class, AnnotationlessInterface.class.getMethod("method"));
final String oneAnnotatedMethodKey =
Feign.configKey(OneMethodInterface.class, OneMethodInterface.class.getMethod("method"));
final String onceAnnotatedMethodKey =
Feign.configKey(OneMethodOneAnnotationInterface.class, OneMethodOneAnnotationInterface.class.getMethod("method"));
ret.add(new TestCase("Methodless interface")
.clazz(MethodlessInterface.class)
.methodKey(madeUpMethodKey)
.response(emptyConflictResponse)
.expectedResult(FeignException.errorStatus(madeUpMethodKey, emptyConflictResponse)));
ret.add(new TestCase("Annotationless interface")
.clazz(AnnotationlessInterface.class)
.methodKey(annotationlessMethodKey)
.response(emptyConflictResponse)
.expectedResult(FeignException.errorStatus(annotationlessMethodKey, emptyConflictResponse)));
ret.add(new TestCase("Interface with one method mapped to parameterless exception")
.clazz(OneMethodInterface.class)
.methodKey(oneAnnotatedMethodKey)
.response(emptyBadRequestResponse)
.expectedResult(new ParameterlessException()));
ret.add(new TestCase("Interface with one method mapped to parametered exception")
.clazz(OneMethodInterface.class)
.methodKey(oneAnnotatedMethodKey)
.response(emptyConflictResponse)
.expectedResult(new ParameteredException(emptyConflictResponse)));
ret.add(new TestCase("Interface with one method mapped to an exception which can't be constructed by reflection")
.clazz(OneMethodInterface.class)
.methodKey(oneAnnotatedMethodKey)
.response(emptyIAmATeapotResponse)
.expectedResult(FeignException.errorStatus(oneAnnotatedMethodKey, emptyIAmATeapotResponse)));
ret.add(new TestCase("Interface with one method, not mapped to the response code returned")
.clazz(OneMethodInterface.class)
.methodKey(oneAnnotatedMethodKey)
.response(emptyUnauthorizedResponse)
.expectedResult(FeignException.errorStatus(oneAnnotatedMethodKey, emptyUnauthorizedResponse)));
ret.add(new TestCase("Interface with one method that has one annotation")
.clazz(OneMethodOneAnnotationInterface.class)
.methodKey(onceAnnotatedMethodKey)
.response(emptyBadRequestResponse)
.expectedResult(new ParameterlessException()));
ret.add(new TestCase("Bad request on an interface in which bad request isn't mapped.")
.clazz(AnnotationlessInterface.class)
.methodKey(annotationlessMethodKey)
.response(emptyBadRequestResponse)
.expectedResult(new IllegalArgumentException("blah")));
ret.add(new TestCase("Request with invalid token.")
.clazz(OneMethodOneAnnotationInterface.class)
.methodKey(onceAnnotatedMethodKey)
.response(emptyForbiddenResponse)
.expectedResult(new InvalidTokenException("blah")));
ret.add(new TestCase("Internal Server Error on an interface in which internal server error isn't mapped.")
.clazz(AnnotationlessInterface.class)
.methodKey(annotationlessMethodKey)
.response(emptyInternalServerErrorResponse)
.expectedResult(new InternalServerError("blah")));
return ret;
}
@SuppressWarnings("ThrowableResultOfMethodCallIgnored")
@Test
public void checkMapping() {
final AnnotatedErrorDecoder testSubject = new AnnotatedErrorDecoder(
LoggerFactory.getLogger(AnnotatedErrorDecoderTest.class.getName()), testCase.getClazz());
final Exception result = testSubject.decode(testCase.getMethodKey(), testCase.getResponse());
Assert.assertEquals("Test case \"" + testCase.getName() + "\" failed.",
testCase.expectedResult().getClass(), result.getClass());
Assert.assertEquals("Test case \"" + testCase.getName() + "\" failed.",
testCase.expectedResult().getMessage(), result.getMessage());
}
private interface MethodlessInterface {
}
private interface AnnotationlessInterface {
@SuppressWarnings("unused")
void method();
}
private interface OneMethodInterface {
@SuppressWarnings("unused")
@ThrowsException(status = HttpStatus.BAD_REQUEST, exception = ParameterlessException.class)
@ThrowsException(status = HttpStatus.CONFLICT, exception = ParameteredException.class)
@ThrowsException(status = HttpStatus.I_AM_A_TEAPOT, exception = WrongParameteredException.class)
void method();
}
private interface OneMethodOneAnnotationInterface {
@SuppressWarnings("unused")
@ThrowsException(status = HttpStatus.BAD_REQUEST, exception = ParameterlessException.class)
void method();
}
private static class TestCase {
private final String name;
private Class clazz;
private String methodKey;
private Response response;
private Exception expectedResult;
private TestCase(final String name) {
this.name = name;
}
public String toString() {
return name;
}
TestCase clazz(final Class newVal) {
clazz = newVal;
return this;
}
TestCase methodKey(final String newVal) {
methodKey = newVal;
return this;
}
TestCase response(final Response newVal) {
response = newVal;
return this;
}
TestCase expectedResult(final Exception newVal) {
expectedResult = newVal;
return this;
}
Class getClazz() {
return clazz;
}
String getMethodKey() {
return methodKey;
}
Response getResponse() {
return response;
}
Exception expectedResult() {
return expectedResult;
}
String getName() {
return name;
}
}
private static class ParameterlessException extends RuntimeException {
@SuppressWarnings("WeakerAccess")
public ParameterlessException() {
super("I am a parameterless exception. Aren't I cool.");
}
}
private static class ParameteredException extends RuntimeException {
@SuppressWarnings("WeakerAccess")
public ParameteredException(final Response response) {
super("I am a parametered exception with a response of " + response.toString());
}
}
private static class WrongParameteredException extends RuntimeException {
public WrongParameteredException(final String message) {
super(message);
}
}
}