

package gov.va.med.cds.filter;


import gov.va.med.cds.clinicaldata.FilterMetaData;
import gov.va.med.cds.clinicaldata.FilterMetaDataInterface;
import gov.va.med.cds.exception.FilterCacheException;
import gov.va.med.cds.junit.runners.Suite;
import gov.va.med.cds.junit.runners.SuiteAwareRunner;
import gov.va.med.cds.request.ValidationException;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.Schema;
import javax.xml.validation.Validator;

import junit.framework.Assert;

import org.easymock.EasyMock;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.xml.sax.SAXException;


/**
 * Tests the FilterManager class with good and bad filters.
 * 
 */
@RunWith( SuiteAwareRunner.class )
public class FilterManagerTest
{
    private static final String RDI_GOOD_FILTER = "<?xml version='1.0' encoding='UTF-8'?><filter:filter xmlns:filter='Filter' vhimVersion='Test_Vhim_X_00'><filterId>RDI_GOOD_FILTER</filterId></filter:filter>";
    private static final String RDI_BAD_FILTER = "<?xml version='1.0' encoding='UTF-8'?><filter:filter xmlns:filter='Filter' vhimVersion='Test_Vhim_X_00'><filterId>RDI_BAD_FILTER</filterId></filter:filter>";
    private static final String RDI_IC_RX_CANNED_FILTER_ID = "RDI_IC_RX_CANNED_FILTER_ID";
    private static final String VIM_VIRSION = "Test_Vhim_X_00";

    private FilterServiceInterface filterServiceMock = EasyMock.createMock( FilterServiceInterface.class );
    private FilterCachePersistenceInterface filterCachePersistenceMock = EasyMock.createMock( FilterCachePersistenceInterface.class );;
    private FilterMemoryCacheInterface filterMemoryCacheMock = EasyMock.createMock( FilterMemoryCacheInterface.class );

    private Collection<String> vhimVersions = null;
    private Collection<String> filterIds = null;

    private FilterManager filterManager = null;


    /**
     * Check out Filter Cache with a good filter.
     * 
     * @throws Exception
     */
    @Test
    @Suite( groups = { "checkintest" } )
    public void testFilterCacheValidatorWithGoodFilter( )
        throws Exception
    {
        initFilterManagerWithMocks();
        EasyMock.reset( filterServiceMock, filterCachePersistenceMock, filterMemoryCacheMock );

        Schema filterSchemaMock = EasyMock.createMock( Schema.class );
        Validator filterSchemaValidatorMock = EasyMock.createMock( Validator.class );

        EasyMock.expect( filterMemoryCacheMock.getFilterSchema( RDI_IC_RX_CANNED_FILTER_ID ) ).andReturn( filterSchemaMock );
        EasyMock.expect( filterSchemaMock.newValidator() ).andReturn( filterSchemaValidatorMock );
        filterSchemaValidatorMock.validate( EasyMock.isA( StreamSource.class ) );

        EasyMock.replay( filterServiceMock, filterCachePersistenceMock, filterMemoryCacheMock, filterSchemaMock, filterSchemaValidatorMock );
        filterManager.validateFilterXml( RDI_IC_RX_CANNED_FILTER_ID, RDI_GOOD_FILTER );
        EasyMock.verify( filterServiceMock, filterCachePersistenceMock, filterMemoryCacheMock, filterSchemaMock, filterSchemaValidatorMock );

    }


    /**
     * Check out the Filter Cache with a bad filter.
     * 
     * @throws Exception
     */
    @Test( expected = ValidationException.class )
    @Suite( groups = { "checkintest" } )
    public void testFilterCacheValidatorWithBadFilter_SAXException( )
        throws Exception
    {
        initFilterManagerWithMocks();
        EasyMock.reset( filterServiceMock, filterCachePersistenceMock, filterMemoryCacheMock );

        Schema filterSchemaMock = EasyMock.createMock( Schema.class );
        Validator filterSchemaValidatorMock = EasyMock.createMock( Validator.class );

        EasyMock.expect( filterMemoryCacheMock.getFilterSchema( RDI_IC_RX_CANNED_FILTER_ID ) ).andReturn( filterSchemaMock );
        EasyMock.expect( filterSchemaMock.newValidator() ).andReturn( filterSchemaValidatorMock );
        filterSchemaValidatorMock.validate( EasyMock.isA( StreamSource.class ) );
        EasyMock.expectLastCall().andThrow( new SAXException() );

        EasyMock.replay( filterServiceMock, filterCachePersistenceMock, filterMemoryCacheMock, filterSchemaMock, filterSchemaValidatorMock );
        filterManager.validateFilterXml( RDI_IC_RX_CANNED_FILTER_ID, RDI_BAD_FILTER );
        EasyMock.verify( filterServiceMock, filterCachePersistenceMock, filterMemoryCacheMock, filterSchemaMock, filterSchemaValidatorMock );

    }


