

package gov.va.med.cds.filter;


import gov.va.med.cds.clinicaldata.FilterMetaDataInterface;
import gov.va.med.cds.junit.runners.Suite;
import gov.va.med.cds.junit.runners.SuiteAwareRunner;
import gov.va.med.cds.util.LRUHashMap;

import java.lang.reflect.Field;
import java.util.HashSet;
import java.util.concurrent.locks.Lock;

import javax.xml.validation.Schema;

import org.easymock.EasyMock;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;


/**
 * Tests FilterMemoryCache class functionality like creating filter cache, loading the cache with filter schemas,
 * resizing the filter cache
 * 
 */
@RunWith( SuiteAwareRunner.class )
public class FilterMemoryCacheTest
{
    private static final String CDS_FILTER_XSD = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><xs:schema xmlns:xs=\"http://www.w3.org/2001/XMLSchema\" xmlns:vhimversion=\"Vhimversion\" xmlns:filter=\"Filter\" targetNamespace=\"Filter\"><!--imports for all non target name spaces--><!-- global definition --><xs:element name=\"filter\" type=\"filter:Filter\"/><!--type definitions--><xs:complexType name=\"Filter\"><xs:sequence><xs:element name=\"filterId\" type=\"filter:FilterId\"/><xs:element name=\"patients\" type=\"filter:patient\" maxOccurs=\"unbounded\"/><xs:element name=\"entryPointFilter\" type=\"filter:EntryFilter\" maxOccurs=\"unbounded\"/><xs:element name=\"correlationFilter\" type=\"filter:CorrelationFilter\" minOccurs=\"0\" maxOccurs=\"unbounded\"/><xs:element name=\"aggregationFilter\" type=\"filter:CorrelationFilter\" minOccurs=\"0\" maxOccurs=\"unbounded\"/></xs:sequence><xs:attribute name=\"vhimVersion\" type=\"xs:string\" use=\"required\"/></xs:complexType><xs:complexType name=\"patient\"><xs:choice><xs:element name=\"resolvedIdentifiers\" type=\"filter:PersonIdentifier\" maxOccurs=\"unbounded\"/><xs:sequence><xs:element name=\"NationalId\" type=\"xs:string\"/><xs:element name=\"excludeIdentifiers\" type=\"filter:PersonIdentifier\" minOccurs=\"0\" maxOccurs=\"unbounded\"/></xs:sequence></xs:choice></xs:complexType><xs:complexType name=\"PersonIdentifier\"><xs:sequence><xs:element name=\"identity\" type=\"xs:string\"/><xs:element name=\"assigningAuthority\" type=\"xs:string\"/><xs:element name=\"assigningFacility\" type=\"xs:string\"/></xs:sequence></xs:complexType><xs:complexType name=\"EntityIdentifier\"><xs:sequence><xs:element name=\"identity\" type=\"xs:string\" minOccurs=\"0\"/><xs:element name=\"namespaceId\" type=\"xs:string\" default=\"VHA\" minOccurs=\"0\"/><xs:element name=\"universalId\" type=\"xs:string\" minOccurs=\"0\"/><xs:element name=\"universalIdType\" type=\"xs:string\" minOccurs=\"0\"/></xs:sequence></xs:complexType><xs:complexType name=\"EntryFilter\"><xs:sequence><xs:element name=\"domainEntryPoint\" type=\"xs:string\"/><xs:element name=\"startDate\" type=\"filter:DateParameter\" minOccurs=\"0\"/><xs:element name=\"endDate\" type=\"filter:DateParameter\" minOccurs=\"0\"/><xs:element name=\"recordIdentifiers\" type=\"filter:EntityIdentifier\" minOccurs=\"0\" maxOccurs=\"unbounded\"/><xs:element name=\"xpathQuery\" type=\"filter:XpathQuery\" minOccurs=\"0\"/></xs:sequence><xs:attribute name=\"queryName\" type=\"xs:ID\" use=\"required\"/></xs:complexType><xs:complexType name=\"CorrelationFilter\"><xs:sequence><xs:element name=\"xpathQuery\" type=\"filter:XpathQuery\"/></xs:sequence><xs:attribute name=\"queryName\" use=\"required\"/></xs:complexType><xs:complexType name=\"XpathQuery\"><xs:sequence><xs:element name=\"xpath\" type=\"filter:XPath\"/><xs:element name=\"parameter\" type=\"filter:Parameter\" minOccurs=\"0\" maxOccurs=\"unbounded\"/></xs:sequence></xs:complexType><xs:simpleType name=\"DateParameter\"><xs:restriction base=\"xs:date\"/></xs:simpleType><xs:complexType name=\"Parameter\"><xs:all><xs:element name=\"value\"><xs:simpleType><xs:restriction base=\"xs:string\"/></xs:simpleType></xs:element></xs:all><xs:attribute name=\"name\" use=\"required\"/><xs:attribute name=\"type\"/></xs:complexType><xs:simpleType name=\"XPath\"><xs:restriction base=\"xs:string\"/></xs:simpleType><xs:simpleType name=\"FilterId\"><xs:restriction base=\"xs:string\"/></xs:simpleType></xs:schema>";
    private LRUHashMap<String, FilterMetaDataInterface> filterMemoryCacheMapMock = null;
    private FilterMemoryCacheInterface filterMemoryCache = null;
    private FilterMetaDataInterface filterMetaDataMock = null;
    private Lock readLockMock;
    private Lock writeLockMock;


