/*!
* Copyright 2010 - 2016 Pentaho Corporation. All rights reserved.
*
* Licensed 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 com.pentaho.di.purge;
import static org.junit.Assert.fail;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.io.ByteArrayInputStream;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import org.pentaho.di.core.exception.KettleException;
import org.pentaho.di.repository.ObjectId;
import org.pentaho.di.repository.RepositoryElementInterface;
import org.pentaho.platform.api.repository2.unified.IUnifiedRepository;
import org.pentaho.platform.api.repository2.unified.RepositoryRequest;
import org.pentaho.platform.api.repository2.unified.VersionSummary;
import org.pentaho.platform.repository2.unified.webservices.DefaultUnifiedRepositoryWebService;
import org.pentaho.platform.repository2.unified.webservices.RepositoryFileTreeDto;
/**
* Created by tkafalas 7/14/14.
*/
public class UnifiedRepositoryPurgeServiceTest {
private static final String[][] versionData = new String[][] {
{ "100", "1", "01/01/2000", "Bugs Bunny", "original", "1.0" },
{ "101", "1", "01/01/2002", "Bugs Bunny", "1st change", "1.1" },
{ "102", "1", "01/01/2004", "Micky Mouse", "2nd change", "1.2" },
{ "103", "1", "01/01/2006", "Micky Mouse", "3rd change", "1.3" },
{ "104", "1", "01/01/2008", "Micky Mouse", "4th change", "1.4" },
{ "105", "1", "01/01/2010", "Donald Duck", "5th change", "1.5" },
{ "200", "2", "01/01/2001", "Donald Duck", "original", "1.0" },
{ "201", "2", "01/01/2003", "Fred Flintstone", "1st change", "1.1" },
{ "202", "2", "01/01/2005", "Fred Flintstone", "2nd change", "1.2" },
{ "203", "2", "01/01/2013", "Barny Rubble", "3rd change", "1.3" }, };
private static final DateFormat DATE_FORMAT = new SimpleDateFormat( "MM/dd/yyyy" );
private static final String treeResponse =
"<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>"
+ "<repositoryFileTreeDto><children><children><file><folder>false</folder><hidden>false</hidden><id>1</id><locked>false</locked><name>file1.ktr</name><ownerType>-1</ownerType><path>/home/joe/file1.ktr</path><versionId>1.5</versionId><versioned>true</versioned></file></children>"
+ "<children><children><file><folder>false</folder><hidden>false</hidden><id>2</id><locked>false</locked><name>file2.ktr</name><ownerType>-1</ownerType><path>/home/joe/newdirTest/file2.ktr</path><versionId>1.3</versionId><versioned>true</versioned></file></children>"
+ "</children><file><folder>true</folder><hidden>false</hidden><id>homejoessn</id><locked>false</locked><name>joe</name><ownerType>-1</ownerType><path>/home/joe</path><versioned>false</versioned></file></children>"
+ "<file><folder>true</folder><hidden>false</hidden><id>homessn</id><locked>false</locked><name>home</name><ownerType>-1</ownerType><path>/home</path><versioned>false</versioned></file>"
+ "</repositoryFileTreeDto>";
private static RepositoryElementInterface element1;
static {
// Setup a mocked RepositoryElementInterface so alternate methods can be called for maximum code coverage
element1 = mock( RepositoryElementInterface.class );
ObjectId mockObjectId1 = mock( ObjectId.class );
when( mockObjectId1.getId() ).thenReturn( "1" );
when( element1.getObjectId() ).thenReturn( mockObjectId1 );
}
private HashMap<String, List<VersionSummary>> processVersionMap( IUnifiedRepository mockRepo ) {
// Build versionListMap
final HashMap<String, List<VersionSummary>> versionListMap = new HashMap<String, List<VersionSummary>>();
List<VersionSummary> mockVersionList = new ArrayList<VersionSummary>();
Date d = null;
String fileId = null;
for ( String[] values : versionData ) {
d = getDate( values[2] );
if ( !values[1].equals( fileId ) ) {
if ( fileId != null ) {
versionListMap.put( fileId, mockVersionList );
}
mockVersionList = new ArrayList<VersionSummary>();
}
fileId = values[1];
VersionSummary versionSummary =
new VersionSummary( values[0], fileId, false, d, values[3], values[4], Arrays
.asList( new String[] { values[5] } ) );
mockVersionList.add( versionSummary );
}
versionListMap.put( fileId, mockVersionList );
final ArgumentCaptor<String> fileIdArgument = ArgumentCaptor.forClass( String.class );
when( mockRepo.getVersionSummaries( fileIdArgument.capture() ) ).thenAnswer( new Answer<List<VersionSummary>>() {
public List<VersionSummary> answer( InvocationOnMock invocation ) throws Throwable {
return versionListMap.get( fileIdArgument.getValue() );
}
} );
return versionListMap;
}
@Test
public void deleteAllVersionsTest() throws KettleException {
IUnifiedRepository mockRepo = mock( IUnifiedRepository.class );
final HashMap<String, List<VersionSummary>> versionListMap = processVersionMap( mockRepo );
UnifiedRepositoryPurgeService purgeService = new UnifiedRepositoryPurgeService( mockRepo );
String fileId = "1";
purgeService.deleteAllVersions( element1 );
verifyAllVersionsDeleted( versionListMap, mockRepo, "1" );
verify( mockRepo, never() ).deleteFileAtVersion( eq( "2" ), anyString() );
}
@Test
public void deleteVersionTest() throws KettleException {
IUnifiedRepository mockRepo = mock( IUnifiedRepository.class );
final HashMap<String, List<VersionSummary>> versionListMap = processVersionMap( mockRepo );
UnifiedRepositoryPurgeService purgeService = new UnifiedRepositoryPurgeService( mockRepo );
String fileId = "1";
String versionId = "103";
purgeService.deleteVersion( element1, versionId );
verify( mockRepo, times( 1 ) ).deleteFileAtVersion( fileId, versionId );
verify( mockRepo, never() ).deleteFileAtVersion( eq( "2" ), anyString() );
}
@Test
public void keepNumberOfVersions0Test() throws KettleException {
IUnifiedRepository mockRepo = mock( IUnifiedRepository.class );
final HashMap<String, List<VersionSummary>> versionListMap = processVersionMap( mockRepo );
UnifiedRepositoryPurgeService purgeService = new UnifiedRepositoryPurgeService( mockRepo );
String fileId = "1";
int versionCount = 0;
purgeService.keepNumberOfVersions( element1, versionCount );
verifyVersionCountDeletion( versionListMap, mockRepo, fileId, versionCount );
verify( mockRepo, never() ).deleteFileAtVersion( eq( "2" ), anyString() );
}
@Test
public void keepNumberOfVersionsTest() throws KettleException {
IUnifiedRepository mockRepo = mock( IUnifiedRepository.class );
final HashMap<String, List<VersionSummary>> versionListMap = processVersionMap( mockRepo );
UnifiedRepositoryPurgeService purgeService = new UnifiedRepositoryPurgeService( mockRepo );
String fileId = "1";
int versionCount = 2;
purgeService.keepNumberOfVersions( element1, versionCount );
verifyVersionCountDeletion( versionListMap, mockRepo, fileId, versionCount );
verify( mockRepo, never() ).deleteFileAtVersion( eq( "2" ), anyString() );
}
@Test
public void deleteVersionsBeforeDate() throws KettleException {
IUnifiedRepository mockRepo = mock( IUnifiedRepository.class );
final HashMap<String, List<VersionSummary>> versionListMap = processVersionMap( mockRepo );
UnifiedRepositoryPurgeService purgeService = new UnifiedRepositoryPurgeService( mockRepo );
String fileId = "1";
Date beforeDate = getDate( "01/02/2006" );
purgeService.deleteVersionsBeforeDate( element1, beforeDate );
verifyDateBeforeDeletion( versionListMap, mockRepo, fileId, beforeDate );
verify( mockRepo, never() ).deleteFileAtVersion( eq( "2" ), anyString() );
}
@Test
public void doPurgeUtilPurgeFileTest() throws PurgeDeletionException {
IUnifiedRepository mockRepo = mock( IUnifiedRepository.class );
final HashMap<String, List<VersionSummary>> versionListMap = processVersionMap( mockRepo );
UnifiedRepositoryPurgeService purgeService = getPurgeService( mockRepo );
PurgeUtilitySpecification spec = new PurgeUtilitySpecification();
spec.purgeFiles = true;
spec.setPath( "/" );
purgeService.doDeleteRevisions( spec );
verifyAllVersionsDeleted( versionListMap, mockRepo, "1" );
verifyAllVersionsDeleted( versionListMap, mockRepo, "2" );
verify( UnifiedRepositoryPurgeService.getRepoWs(), times( 1 ) ).deleteFileWithPermanentFlag( eq( "1" ), eq( true ),
anyString() );
verify( UnifiedRepositoryPurgeService.getRepoWs(), times( 1 ) ).deleteFileWithPermanentFlag( eq( "2" ), eq( true ),
anyString() );
}
@Test
public void doPurgeUtilVersionCountTest() throws PurgeDeletionException {
IUnifiedRepository mockRepo = mock( IUnifiedRepository.class );
final HashMap<String, List<VersionSummary>> versionListMap = processVersionMap( mockRepo );
UnifiedRepositoryPurgeService purgeService = getPurgeService( mockRepo );
PurgeUtilitySpecification spec = new PurgeUtilitySpecification();
spec.setVersionCount( 3 );
spec.setPath( "/" );
purgeService.doDeleteRevisions( spec );
verifyVersionCountDeletion( versionListMap, mockRepo, "1", spec.getVersionCount() );
verifyVersionCountDeletion( versionListMap, mockRepo, "2", spec.getVersionCount() );
}
@Test
public void doPurgeUtilDateBeforeTest() throws PurgeDeletionException {
IUnifiedRepository mockRepo = mock( IUnifiedRepository.class );
final HashMap<String, List<VersionSummary>> versionListMap = processVersionMap( mockRepo );
UnifiedRepositoryPurgeService purgeService = getPurgeService( mockRepo );
PurgeUtilitySpecification spec = new PurgeUtilitySpecification();
spec.setBeforeDate( getDate( "01/02/2006" ) );
spec.setPath( "/" );
purgeService.doDeleteRevisions( spec );
verifyDateBeforeDeletion( versionListMap, mockRepo, "1", spec.getBeforeDate() );
verifyDateBeforeDeletion( versionListMap, mockRepo, "2", spec.getBeforeDate() );
}
@Test
public void doPurgeUtilSharedObjectsTest() throws PurgeDeletionException {
IUnifiedRepository mockRepo = mock( IUnifiedRepository.class );
final HashMap<String, List<VersionSummary>> versionListMap = processVersionMap( mockRepo );
UnifiedRepositoryPurgeService purgeService = getPurgeService( mockRepo );
PurgeUtilitySpecification spec = new PurgeUtilitySpecification();
spec.purgeFiles = true;
spec.setSharedObjects( true );
purgeService.doDeleteRevisions( spec );
// Since each tree call delivers the same mock tree, we expect the files to get deleted once per folder.
String fileId = "1";
String fileLastRevision = "105";
List<VersionSummary> list = versionListMap.get( fileId );
for ( VersionSummary sum : list ) {
final int expectedTimes;
if ( !fileLastRevision.equals( sum.getId() ) ) {
expectedTimes = 4;
} else {
expectedTimes = 0;
}
verify( mockRepo, times( expectedTimes ) ).deleteFileAtVersion( fileId, sum.getId() );
verify( UnifiedRepositoryPurgeService.getRepoWs(), times( 4 ) ).deleteFileWithPermanentFlag( eq( fileId ), eq( true ), anyString() );
}
}
// create the necessary mocks for running a full Purge Utility job
private static UnifiedRepositoryPurgeService getPurgeService( IUnifiedRepository mockRepo ) {
UnifiedRepositoryPurgeService purgeService = new UnifiedRepositoryPurgeService( mockRepo );
DefaultUnifiedRepositoryWebService mockRepoWs = mock( DefaultUnifiedRepositoryWebService.class );
UnifiedRepositoryPurgeService.repoWs = mockRepoWs;
// Create a mocked tree to be returned
JAXBContext jc;
RepositoryFileTreeDto tree = null;
try {
jc = JAXBContext.newInstance( RepositoryFileTreeDto.class );
Unmarshaller unmarshaller = jc.createUnmarshaller();
ByteArrayInputStream xml = new ByteArrayInputStream( treeResponse.getBytes() );
tree = (RepositoryFileTreeDto) unmarshaller.unmarshal( xml );
} catch ( JAXBException e ) {
e.printStackTrace();
fail( "Test class has invalid xml representation of tree" );
}
when( mockRepoWs.getTreeFromRequest( any( RepositoryRequest.class ) ) ).thenReturn( tree );
return purgeService;
}
private static void verifyAllVersionsDeleted( HashMap<String, List<VersionSummary>> versionListMap,
IUnifiedRepository mockRepo, String fileId ) {
List<VersionSummary> list = versionListMap.get( fileId );
int i = 1;
for ( VersionSummary sum : list ) {
if ( i < list.size() ) {
verify( mockRepo, times( 1 ) ).deleteFileAtVersion( fileId, sum.getId() );
}
i++;
}
}
private static void verifyVersionCountDeletion( HashMap<String, List<VersionSummary>> versionListMap,
IUnifiedRepository mockRepo, String fileId, int versionCount ) {
List<VersionSummary> list = versionListMap.get( fileId );
int i = 1;
for ( VersionSummary sum : list ) {
if ( i <= list.size() - versionCount ) {
verify( mockRepo, times( 1 ) ).deleteFileAtVersion( fileId, sum.getId() );
}
i++;
}
}
private static void verifyDateBeforeDeletion( HashMap<String, List<VersionSummary>> versionListMap,
IUnifiedRepository mockRepo, String fileId, Date beforeDate ) {
int i = 0;
List<VersionSummary> list = versionListMap.get( fileId );
for ( VersionSummary sum : list ) {
if ( beforeDate.after( sum.getDate() ) && !sum.getId().equals( list.get( list.size() - 1 ).getId() ) ) {
verify( mockRepo, times( 1 ) ).deleteFileAtVersion( fileId, sum.getId() );
} else {
verify( mockRepo, never() ).deleteFileAtVersion( fileId, sum.getId() );
}
}
}
private static Date getDate( String dateString ) {
Date date = null;
try {
date = DATE_FORMAT.parse( dateString );
} catch ( ParseException e ) {
fail( "Bad Date format in test class" );
}
return date;
}
}