/* 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.labs64.netlicensing.service;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.core.Application;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.text.WordUtils;
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.test.JerseyTest;
import org.glassfish.jersey.test.TestProperties;
import com.labs64.netlicensing.domain.Constants;
import com.labs64.netlicensing.domain.vo.SecurityMode;
import com.labs64.netlicensing.schema.SchemaFunction;
import com.labs64.netlicensing.schema.context.InfoEnum;
import com.labs64.netlicensing.schema.context.Item;
import com.labs64.netlicensing.schema.context.Netlicensing;
import com.labs64.netlicensing.schema.context.ObjectFactory;
import com.labs64.netlicensing.schema.context.Property;
import com.labs64.netlicensing.util.JAXBUtils;
/**
* Base class for integration tests for NetLicensing services.
*/
abstract class BaseServiceTest extends JerseyTest {
static final String REST_API_PATH = "/core/v2/rest";
static final String BASE_URL_UNITTEST = "http://localhost:9998";
static final String BASE_URL_PROD = "https://go.netlicensing.io";
static final String BASE_URL = BASE_URL_UNITTEST + REST_API_PATH;
static final String USER = "demo";
static final String PASS = "demo";
static final String TEST_CASE_BASE = "mock/";
static com.labs64.netlicensing.domain.vo.Context createContext() {
return new com.labs64.netlicensing.domain.vo.Context()
.setBaseUrl(BASE_URL)
.setSecurityMode(SecurityMode.BASIC_AUTHENTICATION)
.setUsername(USER)
.setPassword(PASS);
}
@Override
protected final Application configure() {
enable(TestProperties.LOG_TRAFFIC);
return new ResourceConfig(getResourceClass());
}
/**
* @return NLIC mock resource class
*/
protected abstract Class<?> getResourceClass();
// *** Abstract NLIC service test mock resource ***
public static abstract class AbstractNLICServiceResource {
/** ID of the service, i.e. "product", "licensee", etc */
private final String serviceId;
protected final ObjectFactory objectFactory = new ObjectFactory();
/**
* @param serviceId
* service identifier
*/
public AbstractNLICServiceResource(final String serviceId) {
this.serviceId = serviceId;
}
/**
* Mock for "create entity" service.
*
* @param formParams
* POST request body parameters
* @return response with XML representation of the created entity
*/
@POST
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
public Response create(final MultivaluedMap<String, String> formParams) {
return create(formParams, new HashMap<String, String>());
}
/**
* Mock for "get entity" service.
*
* @return response with XML representation of the entity
*/
@GET
@Path("{number}")
public Response get(@PathParam("number") final String number) {
final String xmlResourcePath = String.format("%snetlicensing-%s-get.xml", TEST_CASE_BASE,
serviceId.toLowerCase());
final Netlicensing netlicensing = JAXBUtils.readObject(xmlResourcePath, Netlicensing.class);
return Response.ok(netlicensing).build();
}
/**
* Mock for "list entities" service.
*
* @return response with XML representation of the entities page
*/
@GET
public Response list() {
final String xmlResourcePath = String.format("%snetlicensing-%s-list.xml", TEST_CASE_BASE,
serviceId.toLowerCase());
final Netlicensing netlicensing = JAXBUtils.readObject(xmlResourcePath, Netlicensing.class);
return Response.ok(netlicensing).build();
}
/**
* Mock for "update entity" service.
*
* @param formParams
* POST request body parameters
* @return response with XML representation of the updated entity
*/
@POST
@Path("{number}")
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
public Response update(@PathParam("number") final String number, final MultivaluedMap<String, String> formParams) {
final String resourcePath = String.format("%snetlicensing-%s-update.xml", TEST_CASE_BASE,
serviceId.toLowerCase());
final Netlicensing netlicensing = JAXBUtils.readObject(resourcePath, Netlicensing.class);
final List<Property> properties = netlicensing.getItems().getItem().get(0).getProperty();
for (final String paramKey : formParams.keySet()) {
final Property property = SchemaFunction.propertyByName(properties, paramKey);
final String paramValue = formParams.getFirst(paramKey);
if (paramValue != null && paramValue.trim().equals("")) {
properties.remove(property);
} else {
if (!properties.contains(property)) {
properties.add(property);
}
property.setValue(paramValue);
}
}
return Response.ok(netlicensing).build();
}
/**
* Mock for "delete entity" service.
*
* @param number
* entity number
* @param uriInfo
* context URI info
* @return response with "No Content" status
*/
@DELETE
@Path("{number}")
public Response delete(@PathParam("number") final String number, @Context final UriInfo uriInfo) {
return delete(number, "EXPECTED", uriInfo.getQueryParameters());
}
/**
* Defines common functionality for a "create entity" service.
*
* @param formParams
* POST request body parameters
* @param defaultPropertyValues
* default values for the entity properties
* @return response with XML representation of the created entity
*/
protected Response create(final MultivaluedMap<String, String> formParams,
final Map<String, String> defaultPropertyValues) {
final Netlicensing netlicensing = objectFactory.createNetlicensing();
netlicensing.setItems(objectFactory.createNetlicensingItems());
final Item item = objectFactory.createItem();
item.setType(WordUtils.capitalize(serviceId));
netlicensing.getItems().getItem().add(item);
final Map<String, String> propertyValues = new HashMap<String, String>(defaultPropertyValues);
for (final String paramKey : formParams.keySet()) {
propertyValues.put(paramKey, formParams.getFirst(paramKey));
}
final List<Property> properties = netlicensing.getItems().getItem().get(0).getProperty();
for (final String propertyName : propertyValues.keySet()) {
final Property property = SchemaFunction.propertyByName(properties, propertyName);
if (!properties.contains(property)) {
properties.add(property);
}
property.setValue(propertyValues.get(propertyName));
}
return Response.ok(netlicensing).build();
}
/**
* Defines common functionality for a "delete entity" service.
*
* @param number
* entity number
* @param expectedNumber
* expected entity number
* @param queryParams
* query parameters
* @return response with "No Content" status
*/
protected Response delete(final String number, final String expectedNumber,
final MultivaluedMap<String, String> queryParams) {
if (!expectedNumber.equals(number)) {
final String entityStr = StringUtils.join(StringUtils.splitByCharacterTypeCamelCase(serviceId), ' ')
.toLowerCase();
return errorResponse("NotFoundException", String.format("requested %s does not exist", entityStr));
}
// for testing purposes parameter "forceCascade" for "existing" entities should always be true if not absent
final boolean hasForceCascade = queryParams != null && queryParams.containsKey(Constants.CASCADE);
if (hasForceCascade && !Boolean.valueOf(queryParams.getFirst(Constants.CASCADE))) {
return unexpectedValueErrorResponse(Constants.CASCADE);
}
return Response.status(Response.Status.NO_CONTENT).build();
}
/**
* Generates error response for the service mock
*
* @param errorIdsAndMessages
* array where every string with even index is exception ID and every string with odd index is
* corresponding error message
* @return response object
*/
protected final Response errorResponse(final String... errorIdsAndMessages) {
if (errorIdsAndMessages.length % 2 != 0) {
throw new IllegalArgumentException("Some exception ID doesn't have corresponding error message");
}
final Netlicensing netlicensing = objectFactory.createNetlicensing();
for (int i = 0; i < errorIdsAndMessages.length; i += 2) {
final String exceptionId = errorIdsAndMessages[i];
final String errorMessage = errorIdsAndMessages[i + 1];
SchemaFunction.addInfo(netlicensing, exceptionId, InfoEnum.ERROR, errorMessage);
}
return Response.status(Response.Status.BAD_REQUEST).entity(netlicensing).build();
}
/**
* Generates UnexpectedValueException response for the service mock
*
* @param parameterName
* parameter name
* @return response object
*/
protected final Response unexpectedValueErrorResponse(final String parameterName) {
return errorResponse("UnexpectedValueException",
String.format("Unexpected value of parameter '%s'", parameterName));
}
/**
* @param formParams
* form params map
* @param paramKey
* parameter key
*/
protected void roundParamValueToTwoDecimalPlaces(final MultivaluedMap<String, String> formParams,
final String paramKey) {
if (formParams.containsKey(paramKey)) {
final String priceStr = formParams.getFirst(paramKey);
final BigDecimal roundedPrice = new BigDecimal(priceStr).setScale(2, BigDecimal.ROUND_HALF_UP);
formParams.putSingle(paramKey, roundedPrice.toString());
}
}
}
}