    @Before
    @Suite( groups = "checkintest" )
    public void beforeFilterMemoryCacheTestClassSetUp( )
        throws Exception
    {
        filterMetaDataMock = EasyMock.createMock( FilterMetaDataInterface.class );
        filterMemoryCacheMapMock = EasyMock.createMock( LRUHashMap.class );

        filterMemoryCache = new FilterMemoryCache( filterMemoryCacheMapMock );

        readLockMock = EasyMock.createMock( Lock.class );
        writeLockMock = EasyMock.createMock( Lock.class );
        setFilterMemoryCacheLocksToMocks( readLockMock, writeLockMock );
    }


    @Test
    @Suite( groups = "checkintest" )
    public void testClearFilterCache( )
        throws Exception
    {
        //Expectations 
        writeLockMock.lock();
        filterMemoryCacheMapMock.clear();
        writeLockMock.unlock();

        EasyMock.replay( filterMetaDataMock, readLockMock, writeLockMock );
        filterMemoryCache.clear();
        EasyMock.verify( filterMetaDataMock, readLockMock, writeLockMock );
    }


    @Test
    @Suite( groups = "checkintest" )
    public void testLoadFilterMetaDataIntoMemoryCache( )
        throws Exception
    {
        EasyMock.expect( filterMemoryCacheMapMock.containsKey( "filterId" ) ).andReturn( true );
        EasyMock.replay( filterMetaDataMock, readLockMock, writeLockMock, filterMemoryCacheMapMock );

        filterMemoryCache.loadFilterMetaDataIntoMemoryCache( "filterId", filterMetaDataMock );
        EasyMock.verify( filterMetaDataMock, readLockMock, writeLockMock, filterMemoryCacheMapMock );
    }


    @Test
    @Suite( groups = "checkintest" )
    public void testLoadFilterMetaDataIntoMemoryCache_NotInCache( )
        throws Exception
    {
        EasyMock.expect( filterMemoryCacheMapMock.containsKey( "filterId" ) ).andReturn( false ).times( 2 );
        writeLockMock.lock();
        EasyMock.expect( filterMemoryCacheMapMock.put( "filterId", filterMetaDataMock ) ).andReturn( filterMetaDataMock );
        writeLockMock.unlock();
        EasyMock.replay( filterMetaDataMock, readLockMock, writeLockMock, filterMemoryCacheMapMock );

        filterMemoryCache.loadFilterMetaDataIntoMemoryCache( "filterId", filterMetaDataMock );
        EasyMock.verify( filterMetaDataMock, readLockMock, writeLockMock, filterMemoryCacheMapMock );
    }


    @Test
    @Suite( groups = "checkintest" )
    public void testgetFilterString( )
        throws Exception
    {
        EasyMock.expect( filterMemoryCacheMapMock.get( "filterId" ) ).andReturn( filterMetaDataMock );
        EasyMock.expect( filterMetaDataMock.getFilterSchemaXml() ).andReturn( "schema XML" );

        EasyMock.replay( filterMetaDataMock, readLockMock, writeLockMock, filterMemoryCacheMapMock );
        filterMemoryCache.getFilterString( "filterId" );
        EasyMock.verify( filterMetaDataMock, readLockMock, writeLockMock, filterMemoryCacheMapMock );
    }


