/* * #%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.hold; import static org.alfresco.module.org_alfresco_module_rm.test.util.AlfMock.generateQName; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyString; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doReturn; 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.Serializable; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.model.ContentModel; import org.alfresco.module.org_alfresco_module_rm.test.util.BaseUnitTest; import org.alfresco.service.cmr.repository.ChildAssociationRef; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.namespace.NamespaceService; import org.alfresco.service.namespace.QName; import org.alfresco.service.namespace.RegexQNamePattern; import org.junit.Before; import org.junit.Test; import org.mockito.ArgumentCaptor; import org.mockito.InjectMocks; import org.mockito.Spy; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; /** * Hold service implementation unit test * * @author Roy Wetherall * @since 2.2 */ public class HoldServiceImplUnitTest extends BaseUnitTest { /** test values */ private static final String HOLD_NAME = "holdname"; private static final String HOLD_REASON = "holdreason"; private static final String HOLD_DESCRIPTION = "holddescription"; protected NodeRef holdContainer; protected NodeRef hold; protected NodeRef hold2; @Spy @InjectMocks HoldServiceImpl holdService; @Before @Override public void before() throws Exception { super.before(); // setup objects used in mock interactions holdContainer = generateNodeRef(TYPE_HOLD_CONTAINER); hold = generateNodeRef(TYPE_HOLD); hold2 = generateNodeRef(TYPE_HOLD); // setup interactions doReturn(holdContainer).when(mockedFilePlanService).getHoldContainer(filePlan); } @Test public void isHold() { assertTrue(holdService.isHold(hold)); assertFalse(holdService.isHold(recordFolder)); } @Test public void heldByMultipleResults() { // setup record folder in multiple holds List<ChildAssociationRef> holds = new ArrayList<ChildAssociationRef>(2); holds.add(new ChildAssociationRef(ASSOC_FROZEN_RECORDS, hold, ASSOC_FROZEN_RECORDS, recordFolder, true, 1)); holds.add(new ChildAssociationRef(ASSOC_FROZEN_RECORDS, hold2, ASSOC_FROZEN_RECORDS, recordFolder, true, 2)); doReturn(holds).when(mockedNodeService).getParentAssocs(recordFolder, ASSOC_FROZEN_RECORDS, ASSOC_FROZEN_RECORDS); // check that both holds are found for record folder List<NodeRef> heldByHolds = holdService.heldBy(recordFolder, true); assertNotNull(heldByHolds); assertEquals(2, heldByHolds.size()); assertTrue(holdService.heldBy(recordFolder, false).isEmpty()); // check that both holds are found for record (even thou they are one level deep) heldByHolds = holdService.heldBy(record, true); assertNotNull(heldByHolds); assertEquals(2, heldByHolds.size()); assertTrue(holdService.heldBy(record, false).isEmpty()); } @Test (expected=AlfrescoRuntimeException.class) public void getHold() { // setup node service interactions when(mockedNodeService.getChildByName(eq(holdContainer), eq(ContentModel.ASSOC_CONTAINS), anyString())).thenReturn(null) .thenReturn(hold) .thenReturn(recordFolder); // no hold NodeRef noHold = holdService.getHold(filePlan, "notAHold"); assertNull(noHold); // found hold NodeRef someHold = holdService.getHold(filePlan, "someHold"); assertNotNull(someHold); assertEquals(TYPE_HOLD, mockedNodeService.getType(someHold)); // ensure runtime exception is thrown holdService.getHold(filePlan, "notHold"); } @Test (expected=RuntimeException.class) public void getHeldNotAHold() { holdService.getHeld(recordFolder); } @Test public void getHeldNoResults() { assertTrue(holdService.getHeld(hold).isEmpty()); } @Test public void getHeldWithResults() { // setup record folder in hold List<ChildAssociationRef> holds = new ArrayList<ChildAssociationRef>(1); holds.add(new ChildAssociationRef(ASSOC_FROZEN_RECORDS, hold, ASSOC_FROZEN_RECORDS, recordFolder, true, 1)); doReturn(holds).when(mockedNodeService).getChildAssocs(hold, ASSOC_FROZEN_RECORDS, RegexQNamePattern.MATCH_ALL); List<NodeRef> list = holdService.getHeld(hold); assertEquals(1, list.size()); assertEquals(recordFolder, list.get(0)); } @SuppressWarnings({ "unchecked", "rawtypes" }) @Test public void createHold() { // setup node service interactions when(mockedNodeService.createNode(eq(holdContainer), eq(ContentModel.ASSOC_CONTAINS), any(QName.class) , eq(TYPE_HOLD), any(Map.class))) .thenReturn(new ChildAssociationRef(ContentModel.ASSOC_CONTAINS, holdContainer, generateQName(), hold)); // create hold NodeRef newHold = holdService.createHold(filePlan, HOLD_NAME, HOLD_REASON, HOLD_DESCRIPTION); assertNotNull(newHold); assertEquals(TYPE_HOLD, mockedNodeService.getType(newHold)); assertEquals(hold, newHold); // check the node service interactions ArgumentCaptor<Map> propertyMapCaptor = ArgumentCaptor.forClass(Map.class); ArgumentCaptor<QName> assocNameCaptor = ArgumentCaptor.forClass(QName.class); verify(mockedNodeService).createNode(eq(holdContainer), eq(ContentModel.ASSOC_CONTAINS), assocNameCaptor.capture() , eq(TYPE_HOLD), propertyMapCaptor.capture()); // check property map Map<QName, Serializable> propertyMap = (Map<QName, Serializable>)propertyMapCaptor.getValue(); assertNotNull(propertyMap); assertEquals(3, propertyMap.size()); assertTrue(propertyMap.containsKey(ContentModel.PROP_NAME)); assertEquals(HOLD_NAME, propertyMap.get(ContentModel.PROP_NAME)); assertTrue(propertyMap.containsKey(ContentModel.PROP_DESCRIPTION)); assertEquals(HOLD_DESCRIPTION, propertyMap.get(ContentModel.PROP_DESCRIPTION)); assertTrue(propertyMap.containsKey(PROP_HOLD_REASON)); assertEquals(HOLD_REASON, propertyMap.get(PROP_HOLD_REASON)); // check assoc name assertNotNull(assocNameCaptor.getValue()); assertEquals(NamespaceService.CONTENT_MODEL_1_0_URI, assocNameCaptor.getValue().getNamespaceURI()); assertEquals(HOLD_NAME, assocNameCaptor.getValue().getLocalName()); } @Test public void getHoldReason() { // setup node service interactions when(mockedNodeService.exists(hold)) .thenReturn(false) .thenReturn(true) .thenReturn(true) .thenReturn(true); when(mockedNodeService.getProperty(eq(hold), eq(PROP_HOLD_REASON))) .thenReturn(null) .thenReturn(HOLD_REASON); // node does not exist assertNull(holdService.getHoldReason(hold)); // node isn't a hold assertNull(holdService.getHoldReason(recordFolder)); // hold reason isn't set assertNull(holdService.getHoldReason(hold)); // hold reason set assertEquals(HOLD_REASON, holdService.getHoldReason(hold)); } @Test public void setHoldReason() { // setup node service interactions when(mockedNodeService.exists(hold)) .thenReturn(false) .thenReturn(true) .thenReturn(true); // node does not exist holdService.setHoldReason(hold, HOLD_REASON); verify(mockedNodeService, never()).setProperty(hold, PROP_HOLD_REASON, HOLD_REASON); // node isn't a hold holdService.setHoldReason(recordFolder, HOLD_REASON); verify(mockedNodeService, never()).setProperty(hold, PROP_HOLD_REASON, HOLD_REASON); // set hold reason holdService.setHoldReason(hold, HOLD_REASON); verify(mockedNodeService).setProperty(hold, PROP_HOLD_REASON, HOLD_REASON); } @Test (expected=AlfrescoRuntimeException.class) public void deleteHoldNotAHold() { holdService.deleteHold(recordFolder); verify(mockedNodeService, never()).deleteNode(hold); } @Test public void deleteHold() { // delete hold holdService.deleteHold(hold); verify(mockedNodeService).deleteNode(hold); // TODO check interactions with policy component!!! } @Test (expected=AlfrescoRuntimeException.class) public void addToHoldNotAHold() { holdService.addToHold(recordFolder, recordFolder); } @Test (expected=AlfrescoRuntimeException.class) public void addToHoldNotARecordFolderOrRecord() { NodeRef anotherThing = generateNodeRef(TYPE_RECORD_CATEGORY); holdService.addToHold(hold, anotherThing); } @SuppressWarnings("unchecked") @Test public void addToHoldNotInHold() { holdService.addToHold(hold, recordFolder); verify(mockedNodeService).addChild(hold, recordFolder, ASSOC_FROZEN_RECORDS, ASSOC_FROZEN_RECORDS); verify(mockedNodeService).addAspect(eq(recordFolder), eq(ASPECT_FROZEN), any(Map.class)); verify(mockedNodeService).addAspect(eq(record), eq(ASPECT_FROZEN), any(Map.class)); verify(mockedRecordsManagementAuditService, times(1)).auditEvent(eq(recordFolder), anyString()); holdService.addToHold(hold, record); verify(mockedNodeService).addChild(hold, record, ASSOC_FROZEN_RECORDS, ASSOC_FROZEN_RECORDS); verify(mockedNodeService).addAspect(eq(recordFolder), eq(ASPECT_FROZEN), any(Map.class)); verify(mockedNodeService, times(2)).addAspect(eq(record), eq(ASPECT_FROZEN), any(Map.class)); verify(mockedRecordsManagementAuditService, times(1)).auditEvent(eq(record), anyString()); } @SuppressWarnings("unchecked") @Test public void addToHoldAlreadyInHold() { doReturn(Collections.singletonList(recordFolder)).when(holdService).getHeld(hold); holdService.addToHold(hold, recordFolder); verify(mockedNodeService, never()).addChild(hold, recordFolder, ASSOC_FROZEN_RECORDS, ASSOC_FROZEN_RECORDS); verify(mockedNodeService, never()).addAspect(eq(recordFolder), eq(ASPECT_FROZEN), any(Map.class)); verify(mockedNodeService, never()).addAspect(eq(record), eq(ASPECT_FROZEN), any(Map.class)); verify(mockedRecordsManagementAuditService, never()).auditEvent(eq(recordFolder), anyString()); } @SuppressWarnings("unchecked") @Test public void addToHoldAldeadyFrozen() { doReturn(true).when(mockedNodeService).hasAspect(recordFolder, ASPECT_FROZEN); doReturn(true).when(mockedNodeService).hasAspect(record, ASPECT_FROZEN); holdService.addToHold(hold, recordFolder); verify(mockedNodeService, times(1)).addChild(hold, recordFolder, ASSOC_FROZEN_RECORDS, ASSOC_FROZEN_RECORDS); verify(mockedNodeService, never()).addAspect(eq(recordFolder), eq(ASPECT_FROZEN), any(Map.class)); verify(mockedNodeService, never()).addAspect(eq(record), eq(ASPECT_FROZEN), any(Map.class)); verify(mockedRecordsManagementAuditService, times(1)).auditEvent(eq(recordFolder), anyString()); } @SuppressWarnings("unchecked") @Test public void addToHolds() { // ensure the interaction indicates that a node has the frozen aspect applied if it has doAnswer(new Answer<Void>() { public Void answer(InvocationOnMock invocation) { NodeRef nodeRef = (NodeRef)invocation.getArguments()[0]; doReturn(true).when(mockedNodeService).hasAspect(nodeRef, ASPECT_FROZEN); return null; } }).when(mockedNodeService).addAspect(any(NodeRef.class), eq(ASPECT_FROZEN), any(Map.class)); // build a list of holds List<NodeRef> holds = new ArrayList<NodeRef>(2); holds.add(hold); holds.add(hold2); // add the record folder to both holds holdService.addToHolds(holds, recordFolder); // verify the interactions verify(mockedNodeService, times(1)).addChild(hold, recordFolder, ASSOC_FROZEN_RECORDS, ASSOC_FROZEN_RECORDS); verify(mockedNodeService, times(1)).addChild(hold2, recordFolder, ASSOC_FROZEN_RECORDS, ASSOC_FROZEN_RECORDS); verify(mockedNodeService, times(1)).addAspect(eq(recordFolder), eq(ASPECT_FROZEN), any(Map.class)); verify(mockedNodeService, times(1)).addAspect(eq(record), eq(ASPECT_FROZEN), any(Map.class)); verify(mockedRecordsManagementAuditService, times(2)).auditEvent(eq(recordFolder), anyString()); } @Test (expected=AlfrescoRuntimeException.class) public void removeFromHoldNotAHold() { holdService.removeFromHold(recordFolder, recordFolder); } @Test public void removeFromHoldNotInHold() { holdService.removeFromHold(hold, recordFolder); verify(mockedNodeService, never()).removeChild(hold, recordFolder); verify(mockedNodeService, never()).removeAspect(recordFolder, ASPECT_FROZEN); verify(mockedNodeService, never()).removeAspect(record, ASPECT_FROZEN); verify(mockedRecordsManagementAuditService, never()).auditEvent(eq(recordFolder), anyString()); } @Test public void removeFromHold() { doReturn(Collections.singletonList(recordFolder)).when(holdService).getHeld(hold); doReturn(true).when(mockedNodeService).hasAspect(recordFolder, ASPECT_FROZEN); doReturn(true).when(mockedNodeService).hasAspect(record, ASPECT_FROZEN); holdService.removeFromHold(hold, recordFolder); verify(mockedNodeService, times(1)).removeChild(hold, recordFolder); verify(mockedNodeService, times(1)).removeAspect(recordFolder, ASPECT_FROZEN); verify(mockedNodeService, times(1)).removeAspect(record, ASPECT_FROZEN); verify(mockedRecordsManagementAuditService, times(1)).auditEvent(eq(recordFolder), anyString()); } @Test public void removeFromHolds() { doReturn(Collections.singletonList(recordFolder)).when(holdService).getHeld(hold); doReturn(Collections.singletonList(recordFolder)).when(holdService).getHeld(hold2); doReturn(true).when(mockedNodeService).hasAspect(recordFolder, ASPECT_FROZEN); doReturn(true).when(mockedNodeService).hasAspect(record, ASPECT_FROZEN); // build a list of holds List<NodeRef> holds = new ArrayList<NodeRef>(2); holds.add(hold); holds.add(hold2); holdService.removeFromHolds(holds, recordFolder); verify(mockedNodeService, times(1)).removeChild(hold, recordFolder); verify(mockedNodeService, times(1)).removeChild(hold2, recordFolder); verify(mockedNodeService, times(1)).removeAspect(recordFolder, ASPECT_FROZEN); verify(mockedNodeService, times(1)).removeAspect(record, ASPECT_FROZEN); verify(mockedRecordsManagementAuditService, times(2)).auditEvent(any(NodeRef.class), anyString()); } @Test public void removeFromAllHolds() { // build a list of holds List<NodeRef> holds = new ArrayList<NodeRef>(2); holds.add(hold); holds.add(hold2); doAnswer(new Answer<Void>() { public Void answer(InvocationOnMock invocation) { doReturn(Collections.singletonList(hold2)).when(holdService).heldBy(recordFolder, true); return null; } }).when(mockedNodeService).removeChild(hold, recordFolder); doAnswer(new Answer<Void>() { public Void answer(InvocationOnMock invocation) { doReturn(new ArrayList<NodeRef>()).when(holdService).heldBy(recordFolder, true); return null; } }).when(mockedNodeService).removeChild(hold2, recordFolder); // define interactions doReturn(holds).when(holdService).heldBy(recordFolder, true); doReturn(Collections.singletonList(recordFolder)).when(holdService).getHeld(hold); doReturn(Collections.singletonList(recordFolder)).when(holdService).getHeld(hold2); doReturn(true).when(mockedNodeService).hasAspect(recordFolder, ASPECT_FROZEN); doReturn(true).when(mockedNodeService).hasAspect(record, ASPECT_FROZEN); // remove record folder from all holds holdService.removeFromAllHolds(recordFolder); // verify interactions verify(mockedNodeService, times(1)).removeChild(hold, recordFolder); verify(mockedNodeService, times(1)).removeChild(hold2, recordFolder); verify(mockedNodeService, times(1)).removeAspect(recordFolder, ASPECT_FROZEN); verify(mockedNodeService, times(1)).removeAspect(record, ASPECT_FROZEN); } }