com.temenos.interaction.media.hal.TestHALProvider.java Source code

Java tutorial

Introduction

Here is the source code for com.temenos.interaction.media.hal.TestHALProvider.java

Source

package com.temenos.interaction.media.hal;

/*
 * #%L
 * interaction-media-hal
 * %%
 * Copyright (C) 2012 - 2013 Temenos Holdings N.V.
 * %%
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 * #L%
 */

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.GenericEntity;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Request;
import javax.ws.rs.core.UriInfo;

import org.apache.commons.beanutils.PropertyUtils;
import org.custommonkey.xmlunit.Diff;
import org.custommonkey.xmlunit.XMLAssert;
import org.junit.Test;
import org.odata4j.core.OEntities;
import org.odata4j.core.OEntity;
import org.odata4j.core.OEntityKey;
import org.odata4j.core.OLink;
import org.odata4j.core.OProperties;
import org.odata4j.core.OProperty;
import org.odata4j.edm.EdmEntitySet;
import org.odata4j.edm.EdmEntityType;
import org.odata4j.edm.EdmProperty;
import org.odata4j.edm.EdmSimpleType;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.temenos.interaction.core.MultivaluedMapImpl;
import com.temenos.interaction.core.command.CommandHelper;
import com.temenos.interaction.core.entity.Entity;
import com.temenos.interaction.core.entity.EntityMetadata;
import com.temenos.interaction.core.entity.Metadata;
import com.temenos.interaction.core.entity.vocabulary.Vocabulary;
import com.temenos.interaction.core.entity.vocabulary.terms.TermComplexType;
import com.temenos.interaction.core.entity.vocabulary.terms.TermValueType;
import com.temenos.interaction.core.hypermedia.Action;
import com.temenos.interaction.core.hypermedia.DefaultResourceStateProvider;
import com.temenos.interaction.core.hypermedia.Link;
import com.temenos.interaction.core.hypermedia.ResourceState;
import com.temenos.interaction.core.hypermedia.ResourceStateMachine;
import com.temenos.interaction.core.hypermedia.ResourceStateProvider;
import com.temenos.interaction.core.hypermedia.Transition;
import com.temenos.interaction.core.resource.CollectionResource;
import com.temenos.interaction.core.resource.EntityResource;
import com.temenos.interaction.core.resource.MetaDataResource;
import com.temenos.interaction.core.resource.RESTResource;

public class TestHALProvider {

    /*
     * Test the getSize operation of GET with this provider
     */
    @Test
    public void testSize() {
        HALProvider hp = new HALProvider(mock(Metadata.class), mock(ResourceStateProvider.class));
        assertEquals(-1, hp.getSize(null, null, null, null, null));
    }

    /** utility method to parse a json string for use in comparisons
     */
    public Map parseJson(String json) throws IOException {
        //converting json to Map
        byte[] mapData = json.getBytes();
        Map<String, Object> myMap = new HashMap<String, Object>();

        ObjectMapper objectMapper = new ObjectMapper();
        myMap = objectMapper.readValue(mapData, HashMap.class);
        return myMap;
    }

    /*
     * Test the getSize operation of GET with this provider
     */
    @SuppressWarnings("unchecked")
    @Test
    public void testDeserialise() throws IOException, URISyntaxException {
        ResourceStateMachine sm = new ResourceStateMachine(
                new ResourceState("Children", "initial", new ArrayList<Action>(), "/children"));
        HALProvider hp = new HALProvider(createMockChildVocabMetadata(), new DefaultResourceStateProvider(sm));
        UriInfo mockUriInfo = mock(UriInfo.class);
        when(mockUriInfo.getBaseUri()).thenReturn(new URI("http://www.temenos.com/rest.svc"));
        when(mockUriInfo.getPath()).thenReturn("/children");
        hp.setUriInfo(mockUriInfo);
        Request requestContext = mock(Request.class);
        when(requestContext.getMethod()).thenReturn("GET");
        hp.setRequestContext(requestContext);

        String strEntityStream = "<resource href=\"/children\"><name>noah</name><age>2</age></resource>";
        InputStream entityStream = new ByteArrayInputStream(strEntityStream.getBytes());
        GenericEntity<EntityResource<Entity>> ge = new GenericEntity<EntityResource<Entity>>(
                new EntityResource<Entity>()) {
        };
        EntityResource<Entity> er = (EntityResource<Entity>) hp.readFrom(RESTResource.class, ge.getType(), null,
                MediaType.APPLICATION_HAL_XML_TYPE, null, entityStream);
        assertNotNull(er.getEntity());
        Entity entity = er.getEntity();
        assertEquals("Children", entity.getName());
        assertNotNull(entity.getProperties());
        // string type
        assertEquals("noah", entity.getProperties().getProperty("name").getValue());
        // long type
        // TODO handle non string entity properties
        assertEquals(new Long(2), entity.getProperties().getProperty("age").getValue());
    }

    @SuppressWarnings("unchecked")
    @Test
    public void testDeserialiseResolveEntityName() throws IOException, URISyntaxException {
        ResourceStateMachine sm = new ResourceStateMachine(
                new ResourceState("Children", "initial", new ArrayList<Action>(), "/children"));
        HALProvider hp = new HALProvider(createMockChildVocabMetadata(), new DefaultResourceStateProvider(sm));
        UriInfo mockUriInfo = mock(UriInfo.class);
        when(mockUriInfo.getBaseUri()).thenReturn(new URI("http://www.temenos.com/rest.svc"));
        when(mockUriInfo.getPath()).thenReturn("/children");
        hp.setUriInfo(mockUriInfo);
        Request requestContext = mock(Request.class);
        when(requestContext.getMethod()).thenReturn("GET");
        hp.setRequestContext(requestContext);

        String strEntityStream = "<resource href=\"/children\"><name>noah</name><age>2</age></resource>";
        InputStream entityStream = new ByteArrayInputStream(strEntityStream.getBytes());
        GenericEntity<EntityResource<Entity>> ge = new GenericEntity<EntityResource<Entity>>(
                new EntityResource<Entity>()) {
        };
        EntityResource<Entity> er = (EntityResource<Entity>) hp.readFrom(RESTResource.class, ge.getType(), null,
                MediaType.APPLICATION_HAL_XML_TYPE, null, entityStream);
        assertNotNull(er.getEntity());
        Entity entity = er.getEntity();
        assertEquals("Children", entity.getName());
    }

    @SuppressWarnings("unchecked")
    @Test
    public void testDeserialiseResolveEntityNameJSON() throws IOException, URISyntaxException {
        ResourceStateMachine sm = new ResourceStateMachine(
                new ResourceState("Children", "initial", new ArrayList<Action>(), "/children"));
        HALProvider hp = new HALProvider(createMockChildVocabMetadata(), new DefaultResourceStateProvider(sm));
        UriInfo mockUriInfo = mock(UriInfo.class);
        when(mockUriInfo.getBaseUri()).thenReturn(new URI("http://www.temenos.com/rest.svc"));
        when(mockUriInfo.getPath()).thenReturn("/children");
        hp.setUriInfo(mockUriInfo);
        Request requestContext = mock(Request.class);
        when(requestContext.getMethod()).thenReturn("GET");
        hp.setRequestContext(requestContext);

        String strEntityStream = "{ \"_links\": { \"self\": { \"href\": \"http://www.temenos.com/rest.svc/children\" } }, \"name\": \"noah\", \"age\": 2 }";
        InputStream entityStream = new ByteArrayInputStream(strEntityStream.getBytes());
        GenericEntity<EntityResource<Entity>> ge = new GenericEntity<EntityResource<Entity>>(
                new EntityResource<Entity>()) {
        };
        EntityResource<Entity> er = (EntityResource<Entity>) hp.readFrom(RESTResource.class, ge.getType(), null,
                MediaType.APPLICATION_HAL_JSON_TYPE, null, entityStream);
        assertNotNull(er.getEntity());
        Entity entity = er.getEntity();
        assertEquals("Children", entity.getName());
    }

