org.opencommercesearch.client.ProductApiTest.java Source code

Java tutorial

Introduction

Here is the source code for org.opencommercesearch.client.ProductApiTest.java

Source

package org.opencommercesearch.client;

/*
* Licensed to OpenCommerceSearch under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. OpenCommerceSearch 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.
*/

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException;
import com.fasterxml.jackson.databind.util.ISO8601DateFormat;

import org.apache.commons.lang.StringUtils;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.opencommercesearch.client.impl.Attribute;
import org.opencommercesearch.client.impl.Availability;
import org.opencommercesearch.client.impl.Brand;
import org.opencommercesearch.client.impl.Category;
import org.opencommercesearch.client.impl.CustomerReview;
import org.opencommercesearch.client.impl.Facet;
import org.opencommercesearch.client.impl.Image;
import org.opencommercesearch.client.impl.Metadata;
import org.opencommercesearch.client.impl.Sku;
import org.opencommercesearch.client.impl.spellcheck.SpellCheck;
import org.opencommercesearch.client.impl.spellcheck.Term;
import org.opencommercesearch.client.request.BaseRequest;
import org.opencommercesearch.client.request.BrandCategoryRequest;
import org.opencommercesearch.client.request.BrandRequest;
import org.opencommercesearch.client.request.BrowseBrandCategoryRequest;
import org.opencommercesearch.client.request.BrowseBrandRequest;
import org.opencommercesearch.client.request.BrowseCategoryRequest;
import org.opencommercesearch.client.request.CategoryRequest;
import org.opencommercesearch.client.request.Request;
import org.opencommercesearch.client.request.SearchRequest;
import org.opencommercesearch.client.response.BrandResponse;
import org.opencommercesearch.client.response.BrowseResponse;
import org.opencommercesearch.client.response.CategoryResponse;
import org.opencommercesearch.client.response.DefaultResponse;
import org.opencommercesearch.client.response.SearchResponse;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import org.restlet.Client;
import org.restlet.data.MediaType;
import org.restlet.data.Status;
import org.restlet.representation.StreamRepresentation;
import org.restlet.representation.StringRepresentation;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Properties;
import java.util.Set;

import static org.junit.Assert.*;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.*;

/**
 * @author jmendez
 * @author rmerizalde
 */

@RunWith(PowerMockRunner.class)
@PrepareForTest(Client.class)
public class ProductApiTest {
    private static final String SEARCH_RESPONSE_JSON = "data/searchResponse-full.json";
    private static final String CATEGORY_RESPONSE_JSON = "data/categoryResponse-full.json";
    private static final String BRAND_RESPONSE_JSON = "data/brandResponse-full.json";
    private static final String ALL_BRAND_RESPONSE_JSON = "data/allBrandResponse-full.json";
    private static final String SPELLCHECK_JSON = "data/spellcheck.json";

    Client client = PowerMockito.mock(Client.class);

    @Test
    public void testLoadProperties() throws IOException {
        //TODO Implement this
        ProductApi productApi = new ProductApi();
        Properties properties = productApi.loadProperties();

        assertEquals("localhost:9000", properties.getProperty("host"));
        assertEquals("v1", properties.getProperty("version"));
        assertEquals("false", properties.getProperty("preview"));
        assertEquals("2", properties.getProperty("maxRetries"));

        productApi.initProperties();

        assertEquals("localhost:9000", productApi.getHost());
        assertEquals("v1", productApi.getVersion());
        assertEquals(false, productApi.getPreview());
        assertEquals(2, productApi.getMaxRetries());

        assertEquals("http://localhost:9000/v1/dummy/endpoint?preview=false",
                productApi.getRequestUrl(new TestRequest()));
    }

    @Test
    public void testHandle() throws Exception {
        ProductApi productApi = new ProductApi();
        productApi.setClient(client);

        org.restlet.Response clientResponse = new org.restlet.Response(new org.restlet.Request());
        clientResponse.setEntity(new TestStreamRepresentation("{\"message\":\"test response\"}"));
        when(client.handle(any(org.restlet.Request.class))).thenReturn(clientResponse);

        TestRequest testRequest = new TestRequest();
        TestResponse response = (TestResponse) productApi.handle(testRequest, TestResponse.class);

        assertEquals("Got a search response", "test response", response.getMessage());
    }

