gov.nij.bundles.intermediaries.ers.EntityResolutionServiceIntermediaryTest.java Source code

Java tutorial

Introduction

Here is the source code for gov.nij.bundles.intermediaries.ers.EntityResolutionServiceIntermediaryTest.java

Source

/*
 * Copyright 2013 SEARCH Group, Incorporated. 
 * 
 * See the NOTICE file distributed with  this work for additional information 
 * regarding copyright ownership.  SEARCH Group Inc. 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.
 */
package gov.nij.bundles.intermediaries.ers;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;

import java.io.File;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import javax.annotation.Resource;
import javax.xml.namespace.NamespaceContext;
import javax.xml.namespace.QName;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathFactory;

import org.apache.camel.EndpointInject;
import org.apache.camel.Exchange;
import org.apache.camel.Produce;
import org.apache.camel.ProducerTemplate;
import org.apache.camel.builder.AdviceWithRouteBuilder;
import org.apache.camel.component.cxf.common.message.CxfConstants;
import org.apache.camel.component.mock.MockEndpoint;
import org.apache.camel.converter.jaxp.XmlConverter;
import org.apache.camel.impl.DefaultExchange;
import org.apache.camel.model.ModelCamelContext;
import org.apache.camel.test.spring.CamelSpringJUnit4ClassRunner;
import org.apache.camel.test.spring.UseAdviceWith;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.cxf.binding.soap.SoapHeader;
import org.apache.cxf.headers.Header;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.ContextConfiguration;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

/**
 * This is the test framework to test the Entity Resolution Service.
 * 
 */