    @SuppressWarnings("unchecked")
    @Test
    public void testDeserialisePlainJSON() throws IOException, URISyntaxException {
        ResourceStateMachine sm = new ResourceStateMachine(
                new ResourceState("Children", "initial", new ArrayList<Action>(), "/children"));
        HALProvider hp = new HALProvider(createMockChildVocabMetadata(), new DefaultResourceStateProvider(sm),
                new com.theoryinpractise.halbuilder.standard.StandardRepresentationFactory().withReader(
                        javax.ws.rs.core.MediaType.APPLICATION_JSON,
                        com.theoryinpractise.halbuilder.json.JsonRepresentationReader.class));
        UriInfo mockUriInfo = mock(UriInfo.class);
        when(mockUriInfo.getBaseUri()).thenReturn(new URI("http://www.temenos.com/rest.svc"));
        when(mockUriInfo.getPath()).thenReturn("/children");
        hp.setUriInfo(mockUriInfo);
        Request requestContext = mock(Request.class);
        when(requestContext.getMethod()).thenReturn("GET");
        hp.setRequestContext(requestContext);

        String strEntityStream = "{ \"_links\": { \"self\": { \"href\": \"http://www.temenos.com/rest.svc/children\" } }, \"name\": \"noah\", \"age\": 2 }";
        InputStream entityStream = new ByteArrayInputStream(strEntityStream.getBytes());
        GenericEntity<EntityResource<Entity>> ge = new GenericEntity<EntityResource<Entity>>(
                new EntityResource<Entity>()) {
        };
        EntityResource<Entity> er = (EntityResource<Entity>) hp.readFrom(RESTResource.class, ge.getType(), null,
                javax.ws.rs.core.MediaType.APPLICATION_JSON_TYPE, null, entityStream);
        assertNotNull(er.getEntity());
        Entity entity = er.getEntity();
        assertEquals("Children", entity.getName());
    }

    @SuppressWarnings("unchecked")
    @Test
    public void testNestedDeserialiseResolveEntityNameJSON() throws IOException, URISyntaxException,
            IllegalAccessException, InvocationTargetException, NoSuchMethodException {
        ResourceStateMachine sm = new ResourceStateMachine(
                new ResourceState("students", "initial", new ArrayList<Action>(), "/students"));
        HALProvider hp = new HALProvider(createNestedMockStudentsVocabMetadata(),
                new DefaultResourceStateProvider(sm));
        UriInfo mockUriInfo = mock(UriInfo.class);
        when(mockUriInfo.getBaseUri()).thenReturn(new URI("http://www.temenos.com/rest.svc"));
        when(mockUriInfo.getPath()).thenReturn("/students");
        hp.setUriInfo(mockUriInfo);
        Request requestContext = mock(Request.class);
        when(requestContext.getMethod()).thenReturn("GET");
        hp.setRequestContext(requestContext);

        String strEntityStream = "{'_links':{'self':{'href':'http://www.temenos.com/rest.svc/students'}},'name':'noah','age':'2','tuitions':[{'Duration':'2.5','TutionName':'Maths'},{'TutionName':'English','Duration':'2'}]}"
                .replace('\'', '\"');

        InputStream entityStream = new ByteArrayInputStream(strEntityStream.getBytes());
        GenericEntity<EntityResource<Entity>> ge = new GenericEntity<EntityResource<Entity>>(
                new EntityResource<Entity>()) {
        };
        EntityResource<Entity> er = (EntityResource<Entity>) hp.readFrom(RESTResource.class, ge.getType(), null,
                MediaType.APPLICATION_HAL_JSON_TYPE, null, entityStream);
        assertNotNull(er.getEntity());
        Entity entity = er.getEntity();
        assertEquals("students", entity.getName());
        assertEquals(2L, PropertyUtils.getProperty(entity, "properties.properties.age.value"));
        assertEquals("noah", PropertyUtils.getProperty(entity, "properties.properties.name.value"));
        assertEquals("2.5", PropertyUtils.getProperty(entity,
                "properties.properties.tuitions.value[0].properties.Duration.value"));
    }

    @SuppressWarnings("unchecked")
    @Test
    public void testMoreComplexNestedDeserialiseResolveEntityNameJSON() throws IOException, URISyntaxException,
            IllegalAccessException, InvocationTargetException, NoSuchMethodException {
        ResourceStateMachine sm = new ResourceStateMachine(
                new ResourceState("students", "initial", new ArrayList<Action>(), "/students"));
        HALProvider hp = new HALProvider(createMoreComplexNestedMockStudentVocabMetadata(),
                new DefaultResourceStateProvider(sm));
        UriInfo mockUriInfo = mock(UriInfo.class);
        when(mockUriInfo.getBaseUri()).thenReturn(new URI("http://www.temenos.com/rest.svc"));
        when(mockUriInfo.getPath()).thenReturn("/students");
        hp.setUriInfo(mockUriInfo);
        Request requestContext = mock(Request.class);
        when(requestContext.getMethod()).thenReturn("GET");
        hp.setRequestContext(requestContext);

        String strEntityStream = "{'_links':{'self':{'href':'http://www.temenos.com/rest.svc/students'}},'name':'noah','age':'2','address': {'houseNumber':'123', 'roadName': 'Fenchurch Street'},'tuitions':[{'Duration':'2.5','TutionName':'Maths'},{'TutionName':'English','Duration':'2', 'Teachers': [{'Name': 'John'}, {'Name': 'Martin'}]}]}"
                .replace('\'', '\"');

        InputStream entityStream = new ByteArrayInputStream(strEntityStream.getBytes());
        GenericEntity<EntityResource<Entity>> ge = new GenericEntity<EntityResource<Entity>>(
                new EntityResource<Entity>()) {
        };
        EntityResource<Entity> er = (EntityResource<Entity>) hp.readFrom(RESTResource.class, ge.getType(), null,
                MediaType.APPLICATION_HAL_JSON_TYPE, null, entityStream);
        assertNotNull(er.getEntity());
        Entity entity = er.getEntity();
        assertEquals("students", entity.getName());
        assertEquals(2L, PropertyUtils.getProperty(entity, "properties.properties(age).value"));
        assertEquals("noah", PropertyUtils.getProperty(entity, "properties.properties(name).value"));
        assertEquals("123", PropertyUtils.getProperty(entity,
                "properties.properties(address).value.properties(houseNumber).value"));
        assertEquals("Fenchurch Street", PropertyUtils.getProperty(entity,
                "properties.properties(address).value.properties(roadName).value"));
        assertEquals("2.5", PropertyUtils.getProperty(entity,
                "properties.properties(tuitions).value[0].properties(Duration).value"));
    }

    @Test
    public void testIsWritable() {
        ResourceStateMachine sm = new ResourceStateMachine(
                new ResourceState("Children", "initial", new ArrayList<Action>(), "/children"));
        HALProvider provider = new HALProvider(createNestedMockChildVocabMetadata(),
                new DefaultResourceStateProvider(sm));
        GenericEntity<EntityResource<Entity>> entity = new GenericEntity<EntityResource<Entity>>(
                new EntityResource<Entity>()) {
        };
        assertTrue(provider.isWriteable(RESTResource.class, entity.getType(), null,
                MediaType.APPLICATION_HAL_JSON_TYPE));
        assertTrue(provider.isWriteable(RESTResource.class, entity.getType(), null,
                MediaType.APPLICATION_HAL_XML_TYPE));
        assertTrue(provider.isWriteable(RESTResource.class, entity.getType(), null,
                javax.ws.rs.core.MediaType.APPLICATION_JSON_TYPE));
    }

