/* * #%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.record; import static org.alfresco.module.org_alfresco_module_rm.test.util.AlfMock.generateQName; import static org.alfresco.module.org_alfresco_module_rm.test.util.AlfMock.generateText; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.mockito.Matchers.any; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doReturn; 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.util.HashSet; import java.util.Map; import java.util.Set; import org.alfresco.model.ContentModel; import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionSchedule; import org.alfresco.module.org_alfresco_module_rm.test.util.BaseUnitTest; import org.alfresco.repo.policy.Behaviour; 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.apache.commons.collections.CollectionUtils; import org.junit.Before; import org.junit.Test; import org.mockito.InjectMocks; import org.mockito.Spy; /** * Unit test for RecordServiceImpl * * @author Roy Wetherall * @since 2.2 */ public class RecordServiceImplUnitTest extends BaseUnitTest { private NodeRef nonStandardFilePlanComponent; private NodeRef nonStandardFilePlan; private static QName TYPE_MY_FILE_PLAN = generateQName(); private static QName ASPECT_FOR_FILE_PLAN = generateQName(); @Spy @InjectMocks private RecordServiceImpl recordService; @SuppressWarnings("unchecked") @Before @Override public void before() throws Exception { super.before(); nonStandardFilePlanComponent = generateNodeRef(TYPE_RECORD_CATEGORY); nonStandardFilePlan = generateNodeRef(TYPE_MY_FILE_PLAN); // set-up node service when(mockedNodeService.getProperty(nonStandardFilePlanComponent, PROP_ROOT_NODEREF)).thenReturn(nonStandardFilePlan); // set-up dictionary service when(mockedDictionaryService.getAllAspects()).thenReturn(CollectionUtils.EMPTY_COLLECTION); // mock up getting behaviours when(recordService.getBehaviour(any(String.class))).thenReturn(mock(Behaviour.class)); } @Test public void testRegisterRecordMetadataAspect() { Map<QName, Set<QName>> map = recordService.getRecordMetadataAspectsMap(); assertTrue(map.isEmpty()); recordService.registerRecordMetadataAspect(ASPECT_FOR_FILE_PLAN, TYPE_FILE_PLAN); map = recordService.getRecordMetadataAspectsMap(); assertEquals(1, map.size()); assertTrue(map.containsKey(ASPECT_FOR_FILE_PLAN)); Set<QName> types = map.get(ASPECT_FOR_FILE_PLAN); assertNotNull(types); assertEquals(1, types.size()); assertTrue(types.contains(TYPE_FILE_PLAN)); } /** * Given invalid types * When linking * Then exception thrown */ @Test public void linkNonRecord() { NodeRef nonRecord = generateNodeRef(TYPE_CONTENT); NodeRef recordFolder = generateRecordFolder(); // set expected exception exception.expect(RecordLinkRuntimeException.class); // link recordService.link(nonRecord, recordFolder); } @Test public void linkNonRecordFolder() { NodeRef record = generateRecord(); NodeRef nonRecordFolder = generateNodeRef(TYPE_FOLDER); // set expected exception exception.expect(RecordLinkRuntimeException.class); // link recordService.link(record, nonRecordFolder); } /** * Given that the record is already a child of the record folder * When I try to link the record to the same record folder * Then an exception is thrown */ @Test public void linkRecordToRecordFolderFailsIfAlreadyAChild() { NodeRef record = generateRecord(); NodeRef recordFolder = generateRecordFolder(); // given that the record is already a child of the record folder makeChildrenOf(recordFolder, record); // set expected exception exception.expect(RecordLinkRuntimeException.class); // link recordService.link(record, recordFolder); } /** * Given a record that is not a child of a record folder * When I link the record to the record folder * Then the record is now linked to the record folder */ @Test public void linkRecordToRecordFolder() { NodeRef record = generateRecord(); NodeRef recordFolder = generateRecordFolder(); // given that the record is already a child of the record folder makeChildrenOf(generateRecordFolder(), record); // set the name of the record String name = generateText(); doReturn(name).when(mockedNodeService).getProperty(record, PROP_NAME); // link recordService.link(record, recordFolder); // verify link was created verify(mockedNodeService, times(1)).addChild( recordFolder, record, ContentModel.ASSOC_CONTAINS, QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, name)); } /** * Given that the source record has no disposition schedule * When I link * Then it is successful */ @Test public void linkNoSourceDisposition() { // create record and record folder NodeRef record = generateRecord(); NodeRef recordFolder = generateRecordFolder(); makeChildrenOf(generateRecordFolder(), record); // set the name of the record String name = generateText(); doReturn(name).when(mockedNodeService).getProperty(record, PROP_NAME); // set dispositions when(mockedDispositionService.getDispositionSchedule(record)) .thenReturn(null); // link recordService.link(record, recordFolder); // verify link was created verify(mockedNodeService, times(1)).addChild( recordFolder, record, ContentModel.ASSOC_CONTAINS, QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, name)); } /** * Given that the target record folder has no disposition schedule * When I link * Then it is successful */ @Test public void linkNoTargetDisposition() { // create record and record folder NodeRef record = generateRecord(); NodeRef recordFolder = generateRecordFolder(); makeChildrenOf(generateRecordFolder(), record); // set the name of the record String name = generateText(); doReturn(name).when(mockedNodeService).getProperty(record, PROP_NAME); // set dispositions when(mockedDispositionService.getDispositionSchedule(record)) .thenReturn(mock(DispositionSchedule.class)); when(mockedDispositionService.getDispositionSchedule(record)) .thenReturn(null); // link recordService.link(record, recordFolder); // verify link was created verify(mockedNodeService, times(1)).addChild( recordFolder, record, ContentModel.ASSOC_CONTAINS, QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, name)); } /** * Given that the source record and target record folder have incompatible disposition schedules * When I link * Then I expect a failure */ @Test public void linkIncompatibleDispositions() { // create record and record folder NodeRef record = generateRecord(); NodeRef recordFolder = generateRecordFolder(); makeChildrenOf(generateRecordFolder(), record); // set the name of the record String name = generateText(); doReturn(name).when(mockedNodeService).getProperty(record, PROP_NAME); // set dispositions DispositionSchedule recordDispositionSchedule = mock(DispositionSchedule.class); when(recordDispositionSchedule.isRecordLevelDisposition()) .thenReturn(true); when(mockedDispositionService.getOriginDispositionSchedule(record)) .thenReturn(recordDispositionSchedule); DispositionSchedule recordFolderDispositionSchedule = mock(DispositionSchedule.class); when(recordFolderDispositionSchedule.isRecordLevelDisposition()) .thenReturn(false); when(mockedDispositionService.getDispositionSchedule(recordFolder)) .thenReturn(recordFolderDispositionSchedule); // expect exception exception.expect(RecordLinkRuntimeException.class); // link recordService.link(record, recordFolder); } /** * Given that the source record and target record folder have compatible disposition schedules * When I link * Then it is successful */ @Test public void linkCompatibleDispositions() { // create record and record folder NodeRef record = generateRecord(); NodeRef recordFolder = generateRecordFolder(); makeChildrenOf(generateRecordFolder(), record); // set the name of the record String name = generateText(); doReturn(name).when(mockedNodeService).getProperty(record, PROP_NAME); // set dispositions DispositionSchedule recordDispositionSchedule = mock(DispositionSchedule.class); when(recordDispositionSchedule.isRecordLevelDisposition()) .thenReturn(true); when(mockedDispositionService.getDispositionSchedule(record)) .thenReturn(recordDispositionSchedule); DispositionSchedule recordFolderDispositionSchedule = mock(DispositionSchedule.class); when(recordFolderDispositionSchedule.isRecordLevelDisposition()) .thenReturn(true); when(mockedDispositionService.getDispositionSchedule(recordFolder)) .thenReturn(recordFolderDispositionSchedule); // link recordService.link(record, recordFolder); // verify link was created verify(mockedNodeService, times(1)).addChild( recordFolder, record, ContentModel.ASSOC_CONTAINS, QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, name)); } /** * Given invalid types * When unlinking * Then exception thrown */ @Test public void unlinkNonRecord() { NodeRef nonRecord = generateNodeRef(TYPE_CONTENT); NodeRef recordFolder = generateRecordFolder(); // set expected exception exception.expect(RecordLinkRuntimeException.class); // unlink recordService.unlink(nonRecord, recordFolder); } @Test public void unlinkNonRecordFolder() { NodeRef record = generateRecord(); NodeRef nonRecordFolder = generateNodeRef(TYPE_FOLDER); // set expected exception exception.expect(RecordLinkRuntimeException.class); // unlink recordService.unlink(record, nonRecordFolder); } /** * Given a record folder is a records primary parent * When I try and unlink the record from that record folder * Then an exception is thrown */ @Test public void unlinkRecordFromPrimaryRecordFolder() { NodeRef record = generateRecord(); NodeRef recordFolder = generateRecordFolder(); // given that the record is already a child of the record folder makePrimaryParentOf(record, recordFolder); // set expected exception exception.expect(RecordLinkRuntimeException.class); // link recordService.unlink(record, recordFolder); } /** * Given a record that is linked to a record * And that the record is not the primary parent of the record * When I unlink the record to the record folder * Then the record is no longer linked to the record folder */ @Test public void unlinkRecordFromRecordFolder() { NodeRef record = generateRecord(); NodeRef recordFolder = generateRecordFolder(); // the records primary parent is another record folder makePrimaryParentOf(record, generateRecordFolder()); // unlink recordService.unlink(record, recordFolder); // verify link was created verify(mockedNodeService, times(1)).removeChild(recordFolder, record); } /** * Given that a new record is being created * When the behaviour is triggered * Then the record is stored for later reference in the transaction */ @SuppressWarnings("unchecked") @Test public void onCreateChildAssociationNewRecord() { // standard content node NodeRef nodeRef = generateCmContent("test.txt"); ChildAssociationRef assoc = generateChildAssociationRef(generateNodeRef(), nodeRef); doNothing().when(recordService).file(nodeRef); // doesn't have no content aspect when(mockedNodeService.hasAspect(nodeRef, ContentModel.ASPECT_NO_CONTENT)) .thenReturn(false); Set<Object> values = mock(HashSet.class); when(mockedTransactionalResourceHelper.getSet(RecordServiceImpl.KEY_NEW_RECORDS)) .thenReturn(values); // trigger behaviour recordService.onCreateChildAssociation(assoc, true); // verify verify(values, times(1)).add(nodeRef); } /** * Given that an existing record is linked * When the behaviour is triggered * Then the record is not stored for later reference in the transaction */ @SuppressWarnings("unchecked") @Test public void onCreateChildAssociationExistingRecord() { // standard content node NodeRef nodeRef = generateCmContent("test.txt"); ChildAssociationRef assoc = generateChildAssociationRef(generateNodeRef(), nodeRef); doNothing().when(recordService).file(nodeRef); // doesn't have no content aspect when(mockedNodeService.hasAspect(nodeRef, ContentModel.ASPECT_NO_CONTENT)) .thenReturn(false); Set<Object> values = mock(HashSet.class); when(mockedTransactionalResourceHelper.getSet(RecordServiceImpl.KEY_NEW_RECORDS)) .thenReturn(values); // trigger behaviour recordService.onCreateChildAssociation(assoc, false); // verify verify(values, never()).add(nodeRef); } }