001/* 002 * Licensed to DuraSpace under one or more contributor license agreements. 003 * See the NOTICE file distributed with this work for additional information 004 * regarding copyright ownership. 005 * 006 * DuraSpace licenses this file to you under the Apache License, 007 * Version 2.0 (the "License"); you may not use this file except in 008 * compliance with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018package org.fcrepo.kernel.impl.services; 019 020import static org.junit.Assert.assertEquals; 021import static org.mockito.ArgumentMatchers.any; 022import static org.mockito.Mockito.times; 023import static org.mockito.Mockito.verify; 024import static org.mockito.Mockito.when; 025import static org.springframework.test.util.ReflectionTestUtils.setField; 026 027import java.util.List; 028import java.util.Optional; 029import java.util.UUID; 030 031import javax.inject.Inject; 032 033import org.fcrepo.kernel.api.ContainmentIndex; 034import org.fcrepo.kernel.api.Transaction; 035import org.fcrepo.kernel.api.auth.ACLHandle; 036import org.fcrepo.kernel.api.exception.RepositoryRuntimeException; 037import org.fcrepo.kernel.api.identifiers.FedoraId; 038import org.fcrepo.kernel.api.models.Binary; 039import org.fcrepo.kernel.api.models.Container; 040import org.fcrepo.kernel.api.models.NonRdfSourceDescription; 041import org.fcrepo.kernel.api.models.ResourceFactory; 042import org.fcrepo.kernel.api.models.ResourceHeaders; 043import org.fcrepo.kernel.api.models.WebacAcl; 044import org.fcrepo.kernel.api.observer.EventAccumulator; 045import org.fcrepo.kernel.api.services.MembershipService; 046import org.fcrepo.kernel.api.services.ReferenceService; 047import org.fcrepo.kernel.impl.TestTransactionHelper; 048import org.fcrepo.kernel.impl.operations.DeleteResourceOperation; 049import org.fcrepo.kernel.impl.operations.DeleteResourceOperationFactoryImpl; 050import org.fcrepo.persistence.api.PersistentStorageSession; 051import org.fcrepo.persistence.api.PersistentStorageSessionManager; 052import org.fcrepo.search.api.SearchIndex; 053 054import org.junit.After; 055import org.junit.Before; 056import org.junit.Test; 057import org.junit.runner.RunWith; 058import org.mockito.ArgumentCaptor; 059import org.mockito.Captor; 060import org.mockito.InjectMocks; 061import org.mockito.Mock; 062import org.mockito.MockitoAnnotations; 063import org.springframework.test.context.ContextConfiguration; 064import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; 065 066import com.github.benmanes.caffeine.cache.Cache; 067 068/** 069 * DeleteResourceServiceTest 070 * 071 * @author dbernstein 072 */ 073@RunWith(SpringJUnit4ClassRunner.class) 074@ContextConfiguration("/containmentIndexTest.xml") 075public class DeleteResourceServiceImplTest { 076 077 private static final String USER = "fedoraAdmin"; 078 079 private Transaction tx; 080 081 @Mock 082 private EventAccumulator eventAccumulator; 083 084 @Mock 085 private PersistentStorageSession pSession; 086 087 @Inject 088 private ContainmentIndex containmentIndex; 089 090 @Mock 091 private SearchIndex searchIndex; 092 093 @Mock 094 private PersistentStorageSessionManager psManager; 095 096 @Mock 097 private ResourceFactory resourceFactory; 098 099 @Mock 100 private Container container; 101 102 @Mock 103 private Container childContainer; 104 105 @Mock 106 private Binary binary; 107 108 @Mock 109 private WebacAcl acl; 110 111 @Mock 112 private NonRdfSourceDescription binaryDesc; 113 114 @Mock 115 private ReferenceService referenceService; 116 117 @Mock 118 private MembershipService membershipService; 119 120 @Mock 121 private ResourceHeaders resourceHeaders; 122 @Mock 123 private ResourceHeaders childHeaders; 124 @Mock 125 private ResourceHeaders descHeaders; 126 @Mock 127 private ResourceHeaders aclHeaders; 128 129 @Mock 130 private Cache<String, Optional<ACLHandle>> authHandleCache; 131 132 @Captor 133 private ArgumentCaptor<DeleteResourceOperation> operationCaptor; 134 135 @InjectMocks 136 private DeleteResourceServiceImpl service; 137 138 private static final FedoraId RESOURCE_ID = FedoraId.create("test-resource"); 139 private static final FedoraId CHILD_RESOURCE_ID = FedoraId.create("test-resource-child"); 140 private static final FedoraId RESOURCE_DESCRIPTION_ID = 141 FedoraId.create("test-resource-description"); 142 private static final FedoraId RESOURCE_ACL_ID = FedoraId.create("test-resource-acl"); 143 144 @Before 145 public void setup() { 146 MockitoAnnotations.openMocks(this); 147 final String txId = UUID.randomUUID().toString(); 148 tx = TestTransactionHelper.mockTransaction(txId, false); 149 when(psManager.getSession(any(Transaction.class))).thenReturn(pSession); 150 final DeleteResourceOperationFactoryImpl factoryImpl = new DeleteResourceOperationFactoryImpl(); 151 setField(service, "deleteResourceFactory", factoryImpl); 152 setField(service, "containmentIndex", containmentIndex); 153 setField(service, "eventAccumulator", eventAccumulator); 154 setField(service, "referenceService", referenceService); 155 setField(service, "membershipService", membershipService); 156 setField(service, "searchIndex", searchIndex); 157 when(container.getFedoraId()).thenReturn(RESOURCE_ID); 158 159 when(pSession.getHeaders(RESOURCE_ID, null)).thenReturn(resourceHeaders); 160 when(pSession.getHeaders(CHILD_RESOURCE_ID, null)).thenReturn(childHeaders); 161 when(pSession.getHeaders(RESOURCE_DESCRIPTION_ID, null)).thenReturn(descHeaders); 162 when(pSession.getHeaders(RESOURCE_ACL_ID, null)).thenReturn(aclHeaders); 163 } 164 165 @After 166 public void cleanUp() { 167 containmentIndex.reset(); 168 } 169 170 @Test 171 public void testContainerDelete() throws Exception { 172 when(container.isAcl()).thenReturn(false); 173 when(container.getAcl()).thenReturn(null); 174 175 service.perform(tx, container, USER); 176 containmentIndex.commitTransaction(tx); 177 verifyResourceOperation(RESOURCE_ID, operationCaptor, pSession); 178 } 179 180 @Test 181 public void testRecursiveDelete() throws Exception { 182 when(container.isAcl()).thenReturn(false); 183 when(container.getAcl()).thenReturn(null); 184 when(childContainer.getFedoraId()).thenReturn(CHILD_RESOURCE_ID); 185 when(childContainer.isAcl()).thenReturn(false); 186 when(childContainer.getAcl()).thenReturn(null); 187 188 when(resourceFactory.getResource(tx, CHILD_RESOURCE_ID)).thenReturn(childContainer); 189 containmentIndex.addContainedBy(tx, container.getFedoraId(), childContainer.getFedoraId()); 190 191 when(container.isAcl()).thenReturn(false); 192 when(container.getAcl()).thenReturn(null); 193 194 assertEquals(1, containmentIndex.getContains(tx, RESOURCE_ID).count()); 195 service.perform(tx, container, USER); 196 197 verify(pSession, times(2)).persist(operationCaptor.capture()); 198 final List<DeleteResourceOperation> operations = operationCaptor.getAllValues(); 199 assertEquals(2, operations.size()); 200 201 assertEquals(CHILD_RESOURCE_ID, operations.get(0).getResourceId()); 202 assertEquals(RESOURCE_ID, operations.get(1).getResourceId()); 203 204 assertEquals(0, containmentIndex.getContains(tx, RESOURCE_ID).count()); 205 206 verify(tx).lockResource(RESOURCE_ID); 207 verify(tx).lockResource(CHILD_RESOURCE_ID); 208 } 209 210 private void verifyResourceOperation(final FedoraId fedoraID, 211 final ArgumentCaptor<DeleteResourceOperation> captor, 212 final PersistentStorageSession pSession) throws Exception { 213 verify(pSession).persist(captor.capture()); 214 final DeleteResourceOperation containerOperation = captor.getValue(); 215 assertEquals(fedoraID, containerOperation.getResourceId()); 216 } 217 218 @Test 219 public void testAclDelete() throws Exception { 220 when(acl.getFedoraId()).thenReturn(RESOURCE_ACL_ID); 221 when(acl.isAcl()).thenReturn(true); 222 service.perform(tx, acl, USER); 223 verifyResourceOperation(RESOURCE_ACL_ID, operationCaptor, pSession); 224 } 225 226 @Test(expected = RepositoryRuntimeException.class) 227 public void testBinaryDescriptionDelete() throws Exception { 228 when(binaryDesc.getFedoraId()).thenReturn(RESOURCE_DESCRIPTION_ID); 229 service.perform(tx, binaryDesc, USER); 230 } 231 232 @Test 233 public void testBinaryDeleteWithAcl() throws Exception { 234 when(binary.getFedoraId()).thenReturn(RESOURCE_ID); 235 when(binary.isAcl()).thenReturn(false); 236 when(binary.getDescription()).thenReturn(binaryDesc); 237 when(binaryDesc.getFedoraId()).thenReturn(RESOURCE_DESCRIPTION_ID); 238 when(binary.getAcl()).thenReturn(acl); 239 when(acl.getFedoraId()).thenReturn(RESOURCE_ACL_ID); 240 241 service.perform(tx, binary, USER); 242 243 verify(pSession, times(3)).persist(operationCaptor.capture()); 244 final List<DeleteResourceOperation> operations = operationCaptor.getAllValues(); 245 assertEquals(3, operations.size()); 246 247 assertEquals(RESOURCE_DESCRIPTION_ID, operations.get(0).getResourceId()); 248 assertEquals(RESOURCE_ACL_ID, operations.get(1).getResourceId()); 249 assertEquals(RESOURCE_ID, operations.get(2).getResourceId()); 250 251 verify(tx).lockResource(RESOURCE_ID); 252 verify(tx).lockResource(RESOURCE_DESCRIPTION_ID); 253 verify(tx).lockResource(RESOURCE_ACL_ID); 254 } 255 256}