    @Test
    public void testIsWritableReturnsTrueForCollectionResourceObjects() {
        ResourceStateMachine sm = new ResourceStateMachine(
                new ResourceState("Children", "initial", new ArrayList<Action>(), "/children"));
        HALProvider provider = new HALProvider(createNestedMockChildVocabMetadata(),
                new DefaultResourceStateProvider(sm));
        GenericEntity<CollectionResource<Entity>> entity = new GenericEntity<CollectionResource<Entity>>(
                new CollectionResource<Entity>()) {
        };
        assertTrue(provider.isWriteable(RESTResource.class, entity.getType(), null,
                MediaType.APPLICATION_HAL_JSON_TYPE));
        assertTrue(provider.isWriteable(RESTResource.class, entity.getType(), null,
                MediaType.APPLICATION_HAL_XML_TYPE));
        assertTrue(provider.isWriteable(RESTResource.class, entity.getType(), null,
                javax.ws.rs.core.MediaType.APPLICATION_JSON_TYPE));
    }

    @Test
    public void testIsWritableReturnsFalseForUnwritableTypes() {
        ResourceStateMachine sm = new ResourceStateMachine(
                new ResourceState("Children", "initial", new ArrayList<Action>(), "/children"));
        HALProvider provider = new HALProvider(createNestedMockChildVocabMetadata(),
                new DefaultResourceStateProvider(sm));
        GenericEntity<EntityResource<Entity>> entity = new GenericEntity<EntityResource<Entity>>(
                new EntityResource<Entity>()) {
        };
        assertFalse(provider.isWriteable(RESTResource.class, entity.getType(), null,
                javax.ws.rs.core.MediaType.APPLICATION_SVG_XML_TYPE));
    }

    @Test
    public void testIsWritableReturnsFalseForNonEntityOrCollectionTypes() {
        ResourceStateMachine sm = new ResourceStateMachine(
                new ResourceState("Children", "initial", new ArrayList<Action>(), "/children"));
        HALProvider provider = new HALProvider(createNestedMockChildVocabMetadata(),
                new DefaultResourceStateProvider(sm));
        GenericEntity<EntityResource<Entity>> entity = new GenericEntity<EntityResource<Entity>>(
                new EntityResource<Entity>()) {
        };
        assertFalse(provider.isWriteable(RESTResource.class, entity.getType(), null,
                javax.ws.rs.core.MediaType.APPLICATION_SVG_XML_TYPE));
    }

    @SuppressWarnings("unchecked")
    @Test
    public void testDeserialiseResolveEntityNameWithId() throws IOException, URISyntaxException {
        ResourceStateMachine sm = new ResourceStateMachine(
                new ResourceState("Children", "initial", new ArrayList<Action>(), "/children/{id}", "id", null));
        HALProvider hp = new HALProvider(createMockChildVocabMetadata(), new DefaultResourceStateProvider(sm));
        UriInfo mockUriInfo = mock(UriInfo.class);
        when(mockUriInfo.getBaseUri()).thenReturn(new URI("http://www.temenos.com/rest.svc"));
        when(mockUriInfo.getPath()).thenReturn("/children/123");
        MultivaluedMap<String, String> mockPathParameters = new MultivaluedMapImpl<String>();
        when(mockUriInfo.getPathParameters()).thenReturn(mockPathParameters);
        hp.setUriInfo(mockUriInfo);
        Request requestContext = mock(Request.class);
        when(requestContext.getMethod()).thenReturn("GET");
        hp.setRequestContext(requestContext);

        String strEntityStream = "<resource href=\"/children/123\"><name>noah</name><age>2</age></resource>";
        InputStream entityStream = new ByteArrayInputStream(strEntityStream.getBytes());
        GenericEntity<EntityResource<Entity>> ge = new GenericEntity<EntityResource<Entity>>(
                new EntityResource<Entity>()) {
        };
        EntityResource<Entity> er = (EntityResource<Entity>) hp.readFrom(RESTResource.class, ge.getType(), null,
                MediaType.APPLICATION_HAL_XML_TYPE, null, entityStream);
        assertNotNull(er.getEntity());
        Entity entity = er.getEntity();
        assertEquals("Children", entity.getName());
    }

    @SuppressWarnings("unchecked")
    @Test
    public void testDeserialiseResolveEntityNameWithIdODataPath() throws IOException, URISyntaxException {
        ResourceStateMachine sm = new ResourceStateMachine(new ResourceState("Children", "initial",
                new ArrayList<Action>(), "/children({id})/updated", "id", null));
        HALProvider hp = new HALProvider(createMockChildVocabMetadata(), new DefaultResourceStateProvider(sm));
        UriInfo mockUriInfo = mock(UriInfo.class);
        when(mockUriInfo.getBaseUri()).thenReturn(new URI("http://www.temenos.com/rest.svc"));
        when(mockUriInfo.getPath()).thenReturn("/children(123)/updated");
        MultivaluedMap<String, String> mockPathParameters = new MultivaluedMapImpl<String>();
        mockPathParameters.add("id", "123");
        when(mockUriInfo.getPathParameters()).thenReturn(mockPathParameters);
        hp.setUriInfo(mockUriInfo);
        Request requestContext = mock(Request.class);
        when(requestContext.getMethod()).thenReturn("GET");
        hp.setRequestContext(requestContext);

        String strEntityStream = "<resource href=\"/children(123)/updated\"><name>noah</name><age>2</age></resource>";
        InputStream entityStream = new ByteArrayInputStream(strEntityStream.getBytes());
        GenericEntity<EntityResource<Entity>> ge = new GenericEntity<EntityResource<Entity>>(
                new EntityResource<Entity>()) {
        };
        EntityResource<Entity> er = (EntityResource<Entity>) hp.readFrom(RESTResource.class, ge.getType(), null,
                MediaType.APPLICATION_HAL_XML_TYPE, null, entityStream);
        assertNotNull(er.getEntity());
        Entity entity = er.getEntity();
        assertEquals("Children", entity.getName());
    }

    @SuppressWarnings("unchecked")
    @Test
    public void testDeserialiseResolveEntityNameUriInfo() throws IOException, URISyntaxException {
        ResourceStateMachine sm = new ResourceStateMachine(
                new ResourceState("Children", "initial", new ArrayList<Action>(), "/children"));
        HALProvider hp = new HALProvider(createMockChildVocabMetadata(), new DefaultResourceStateProvider(sm));
        UriInfo mockUriInfo = mock(UriInfo.class);
        when(mockUriInfo.getBaseUri()).thenReturn(new URI("http://www.temenos.com/rest.svc/"));
        when(mockUriInfo.getPath()).thenReturn("/children");
        hp.setUriInfo(mockUriInfo);
        Request requestContext = mock(Request.class);
        when(requestContext.getMethod()).thenReturn("GET");
        hp.setRequestContext(requestContext);

        String strEntityStream = "<resource><name>noah</name><age>2</age></resource>";
        InputStream entityStream = new ByteArrayInputStream(strEntityStream.getBytes());
        GenericEntity<EntityResource<Entity>> ge = new GenericEntity<EntityResource<Entity>>(
                new EntityResource<Entity>()) {
        };
        EntityResource<Entity> er = (EntityResource<Entity>) hp.readFrom(RESTResource.class, ge.getType(), null,
                MediaType.APPLICATION_HAL_XML_TYPE, null, entityStream);
        assertNotNull(er.getEntity());
        Entity entity = er.getEntity();
        assertEquals("Children", entity.getName());
    }