    @Test
    public void testHandleWithInvalidJson() throws Exception {
        ProductApi productApi = new ProductApi();
        productApi.setClient(client);
        org.restlet.Response clientResponse = new org.restlet.Response(new org.restlet.Request());
        clientResponse.setEntity(new TestStreamRepresentation("{\"unknownFieldName\":\"test response\"}"));
        when(client.handle(any(org.restlet.Request.class))).thenReturn(clientResponse);

        TestRequest testRequest = new TestRequest();
        try {
            productApi.handle(testRequest, TestResponse.class);
        } catch (Exception e) {
            assertTrue(e instanceof ProductApiException);
            //The exception must have details.
            Throwable cause = e.getCause();
            assertTrue("Got a JSON exception.", cause instanceof UnrecognizedPropertyException);
            //Must not have chained exceptions. A JSON mapping error causes the API to return immediately.
            assertNull("There are no chained exceptions", cause.getCause());
        }
    }

    @Test
    public void testHandleWith404And400() throws Exception {
        //404 and 400 should not cause retries.
        ProductApi productApi = new ProductApi();
        productApi.setClient(client);
        org.restlet.Response clientResponse = new org.restlet.Response(new org.restlet.Request());
        clientResponse.setStatus(Status.CLIENT_ERROR_NOT_FOUND);
        when(client.handle(any(org.restlet.Request.class))).thenReturn(clientResponse).thenReturn(clientResponse);

        TestRequest testRequest = new TestRequest();
        try {
            productApi.handle(testRequest, TestResponse.class);
        } catch (ProductApiException e) {
            assertEquals(404, e.getStatusCode());
            assertNull("Unexected chained exceptions", e.getCause());
        } catch (Exception ex) {
            fail("Unexpected exception " + ex);
        }

        clientResponse.setStatus(Status.CLIENT_ERROR_BAD_REQUEST);

        try {
            productApi.handle(testRequest, TestResponse.class);
        } catch (ProductApiException e) {
            assertEquals(400, e.getStatusCode());
            assertNull("Found chained exceptions", e.getCause());
        } catch (Exception ex) {
            fail("Unexpected exception " + ex);
        }
    }

    @Test
    public void testHandleWithRetry() throws Exception {
        ProductApi productApi = new ProductApi();
        productApi.setClient(client);
        productApi.setMaxRetries(2);

        org.restlet.Response response500 = new org.restlet.Response(new org.restlet.Request());
        org.restlet.Response response1001 = new org.restlet.Response(new org.restlet.Request());
        org.restlet.Response response1000 = new org.restlet.Response(new org.restlet.Request());
        response500.setStatus(Status.SERVER_ERROR_INTERNAL);
        response1001.setStatus(Status.CONNECTOR_ERROR_COMMUNICATION);
        response1000.setStatus(Status.CONNECTOR_ERROR_CONNECTION);

        when(client.handle(any(org.restlet.Request.class))).thenReturn(response500).thenReturn(response1001)
                .thenReturn(response1000);

        TestRequest testRequest = new TestRequest();
        try {
            productApi.handle(testRequest, TestResponse.class);
        } catch (ProductApiException e) {
            assertEquals(-1, e.getStatusCode());
            assertTrue("Thrown exception after many retries.",
                    e.getMessage().contains("Failed to handle API request"));
            //Must have chained exceptions. A JSON mapping error causes the API to return immediately.
            assertNotNull("There are chained exceptions", e.getCause());
            Throwable cause1 = e.getCause();
            assertTrue("Cause 1 ProductApiException", cause1 instanceof ProductApiException);
            assertEquals(Status.CONNECTOR_ERROR_CONNECTION.getCode(),
                    ((ProductApiException) cause1).getStatusCode());
            assertNotNull(cause1.getCause());

            Throwable cause2 = cause1.getCause();
            assertTrue("Cause 2 ProductApiException", cause2 instanceof ProductApiException);
            assertEquals(Status.CONNECTOR_ERROR_COMMUNICATION.getCode(),
                    ((ProductApiException) cause2).getStatusCode());
            assertNotNull(cause1.getCause());

            Throwable cause3 = cause2.getCause();
            assertTrue("Cause 2 ProductApiException", cause3 instanceof ProductApiException);
            assertEquals(Status.SERVER_ERROR_INTERNAL.getCode(), ((ProductApiException) cause3).getStatusCode());
            assertNull("No more than 3 retries", cause3.getCause());
        } catch (Exception ex) {
            fail("Unexpected exception " + ex);
        }
    }

