

package gov.va.med.cds.testharness.xml;


import gov.va.med.cds.junit.runners.SuiteAwareRunner;
import gov.va.med.cds.junit.runners.Suite;
import gov.va.med.cds.testharness.AbstractLoggingTest;
import gov.va.med.cds.util.StreamUtil;

import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.core.io.Resource;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import javax.xml.transform.TransformerException;


/**
 * Test to show use of XmlComparator
 */
@RunWith( SuiteAwareRunner.class )
public class XmlComparatorTest
    extends
        AbstractLoggingTest
{
    private XmlComparator xmlComparator = new XmlComparator();

    private static DefaultResourceLoader resourceLoader;


    /**
     * Tests that root nodes of two xmls are not equal
     *
     * @throws XmlCompareException
     */
    @Test( expected = gov.va.med.cds.testharness.xml.XmlCompareException.class )
    @Suite( groups = "checkintest" )
    public void testSimpleRootCompareFail( )
        throws XmlCompareException
    {
        String sourceXml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><myRoot><first></first></myRoot>";
        String controlXml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><notMyRoot><first></first></notMyRoot>";
        xmlComparator.assertXMLSimilar( sourceXml, controlXml );
    }


    /**
     * Tests that the root nodes of two xmls are equal
     *
     * @throws XmlCompareException
     */
    @Test
    @Suite( groups = "checkintest" )
    public void testSimpleRootCompareSucceed( )
        throws XmlCompareException
    {
        String sourceXml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><myRoot></myRoot>";
        String controlXml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><myRoot></myRoot>";
        xmlComparator.assertXMLSimilar( sourceXml, controlXml );
    }


    /**
     * Tests that the values of the root nodes of two xmls are not equal
     *
     * @throws XmlCompareException
     */
    @Test( expected = gov.va.med.cds.testharness.xml.XmlCompareException.class )
    @Suite( groups = "checkintest" )
    public void testSimpleRootValueCompareFail( )
        throws XmlCompareException
    {
        String sourceXml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><myRoot>something</myRoot>";
        String controlXml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><myRoot>not something</myRoot>";
        xmlComparator.assertXMLSimilar( sourceXml, controlXml );
    }


    /**
     * Tests that the attribute values of the root nodes of two xmls are not equal
     *
     * @throws XmlCompareException
     */
    @Test( expected = gov.va.med.cds.testharness.xml.XmlCompareException.class )
    @Suite( groups = "checkintest" )
    public void testSimpleRootAttributesValueCompareFail( )
        throws XmlCompareException
    {
        String sourceXml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><myRoot first=\"1\" second=\"2\"></myRoot>";
        String controlXml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><myRoot first=\"1\" second=\"3\"></myRoot>";
        xmlComparator.assertXMLSimilar( sourceXml, controlXml );
    }


    /**
     * Tests that the attribute names of root nodes of two xmls are not equal
     *
     * @throws XmlCompareException
     */
    @Test( expected = gov.va.med.cds.testharness.xml.XmlCompareException.class )
    @Suite( groups = "checkintest" )
    public void testSimpleRootAttributesNameCompareFail( )
        throws XmlCompareException
    {
        String sourceXml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><myRoot first=\"1\" second=\"2\"></myRoot>";
        String controlXml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><myRoot first=\"1\" other=\"2\"></myRoot>";
        xmlComparator.assertXMLSimilar( sourceXml, controlXml );
    }


    /**
     * Tests that the number of attributes of root nodes of two xmls are not the same
     *
     * @throws XmlCompareException
     */
    @Test( expected = gov.va.med.cds.testharness.xml.XmlCompareException.class )
    @Suite( groups = "checkintest" )
    public void testSimpleRootAttributesCountMismatchCompareFail( )
        throws XmlCompareException
    {
        String sourceXml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><myRoot first=\"1\" second=\"2\"></myRoot>";
        String controlXml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><myRoot first=\"1\" second=\"2\" third=\"3\"></myRoot>";
        xmlComparator.assertXMLSimilar( sourceXml, controlXml );
    }


    /**
     * Tests that the attribute names don't have to be in the same order
     *
     * @throws XmlCompareException
     */
    @Test
    @Suite( groups = "checkintest" )
    public void testSimpleRootAttributesOrderCompare( )
        throws XmlCompareException
    {
        String sourceXml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><myRoot third=\"3\" first=\"1\" second=\"2\"><first><second></second></first></myRoot>";
        String controlXml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><myRoot first=\"1\" second=\"2\" third=\"3\"><first><second></second></first></myRoot>";
        // xmlComparator.assertXMLSimilar( sourceXml, controlXml );
        xmlComparator.assertXMLSimilar( sourceXml, controlXml, "test" );
    }


    /**
     * Tests that name, attributes, attribute values and element values are the same for a given node
     *
     * @throws XmlCompareException
     */
    @Test
    @Suite( groups = "checkintest" )
    public void testSimpleRootAttributesOrderValueCompare( )
        throws XmlCompareException
    {
        String sourceXml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><myRoot third=\"3\" first=\"1\" second=\"2\">This is Correct</myRoot>";
        String controlXml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><myRoot first=\"1\" second=\"2\" third=\"3\">This is Correct</myRoot>";
        xmlComparator.assertXMLSimilar( sourceXml, controlXml );
    }


    /**
     * Tests that nested element names are different
     *
     * @throws XmlCompareException
     */
    @Test( expected = gov.va.med.cds.testharness.xml.XmlCompareException.class )
    @Suite( groups = "checkintest" )
    public void testSimpleNameCompare( )
        throws XmlCompareException
    {
        String sourceXml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><root><first>firstData</first></root>";
        String controlXml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><root><second>firstData</second></root>";
        xmlComparator.assertXMLSimilar( sourceXml, controlXml );
    }


    /**
     * Tests that nested element values are not equal
     *
     * @throws XmlCompareException
     */
    @Test( expected = gov.va.med.cds.testharness.xml.XmlCompareException.class )
    @Suite( groups = "checkintest" )
    public void testListItemValueFailure( )
        throws XmlCompareException
    {
        String sourceXml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><root first=\"1\" second=\"2\"><item a=\"food\" b=\"medicine\">firstItem</item><item b=\"medicine\" a=\"food\">secondtem</item></root>";
        String controlXml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><root first=\"1\" second=\"2\"><item a=\"food\" b=\"medicine\">firstItem</item><item b=\"medicine\" a=\"food\">mismatch item</item></root>";
        xmlComparator.assertXMLSimilar( sourceXml, controlXml );
    }


    /**
     * Tests that the order of child elements doesn't matter
     *
     * @throws XmlCompareException
     */
    @Test
    @Suite( groups = "checkintest" )
    public void testListItemOutOfOrder( )
        throws XmlCompareException
    {
        String sourceXml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><root first=\"1\" second=\"2\"><item a=\"food\" b=\"medicine\">firstItem</item><item b=\"medicine\" a=\"food\">secondtem</item></root>";
        String controlXml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><root second=\"2\" first=\"1\"><item b=\"medicine\" a=\"food\">secondtem</item><item b=\"medicine\" a=\"food\">firstItem</item></root>";
        xmlComparator.assertXMLSimilar( sourceXml, controlXml );
    }


    /**
     * Tests that identical child list is the same
     *
     * @throws XmlCompareException
     */
    @Test
    @Suite( groups = "checkintest" )
    public void testSequence( )
        throws XmlCompareException
    {
        String sourceXml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><root first=\"1\" second=\"2\"><allergy attr1=\"something1\" attr2=\"something2\">allergyvalue</allergy><vital v1=\"bp\" v2=\"temp\">vitalvalue</vital></root>";
        String controlXml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><root first=\"1\" second=\"2\"><allergy attr1=\"something1\" attr2=\"something2\">allergyvalue</allergy><vital v1=\"bp\" v2=\"temp\">vitalvalue</vital></root>";
        xmlComparator.assertXMLSimilar( sourceXml, controlXml );
    }


    /**
     * Test that the order of a repeating element of the same name doesn't matter
     *
     * @throws XmlCompareException
     */
    @Test
    @Suite( groups = "checkintest" )
    public void testSequenceOutofOrder( )
        throws XmlCompareException
    {
        String sourceXml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><root first=\"1\" second=\"2\"><allergy attr1=\"something1\" attr2=\"something2\">allergyvalue</allergy><vital v1=\"bp\" v2=\"temp\">vitalvalue</vital></root>";
        String controlXml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><root first=\"1\" second=\"2\"><vital v2=\"temp\" v1=\"bp\">vitalvalue</vital><allergy attr2=\"something2\" attr1=\"something1\">allergyvalue</allergy></root>";
        xmlComparator.assertXMLSimilar( sourceXml, controlXml );
    }


    /**
     * Tests that a list of elements with the same names, but different values doesn't compare
     *
     * @throws XmlCompareException
     */
    @Test( expected = gov.va.med.cds.testharness.xml.XmlCompareException.class )
    @Suite( groups = "checkintest" )
    public void testSequenceOutofOrderMismatchValue( )
        throws XmlCompareException
    {
        String sourceXml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><root first=\"1\" second=\"2\"><allergy attr1=\"something1\" attr2=\"something2\">allergyvalue</allergy><vital v1=\"bp\" v2=\"temp\">vitalvalue</vital></root>";
        String controlXml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><root first=\"1\" second=\"2\"><vital v2=\"temp\" v1=\"bp\">vitalvalue</vital><allergy attr2=\"something2\" attr1=\"something1\">allergyvalue DIFFERENT</allergy></root>";
        xmlComparator.assertXMLSimilar( sourceXml, controlXml );
    }


    /**
     * Test that xml from different files doesn't compare
     *
     * @throws XmlCompareException
     * @throws IOException
     */
    @Test( expected = gov.va.med.cds.testharness.xml.XmlCompareException.class )
    @Suite( groups = "checkintest" )
    public void testXmlSimpleFileCompareFail( )
        throws XmlCompareException,
            IOException
    {
        Resource resource = resourceLoader.getResource( "classpath:gov/va/med/cds/testharness/xml/testSourceDifferent.xml" );
        String sourceXml = StreamUtil.resourceToString( resource );

        resource = resourceLoader.getResource( "classpath:gov/va/med/cds/testharness/xml/testControl.xml" );
        String controlXml = StreamUtil.resourceToString( resource );
        xmlComparator.assertXMLSimilar( sourceXml, controlXml );
    }


    /**
     * Test that xml compared against xml from the same file is indeed equal
     *
     * @throws XmlCompareException
     * @throws IOException
     */
    @Test
    @Suite( groups = "checkintest" )
    public void testXmlSimpleFileCompare( )
        throws XmlCompareException,
            IOException
    {
        Resource resource = resourceLoader.getResource( "classpath:gov/va/med/cds/testharness/xml/AllergyAppend.xml" );
        String sourceXml = StreamUtil.resourceToString( resource );

        resource = resourceLoader.getResource( "classpath:gov/va/med/cds/testharness/xml/AllergyAppendSame.xml" );
        String controlXml = StreamUtil.resourceToString( resource );
        xmlComparator.assertXMLSimilar( sourceXml, controlXml );
    }


    /**
     * Test that the xml is not equal even if the Severity Observation Events are removed by transform
     *
     * @throws XmlCompareException
     * @throws IOException
     * @throws TransformerException
     */
    //    @Test( groups = { "checkintest" }, expectedExceptions = { XmlCompareException.class } )
    //    public void testXmlFileBadPartialTransformAndCompare( )
    //        throws XmlCompareException,
    //            IOException,
    //            TransformerException
    //    {
    //        List<String> excludes = new ArrayList<String>();
    //
    //        Resource resource = resourceLoader.getResource( "classpath:gov/va/med/cds/testharness/xml/allergyRead00009Result.xml" );
    //        String sourceXml = StreamUtil.resourceToString( resource );
    //
    //        resource = resourceLoader.getResource( "classpath:gov/va/med/cds/testharness/xml/allergyRead00009Control.xml" );
    //        String controlXml = StreamUtil.resourceToString( resource );
    //
    //        xmlComparator.assertXMLSimilar( sourceXml, controlXml, excludes );
    //    }
    /**
     * Test that the xmls are not equal if nothing is removed by the transform. This shows that the transform does occur
     * and didn't throw out anything it wasn't supposed.
     *
     * @throws XmlCompareException
     * @throws IOException
     * @throws TransformerException
     */
    @Test( expected = gov.va.med.cds.testharness.xml.XmlCompareException.class )
    @Suite( groups = "checkintest" )
    public void testXmlFileBadTransformAndCompare( )
        throws XmlCompareException,
            IOException,
            TransformerException
    {
        List<String> excludes = new ArrayList<String>();

        Resource resource = resourceLoader.getResource( "classpath:gov/va/med/cds/testharness/xml/allergyRead00009Result.xml" );
        String sourceXml = StreamUtil.resourceToString( resource );

        resource = resourceLoader.getResource( "classpath:gov/va/med/cds/testharness/xml/allergyRead00009Control.xml" );
        String controlXml = StreamUtil.resourceToString( resource );

        xmlComparator.assertXMLSimilar( sourceXml, controlXml, excludes );
    }


    /**
     * Shows that the xmls do compare if the changedDate attributes are stripped out by the transform.
     *
     * @throws XmlCompareException
     * @throws IOException
     * @throws TransformerException
     */
    @Test
    @Suite( groups = "checkintest" )
    public void testXmlFileTransformAndCompare( )
        throws XmlCompareException,
            IOException,
            TransformerException
    {
        List<String> excludes = new ArrayList<String>();
        excludes.add( "/clinicaldata:clinicalData/intoleranceCondition/reactionObservationEvent/reactionOnsetHistory/@changedDate" );
        //Resource xsltResource = resourceLoader.getResource( "classpath:gov/va/med/cds/testharness/xml/vhim3400/removeChangedDate.xslt" );

        Resource resource = resourceLoader.getResource( "classpath:gov/va/med/cds/testharness/xml/allergyRead00009Result.xml" );
        String sourceXml = StreamUtil.resourceToString( resource );

        resource = resourceLoader.getResource( "classpath:gov/va/med/cds/testharness/xml/allergyRead00009Control.xml" );
        String controlXml = StreamUtil.resourceToString( resource );

        xmlComparator.assertXMLSimilar( sourceXml, controlXml, excludes );
    }


    /**
     * Shows that the xmls do compare if the changedDate attributes are stripped out by the transform.
     *
     * @throws XmlCompareException
     * @throws IOException
     * @throws TransformerException
     */
    @Test
    @Suite( groups = "checkintest" )
    public void testRemoveChangedDateAndCompare( )
        throws XmlCompareException,
            IOException,
            TransformerException
    {
        List<String> excludes = new ArrayList<String>();
        //Resource xsltResource = resourceLoader.getResource( "classpath:gov/va/med/cds/testharness/xml/vhim3400/removeChangedDate.xslt" );

        Resource resource = resourceLoader.getResource( "classpath:gov/va/med/cds/testharness/xml/testSource2.xml" );
        String sourceXml = StreamUtil.resourceToString( resource );

        resource = resourceLoader.getResource( "classpath:gov/va/med/cds/testharness/xml/testControl2.xml" );
        String controlXml = StreamUtil.resourceToString( resource );

        xmlComparator.assertXMLSimilar( sourceXml, controlXml, excludes );
    }


    /**
     * This test strips out the errorId elements via xslt transform and compares results. Requires that the transformed
     * results is the same
     *
     * @throws XmlCompareException
     * @throws IOException
     * @throws TransformerException
     */
    @Test
    @Suite( groups = "checkintest" )
    public void testRemoveErrorIdAndCompareSame( )
        throws XmlCompareException,
            IOException,
            TransformerException
    {
        List<String> excludes = new ArrayList<String>();
        excludes.add( "/clinicaldata:clinicalData/errors/fatalError/errorId" );
        //        Resource xsltResource = resourceLoader.getResource( "classpath:gov/va/med/cds/testharness/xml/vhim3400/removeErrorId.xslt" );

        Resource resource = resourceLoader.getResource( "classpath:gov/va/med/cds/testharness/xml/testSourceErrorSectionSame.xml" );
        String sourceXml = StreamUtil.resourceToString( resource );

        resource = resourceLoader.getResource( "classpath:gov/va/med/cds/testharness/xml/testControlErrorSection.xml" );
        String controlXml = StreamUtil.resourceToString( resource );

        xmlComparator.assertXMLSimilar( sourceXml, controlXml, excludes );
    }


    /**
     * This test strips out the errorId elements via xslt transform and compares results. Requires that the transformed
     * results are different
     *
     * @throws XmlCompareException
     * @throws IOException
     * @throws TransformerException
     */
    @Test( expected = gov.va.med.cds.testharness.xml.XmlCompareException.class )
    @Suite( groups = "checkintest" )
    public void testRemoveErrorIdAndCompareDifferent( )
        throws XmlCompareException,
            IOException,
            TransformerException
    {
        List<String> excludes = new ArrayList<String>();
        //        Resource xsltResource = resourceLoader.getResource( "classpath:gov/va/med/cds/testharness/xml/vhim3400/removeErrorId.xslt" );

        Resource resource = resourceLoader.getResource( "classpath:gov/va/med/cds/testharness/xml/testSourceErrorSectionDifferent.xml" );
        String sourceXml = StreamUtil.resourceToString( resource );

        resource = resourceLoader.getResource( "classpath:gov/va/med/cds/testharness/xml/testControlErrorSection.xml" );
        String controlXml = StreamUtil.resourceToString( resource );

        xmlComparator.assertXMLSimilar( sourceXml, controlXml, excludes );
    }


    /**
     * Shows that the xmls do compare without transformation.
     *
     * @throws XmlCompareException
     * @throws IOException
     * @throws TransformerException
     */
    @Test
    @Suite( groups = "checkintest" )
    public void testNoTransformationCompare( )
        throws XmlCompareException,
            IOException,
            TransformerException
    {
        Resource resource = resourceLoader.getResource( "classpath:gov/va/med/cds/testharness/xml/allergyConcurrentReadSource.xml" );
        String sourceXml = StreamUtil.resourceToString( resource );

        resource = resourceLoader.getResource( "classpath:gov/va/med/cds/testharness/xml/allergyConcurrentReadControl.xml" );
        String controlXml = StreamUtil.resourceToString( resource );

        xmlComparator.assertXMLSimilar( sourceXml, controlXml );
    }


    @BeforeClass
    public static void beforeXmlComparatorTestClassSetUp( )
        throws Exception
    {
        resourceLoader = new DefaultResourceLoader();
    }


    @AfterClass
    public static void afterXmlComparatorTestClassTearDown( )
        throws Exception
    {
        resourceLoader = null;
    }
}