    @SuppressWarnings("unchecked")
    @Test
    public void testDeserialiseResolveEntityNameUriInfoRelative() throws IOException, URISyntaxException {
        ResourceStateMachine sm = new ResourceStateMachine(
                new ResourceState("Children", "initial", new ArrayList<Action>(), "/Orders()"));
        HALProvider hp = new HALProvider(createMockChildVocabMetadata(), new DefaultResourceStateProvider(sm));
        UriInfo mockUriInfo = mock(UriInfo.class);
        when(mockUriInfo.getBaseUri()).thenReturn(new URI("http://www.temenos.com/rest.svc/"));
        when(mockUriInfo.getPath()).thenReturn("Orders()");
        hp.setUriInfo(mockUriInfo);
        Request requestContext = mock(Request.class);
        when(requestContext.getMethod()).thenReturn("GET");
        hp.setRequestContext(requestContext);

        String strEntityStream = "<resource><name>noah</name><age>2</age></resource>";
        InputStream entityStream = new ByteArrayInputStream(strEntityStream.getBytes());
        GenericEntity<EntityResource<Entity>> ge = new GenericEntity<EntityResource<Entity>>(
                new EntityResource<Entity>()) {
        };
        EntityResource<Entity> er = (EntityResource<Entity>) hp.readFrom(RESTResource.class, ge.getType(), null,
                MediaType.APPLICATION_HAL_XML_TYPE, null, entityStream);
        assertNotNull(er.getEntity());
        Entity entity = er.getEntity();
        assertEquals("Children", entity.getName());
    }

    @SuppressWarnings("unchecked")
    @Test
    public void testDeserialiseResolveEntityNameUriInfoWithId() throws IOException, URISyntaxException {
        ResourceStateMachine sm = new ResourceStateMachine(
                new ResourceState("Children", "initial", new ArrayList<Action>(), "/children/{id}", "id", null));
        HALProvider hp = new HALProvider(createMockChildVocabMetadata(), new DefaultResourceStateProvider(sm));
        UriInfo mockUriInfo = mock(UriInfo.class);
        when(mockUriInfo.getBaseUri()).thenReturn(new URI("http://www.temenos.com/rest.svc/"));
        when(mockUriInfo.getPath()).thenReturn("/children/123");
        MultivaluedMap<String, String> mockPathParameters = new MultivaluedMapImpl<String>();
        mockPathParameters.add("id", "123");
        when(mockUriInfo.getPathParameters()).thenReturn(mockPathParameters);
        hp.setUriInfo(mockUriInfo);
        Request requestContext = mock(Request.class);
        when(requestContext.getMethod()).thenReturn("GET");
        hp.setRequestContext(requestContext);

        String strEntityStream = "<resource><name>noah</name><age>2</age></resource>";
        InputStream entityStream = new ByteArrayInputStream(strEntityStream.getBytes());
        GenericEntity<EntityResource<Entity>> ge = new GenericEntity<EntityResource<Entity>>(
                new EntityResource<Entity>()) {
        };
        EntityResource<Entity> er = (EntityResource<Entity>) hp.readFrom(RESTResource.class, ge.getType(), null,
                MediaType.APPLICATION_HAL_XML_TYPE, null, entityStream);
        assertNotNull(er.getEntity());
        Entity entity = er.getEntity();
        assertEquals("Children", entity.getName());
    }

    @SuppressWarnings("unchecked")
    @Test
    public void testDeserialiseResolveEntityNameUriInfoWithIdODataPath() throws IOException, URISyntaxException {
        ResourceStateMachine sm = new ResourceStateMachine(new ResourceState("Children", "initial",
                new ArrayList<Action>(), "/children({id})/updated", "id", null));
        HALProvider hp = new HALProvider(createMockChildVocabMetadata(), new DefaultResourceStateProvider(sm));
        UriInfo mockUriInfo = mock(UriInfo.class);
        when(mockUriInfo.getBaseUri()).thenReturn(new URI("http://www.temenos.com/rest.svc/"));
        when(mockUriInfo.getPath()).thenReturn("/children(123)/updated");
        MultivaluedMap<String, String> mockPathParameters = new MultivaluedMapImpl<String>();
        mockPathParameters.add("id", "123");
        when(mockUriInfo.getPathParameters()).thenReturn(mockPathParameters);
        hp.setUriInfo(mockUriInfo);
        Request requestContext = mock(Request.class);
        when(requestContext.getMethod()).thenReturn("GET");
        hp.setRequestContext(requestContext);

        String strEntityStream = "<resource><name>noah</name><age>2</age></resource>";
        InputStream entityStream = new ByteArrayInputStream(strEntityStream.getBytes());
        GenericEntity<EntityResource<Entity>> ge = new GenericEntity<EntityResource<Entity>>(
                new EntityResource<Entity>()) {
        };
        EntityResource<Entity> er = (EntityResource<Entity>) hp.readFrom(RESTResource.class, ge.getType(), null,
                MediaType.APPLICATION_HAL_XML_TYPE, null, entityStream);
        assertNotNull(er.getEntity());
        Entity entity = er.getEntity();
        assertEquals("Children", entity.getName());
    }

    private Metadata createMockChildVocabMetadata() {
        EntityMetadata vocs = new EntityMetadata("Children");
        Vocabulary vocId = new Vocabulary();
        vocId.setTerm(new TermValueType(TermValueType.TEXT));
        vocs.setPropertyVocabulary("name", vocId);
        Vocabulary vocBody = new Vocabulary();
        vocBody.setTerm(new TermValueType(TermValueType.INTEGER_NUMBER));
        vocs.setPropertyVocabulary("age", vocBody);
        Metadata metadata = new Metadata("Family");
        metadata.setEntityMetadata(vocs);
        return metadata;
    }

    private Metadata createNestedMockChildVocabMetadata() {
        EntityMetadata vocs = new EntityMetadata("Children");
        Vocabulary vocId = new Vocabulary();
        vocId.setTerm(new TermValueType(TermValueType.TEXT));
        vocs.setPropertyVocabulary("name", vocId);
        Vocabulary vocBody = new Vocabulary();
        vocBody.setTerm(new TermValueType(TermValueType.INTEGER_NUMBER));
        vocs.setPropertyVocabulary("age", vocBody);

        Vocabulary vocRides = new Vocabulary();
        vocRides.setTerm(new TermComplexType(true));
        vocs.setPropertyVocabulary("tuitions", vocRides);
        Vocabulary vocTutionName = new Vocabulary();
        vocTutionName.setTerm(new TermValueType(TermValueType.TEXT));
        vocs.setPropertyVocabulary("TutionName", vocTutionName,
                Collections.enumeration(Collections.singletonList("tuitions")));
        Vocabulary vocTutionDuration = new Vocabulary();
        vocTutionName.setTerm(new TermValueType(TermValueType.TEXT));
        vocs.setPropertyVocabulary("Duration", vocTutionDuration,
                Collections.enumeration(Collections.singletonList("tuitions")));

        Metadata metadata = new Metadata("Family");
        metadata.setEntityMetadata(vocs);
        return metadata;
    }