    /**
     * Check out the Filter Cache with a bad filter.
     * 
     * @throws Exception
     */
    @Test( expected = FilterCacheException.class )
    @Suite( groups = { "checkintest" } )
    public void testFilterCacheValidatorWithBadFilter_IOException( )
        throws Exception
    {
        initFilterManagerWithMocks();
        EasyMock.reset( filterServiceMock, filterCachePersistenceMock, filterMemoryCacheMock );

        Schema filterSchemaMock = EasyMock.createMock( Schema.class );
        Validator filterSchemaValidatorMock = EasyMock.createMock( Validator.class );

        EasyMock.expect( filterMemoryCacheMock.getFilterSchema( RDI_IC_RX_CANNED_FILTER_ID ) ).andReturn( filterSchemaMock );
        EasyMock.expect( filterSchemaMock.newValidator() ).andReturn( filterSchemaValidatorMock );
        filterSchemaValidatorMock.validate( EasyMock.isA( StreamSource.class ) );
        EasyMock.expectLastCall().andThrow( new IOException() );

        EasyMock.replay( filterServiceMock, filterCachePersistenceMock, filterMemoryCacheMock, filterSchemaMock, filterSchemaValidatorMock );
        filterManager.validateFilterXml( RDI_IC_RX_CANNED_FILTER_ID, RDI_BAD_FILTER );
        EasyMock.verify( filterServiceMock, filterCachePersistenceMock, filterMemoryCacheMock, filterSchemaMock, filterSchemaValidatorMock );

    }


    /**
     * Test increase memory cache
     */
    @Test
    @Suite( groups = { "checkintest" } )
    public void testResizeFilterMemoryCache_Increase( )
        throws Exception
    {
        initFilterManagerWithMocks();
        EasyMock.reset( filterServiceMock, filterCachePersistenceMock, filterMemoryCacheMock );

        EasyMock.expect( filterMemoryCacheMock.getFilterCacheThreshold() ).andReturn( 50 );
        filterMemoryCacheMock.reSize( 100 );

        EasyMock.replay( filterServiceMock, filterCachePersistenceMock, filterMemoryCacheMock );
        filterManager.resizeFilterMemoryCache( 100 );
        EasyMock.verify( filterServiceMock, filterCachePersistenceMock, filterMemoryCacheMock );

    }


    /**
     * Test down-sizing memory cache
     */
    @Test
    @Suite( groups = { "checkintest" } )
    public void testResizeFilterMemoryCache_Decrease( )
        throws Exception
    {
        initFilterManagerWithMocks();
        EasyMock.reset( filterServiceMock, filterCachePersistenceMock, filterMemoryCacheMock );

        EasyMock.expect( filterMemoryCacheMock.getFilterCacheThreshold() ).andReturn( 100 );
        filterMemoryCacheMock.reSize( 50 );
        EasyMock.expect( filterMemoryCacheMock.isEmpty() ).andReturn( false );
        filterMemoryCacheMock.clear();
        filterServiceMock.initialize();
        EasyMock.expect( filterServiceMock.getVhimVersions() ).andReturn( vhimVersions );
        EasyMock.expect( filterServiceMock.getActiveFilterIds( VIM_VIRSION ) ).andReturn( filterIds );
        FilterMetaDataInterface filterMetaData = new FilterMetaData();
        EasyMock.expect( filterServiceMock.getFilterMetaData( RDI_GOOD_FILTER ) ).andReturn( filterMetaData );
        filterCachePersistenceMock.persistFilterSchema( filterMetaData );
        filterCachePersistenceMock.loadAllFilterSchemasIntoMemoryCache( filterMemoryCacheMock );

        EasyMock.replay( filterServiceMock, filterCachePersistenceMock, filterMemoryCacheMock );
        filterManager.resizeFilterMemoryCache( 50 );
        EasyMock.verify( filterServiceMock, filterCachePersistenceMock, filterMemoryCacheMock );

    }