    @Test
    public void testGetUrl() throws IOException {
        ProductApi productApi = new ProductApi();
        String url = productApi.getRequestUrl(new TestRequest());

        assertEquals("http://localhost:9000/v1/dummy/endpoint?preview=false", url);

        productApi.setVersion("v2");
        url = productApi.getRequestUrl(new TestRequest());
        assertEquals("http://localhost:9000/v2/dummy/endpoint?preview=false", url);

        productApi.setPreview(true);
        url = productApi.getRequestUrl(new TestRequest());
        assertEquals("http://localhost:9000/v2/dummy/endpoint?preview=true", url);

        productApi.setHost("myapiserver:8999");
        url = productApi.getRequestUrl(new TestRequest());
        assertEquals("http://myapiserver:8999/v2/dummy/endpoint?preview=true", url);
    }

    @Test
    public void testBrowse() throws IOException, ProductApiException, NoSuchMethodException,
            InvocationTargetException, IllegalAccessException {
        BrowseBrandCategoryRequest request = new BrowseBrandCategoryRequest();
        request.setQuery("jackets");
        request.setSite("bcs");
        request.addField("*");
        request.setBrandId("00");
        request.setCategoryId("cat00");
        doTestSearchAndBrowse(request, ProductApi.class.getMethod("browse", BrowseBrandCategoryRequest.class),
                BrowseResponse.class);
    }

    @Test
    public void testBrowseByBrand() throws IOException, ProductApiException, NoSuchMethodException,
            InvocationTargetException, IllegalAccessException {
        BrowseBrandRequest request = new BrowseBrandRequest();
        request.setQuery("jackets");
        request.setSite("bcs");
        request.addField("*");
        request.setBrandId("00");
        doTestSearchAndBrowse(request, ProductApi.class.getMethod("browseByBrand", BrowseBrandRequest.class),
                BrowseResponse.class);
    }

    @Test
    public void testBrowseByCategory() throws IOException, ProductApiException, NoSuchMethodException,
            InvocationTargetException, IllegalAccessException {
        BrowseCategoryRequest request = new BrowseCategoryRequest();
        request.setQuery("jackets");
        request.setSite("bcs");
        request.addField("*");
        request.setCategoryId("cat00");
        doTestSearchAndBrowse(request, ProductApi.class.getMethod("browseByCategory", BrowseCategoryRequest.class),
                BrowseResponse.class);
    }

    @Test
    public void testSearch() throws IOException, ProductApiException, NoSuchMethodException,
            InvocationTargetException, IllegalAccessException {
        SearchRequest searchRequest = new SearchRequest();
        searchRequest.setQuery("jackets");
        searchRequest.setSite("bcs");
        searchRequest.addField("*");
        doTestSearchAndBrowse(searchRequest, ProductApi.class.getMethod("search", SearchRequest.class),
                SearchResponse.class);
    }

    @Test
    public void testSearchSpellCheck() throws IOException, ProductApiException, NoSuchMethodException,
            InvocationTargetException, IllegalAccessException {
        SearchRequest searchRequest = new SearchRequest();
        searchRequest.setQuery("nort fase");
        searchRequest.setSite("bcs");
        searchRequest.addField("*");

        SearchResponse response = (SearchResponse) callMethod(searchRequest,
                ProductApi.class.getMethod("search", SearchRequest.class), SearchResponse.class, SPELLCHECK_JSON);
        Metadata metadata = response.getMetadata();
        SpellCheck spellCheck = metadata.getSpellCheck();
        assertNotNull(spellCheck.getCollation());
        assertEquals("north face", spellCheck.getCollation());
        assertNull(metadata.getPartialMatch());

        Term[] terms = spellCheck.getTerms();
        assertNotNull(terms);
        assertEquals(2, terms.length);

        Term north = terms[0];
        assertEquals("nort", north.getTerm());
        assertEquals(10, north.getFound());
        assertEquals(0, north.getStartOffset());
        assertEquals(4, north.getEndOffset());
        String[] northSuggestions = north.getSuggestions();
        assertNotNull(northSuggestions);
        assertEquals(10, northSuggestions.length);
        assertEquals("north", northSuggestions[0]);
        assertEquals("norte", northSuggestions[1]);

        Term face = terms[1];
        assertEquals("fase", face.getTerm());
        assertEquals(10, face.getFound());
        assertEquals(5, face.getStartOffset());
        assertEquals(9, face.getEndOffset());
        String[] faceSuggestions = face.getSuggestions();
        assertNotNull(faceSuggestions);
        assertEquals(10, faceSuggestions.length);
        assertEquals("face", faceSuggestions[0]);
        assertEquals("fast", faceSuggestions[1]);
    }