    private Metadata createNestedMockStudentsVocabMetadata() {
        EntityMetadata vocs = new EntityMetadata("students");
        Vocabulary vocId = new Vocabulary();
        vocId.setTerm(new TermValueType(TermValueType.TEXT));
        vocs.setPropertyVocabulary("name", vocId);
        Vocabulary vocBody = new Vocabulary();
        vocBody.setTerm(new TermValueType(TermValueType.INTEGER_NUMBER));
        vocs.setPropertyVocabulary("age", vocBody);

        Vocabulary vocRides = new Vocabulary();
        vocRides.setTerm(new TermComplexType(true));
        vocs.setPropertyVocabulary("tuitions", vocRides);
        Vocabulary vocTutionName = new Vocabulary();
        vocTutionName.setTerm(new TermValueType(TermValueType.TEXT));
        vocs.setPropertyVocabulary("TutionName", vocTutionName,
                Collections.enumeration(Collections.singletonList("tuitions")));
        Vocabulary vocTutionDuration = new Vocabulary();
        vocTutionName.setTerm(new TermValueType(TermValueType.TEXT));
        vocs.setPropertyVocabulary("Duration", vocTutionDuration,
                Collections.enumeration(Collections.singletonList("tuitions")));

        Metadata metadata = new Metadata("Faculty");
        metadata.setEntityMetadata(vocs);
        return metadata;
    }

    private Metadata createMoreComplexNestedMockStudentVocabMetadata() {
        EntityMetadata vocs = new EntityMetadata("students");
        Vocabulary vocId = new Vocabulary();
        vocId.setTerm(new TermValueType(TermValueType.TEXT));
        vocs.setPropertyVocabulary("name", vocId);
        Vocabulary vocBody = new Vocabulary();
        vocBody.setTerm(new TermValueType(TermValueType.INTEGER_NUMBER));
        vocs.setPropertyVocabulary("age", vocBody);

        Vocabulary vocRides = new Vocabulary();
        vocRides.setTerm(new TermComplexType(true));
        vocs.setPropertyVocabulary("tuitions", vocRides);
        Vocabulary vocTeachers = new Vocabulary();
        vocTeachers.setTerm(new TermComplexType(true));
        vocs.setPropertyVocabulary("Teachers", vocTeachers,
                Collections.enumeration(Collections.singletonList("tuitions")));
        Vocabulary vocTutionName = new Vocabulary();
        vocTutionName.setTerm(new TermValueType(TermValueType.TEXT));
        vocs.setPropertyVocabulary("TutionName", vocTutionName,
                Collections.enumeration(Collections.singletonList("tuitions")));
        Vocabulary vocTutionDuration = new Vocabulary();
        vocTutionName.setTerm(new TermValueType(TermValueType.TEXT));
        vocs.setPropertyVocabulary("Duration", vocTutionDuration,
                Collections.enumeration(Collections.singletonList("tuitions")));

        Vocabulary vocTeacherName = new Vocabulary();
        vocTeacherName.setTerm(new TermValueType(TermValueType.TEXT));
        vocs.setPropertyVocabulary("Name", vocTeacherName,
                Collections.enumeration(Arrays.asList(new String[] { "tuitions", "Teachers" })));

        Vocabulary vocAddress = new Vocabulary();
        vocAddress.setTerm(new TermComplexType(true));
        vocs.setPropertyVocabulary("address", vocAddress);

        Vocabulary vocAddressHouseNumber = new Vocabulary();
        vocAddressHouseNumber.setTerm(new TermValueType(TermValueType.NUMBER));
        vocs.setPropertyVocabulary("houseNumber", vocAddressHouseNumber,
                Collections.enumeration(Arrays.asList(new String[] { "address" })));

        Vocabulary vocAddressRoadName = new Vocabulary();
        vocAddressRoadName.setTerm(new TermValueType(TermValueType.NUMBER));
        vocs.setPropertyVocabulary("roadName", vocAddressRoadName,
                Collections.enumeration(Arrays.asList(new String[] { "address" })));

        Metadata metadata = new Metadata("Faculty");
        metadata.setEntityMetadata(vocs);
        return metadata;
    }

    private Metadata createObjectNestedVocabMetadataStub() {
        EntityMetadata vocs = new EntityMetadata("Children");
        Vocabulary vocId = new Vocabulary();
        vocId.setTerm(new TermValueType(TermValueType.TEXT));
        vocs.setPropertyVocabulary("name", vocId);
        Vocabulary vocBody = new Vocabulary();
        vocBody.setTerm(new TermValueType(TermValueType.INTEGER_NUMBER));
        vocs.setPropertyVocabulary("age", vocBody);

        Vocabulary vocRides = new Vocabulary();
        vocRides.setTerm(new TermComplexType(true));
        vocs.setPropertyVocabulary("tuitions", vocRides);
        Vocabulary vocTutionName = new Vocabulary();
        vocTutionName.setTerm(new TermValueType(TermValueType.TEXT));
        vocs.setPropertyVocabulary("TutionName", vocTutionName,
                Collections.enumeration(Collections.singletonList("tuitions")));
        Vocabulary vocTutionDuration = new Vocabulary();
        vocTutionName.setTerm(new TermValueType(TermValueType.TEXT));
        vocs.setPropertyVocabulary("Duration", vocTutionDuration,
                Collections.enumeration(Collections.singletonList("tuitions")));

        Vocabulary vocAddress = new Vocabulary();
        vocAddress.setTerm(new TermComplexType(true));
        vocs.setPropertyVocabulary("address", vocAddress);

        Vocabulary vocAddressHouseNumber = new Vocabulary();
        vocAddressHouseNumber.setTerm(new TermValueType(TermValueType.NUMBER));
        vocs.setPropertyVocabulary("houseNumber", vocAddressHouseNumber,
                Collections.enumeration(Arrays.asList(new String[] { "address" })));

        Vocabulary vocAddressRoadName = new Vocabulary();
        vocAddressRoadName.setTerm(new TermValueType(TermValueType.NUMBER));
        vocs.setPropertyVocabulary("roadName", vocAddressRoadName,
                Collections.enumeration(Arrays.asList(new String[] { "address" })));

        Metadata metadata = new Metadata("Family");
        metadata.setEntityMetadata(vocs);
        return metadata;
    }

    @Test(expected = WebApplicationException.class)
    public void testAttemptToSerialiseNonEntityResource() throws Exception {
        EntityResource<?> mdr = mock(EntityResource.class);

        HALProvider hp = new HALProvider(mock(Metadata.class));
        UriInfo mockUriInfo = mock(UriInfo.class);
        when(mockUriInfo.getBaseUri()).thenReturn(new URI("http://www.temenos.com/rest.svc/"));
        hp.setUriInfo(mockUriInfo);
        hp.writeTo(mdr, MetaDataResource.class, null, null, MediaType.APPLICATION_HAL_XML_TYPE, null,
                new ByteArrayOutputStream());
    }

    @Test
    public void testSerialiseSimpleResource() throws Exception {
        // the test key
        OEntityKey entityKey = OEntityKey.create("123");
        // the test properties
        List<OProperty<?>> properties = new ArrayList<OProperty<?>>();
        properties.add(OProperties.string("name", "noah"));
        properties.add(OProperties.string("age", "2"));

        OEntity entity = OEntities.create(createMockChildrenEntitySet(), entityKey, properties,
                new ArrayList<OLink>());
        EntityResource<OEntity> er = CommandHelper.createEntityResource(entity, OEntity.class);
        er.setEntityName("Children");

        HALProvider hp = new HALProvider(createMockChildVocabMetadata());
        UriInfo mockUriInfo = mock(UriInfo.class);
        when(mockUriInfo.getBaseUri()).thenReturn(new URI("http://www.temenos.com/rest.svc/"));
        hp.setUriInfo(mockUriInfo);
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        hp.writeTo(er, EntityResource.class, OEntity.class, null, MediaType.APPLICATION_HAL_XML_TYPE, null, bos);

        String expectedXML = "<resource href=\"http://www.temenos.com/rest.svc/\"><name>noah</name><age>2</age></resource>";
        String responseString = createFlatXML(bos);

        Diff diff = new Diff(expectedXML, responseString);
        // don't worry about the order of the elements in the xml
        assertTrue(diff.similar());
    }

