/* * #%L * Alfresco Records Management Module * %% * Copyright (C) 2005 - 2016 Alfresco Software Limited * %% * This file is part of the Alfresco software. * - * If the software was purchased under a paid Alfresco license, the terms of * the paid license agreement will prevail. Otherwise, the software is * provided under the following open source license terms: * - * Alfresco is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * - * Alfresco is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * - * You should have received a copy of the GNU Lesser General Public License * along with Alfresco. If not, see <http://www.gnu.org/licenses/>. * #L% */ package org.alfresco.module.org_alfresco_module_rm.job; import static org.alfresco.module.org_alfresco_module_rm.test.util.AlfMock.generateQName; import static org.junit.Assert.assertEquals; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyMap; import static org.mockito.Matchers.anyString; import static org.mockito.Matchers.contains; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.verifyZeroInteractions; import java.util.Collections; import java.util.List; import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; import org.alfresco.module.org_alfresco_module_rm.test.util.BaseUnitTest; import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; import org.alfresco.service.cmr.repository.ChildAssociationRef; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.StoreRef; import org.alfresco.service.cmr.search.ResultSet; import org.alfresco.service.cmr.search.SearchService; import org.junit.Before; import org.junit.Test; import org.mockito.InjectMocks; import org.mockito.Mock; /** * Disposition lifecycle job execution unit test. * * @author Roy Wetherall * @since 2.2 */ public class DispositionLifecycleJobExecuterUnitTest extends BaseUnitTest { /** disposition actions */ private static final String CUTOFF = "cutoff"; private static final String RETAIN = "retain"; private static final String DESTROY = "destroy"; /** test query snipit */ private static final String QUERY = "\"" + CUTOFF + "\" OR \"" + RETAIN + "\""; /** mocked result set */ @Mock ResultSet mockedResultSet; /** disposition lifecycle job executer */ @InjectMocks DispositionLifecycleJobExecuter executer; /** * @see org.alfresco.module.org_alfresco_module_rm.test.util.BaseUnitTest#before() */ @Override @Before public void before() throws Exception { super.before(); // setup data List<String> dispositionActions = buildList(CUTOFF, RETAIN); executer.setDispositionActions(dispositionActions); // setup interactions doReturn(mockedResultSet).when(mockedSearchService).query(eq(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE), eq(SearchService.LANGUAGE_FTS_ALFRESCO), anyString()); } /** * Helper method to verify that the query has been executed and closed */ private void verifyQuery() { verify(mockedSearchService, times(1)).query(eq(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE), eq(SearchService.LANGUAGE_FTS_ALFRESCO), contains(QUERY)); verify(mockedResultSet, times(1)).getNodeRefs(); verify(mockedResultSet, times(1)).close(); } /** * When the are no results in query. */ @Test public void noResultsInQuery() { // given doReturn(Collections.EMPTY_LIST).when(mockedResultSet).getNodeRefs(); // when executer.executeImpl(); // then // ensure the query is executed and closed verifyQuery(); // ensure nothing else happens becuase we have no results verifyZeroInteractions(mockedNodeService, mockedRecordFolderService, mockedRetryingTransactionHelper); } /** * When the disposition actions do not match those that can be processed automatically. */ @SuppressWarnings("unchecked") @Test public void dispositionActionDoesNotMatch() { // test data NodeRef node1 = generateNodeRef(); NodeRef node2 = generateNodeRef(); List<NodeRef> nodeRefs = buildList(node1, node2); // given doReturn(nodeRefs).when(mockedResultSet).getNodeRefs(); doReturn(DESTROY).when(mockedNodeService).getProperty(node1, RecordsManagementModel.PROP_DISPOSITION_ACTION); doReturn(DESTROY).when(mockedNodeService).getProperty(node2, RecordsManagementModel.PROP_DISPOSITION_ACTION); // when executer.executeImpl(); // then // ensure the query is executed and closed verifyQuery(); // ensure work is executed in transaction for each node processed verify(mockedNodeService, times(2)).exists(any(NodeRef.class)); verify(mockedRetryingTransactionHelper, times(2)).<Object>doInTransaction(any(RetryingTransactionCallback.class)); // ensure each node is process correctly verify(mockedNodeService, times(1)).getProperty(node1, RecordsManagementModel.PROP_DISPOSITION_ACTION); verify(mockedNodeService, times(1)).getProperty(node2, RecordsManagementModel.PROP_DISPOSITION_ACTION); // ensure no more interactions verifyNoMoreInteractions(mockedNodeService); verifyZeroInteractions(mockedRecordsManagementActionService); } /** * When a node does not exist */ @Test public void nodeDoesNotExist() { // test data NodeRef node1 = generateNodeRef(null, false); List<NodeRef> nodeRefs = buildList(node1); // given doReturn(nodeRefs).when(mockedResultSet).getNodeRefs(); // when executer.executeImpl(); // then // ensure the query is executed and closed verifyQuery(); // ensure the node exist check is made for the node verify(mockedNodeService, times(1)).exists(any(NodeRef.class)); // ensure no more interactions verifyNoMoreInteractions(mockedNodeService); verifyZeroInteractions(mockedRecordsManagementActionService, mockedRetryingTransactionHelper); } /** * When there are disposition actions eligible for processing */ @SuppressWarnings("unchecked") @Test public void dispositionActionProcessed() { // test data NodeRef node1 = generateNodeRef(); NodeRef node2 = generateNodeRef(); List<NodeRef> nodeRefs = buildList(node1, node2); NodeRef parent = generateNodeRef(); ChildAssociationRef parentAssoc = new ChildAssociationRef(ASSOC_NEXT_DISPOSITION_ACTION, parent, generateQName(), generateNodeRef()); // given doReturn(nodeRefs).when(mockedResultSet).getNodeRefs(); doReturn(CUTOFF).when(mockedNodeService).getProperty(node1, RecordsManagementModel.PROP_DISPOSITION_ACTION); doReturn(RETAIN).when(mockedNodeService).getProperty(node2, RecordsManagementModel.PROP_DISPOSITION_ACTION); doReturn(parentAssoc).when(mockedNodeService).getPrimaryParent(any(NodeRef.class)); // when executer.executeImpl(); // then // ensure the query is executed and closed verifyQuery(); // ensure work is executed in transaction for each node processed verify(mockedNodeService, times(2)).exists(any(NodeRef.class)); verify(mockedRetryingTransactionHelper, times(2)).<Object>doInTransaction(any(RetryingTransactionCallback.class)); // ensure each node is process correctly // node1 verify(mockedNodeService, times(1)).getProperty(node1, RecordsManagementModel.PROP_DISPOSITION_ACTION); verify(mockedNodeService, times(1)).getPrimaryParent(node1); verify(mockedRecordsManagementActionService, times(1)).executeRecordsManagementAction(eq(parent), eq(CUTOFF), anyMap()); // node2 verify(mockedNodeService, times(1)).getProperty(node2, RecordsManagementModel.PROP_DISPOSITION_ACTION); verify(mockedNodeService, times(1)).getPrimaryParent(node2); verify(mockedRecordsManagementActionService, times(1)).executeRecordsManagementAction(eq(parent), eq(RETAIN), anyMap()); // ensure no more interactions verifyNoMoreInteractions(mockedNodeService, mockedRecordsManagementActionService); } /** * Brittle unit test that simply checks the generated query is an exact string when the supplied disposition actions * are "CUTOFF" and "RETAIN" (see {@link #before}). */ @Test public void testGetQuery() { String actual = executer.getQuery(); String expected = "TYPE:\"rma:dispositionAction\" + (@rma\\:dispositionAction:(\"cutoff\" OR \"retain\")) AND ISUNSET:\"rma:dispositionActionCompletedAt\" AND ( @rma\\:dispositionEventsEligible:true OR @rma\\:dispositionAsOf:[MIN TO NOW] ) "; assertEquals(expected, actual); } }