    /**
     * test to validate filterId that got loaded into cache from persistence by calling validateValidateXML()
     * 
     * @throws Exception
     */
    @Test
    @Suite( groups = { "checkintest" } )
    public void testFilterIdLoadedIntoFilterCache( )
        throws Exception
    {
        initFilterManagerWithMocks();
        EasyMock.reset( filterServiceMock, filterCachePersistenceMock, filterMemoryCacheMock );

        Schema filterSchemaMock = EasyMock.createMock( Schema.class );
        Validator filterSchemaValidatorMock = EasyMock.createMock( Validator.class );

        EasyMock.expect( filterMemoryCacheMock.getFilterSchema( RDI_IC_RX_CANNED_FILTER_ID ) ).andReturn( null );
        filterCachePersistenceMock.loadFilterIntoMemoryCache( RDI_IC_RX_CANNED_FILTER_ID, filterMemoryCacheMock );
        EasyMock.expect( filterMemoryCacheMock.getFilterSchema( RDI_IC_RX_CANNED_FILTER_ID ) ).andReturn( filterSchemaMock );
        EasyMock.expect( filterSchemaMock.newValidator() ).andReturn( filterSchemaValidatorMock );
        filterSchemaValidatorMock.validate( EasyMock.isA( StreamSource.class ) );

        EasyMock.replay( filterServiceMock, filterCachePersistenceMock, filterMemoryCacheMock, filterSchemaMock, filterSchemaValidatorMock );
        filterManager.validateFilterXml( RDI_IC_RX_CANNED_FILTER_ID, RDI_GOOD_FILTER );
        EasyMock.verify( filterServiceMock, filterCachePersistenceMock, filterMemoryCacheMock, filterSchemaMock, filterSchemaValidatorMock );

    }


    /**
     * Validate filterId that can't be loaded into cache from persistence by calling validateValidateXML()
     * 
     * @throws Exception
     */
    @Test( expected = FilterCacheException.class )
    @Suite( groups = { "checkintest" } )
    public void testFilterIdNotLoadedIntoFilterCache( )
        throws Exception
    {
        initFilterManagerWithMocks();
        EasyMock.reset( filterServiceMock, filterCachePersistenceMock, filterMemoryCacheMock );

        Validator filterSchemaValidatorMock = EasyMock.createMock( Validator.class );

        EasyMock.expect( filterMemoryCacheMock.getFilterSchema( RDI_IC_RX_CANNED_FILTER_ID ) ).andReturn( null ).anyTimes();
        filterCachePersistenceMock.loadFilterIntoMemoryCache( RDI_IC_RX_CANNED_FILTER_ID, filterMemoryCacheMock );
        //EasyMock.expect(filterMemoryCacheMock.getFilterSchema( RDI_IC_RX_CANNED_FILTER_ID)).andReturn( null ).anyTimes();
        EasyMock.expect( filterServiceMock.getFilterMetaData( RDI_IC_RX_CANNED_FILTER_ID ) ).andReturn( null ).anyTimes();

        filterCachePersistenceMock.loadFilterIntoMemoryCache( RDI_IC_RX_CANNED_FILTER_ID, filterMemoryCacheMock );

        EasyMock.replay( filterServiceMock, filterCachePersistenceMock, filterMemoryCacheMock, filterSchemaValidatorMock );
        filterManager.validateFilterXml( RDI_IC_RX_CANNED_FILTER_ID, RDI_GOOD_FILTER );
        EasyMock.verify( filterServiceMock, filterCachePersistenceMock, filterMemoryCacheMock, filterSchemaValidatorMock );

    }