    @Test
    public void testCategoryById() throws IOException, ProductApiException, NoSuchMethodException,
            InvocationTargetException, IllegalAccessException {
        CategoryRequest request = new CategoryRequest("catId001");
        request.addField("*");
        request.setMaxLevels(2);
        doTestCategory(request, ProductApi.class.getMethod("findCategory", CategoryRequest.class));
    }

    @Test
    public void testBrandById() throws IOException, ProductApiException, NoSuchMethodException,
            InvocationTargetException, IllegalAccessException {
        BrandRequest request = new BrandRequest("88");
        request.addField("*");
        doTestBrand(request, ProductApi.class.getMethod("findBrand", BrandRequest.class));
    }

    @Test
    public void testAllBrands() throws IOException, ProductApiException, NoSuchMethodException,
            InvocationTargetException, IllegalAccessException {
        BrandRequest request = new BrandRequest(null);
        request.addField("*");

        BrandResponse response = (BrandResponse) callMethod(request,
                ProductApi.class.getMethod("findBrand", BrandRequest.class), BrandResponse.class,
                ALL_BRAND_RESPONSE_JSON);

        verify(client, times(1)).handle(any(org.restlet.Request.class));
        assertNotNull("Metadata not found in the response", response.getMetadata());
        assertNotNull("Brand not found in the response", response.getBrands());

        assertEquals("Search time matches", 1, response.getMetadata().getTime());
        assertEquals("1", response.getBrands()[0].getId());
        assertEquals("BrandA", response.getBrands()[0].getName());
        assertEquals("2", response.getBrands()[1].getId());
        assertEquals("BrandB", response.getBrands()[1].getName());

    }

    @Test
    public void testTopCategories() throws IOException, ProductApiException, NoSuchMethodException,
            InvocationTargetException, IllegalAccessException {
        CategoryRequest request = new CategoryRequest();
        request.addField("*");
        request.setMaxLevels(2);
        doTestCategory(request, ProductApi.class.getMethod("findCategory", CategoryRequest.class));
    }

    @Test
    public void testBrandCategories() throws IOException, ProductApiException, NoSuchMethodException,
            InvocationTargetException, IllegalAccessException {
        BrandCategoryRequest request = new BrandCategoryRequest("brandId");
        request.addField("*");
        request.setSite("site");
        doTestCategory(request, ProductApi.class.getMethod("findBrandCategories", BrandCategoryRequest.class));
    }

    public <T> Object callMethod(Request request, Method method, Class<T> clazz, String dataFile)
            throws IOException, IllegalArgumentException, IllegalAccessException, InvocationTargetException {
        ProductApi productApi = new ProductApi();
        productApi.setClient(client);
        productApi.setPreview(true);

        org.restlet.Response clientResponse = new org.restlet.Response(new org.restlet.Request());
        ObjectMapper mapper = productApi.getMapper();
        Object response = mapper
                .readValue(new FileStreamRepresentation(dataFile, MediaType.APPLICATION_JSON).getStream(), clazz);
        clientResponse.setEntity(
                new StringRepresentation(mapper.writeValueAsString(response), MediaType.APPLICATION_JSON));

        when(client.handle(any(org.restlet.Request.class))).thenReturn(clientResponse);
        return method.invoke(productApi, request);
    }

