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