    /**
     * Test call passed to filter memory cache
     * 
     * @throws Exception
     */
    @Test
    @Suite( groups = { "checkintest" } )
    public void testGetFilterCacheFilterIds( )
        throws Exception
    {
        initFilterManagerWithMocks();
        EasyMock.reset( filterServiceMock, filterCachePersistenceMock, filterMemoryCacheMock );
        Set<String> filterIdSet = new HashSet<String>();
        filterIdSet.add( "MockId1" );
        filterIdSet.add( "MockId2" );

        EasyMock.expect( filterMemoryCacheMock.getFilterCacheFilterIds() ).andReturn( filterIdSet );
        EasyMock.replay( filterServiceMock, filterCachePersistenceMock, filterMemoryCacheMock );
        List<String> filterIds = filterManager.getFilterCacheFilterIds();
        EasyMock.verify( filterServiceMock, filterCachePersistenceMock, filterMemoryCacheMock );
        Assert.assertTrue( filterIds.size() == 2 );
    }


    /**
     * Test call passed to filter memory cache
     * 
     * @throws Exception
     */
    @Test
    @Suite( groups = { "checkintest" } )
    public void testGetNumberOfFilterSchemasLoadedIntoFilterCache( )
        throws Exception
    {
        initFilterManagerWithMocks();
        EasyMock.reset( filterServiceMock, filterCachePersistenceMock, filterMemoryCacheMock );
        EasyMock.expect( filterMemoryCacheMock.getNumberOfFilterSchemasLoadedIntoFilterCache() ).andReturn( 12 );
        EasyMock.replay( filterServiceMock, filterCachePersistenceMock, filterMemoryCacheMock );
        Assert.assertEquals( 12, filterManager.getNumberOfFilterSchemasLoadedIntoFilterCache() );
        EasyMock.verify( filterServiceMock, filterCachePersistenceMock, filterMemoryCacheMock );

    }


    @Suite( groups = { "checkintest" } )
    public void testRetrieveAndPersistFilterFromService_ThrowsExceptionInConstructor( )
        throws Exception
    {

        vhimVersions = new ArrayList<String>();
        vhimVersions.add( VIM_VIRSION );
        filterIds = new ArrayList<String>();
        filterIds.add( RDI_GOOD_FILTER );

        // Constructor expectations
        filterServiceMock.initialize();
        EasyMock.expect( filterServiceMock.getVhimVersions() ).andReturn( vhimVersions );
        EasyMock.expect( filterServiceMock.getActiveFilterIds( VIM_VIRSION ) ).andReturn( filterIds );
        EasyMock.expect( filterServiceMock.getFilterMetaData( RDI_GOOD_FILTER ) ).andThrow( new RuntimeException() );

        EasyMock.replay( filterServiceMock, filterCachePersistenceMock, filterMemoryCacheMock );
        filterManager = new FilterManager( filterServiceMock, filterCachePersistenceMock, filterMemoryCacheMock );
        EasyMock.verify( filterServiceMock, filterCachePersistenceMock, filterMemoryCacheMock );

    }


    /** 
     * Construction of Filter Manager is pretty involved, moved it in a separate method
     */
    private void initFilterManagerWithMocks( )
    {
        vhimVersions = new ArrayList<String>();
        vhimVersions.add( VIM_VIRSION );
        filterIds = new ArrayList<String>();
        filterIds.add( RDI_GOOD_FILTER );

        // Constructor expectations
        filterServiceMock.initialize();
        EasyMock.expect( filterServiceMock.getVhimVersions() ).andReturn( vhimVersions );
        EasyMock.expect( filterServiceMock.getActiveFilterIds( VIM_VIRSION ) ).andReturn( filterIds );
        FilterMetaDataInterface filterMetaData = new FilterMetaData();
        EasyMock.expect( filterServiceMock.getFilterMetaData( RDI_GOOD_FILTER ) ).andReturn( filterMetaData );
        filterCachePersistenceMock.persistFilterSchema( filterMetaData );
        filterCachePersistenceMock.loadAllFilterSchemasIntoMemoryCache( filterMemoryCacheMock );

        EasyMock.replay( filterServiceMock, filterCachePersistenceMock, filterMemoryCacheMock );

        filterManager = new FilterManager( filterServiceMock, filterCachePersistenceMock, filterMemoryCacheMock );

        EasyMock.verify( filterServiceMock, filterCachePersistenceMock, filterMemoryCacheMock );
        EasyMock.reset( filterServiceMock, filterCachePersistenceMock, filterMemoryCacheMock );
    }

}