    public void doTestCategory(Request request, Method method)
            throws IOException, ProductApiException, InvocationTargetException, IllegalAccessException {
        CategoryResponse response = (CategoryResponse) callMethod(request, method, CategoryResponse.class,
                CATEGORY_RESPONSE_JSON);

        Metadata metadata = response.getMetadata();
        Category[] categories = response.getCategories();

        verify(client, times(1)).handle(any(org.restlet.Request.class));
        assertNotNull("Metadata found in the response", metadata);
        assertNotNull("categories found in the response", categories);

        //Check metadata
        assertEquals("Search time matches", 312, metadata.getTime());

        validateCategories(categories);

    }

    public void doTestBrand(Request request, Method method)
            throws IOException, ProductApiException, InvocationTargetException, IllegalAccessException {
        BrandResponse response = (BrandResponse) callMethod(request, method, BrandResponse.class,
                BRAND_RESPONSE_JSON);

        Metadata metadata = response.getMetadata();
        Brand brand = response.getBrand();

        verify(client, times(1)).handle(any(org.restlet.Request.class));
        assertNotNull("Metadata not found in the response", metadata);
        assertNotNull("Brand not found in the response", brand);

        //Check metadata
        assertEquals("Search time matches", 1, metadata.getTime());
        assertEquals("88", brand.getId());
        assertEquals("The North Face", brand.getName());
        assertEquals("/images/brand/thumb/NorthFacelogo2010.jpg", brand.getLogo());
        assertEquals("/Store/catalog/brandLanding.jsp?brandId=88", brand.getUrl());
        assertEquals(Arrays.asList("site1", "site2"), brand.getSites());
    }

    public <T> void doTestSearchAndBrowse(Request request, Method method, Class<T> clazz)
            throws IOException, ProductApiException, InvocationTargetException, IllegalAccessException {
        SearchResponse response = (SearchResponse) callMethod(request, method, clazz, SEARCH_RESPONSE_JSON);

        Metadata metadata = response.getMetadata();
        Product[] products = response.getProducts();

        verify(client, times(1)).handle(any(org.restlet.Request.class));

        assertNotNull("Metadata found in the response", metadata);
        assertNotNull("Products found in the response", products);

        //Check metadata
        assertEquals("Found results matches", 1472, metadata.getFound());
        assertEquals("Search time matches", 136, metadata.getTime());
        assertEquals("No breadcrumbs in the response", 0, metadata.getBreadCrumbs().length);
        assertEquals("Facet count matches", 7, metadata.getFacets().length);

        //Check facets
        validateFacets(metadata.getFacets());
        //Check products
        validateProducts(products);
    }

    private void validateCategories(Category[] categories) {
        assertEquals("Api should return 1 parent category", 1, categories.length);

        validateCategory(categories[0], "catId001", "Cat-001", "/cat-001", "site");

        assertEquals("Api should return 3 child categories", 3, categories[0].getChildCategories().length);

        validateCategory(categories[0].getChildCategories()[0], "catId0011", "Cat-001-1", "/cat-001-1", "site");
        validateCategory(categories[0].getChildCategories()[1], "catId0012", "Cat-001-2", "/cat-001-2", "site");
        validateCategory(categories[0].getChildCategories()[2], "catId0013", "Cat-001-3", "/cat-001-3", "site");
    }

    private void validateCategory(Category category, String id, String name, String seoUrl, String site) {
        assertEquals(id, category.getId());
        assertEquals(name, category.getName());
        assertEquals(seoUrl, category.getUrl());
        assertTrue("Missing site " + site + " in category", category.getSites().contains(site));
    }

    private void validateFacets(Facet[] facets) {
        assertNotNull("Category facet is not null", facets[0]);
        assertEquals("Category facet is name is correct", "category", facets[0].getName());
        assertEquals("Category facet is not mixed sorting", false, facets[0].isMixedSorting());
        assertEquals("Category facet is not multi select", false, facets[0].isMultiSelect());

        //Check some facet filters
        List<Facet.Filter> filters = facets[0].getFilters();

        assertEquals(9, filters.size());
        assertEquals("1.bcs.Men's Clothing", filters.get(0).getName());
        assertEquals(5086, filters.get(0).getCount());
        assertEquals("category%3A1.bcs.Men%27s%5C+Clothing", filters.get(0).getFilterQueries()); //Path
        assertEquals("category%3A1.bcs.Men%27s%5C+Clothing", filters.get(0).getFilterQuery());

        assertNotNull("Color facet is not null", facets[1]);
        assertEquals("Color facet name is correct", "Colors", facets[1].getName());
        assertEquals("Color UI type is colors", "colors", facets[1].getUiType());
        assertEquals("Color facet is not mixed sorting", false, facets[1].isMixedSorting());
        assertEquals("Color facet is multi select", true, facets[1].isMultiSelect());

        //Check some facet filters
        filters = facets[1].getFilters();
        assertEquals(12, filters.size());
        assertEquals("black", filters.get(0).getName());
        assertEquals(2536, filters.get(0).getCount());
        assertEquals("colorFamily%3Ablack", filters.get(0).getFilterQueries()); //Path
        assertEquals("colorFamily%3Ablack", filters.get(0).getFilterQuery());
    }