    @Test
    public void testSerialiseCollectionResource() throws Exception {
        // the test key
        OEntityKey entityKey = OEntityKey.create("123");
        // the test properties
        List<OProperty<?>> properties = new ArrayList<OProperty<?>>();
        properties.add(OProperties.string("name", "noah"));
        properties.add(OProperties.string("age", "2"));

        Collection<EntityResource<OEntity>> entities = new ArrayList<EntityResource<OEntity>>();
        entities.add(createEntityResourceWithSelfLink(entityKey, properties,
                "http://www.temenos.com/rest.svc/children/1"));
        entities.add(createEntityResourceWithSelfLink(entityKey, properties,
                "http://www.temenos.com/rest.svc/children/2"));
        entities.add(createEntityResourceWithSelfLink(entityKey, properties,
                "http://www.temenos.com/rest.svc/children/3"));
        CollectionResource<OEntity> er = new CollectionResource<OEntity>("Children", entities);
        // mock setting entity name
        er.setEntityName("Children");

        HALProvider hp = new HALProvider(createMockChildVocabMetadata());
        UriInfo mockUriInfo = mock(UriInfo.class);
        when(mockUriInfo.getBaseUri()).thenReturn(new URI("http://www.temenos.com/rest.svc/"));
        hp.setUriInfo(mockUriInfo);
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        hp.writeTo(er, CollectionResource.class, OEntity.class, null, MediaType.APPLICATION_HAL_XML_TYPE, null,
                bos);

        String expectedXML = "<resource href=\"http://www.temenos.com/rest.svc/\"><resource href=\"http://www.temenos.com/rest.svc/children/1\" rel=\"item\"><age>2</age><name>noah</name></resource><resource href=\"http://www.temenos.com/rest.svc/children/2\" rel=\"item\"><age>2</age><name>noah</name></resource><resource href=\"http://www.temenos.com/rest.svc/children/3\" rel=\"item\"><age>2</age><name>noah</name></resource></resource>";
        String responseString = createFlatXML(bos);

        Diff diff = new Diff(expectedXML, responseString);
        // don't worry about the order of the elements in the xml
        assertTrue(diff.similar());
    }

    @Test
    public void testSerialiseEmbeddedResources() throws Exception {
        OEntityKey parentEntityKey = OEntityKey.create("333");
        List<OProperty<?>> parentProperties = new ArrayList<OProperty<?>>();
        parentProperties.add(OProperties.string("name", "aaron"));
        parentProperties.add(OProperties.string("age", "30"));

        OEntityKey childEntityKey = OEntityKey.create("123");
        List<OProperty<?>> childProperties = new ArrayList<OProperty<?>>();
        childProperties.add(OProperties.string("name", "noah"));
        childProperties.add(OProperties.string("age", "2"));

        OEntity childEntity = OEntities.create(createMockChildrenEntitySet(), childEntityKey, childProperties,
                new ArrayList<OLink>());
        EntityResource<OEntity> childEntityResource = CommandHelper.createEntityResource(childEntity,
                OEntity.class);
        childEntityResource.setEntityName("Children");

        OEntity parentEntity = OEntities.create(createMockChildrenEntitySet(), parentEntityKey, parentProperties,
                new ArrayList<OLink>());
        EntityResource<OEntity> parentEntityResource = CommandHelper.createEntityResource(parentEntity,
                OEntity.class);
        parentEntityResource.setEntityName("Children");

        // build the embedded map
        Map<Transition, RESTResource> embedded = new HashMap<Transition, RESTResource>();
        Transition childToParent = new Transition.Builder()
                .source(new ResourceState("PERSON", "child", new ArrayList<Action>(), "/child/{id}"))
                .target(new ResourceState("PERSON", "parent", new ArrayList<Action>(), "/parent/{id}")).build();
        embedded.put(childToParent, parentEntityResource);
        // child has a parent
        childEntityResource.setEmbedded(embedded);

        // mock the test links
        List<Link> links = new ArrayList<Link>();
        links.add(mockLink("child", "self", "/child/123", null));
        links.add(mockLink("parent", "person", "/parent/333", childToParent));
        childEntityResource.setLinks(links);

        HALProvider hp = new HALProvider(createMockChildVocabMetadata());
        UriInfo mockUriInfo = mock(UriInfo.class);
        when(mockUriInfo.getBaseUri()).thenReturn(new URI("http://www.temenos.com/rest.svc/"));
        hp.setUriInfo(mockUriInfo);
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        hp.writeTo(childEntityResource, EntityResource.class, OEntity.class, null,
                MediaType.APPLICATION_HAL_JSON_TYPE, null, bos);

        String expectedXML = "{\"_links\":{\"self\":{\"href\":\"/child/123\"},\"person\":[{\"href\":\"/parent/333\",\"name\":\"parent\",\"title\":\"parent\"}]},\"age\":\"2\",\"name\":\"noah\",\"_embedded\":{\"person\":[{\"_links\":{\"self\":{\"href\":\"/parent/333\"}},\"age\":\"30\",\"name\":\"aaron\"}]}}";
        String responseString = new String(bos.toByteArray(), "UTF-8");
        responseString = responseString.replaceAll(System.getProperty("line.separator"), "");

        Map<String, Object> expectedData = parseJson(expectedXML);
        Map<String, Object> actualData = parseJson(responseString);

        assertEquals(expectedData, actualData);
    }

    private EntityResource<OEntity> createEntityResourceWithSelfLink(OEntityKey entityKey,
            List<OProperty<?>> properties, String selfLink) {
        OEntity oentity = OEntities.create(createMockChildrenEntitySet(), entityKey, properties,
                new ArrayList<OLink>());
        EntityResource<OEntity> entityResource = new EntityResource<OEntity>(oentity);
        Collection<Link> links = new ArrayList<Link>();
        links.add(new Link("id", "self", selfLink, null, null));
        entityResource.setLinks(links);
        return entityResource;
    }

    @Test
    public void testSerialiseBeanResource() throws Exception {
        // the test bean, with elements that should not be serialised
        Children person = new Children("noah", 2, "42");
        EntityResource<Children> er = new EntityResource<Children>(person);
        er.setEntityName("Children");

        HALProvider hp = new HALProvider(createMockChildVocabMetadata());
        UriInfo mockUriInfo = mock(UriInfo.class);
        when(mockUriInfo.getBaseUri()).thenReturn(new URI("http://www.temenos.com/rest.svc/"));
        hp.setUriInfo(mockUriInfo);
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        hp.writeTo(er, EntityResource.class, Children.class, null, MediaType.APPLICATION_HAL_XML_TYPE, null, bos);

        String expectedXML = "<resource href=\"http://www.temenos.com/rest.svc/\"><name>noah</name><age>2</age></resource>";
        String responseString = createFlatXML(bos);

        Diff diff = new Diff(expectedXML, responseString);
        // don't worry about the order of the elements in the xml
        assertTrue(diff.similar());
    }

    @Test
    public void testSerialiseNullBeanResource() throws Exception {
        // null object
        EntityResource<Object> er = new EntityResource<Object>(null);

        HALProvider hp = new HALProvider(createMockChildVocabMetadata());
        UriInfo mockUriInfo = mock(UriInfo.class);
        when(mockUriInfo.getBaseUri()).thenReturn(new URI("http://www.temenos.com/rest.svc/"));
        hp.setUriInfo(mockUriInfo);
        hp.writeTo(er, EntityResource.class, Object.class, null, MediaType.APPLICATION_HAL_XML_TYPE, null,
                new ByteArrayOutputStream());
    }

