/*
* Copyright 2014, The Sporting Exchange Limited
*
* 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 com.betfair.cougar.transport.impl.protocol.http.rescript;
import com.betfair.cougar.core.api.ServiceVersion;
import com.betfair.cougar.core.api.ev.OperationDefinition;
import com.betfair.cougar.core.api.ev.OperationKey;
import com.betfair.cougar.core.api.ev.SimpleOperationDefinition;
import com.betfair.cougar.core.api.exception.CougarMarshallingException;
import com.betfair.cougar.core.api.exception.CougarValidationException;
import com.betfair.cougar.core.api.transcription.EnumDerialisationException;
import com.betfair.cougar.core.api.transcription.EnumUtils;
import com.betfair.cougar.core.api.transcription.Parameter;
import com.betfair.cougar.core.api.transcription.ParameterType;
import com.betfair.cougar.marshalling.impl.databinding.DataBindingManager;
import com.betfair.cougar.marshalling.impl.databinding.DataBindingMap;
import com.betfair.cougar.marshalling.impl.databinding.json.JSONBindingFactory;
import com.betfair.cougar.transport.api.protocol.http.rescript.RescriptBody;
import com.betfair.cougar.transport.api.protocol.http.rescript.RescriptOperationBindingDescriptor;
import com.betfair.cougar.transport.api.protocol.http.rescript.RescriptParamBindingDescriptor;
import com.betfair.cougar.transport.api.protocol.http.rescript.RescriptResponse;
import junit.framework.TestCase;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.core.MediaType;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.util.*;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@RunWith(JUnit4.class)
public class RescriptOperationBindingTest extends TestCase {
private static final String theString = "I hope its time to go home";
protected static enum TestEnum { FOO, UNRECOGNIZED_VALUE }
private HttpServletRequest mockedRequest;
private RescriptOperationBinding operationSimpleGetBinding;
private RescriptOperationBinding operationBodyParamBinding;
private RescriptOperationBinding operationOptionalBodyEnumParamBinding;
private final OperationKey testSimpleGetKey = new OperationKey(new ServiceVersion("v1.0"), "unitTestService", "testSimpleGet");
private final OperationKey testBodyParamKey = new OperationKey(new ServiceVersion("v1.0"), "unitTestService", "testBodyParam");
private final OperationKey testOptionalBodyEnumKey = new OperationKey(new ServiceVersion("v1.0"), "unitTestService", "testBodyEnumParam");
private final OperationDefinition testSimpleGetDef = new SimpleOperationDefinition(
testSimpleGetKey,
new Parameter [] { new Parameter("stringParam", new ParameterType(String.class, null),true),
new Parameter("floatParam", new ParameterType(Float.class, null),true),
new Parameter("longParam", new ParameterType(Long.class, null), true)},
new ParameterType(String.class, null));
private final OperationDefinition testBodyParamDef = new SimpleOperationDefinition(
testBodyParamKey,
new Parameter [] { new Parameter("stringBodyParam", new ParameterType(Long.class, null),true)},
new ParameterType(String.class, null));
private final OperationDefinition testOptionalBodyEnumDef = new SimpleOperationDefinition(
testOptionalBodyEnumKey,
new Parameter [] { new Parameter("enumBodyParam", new ParameterType(TestEnum.class, null),false)},
new ParameterType(String.class, null));
private static String enumBodyParamValue;
@Before
public void init() {
Set<String> contentTypeSet = new HashSet<String>();
contentTypeSet.add("application/json");
contentTypeSet.add("text/json");
DataBindingMap dataBindingMap = new DataBindingMap();
dataBindingMap.setContentTypes(contentTypeSet);
dataBindingMap.setPreferredContentType("application/json");
dataBindingMap.setFactory(new JSONBindingFactory());
DataBindingManager dbm = DataBindingManager.getInstance();
dbm.addBindingMap(dataBindingMap);
mockedRequest = mock(HttpServletRequest.class);
when(mockedRequest.getHeaderNames()).thenReturn(enumerator(new ArrayList<String>().iterator()));
List<RescriptParamBindingDescriptor> paramBindingDescriptors = new ArrayList<RescriptParamBindingDescriptor>();
paramBindingDescriptors.add(new RescriptParamBindingDescriptor("stringParam", RescriptParamBindingDescriptor.ParamSource.QUERY));
paramBindingDescriptors.add(new RescriptParamBindingDescriptor("longParam", RescriptParamBindingDescriptor.ParamSource.QUERY));
paramBindingDescriptors.add(new RescriptParamBindingDescriptor("floatParam", RescriptParamBindingDescriptor.ParamSource.QUERY));
RescriptOperationBindingDescriptor simpleGetOperationDescriptor = new RescriptOperationBindingDescriptor(testSimpleGetKey, "/simpleGet", "GET", paramBindingDescriptors, RescriptResponse.class);
operationSimpleGetBinding = new RescriptOperationBinding(simpleGetOperationDescriptor, testSimpleGetDef, true);
List<RescriptParamBindingDescriptor> simpleGetParamBindings = new ArrayList<RescriptParamBindingDescriptor>();
simpleGetParamBindings.add(new RescriptParamBindingDescriptor("stringBodyParam", RescriptParamBindingDescriptor.ParamSource.BODY));
RescriptOperationBindingDescriptor bodyParamOperationDescriptor = new RescriptOperationBindingDescriptor(testBodyParamKey, "/simpleGet", "GET", simpleGetParamBindings, RescriptResponse.class, NoddyRescriptBody.class);
operationBodyParamBinding = new RescriptOperationBinding(bodyParamOperationDescriptor, testBodyParamDef, true);
List<RescriptParamBindingDescriptor> optionalEnumBodyParams = new ArrayList<RescriptParamBindingDescriptor>();
optionalEnumBodyParams.add(new RescriptParamBindingDescriptor("enumBodyParam", RescriptParamBindingDescriptor.ParamSource.BODY));
RescriptOperationBindingDescriptor optionalBodyEnumOperationDescriptor = new RescriptOperationBindingDescriptor(testOptionalBodyEnumKey, "/simpleGet", "POST", optionalEnumBodyParams, RescriptResponse.class, NoddyRescriptBody.class);
operationOptionalBodyEnumParamBinding = new RescriptOperationBinding(optionalBodyEnumOperationDescriptor, testOptionalBodyEnumDef, true);
}
public static <T> Enumeration<T> enumerator(final Iterator<T> it) {
return new Enumeration<T>() {
@Override
public boolean hasMoreElements() {
return it.hasNext();
}
@Override
public T nextElement() {
return it.next();
}
};
}
@Test
public void testResolveArgsWithValidQueryStringParams() {
//Start with valid query string parameters
when(mockedRequest.getParameter("stringParam")).thenReturn("Hello world");
when(mockedRequest.getParameter("floatParam")).thenReturn("3.21");
when(mockedRequest.getParameter("longParam")).thenReturn("93828123111");
when(mockedRequest.getParameter("stringBodyParam")).thenReturn("is it home time yet?");
Object[] resolvedArguments = operationSimpleGetBinding.resolveArgs(mockedRequest, null, MediaType.APPLICATION_JSON_TYPE, "utf-8");
assertTrue(resolvedArguments[0] instanceof String);
int i=0;
assertEquals("Hello world", (String)resolvedArguments[i++]);
assertEquals(3.21f, (Float)resolvedArguments[i++]);
assertEquals(93828123111L, ((Long)resolvedArguments[i++]).longValue());
}
@Test
public void testResolveArgsWithInvalidQueryStringParams() {
//Start with valid query string parameters
when(mockedRequest.getParameter("stringParam")).thenReturn("Hello world");
when(mockedRequest.getParameter("floatParam")).thenReturn("thisaintnevergonnaparse");
when(mockedRequest.getParameter("longParam")).thenReturn("93828123111");
try {
Object[] resolvedArguments = operationSimpleGetBinding.resolveArgs(mockedRequest, null, MediaType.APPLICATION_JSON_TYPE, "utf-8");
fail("A validation exception should have occurred due to an invalid float argument");
} catch (CougarMarshallingException expected) {
}
}
@Test(expected=CougarMarshallingException.class)
public void testResolveBodyWithGetMethod() {
when(mockedRequest.getMethod()).thenReturn("GET");
operationBodyParamBinding.resolveArgs(mockedRequest, new ByteArrayInputStream("".getBytes()), null, "utf-8");
}
@Test
public void testResolveBody() {
InputStream is = new ByteArrayInputStream(("{\"message\":\"" + theString + "\"}").getBytes());
when(mockedRequest.getMethod()).thenReturn("POST");
Object[] resolvedArgs = operationBodyParamBinding.resolveArgs(mockedRequest, is, MediaType.APPLICATION_JSON_TYPE, "utf-8");
assertEquals(resolvedArgs[0], theString);
}
@Test
public void testResolveNonNullOptionalBodyEnum() {
enumBodyParamValue = "FOO";
InputStream is = new ByteArrayInputStream(("{\"message\":\"" + "dummy" + "\"}").getBytes());
when(mockedRequest.getMethod()).thenReturn("POST");
Object[] resolvedArgs = operationOptionalBodyEnumParamBinding.resolveArgs(mockedRequest, is, MediaType.APPLICATION_JSON_TYPE, "utf-8");
assertEquals(TestEnum.FOO, resolvedArgs[0]);
}
@Test
public void testResolveNullOptionalBodyEnum() {
enumBodyParamValue = null;
InputStream is = new ByteArrayInputStream(("{\"message\":\"" + "dummy" + "\"}").getBytes());
when(mockedRequest.getMethod()).thenReturn("POST");
Object[] resolvedArgs = operationOptionalBodyEnumParamBinding.resolveArgs(mockedRequest, is, MediaType.APPLICATION_JSON_TYPE, "utf-8");
assertEquals(null, resolvedArgs[0]);
}
@Test(expected = CougarMarshallingException.class)
public void testResolveUnrecognizedOptionalBodyEnumWithHardFailure() {
Boolean originalHardFailureValue = EnumUtils.getHardFailureForThisThread();
EnumUtils.setHardFailureForThisThread(true);
try {
enumBodyParamValue = "GARBAGE";
InputStream is = new ByteArrayInputStream(("{\"message\":\"" + "dummy" + "\"}").getBytes());
when(mockedRequest.getMethod()).thenReturn("POST");
operationOptionalBodyEnumParamBinding.resolveArgs(mockedRequest, is, MediaType.APPLICATION_JSON_TYPE, "utf-8");
}
finally {
if (originalHardFailureValue == null) {
originalHardFailureValue = true;
}
EnumUtils.setHardFailureForThisThread(originalHardFailureValue);
}
}
@Test
public void testResolveUnrecognizedOptionalBodyEnumWithoutHardFailure() {
Boolean originalHardFailureValue = EnumUtils.getHardFailureForThisThread();
EnumUtils.setHardFailureForThisThread(false);
try {
enumBodyParamValue = "GARBAGE";
InputStream is = new ByteArrayInputStream(("{\"message\":\"" + "dummy" + "\"}").getBytes());
when(mockedRequest.getMethod()).thenReturn("POST");
Object[] resolvedArgs = operationOptionalBodyEnumParamBinding.resolveArgs(mockedRequest, is, MediaType.APPLICATION_JSON_TYPE, "utf-8");
assertEquals(TestEnum.UNRECOGNIZED_VALUE, resolvedArgs[0]);
}
finally {
if (originalHardFailureValue == null) {
originalHardFailureValue = true;
}
EnumUtils.setHardFailureForThisThread(originalHardFailureValue);
}
}
public static class NoddyRescriptBody implements RescriptBody {
private String message;
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
@Override
public Object getValue(String name) {
if (name.equals("enumBodyParam")) {
return enumBodyParamValue;
}
return theString;
}
}
}