    private void validateProducts(Product[] products) {
        assertEquals("Products in current page match", 10, products.length);
        Product firstProduct = products[0];
        assertNotNull(firstProduct);
        assertEquals("CST0180", firstProduct.getId());
        assertEquals("Goccia Rain Jacket ", firstProduct.getTitle());
        assertEquals(
                "<p>Dont let a little sprinkle stop your ride. Just pull on the Castelli Goccia Rain Jacket and get after it. Waterproof, breathable Torrent fabric keeps you dry while a back vent helps the heat escape. Reflective Castelli graphics make you more visible when riding in traffic.</p>",
                firstProduct.getDescription());
        assertEquals(
                "Dont let a little sprinkle stop your ride. Just pull on the Castelli Mens Goccia Rain Jacket and get after it.",
                firstProduct.getShortDescription());
        assertEquals("557", firstProduct.getSizingChart());
        assertEquals(Integer.valueOf(1), firstProduct.getListRank());
        assertEquals("1969-12-31T18:00:00Z", new ISO8601DateFormat().format(firstProduct.getActivationDate()));
        assertFalse(firstProduct.getPackage());

        validateFeatures(firstProduct.getFeatures());
        validateCostumerReviews(firstProduct.getCustomerReviews());
        validateDetailImages(firstProduct.getDetailImages());
        validateBrand(firstProduct.getBrand());
        validateCategories(firstProduct.getCategories());
        validateSkus(firstProduct.getSkus());
        validateProductSummary(firstProduct.getSummary());
    }

    private void validateFeatures(List<Attribute> features) {
        assertNotNull(features);
        assertEquals(1, features.size());
        assertEquals("feature", features.get(0).getName());
        assertEquals("featureValue", features.get(0).getValue());
    }

    private void validateCostumerReviews(CustomerReview customerReview) {
        assertNotNull(customerReview);
        assertEquals(0, customerReview.getCount());
        assertEquals(0.0, customerReview.getAverage(), 0.001);
        assertEquals(2.5, customerReview.getBayesianAverage(), 0.001);
    }

    private void validateDetailImages(List<Image> images) {
        assertNotNull(images);
        assertEquals(9, images.size());
        assertEquals("Fabric Detail", images.get(0).getTitle());
        assertEquals("/images/items/small/CST/CST0180/YLFLO_D6.jpg", images.get(0).getUrl());
    }

    private void validateBrand(Brand brand) {
        assertNotNull(brand);
        assertEquals("100000522", brand.getId());
        assertEquals("Castelli", brand.getName());
    }

    private void validateCategories(Set<Category> categories) {
        assertNotNull(categories);
        assertEquals(4, categories.size());

        Set<String> categoriesToCheck = new HashSet<String>();
        categoriesToCheck.add("ccCat100199");
        categoriesToCheck.add("bcsCat1411000021");
        categoriesToCheck.add("ccTestCat1411000021");
        categoriesToCheck.add("ccCat100090003");
        for (Category c : categories) {
            assertTrue(categoriesToCheck.contains(c.getId()));
        }
    }