    private String createFlatXML(ByteArrayOutputStream bos) throws Exception {
        String responseString = new String(bos.toByteArray(), "UTF-8");
        responseString = responseString.replaceAll(System.getProperty("line.separator"), "");
        responseString = responseString.replaceAll(">\\s+<", "><");
        return responseString;
    }

    private Link mockLink(String id, String rel, String href, Transition transition) {
        Link link = mock(Link.class);
        when(link.getId()).thenReturn(id);
        when(link.getTitle()).thenReturn(id);
        when(link.getRel()).thenReturn(rel);
        when(link.getHref()).thenReturn(href);
        when(link.getTransition()).thenReturn(transition);
        return link;
    }

    @Test
    public void testSerialiseResourceNoEntity() throws Exception {
        EntityResource<?> er = mock(EntityResource.class);
        when(er.getEntity()).thenReturn(null);

        HALProvider hp = new HALProvider(mock(Metadata.class));
        UriInfo mockUriInfo = mock(UriInfo.class);
        when(mockUriInfo.getBaseUri()).thenReturn(new URI("http://www.temenos.com"));
        hp.setUriInfo(mockUriInfo);
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        hp.writeTo(er, EntityResource.class, null, null, MediaType.APPLICATION_HAL_XML_TYPE, null, bos);

        String expectedXML = "<resource href=\"http://www.temenos.com\"></resource>";
        String responseString = createFlatXML(bos);
        XMLAssert.assertXMLEqual(expectedXML, responseString);
    }

    @Test
    public void testBaseUri() throws Exception {
        EntityResource<?> er = mock(EntityResource.class);
        when(er.getEntity()).thenReturn(null);

        HALProvider hp = new HALProvider(mock(Metadata.class));
        UriInfo mockUriInfo = mock(UriInfo.class);
        // java 1.6 bug getBaseUri returns absolute path
        when(mockUriInfo.getBaseUri()).thenReturn(new URI("http://www.temenos.com/rest.svc/"));
        hp.setUriInfo(mockUriInfo);
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        hp.writeTo(er, EntityResource.class, null, null, MediaType.APPLICATION_HAL_XML_TYPE, null, bos);

        String expectedXML = "<resource href=\"http://www.temenos.com/rest.svc/\"></resource>";
        String responseString = createFlatXML(bos);
        XMLAssert.assertXMLEqual(expectedXML, responseString);
    }

    @Test
    public void testJSONMediaTypeWithCharset() throws Exception {
        EntityResource<?> er = mock(EntityResource.class);
        when(er.getEntity()).thenReturn(null);

        HALProvider hp = new HALProvider(mock(Metadata.class));
        UriInfo mockUriInfo = mock(UriInfo.class);
        // java 1.6 bug getBaseUri returns absolute path
        when(mockUriInfo.getBaseUri()).thenReturn(new URI("http://www.temenos.com/rest.svc/"));
        hp.setUriInfo(mockUriInfo);
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        hp.writeTo(er, EntityResource.class, null, null,
                javax.ws.rs.core.MediaType.valueOf("application/hal+json; charset=utf-8"), null, bos);

        String expectedXML = "{\"_links\":{\"self\":{\"href\":\"http://www.temenos.com/rest.svc/\"}}}";
        String responseString = new String(bos.toByteArray(), "UTF-8");
        responseString = responseString.replaceAll(System.getProperty("line.separator"), "");
        assertEquals(expectedXML, responseString);
    }

    @Test
    public void testXMLMediaTypeWithCharset() throws Exception {
        EntityResource<?> er = mock(EntityResource.class);
        when(er.getEntity()).thenReturn(null);

        HALProvider hp = new HALProvider(mock(Metadata.class));
        UriInfo mockUriInfo = mock(UriInfo.class);
        // java 1.6 bug getBaseUri returns absolute path
        when(mockUriInfo.getBaseUri()).thenReturn(new URI("http://www.temenos.com/rest.svc/"));
        hp.setUriInfo(mockUriInfo);
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        hp.writeTo(er, EntityResource.class, null, null,
                javax.ws.rs.core.MediaType.valueOf("application/hal+xml; charset=utf-8"), null, bos);

        String expectedXML = "<resource href=\"http://www.temenos.com/rest.svc/\"></resource>";
        String responseString = createFlatXML(bos);
        XMLAssert.assertXMLEqual(expectedXML, responseString);
    }

    @Test
    public void testSerialiseResourceWithLinks() throws Exception {
        // the test key
        OEntityKey entityKey = OEntityKey.create("123");
        // the test properties
        List<OProperty<?>> properties = new ArrayList<OProperty<?>>();
        properties.add(OProperties.string("name", "noah"));
        properties.add(OProperties.string("age", "2"));
        // the test links
        List<Link> links = new ArrayList<Link>();
        links.add(mockLink("father", "_person", "humans/31", null));
        links.add(mockLink("mother", "_person", "/rest.svc/humans/32", null));

        OEntity entity = OEntities.create(createMockChildrenEntitySet(), entityKey, properties,
                new ArrayList<OLink>());
        EntityResource<OEntity> er = CommandHelper.createEntityResource(entity, OEntity.class);
        er.setEntityName("Children");
        er.setLinks(links);

        HALProvider hp = new HALProvider(createMockChildVocabMetadata());
        UriInfo mockUriInfo = mock(UriInfo.class);
        when(mockUriInfo.getBaseUri()).thenReturn(new URI("http://www.temenos.com/rest.svc/"));
        hp.setUriInfo(mockUriInfo);
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        hp.writeTo(er, EntityResource.class, OEntity.class, null, MediaType.APPLICATION_HAL_XML_TYPE, null, bos);

        String expectedXML = "<resource href=\"http://www.temenos.com/rest.svc/\"><link href=\"humans/31\" rel=\"_person\" name=\"father\" title=\"father\"/><link href=\"/rest.svc/humans/32\" rel=\"_person\" name=\"mother\" title=\"mother\"/><name>noah</name><age>2</age></resource>";
        String responseString = createFlatXML(bos);

        Diff diff = new Diff(expectedXML, responseString);
        // don't worry about the order of the elements in the xml
        assertTrue(diff.similar());
    }

    @Test
    public void testSerialiseResourceWithRelatedLinks() throws Exception {
        // the test key
        OEntityKey entityKey = OEntityKey.create("123");
        // the test properties
        List<OProperty<?>> properties = new ArrayList<OProperty<?>>();
        properties.add(OProperties.string("name", "noah"));
        properties.add(OProperties.string("age", "2"));
        // the test links
        /*
         * Not sure, but I think relatedEntity and link are the same thing in an OEntity.
         * However, a relatedEntity also has the relatedEntityInline capability.
         * TODO add tests for 'inline' links
         */
        List<Link> links = new ArrayList<Link>();
        links.add(mockLink("father", "_person", "humans/31", null));
        links.add(mockLink("mother", "_person", "humans/32", null));
        links.add(mockLink("siblings", "_family", "humans/phetheans", null));

        OEntity entity = OEntities.create(createMockChildrenEntitySet(), entityKey, properties,
                new ArrayList<OLink>());
        EntityResource<OEntity> er = new EntityResource<OEntity>(entity);
        er.setEntityName("Children");
        er.setLinks(links);

        HALProvider hp = new HALProvider(createMockChildVocabMetadata());
        UriInfo mockUriInfo = mock(UriInfo.class);
        when(mockUriInfo.getBaseUri()).thenReturn(new URI("http://www.temenos.com/rest.svc/"));
        hp.setUriInfo(mockUriInfo);
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        hp.writeTo(er, EntityResource.class, OEntity.class, null, MediaType.APPLICATION_HAL_XML_TYPE, null, bos);

        String expectedXML = "<resource href=\"http://www.temenos.com/rest.svc/\"><link href=\"humans/phetheans\" rel=\"_family\" name=\"siblings\" title=\"siblings\"/><link href=\"humans/31\" rel=\"_person\" name=\"father\" title=\"father\"/><link href=\"humans/32\" rel=\"_person\" name=\"mother\" title=\"mother\"/><name>noah</name><age>2</age></resource>";
        String responseString = createFlatXML(bos);

        Diff diff = new Diff(expectedXML, responseString);
        // don't worry about the order of the elements in the xml
        assertTrue(diff.similar());
    }