@UseAdviceWith
// NOTE: this causes Camel contexts to not start up automatically
@RunWith(CamelSpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath:META-INF/spring/camel-context.xml" })
public class EntityResolutionServiceIntermediaryTest {

    private static final Log log = LogFactory.getLog(EntityResolutionServiceIntermediaryTest.class);

    public static final String CXF_OPERATION_NAME = "Submit-Entity-Merge";
    public static final String CXF_OPERATION_NAMESPACE = "http://nij.gov/Services/WSDL/EntityResolutionService/1.0";

    @Resource
    private ModelCamelContext context;

    @Produce
    protected ProducerTemplate template;

    @EndpointInject(uri = "mock:EntityResolutionResponseEndpoint")
    protected MockEndpoint entityResolutionResponseMock;

    private Exchange senderExchange;
    private NamespaceContext testNamespaceContext;

    @Before
    public void setUp() throws Exception {

        final NamespaceContext baseEntityResolutionNamespaceContext = new EntityResolutionNamespaceContext();
        testNamespaceContext = new NamespaceContext() {

            @Override
            public String getNamespaceURI(String prefix) {
                if ("ext".equals(prefix)) {
                    return "http://local.org/IEPD/Extensions/PersonSearchResults/1.0";
                }
                return baseEntityResolutionNamespaceContext.getNamespaceURI(prefix);
            }

            @Override
            public String getPrefix(String arg0) {
                return baseEntityResolutionNamespaceContext.getPrefix(arg0);
            }

            @SuppressWarnings("rawtypes")
            @Override
            public Iterator getPrefixes(String arg0) {
                return baseEntityResolutionNamespaceContext.getPrefixes(arg0);
            }

        };

        // Advise the person search results endpoint and replace it with a mock endpoint.
        // We then will test this mock endpoint to see if it gets the proper payload.
        context.getRouteDefinitions().get(0).adviceWith(context, new AdviceWithRouteBuilder() {
            @Override
            public void configure() throws Exception {
                // weave the vehicle search results in the route
                // and replace it with the following mock route path
                weaveByToString("To[EntityResolutionResponseEndpoint]").replace()
                        .to("mock:EntityResolutionResponseEndpoint");
                replaceFromWith("direct:entityResolutionRequestServiceEndpoint");
            }
        });

        context.start();

        // We should get one message
        entityResolutionResponseMock.expectedMessageCount(1);

        // Create a new exchange
        senderExchange = new DefaultExchange(context);

        Document doc = createDocument();
        List<SoapHeader> soapHeaders = new ArrayList<SoapHeader>();
        soapHeaders.add(makeSoapHeader(doc, "http://www.w3.org/2005/08/addressing", "MessageID", "12345"));
        soapHeaders.add(makeSoapHeader(doc, "http://www.w3.org/2005/08/addressing", "ReplyTo", "https://reply.to"));
        senderExchange.getIn().setHeader(Header.HEADER_LIST, soapHeaders);

        senderExchange.getIn().setHeader(CxfConstants.OPERATION_NAME, CXF_OPERATION_NAME);
        senderExchange.getIn().setHeader(CxfConstants.OPERATION_NAMESPACE, CXF_OPERATION_NAMESPACE);
    }

    @After
    public void tearDown() throws Exception {
        context.stop();
    }

    @Test
    @DirtiesContext
    public void testSortAscending() throws Exception {
        performSortTest(new File("src/test/resources/xml/EntityMergeRequestMessageSortTest.xml"), 1, 1, 1);
    }

    @Test
    @DirtiesContext
    public void testSortDescending() throws Exception {
        performSortTest(new File("src/test/resources/xml/EntityMergeRequestMessageDescendSortTest.xml"), -1, -1,
                -1);
    }

    @Test
    @DirtiesContext
    public void testSortMixed() throws Exception {
        performSortTest(new File("src/test/resources/xml/EntityMergeRequestMessageMixedSortTest.xml"), -1, 1, 1);
    }

    @Test
    @DirtiesContext
    public void testSortNullValue() throws Exception {
        performSortTest(new File("src/test/resources/xml/EntityMergeRequestMessageNullValueSortTest.xml"), 1, 1, 1);
    }

    @Test
    @DirtiesContext
    public void testSortWithMissingAttributeValue() throws Exception {
        performSortTest(new File("src/test/resources/xml/EntityMergeRequestMessageSortWithNoSortAttributeTest.xml"),
                1, 0, 0);
    }

    private void performSortTest(File inputMessageFile, int factor1, int factor2, int factor3) throws Exception {

        senderExchange.getIn().setBody(inputMessageFile);
        Exchange returnExchange = template.send("direct:entityResolutionRequestServiceEndpoint", senderExchange);

        if (returnExchange.getException() != null) {
            throw new Exception(returnExchange.getException());
        }

        Thread.sleep(3000);

        entityResolutionResponseMock.assertIsSatisfied();
        entityResolutionResponseMock.expectedMessageCount(1);

        Exchange ex = entityResolutionResponseMock.getExchanges().get(0);
        Document responseDocument = ex.getIn().getBody(Document.class);
        //        String docString = new XmlConverter().toString(responseDocument, ex);
        //        log.info("\n" + docString + "\n");

        XPath xp = XPathFactory.newInstance().newXPath();
        xp.setNamespaceContext(testNamespaceContext);

        NodeList personNodes = (NodeList) xp.evaluate(
                "/merge-result:EntityMergeResultMessage/merge-result:EntityContainer/merge-result-ext:Entity/ext:PersonSearchResult/ext:Person",
                responseDocument, XPathConstants.NODESET);
        assertEquals(5, personNodes.getLength());

        List<Node> lastNameNodes = new ArrayList<Node>();
        List<Node> firstNameNodes = new ArrayList<Node>();
        List<Node> idNodes = new ArrayList<Node>();

        for (int i = 0; i < personNodes.getLength(); i++) {
            Node personNode = personNodes.item(i);
            Node lastNameNode = (Node) xp.evaluate("nc:PersonName/nc:PersonSurName", personNode,
                    XPathConstants.NODE);
            lastNameNodes.add(lastNameNode);
            Node firstNameNode = (Node) xp.evaluate("nc:PersonName/nc:PersonGivenName", personNode,
                    XPathConstants.NODE);
            firstNameNodes.add(firstNameNode);
            Node idNode = (Node) xp.evaluate(
                    "jxdm:PersonAugmentation/jxdm:PersonStateFingerprintIdentification/nc:IdentificationID",
                    personNode, XPathConstants.NODE);
            idNodes.add(idNode);
        }

        assertTrue(compareTextNodeLists(personNodes.getLength(), lastNameNodes, firstNameNodes, idNodes, factor1,
                factor2, factor3));
    }

    private boolean compareTextNodeLists(int personNodeCount, List<Node> nodeList1, List<Node> nodeList2,
            List<Node> nodeList3, int factor1, int factor2, int factor3) {
        boolean sorted = true;
        String priorNode1 = nodeList1.get(0).getTextContent();
        String priorNode2 = nodeList2.get(0).getTextContent();
        String priorNode3 = nodeList3.get(0).getTextContent();
        for (int i = 1; sorted && i < personNodeCount; i++) {
            Node node1 = nodeList1.get(i);
            String node1S = node1 == null ? null : node1.getTextContent();
            Node node2 = nodeList2.get(i);
            String node2S = node2 == null ? null : node2.getTextContent();
            Node node3 = nodeList3.get(i);
            String node3S = node3 == null ? null : node3.getTextContent();
            int node1Compare = compareWithNull(priorNode1, node1S) * factor1;
            priorNode1 = node1S;
            if (node1Compare > 0) {
                sorted = false;
                //log.info("Sort fails on last name compare " + priorNode1 + " " + node1S);
            } else if (node1Compare == 0) {
                int node2Compare = compareWithNull(priorNode2, node2S) * factor2;
                priorNode2 = node2S;
                if (node2Compare > 0) {
                    sorted = false;
                    //log.info("Sort fails on first name compare " + priorNode2 + " " + node2S);
                } else if (node2Compare == 0) {
                    int node3Compare = compareWithNull(priorNode3, node3S) * factor3;
                    priorNode3 = node3S;
                    if (node3Compare > 0) {
                        sorted = false;
                        //log.info("Sort fails on id compare " + priorNode3 + " " + node3S);
                    }
                }
            }
        }
        return sorted;
    }

    private int compareWithNull(String s1, String s2) {
        // assumes nulls sort first
        if (s1 == null) {
            if (s2 == null) {
                return 0;
            }
            return 1;
        } else if (s2 == null) {
            return -1;
        }
        //log.info("Returning compare of s1=" + s1 + " to s2=" + s2 + ", return=" + s1.compareTo(s2));
        return s1.compareTo(s2);
    }

    /**
     * Test the entity resolution service.
     * 
     * @throws Exception
     */
    @Test
    @DirtiesContext
    public void testEntityResolution() throws Exception {

        // Read the er search request file from the file system
        File inputFile = new File("src/test/resources/xml/EntityMergeRequestMessageWithAttributeParameters.xml");

        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
        dbf.setNamespaceAware(true);
        Document inputDocument = dbf.newDocumentBuilder().parse(inputFile);

        // Set it as the message message body
        senderExchange.getIn().setBody(inputDocument);

        // Send the one-way exchange. Using template.send will send an one way message
        Exchange returnExchange = template.send("direct:entityResolutionRequestServiceEndpoint", senderExchange);

        // Use getException to see if we received an exception
        if (returnExchange.getException() != null) {
            throw new Exception(returnExchange.getException());
        }

        // Sleep while a response is generated
        Thread.sleep(3000);

        // Assert that the mock endpoint is satisfied
        entityResolutionResponseMock.assertIsSatisfied();

        // We should get one message
        entityResolutionResponseMock.expectedMessageCount(1);

        // Get the first exchange (the only one)
        Exchange ex = entityResolutionResponseMock.getExchanges().get(0);

        // Get the actual response
        Document actualResponse = ex.getIn().getBody(Document.class);
        log.info("Input document: " + new XmlConverter().toString(inputDocument, null));
        log.info("Body recieved by Mock: " + new XmlConverter().toString(actualResponse, ex));

        XPath xp = XPathFactory.newInstance().newXPath();
        xp.setNamespaceContext(testNamespaceContext);
        // note:  slash-slash xpaths are ok here because in tests we don't really care about performance...
        int inputEntityNodeCount = ((NodeList) xp.evaluate("//er-ext:Entity", inputDocument,
                XPathConstants.NODESET)).getLength();
        int outputEntityNodeCount = ((NodeList) xp.evaluate("//merge-result-ext:Entity", actualResponse,
                XPathConstants.NODESET)).getLength();
        NodeList outputOriginalRecordReferenceNodeList = (NodeList) xp
                .evaluate("//merge-result-ext:OriginalRecordReference", actualResponse, XPathConstants.NODESET);
        int outputOriginalRecordReferenceNodeCount = outputOriginalRecordReferenceNodeList.getLength();
        assertEquals(inputEntityNodeCount, outputEntityNodeCount);
        assertEquals(inputEntityNodeCount, outputOriginalRecordReferenceNodeCount);

        NodeList inputPersonNodes = (NodeList) xp.evaluate("//ext:Person", inputDocument, XPathConstants.NODESET);
        for (int i = 0; i < inputPersonNodes.getLength(); i++) {
            String inputLastName = xp.evaluate("nc:PersonName/nc:PersonSurName/text()", inputPersonNodes.item(i));
            if (inputLastName != null) {
                String xpathExpression = "//ext:Person[nc:PersonName/nc:PersonSurName/text()='" + inputLastName
                        + "']";
                assertNotNull(xp.evaluate(xpathExpression, actualResponse, XPathConstants.NODE));
            }
            String inputFirstName = xp.evaluate("nc:PersonName/nc:PersonGivenName/text()",
                    inputPersonNodes.item(i));
            if (inputFirstName != null) {
                String xpathExpression = "//ext:Person[nc:PersonName/nc:PersonGivenName/text()='" + inputFirstName
                        + "']";
                log.info("xpathExpression=" + xpathExpression);
                assertNotNull(xp.evaluate(xpathExpression, actualResponse, XPathConstants.NODE));
            }
            String inputId = xp.evaluate(
                    "jxdm:PersonAugmentation/jxdm:PersonStateFingerprintIdentification/nc:IdentificationID",
                    inputPersonNodes.item(i));
            if (inputId != null) {
                String xpathExpression = "//ext:Person[jxdm:PersonAugmentation/jxdm:PersonStateFingerprintIdentification/nc:IdentificationID/text()='"
                        + inputId + "']";
                assertNotNull(xp.evaluate(xpathExpression, actualResponse, XPathConstants.NODE));
            }
        }

        for (int i = 0; i < outputOriginalRecordReferenceNodeCount; i++) {
            String nodeRef = ((Element) outputOriginalRecordReferenceNodeList.item(i))
                    .getAttributeNS("http://niem.gov/niem/structures/2.0", "ref");
            assertNotNull(xp.evaluate("//merge-result-ext:Entity[@s:id='" + nodeRef + "']", actualResponse,
                    XPathConstants.NODE));
        }

    }

    private SoapHeader makeSoapHeader(Document doc, String namespace, String localName, String value) {
        Element messageId = doc.createElementNS(namespace, localName);
        messageId.setTextContent(value);
        SoapHeader soapHeader = new SoapHeader(new QName(namespace, localName), messageId);
        return soapHeader;
    }

    private Document createDocument() throws Exception {

        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
        dbf.setNamespaceAware(true);
        Document doc = dbf.newDocumentBuilder().newDocument();

        return doc;
    }

}