    private void validateSkus(List<Sku> skus) {
        assertNotNull(skus);
        assertEquals(14, skus.size());
        Sku sku1 = skus.get(0);
        assertEquals("CST0180-BK-L", sku1.getId());
        assertNotNull(sku1.getImage());
        assertEquals("/images/items/medium/CST/CST0180/BK.jpg", sku1.getImage().getUrl());
        assertFalse(sku1.getPastSeason());
        assertEquals("Black, L", sku1.getTitle());
        assertTrue(sku1.getRetail());
        assertFalse(sku1.getCloseout());
        assertTrue(sku1.getOutlet());
        assertEquals(119.95, sku1.getListPrice(), 0.001);
        assertEquals(49.95, sku1.getSalePrice(), 0.001);
        assertEquals(Integer.valueOf(58), sku1.getDiscountPercent());
        assertEquals(Long.valueOf(14), sku1.getAvailability().getStockLevel());
        assertEquals(Availability.Status.InStock, sku1.getAvailability().getStatus());
        assertEquals("/Store/catalog/productLanding.jsp?productId=CST0180", sku1.getUrl());
        assertFalse(sku1.getAllowBackorder());

    }

    private void validateProductSummary(ProductSummary summary) {
        assertNotNull(summary);
        assertEquals(new Integer(2), summary.getColorCount());

        List<String> colorFamilies = summary.getColorFamilies();

        assertNotNull(colorFamilies);
        assertEquals(2, colorFamilies.size());
        assertEquals("yellow", colorFamilies.get(0));
        assertEquals("black", colorFamilies.get(1));

        assertEquals(60.0, summary.getMaxDiscountPercent(), 0.001);
        assertEquals(119.95, summary.getMaxListPrice(), 0.001);
        assertEquals(49.95, summary.getMaxSalePrice(), 0.001);
        assertEquals(58.0, summary.getMinDiscountPercent(), 0.001);
        assertEquals(119.95, summary.getMinListPrice(), 0.001);
        assertEquals(47.98, summary.getMinSalePrice(), 0.001);
    }

    @Test
    public void testGetHttpRequest() throws IOException, ProductApiException {
        SearchRequest sr = new SearchRequest();
        sr.setQuery("some query");
        sr.addField("field1");
        sr.addField("field2");
        sr.addFilterQuery("country:us");
        sr.addFilterQuery("price:[300 TO 400]");
        sr.setOffset(0);
        sr.setLimit(10);
        sr.setOutlet(false);
        sr.setSort("price asc, discount desc");
        sr.setSite("testSite");

        ProductApi productApi = new ProductApi();

        org.restlet.Request request = productApi
                .convertToRestlet("http://localhost:9000/v1" + sr.getEndpoint() + "?preview=false", sr);

        assertNotNull(request);
        assertEquals("GET", request.getMethod().getName());
        assertEquals(
                "http://localhost:9000/v1/products?preview=false&q=some+query&fields=field1%2Cfield2&filterQueries=country%3Aus%7Cprice%3A%5B300+TO+400%5D&offset=0&limit=10&outlet=false&sort=price+asc%2C+discount+desc&site=testSite",
                request.getResourceRef().toString());
    }

    class TestRequest extends BaseRequest {
        @Override
        public String getEndpoint() {
            return "/dummy/endpoint";
        }

        @Override
        public ProductApi.RequestMethod getMethod() {
            return ProductApi.RequestMethod.GET;
        }
    }

    class FileStreamRepresentation extends StreamRepresentation {
        String path = null;

        public FileStreamRepresentation(String path, MediaType mediaType) {
            super(mediaType);
            this.path = path;
        }

        @Override
        public InputStream getStream() throws IOException {
            return Thread.currentThread().getContextClassLoader().getResourceAsStream(path);
        }

        @Override
        public void write(OutputStream outputStream) throws IOException {
            throw new UnsupportedOperationException("Not implemented");
        }
    }

    class TestStreamRepresentation extends StreamRepresentation {

        private String content = StringUtils.EMPTY;

        /**
         * Creates a new TestStreamRepresentation.
         */
        public TestStreamRepresentation(String content) {
            super(MediaType.APPLICATION_JSON);
            this.content = content;
        }

        @Override
        public InputStream getStream() throws IOException {
            return new ByteArrayInputStream(content.getBytes());
        }

        @Override
        public void write(OutputStream outputStream) throws IOException {
            throw new UnsupportedOperationException("Not implemented");
        }
    }
}

class TestResponse extends DefaultResponse {
    private String message;

    public String getMessage() {
        return message;
    }

    @SuppressWarnings("UnusedDeclaration")
    public void setMessage(String message) {
        this.message = message;
    }
}