    @Test
    @Suite( groups = "checkintest" )
    public void testGetFilterSchema( )
        throws Exception
    {
        EasyMock.expect( filterMemoryCacheMapMock.get( "filterId" ) ).andReturn( filterMetaDataMock );
        Schema schema = EasyMock.createMock( Schema.class );
        EasyMock.expect( filterMetaDataMock.getFilterSchema() ).andReturn( schema );

        EasyMock.replay( filterMetaDataMock, readLockMock, writeLockMock, filterMemoryCacheMapMock );
        Assert.assertNotNull( filterMemoryCache.getFilterSchema( "filterId" ) );
        EasyMock.verify( filterMetaDataMock, readLockMock, writeLockMock, filterMemoryCacheMapMock );
    }


    @Test
    @Suite( groups = "checkintest" )
    public void testIsEmpty( )
        throws Exception
    {
        readLockMock.lock();
        EasyMock.expect( filterMemoryCacheMapMock.isEmpty() ).andReturn( true );
        readLockMock.unlock();

        EasyMock.replay( filterMetaDataMock, readLockMock, writeLockMock, filterMemoryCacheMapMock );
        Assert.assertTrue( filterMemoryCache.isEmpty() );
        EasyMock.verify( filterMetaDataMock, readLockMock, writeLockMock, filterMemoryCacheMapMock );

    }


    @Test
    @Suite( groups = "checkintest" )
    public void testGetNumberOfFilterSchemasLoadedIntoFilterCache( )
        throws Exception
    {
        EasyMock.expect( filterMemoryCacheMapMock.size() ).andReturn( 5 );

        EasyMock.replay( filterMetaDataMock, readLockMock, writeLockMock, filterMemoryCacheMapMock );
        Assert.assertEquals( 5, filterMemoryCache.getNumberOfFilterSchemasLoadedIntoFilterCache() );
        EasyMock.verify( filterMetaDataMock, readLockMock, writeLockMock, filterMemoryCacheMapMock );
    }


    @Test
    @Suite( groups = "checkintest" )
    public void testGetFilterCacheThreshold( )
    {
        EasyMock.expect( filterMemoryCacheMapMock.getMapThreshold() ).andReturn( 3 );

        EasyMock.replay( filterMetaDataMock, readLockMock, writeLockMock, filterMemoryCacheMapMock );
        Assert.assertEquals( 3, filterMemoryCache.getFilterCacheThreshold() );
        EasyMock.verify( filterMetaDataMock, readLockMock, writeLockMock, filterMemoryCacheMapMock );
    }


    @Test
    @Suite( groups = "checkintest" )
    public void testsReSize( )
    {
        writeLockMock.lock();
        filterMemoryCacheMapMock.reSize( 12 );
        writeLockMock.unlock();

        EasyMock.replay( filterMetaDataMock, readLockMock, writeLockMock, filterMemoryCacheMapMock );
        filterMemoryCache.reSize( 12 );
        EasyMock.verify( filterMetaDataMock, readLockMock, writeLockMock, filterMemoryCacheMapMock );
    }


    @Test
    @Suite( groups = "checkintest" )
    public void testGetFilterCacheFilterIds( )
    {
        EasyMock.expect( filterMemoryCacheMapMock.keySet() ).andReturn( new HashSet<String>() );

        EasyMock.replay( filterMetaDataMock, readLockMock, writeLockMock, filterMemoryCacheMapMock );
        Assert.assertNotNull( filterMemoryCache.getFilterCacheFilterIds() );
        EasyMock.verify( filterMetaDataMock, readLockMock, writeLockMock, filterMemoryCacheMapMock );
    }


    /**
     *  Use reflection to set FilterMemoryCache private fields to mocks
     */
    private void setFilterMemoryCacheLocksToMocks( Lock readLock, Lock writeLock )
        throws Exception
    {
        Class<? extends FilterMemoryCacheInterface> obj = filterMemoryCache.getClass();
        Field field = obj.getDeclaredField( "readLock" );

        field.setAccessible( true );
        field.set( filterMemoryCache, readLock );

        field = obj.getDeclaredField( "writeLock" );
        field.setAccessible( true );
        field.set( filterMemoryCache, writeLock );
    }

}