    @Test
    public void testSerialiseResourceWithDoubleBarrelledRelatedLinks() throws Exception {
        // the test key
        OEntityKey entityKey = OEntityKey.create("123");
        // the test properties
        List<OProperty<?>> properties = new ArrayList<OProperty<?>>();
        properties.add(OProperties.string("name", "noah"));
        properties.add(OProperties.string("age", "2"));
        // the test links
        /*
         * Not sure, but I think relatedEntity and link are the same thing in an OEntity.
         * However, a relatedEntity also has the relatedEntityInline capability.
         * TODO add tests for 'inline' links
         */
        List<Link> links = new ArrayList<Link>();
        links.add(mockLink("father", "_person edit", "humans/31", null));
        links.add(mockLink("mother", "_person edit", "humans/32", null));
        links.add(mockLink("siblings", "_family", "humans/phetheans", null));

        OEntity entity = OEntities.create(createMockChildrenEntitySet(), entityKey, properties,
                new ArrayList<OLink>());
        EntityResource<OEntity> er = new EntityResource<OEntity>(entity);
        er.setEntityName("Children");
        er.setLinks(links);

        HALProvider hp = new HALProvider(createMockChildVocabMetadata());
        UriInfo mockUriInfo = mock(UriInfo.class);
        when(mockUriInfo.getBaseUri()).thenReturn(new URI("http://www.temenos.com/rest.svc/"));
        hp.setUriInfo(mockUriInfo);
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        hp.writeTo(er, EntityResource.class, OEntity.class, null, MediaType.APPLICATION_HAL_XML_TYPE, null, bos);

        String expectedXML = "<resource href=\"http://www.temenos.com/rest.svc/\">"
                + "<link href=\"humans/phetheans\" rel=\"_family\" name=\"siblings\" title=\"siblings\"/><link href=\"humans/31\" rel=\"_person\" name=\"father\" title=\"father\"/><link href=\"humans/32\" rel=\"_person\" name=\"mother\" title=\"mother\"/>"
                + "<link rel=\"edit\" href=\"humans/31\" name=\"father\" title=\"father\" /><link rel=\"edit\" href=\"humans/32\" name=\"mother\" title=\"mother\" />"
                + "<name>noah</name><age>2</age></resource>";
        String responseString = createFlatXML(bos);

        Diff diff = new Diff(expectedXML, responseString);
        // don't worry about the order of the elements in the xml
        assertTrue(diff.similar());
    }

    @Test
    public void testBuildMapFromOEntity() {
        HALProvider hp = new HALProvider(createMockChildVocabMetadata());

        /* 
         * create an OEntity with more properties than the 
         * supplied metadata.  The point is that we should only
         * fill the map with the defined entities properties.
         */
        OEntityKey entityKey = OEntityKey.create("123");
        List<OProperty<?>> properties = new ArrayList<OProperty<?>>();
        properties.add(OProperties.string("name", "noah"));
        properties.add(OProperties.string("age", "2"));
        properties.add(OProperties.string("shoeSize", "5"));
        OEntity entity = OEntities.create(createMockChildrenEntitySet(), entityKey, properties,
                new ArrayList<OLink>());

        // map of property name to value object
        Map<String, Object> map = new HashMap<String, Object>();
        hp.buildFromOEntity(map, entity, "Children");

        assertEquals(2, map.keySet().size());
        assertTrue(map.keySet().contains("name"));
        assertTrue(map.keySet().contains("age"));
        assertEquals("noah", map.get("name"));
        assertEquals("2", map.get("age"));
    }

    @Test
    public void testSerialiseResourceWithForm() {
        // don't know how to deal with forms yet, possibly embed an xform
    }

    @Test
    public void testSerialiseStreamingResource() {
        // cannot decorate a streaming resource so should fail
    }

    @Test
    public void testDeserialiseResourceWithSelfLinkDifferentFromRequestedResourceLinkForApplicationJson()
            throws IOException, URISyntaxException {
        EntityResource<Entity> er = runDeserialiseResourceWithLinks(
                javax.ws.rs.core.MediaType.APPLICATION_JSON_TYPE, "http://www.temenos.com/rest.svc/name",
                "/children");
        assertNotNull(er.getEntity());
        Entity entity = er.getEntity();
        assertEquals("Children", entity.getName());
    }

    @Test
    public void testDeserialiseResourceWithSelfLinkDifferentFromRequestedResourceLinkForApplicationHalJson()
            throws IOException, URISyntaxException {
        EntityResource<Entity> er = runDeserialiseResourceWithLinks(MediaType.APPLICATION_HAL_JSON_TYPE,
                "http://www.temenos.com/rest.svc/name", "/children");
        assertNotNull(er.getEntity());
        Entity entity = er.getEntity();
        assertEquals("Children", entity.getName());
    }

    @SuppressWarnings("unchecked")
    private EntityResource<Entity> runDeserialiseResourceWithLinks(javax.ws.rs.core.MediaType mediaType,
            String selfLink, String targetResource) throws IOException, URISyntaxException {
        ResourceStateMachine sm = new ResourceStateMachine(
                new ResourceState("Children", "initial", new ArrayList<Action>(), targetResource));
        HALProvider hp = new HALProvider(createMockChildVocabMetadata(), new DefaultResourceStateProvider(sm));
        UriInfo mockUriInfo = mock(UriInfo.class);
        when(mockUriInfo.getBaseUri()).thenReturn(new URI("http://www.temenos.com/rest.svc"));
        when(mockUriInfo.getPath()).thenReturn(targetResource);
        hp.setUriInfo(mockUriInfo);
        Request requestContext = mock(Request.class);
        when(requestContext.getMethod()).thenReturn("GET");
        hp.setRequestContext(requestContext);

        String strEntityStream = "{ \"_links\": { \"self\": { \"href\": \"" + selfLink
                + "\" } }, \"name\": \"noah\", \"age\": 2 }";
        InputStream entityStream = new ByteArrayInputStream(strEntityStream.getBytes());
        GenericEntity<EntityResource<Entity>> ge = new GenericEntity<EntityResource<Entity>>(
                new EntityResource<Entity>()) {
        };
        EntityResource<Entity> er = (EntityResource<Entity>) hp.readFrom(RESTResource.class, ge.getType(), null,
                mediaType, null, entityStream);
        return er;
    }

    private EdmEntitySet createMockChildrenEntitySet() {
        // mock a simple entity (Children entity set)
        List<EdmProperty.Builder> eprops = new ArrayList<EdmProperty.Builder>();
        eprops.add(EdmProperty.newBuilder("ID").setType(EdmSimpleType.STRING));
        eprops.add(EdmProperty.newBuilder("name").setType(EdmSimpleType.STRING));
        eprops.add(EdmProperty.newBuilder("age").setType(EdmSimpleType.STRING));
        EdmEntityType.Builder eet = EdmEntityType.newBuilder().setNamespace("InteractionTest").setName("Children")
                .addKeys(Arrays.asList("ID")).addProperties(eprops);
        EdmEntitySet.Builder ees = EdmEntitySet.newBuilder().setName("Children").setEntityType(eet);
        return ees.build();
    }
}