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 = RESOURCE_ID.resolve("test-resource-child");
128    private static final FedoraId RESOURCE_DESCRIPTION_ID = RESOURCE_ID.resolve("fcr:metadata");
129    private static final FedoraId RESOURCE_ACL_ID = RESOURCE_ID.resolve("fcr:acl");
130
131    @Before
132    public void setup() {
133        MockitoAnnotations.openMocks(this);
134        final String txId = UUID.randomUUID().toString();
135        tx = TestTransactionHelper.mockTransaction(txId, false);
136        when(psManager.getSession(any(Transaction.class))).thenReturn(pSession);
137        final DeleteResourceOperationFactoryImpl factoryImpl = new DeleteResourceOperationFactoryImpl();
138        setField(service, "deleteResourceFactory", factoryImpl);
139        setField(service, "containmentIndex", containmentIndex);
140        setField(service, "eventAccumulator", eventAccumulator);
141        setField(service, "referenceService", referenceService);
142        setField(service, "membershipService", membershipService);
143        setField(service, "searchIndex", searchIndex);
144        when(container.getFedoraId()).thenReturn(RESOURCE_ID);
145
146        when(pSession.getHeaders(RESOURCE_ID, null)).thenReturn(resourceHeaders);
147        when(pSession.getHeaders(CHILD_RESOURCE_ID, null)).thenReturn(childHeaders);
148        when(pSession.getHeaders(RESOURCE_DESCRIPTION_ID, null)).thenReturn(descHeaders);
149        when(pSession.getHeaders(RESOURCE_ACL_ID, null)).thenReturn(aclHeaders);
150    }
151
152    @After
153    public void cleanUp() {
154        containmentIndex.reset();
155    }
156
157    @Test
158    public void testContainerDelete() throws Exception {
159        when(container.isAcl()).thenReturn(false);
160        when(container.getAcl()).thenReturn(null);
161
162        service.perform(tx, container, USER);
163        containmentIndex.commitTransaction(tx);
164        verifyResourceOperation(RESOURCE_ID, operationCaptor, pSession);
165    }
166
167    @Test
168    public void testRecursiveDelete() throws Exception {
169        when(container.isAcl()).thenReturn(false);
170        when(container.getAcl()).thenReturn(null);
171        when(childContainer.getFedoraId()).thenReturn(CHILD_RESOURCE_ID);
172        when(childContainer.isAcl()).thenReturn(false);
173        when(childContainer.getAcl()).thenReturn(null);
174
175        when(resourceFactory.getResource(tx, CHILD_RESOURCE_ID)).thenReturn(childContainer);
176        containmentIndex.addContainedBy(tx, container.getFedoraId(), childContainer.getFedoraId());
177
178        assertEquals(1, containmentIndex.getContains(tx, RESOURCE_ID).count());
179        service.perform(tx, container, USER);
180
181        verify(pSession, times(2)).persist(operationCaptor.capture());
182        final List<DeleteResourceOperation> operations = operationCaptor.getAllValues();
183        assertEquals(2, operations.size());
184
185        assertEquals(CHILD_RESOURCE_ID, operations.get(0).getResourceId());
186        assertEquals(RESOURCE_ID, operations.get(1).getResourceId());
187
188        assertEquals(0, containmentIndex.getContains(tx, RESOURCE_ID).count());
189
190        verify(tx).lockResource(RESOURCE_ID);
191        verify(tx).lockResource(CHILD_RESOURCE_ID);
192    }
193
194    private void verifyResourceOperation(final FedoraId fedoraID,
195                                         final ArgumentCaptor<DeleteResourceOperation> captor,
196                                         final PersistentStorageSession pSession) throws Exception {
197        verify(pSession).persist(captor.capture());
198        final DeleteResourceOperation containerOperation = captor.getValue();
199        assertEquals(fedoraID, containerOperation.getResourceId());
200    }
201
202    @Test
203    public void testAclDelete() throws Exception {
204        when(acl.getFedoraId()).thenReturn(RESOURCE_ACL_ID);
205        when(acl.isAcl()).thenReturn(true);
206        service.perform(tx, acl, USER);
207        verifyResourceOperation(RESOURCE_ACL_ID, operationCaptor, pSession);
208    }
209
210    @Test(expected = RepositoryRuntimeException.class)
211    public void testBinaryDescriptionDelete() throws Exception {
212        when(binaryDesc.getFedoraId()).thenReturn(RESOURCE_DESCRIPTION_ID);
213        service.perform(tx, binaryDesc, USER);
214    }
215
216    @Test
217    public void testBinaryDeleteWithAcl() throws Exception {
218        when(binary.getFedoraId()).thenReturn(RESOURCE_ID);
219        when(binary.isAcl()).thenReturn(false);
220        when(binary.getDescription()).thenReturn(binaryDesc);
221        when(binaryDesc.getFedoraId()).thenReturn(RESOURCE_DESCRIPTION_ID);
222        when(binary.getAcl()).thenReturn(acl);
223        when(acl.getFedoraId()).thenReturn(RESOURCE_ACL_ID);
224
225        service.perform(tx, binary, USER);
226
227        verify(pSession, times(3)).persist(operationCaptor.capture());
228        final List<DeleteResourceOperation> operations = operationCaptor.getAllValues();
229        assertEquals(3, operations.size());
230
231        assertEquals(RESOURCE_DESCRIPTION_ID, operations.get(0).getResourceId());
232        assertEquals(RESOURCE_ACL_ID, operations.get(1).getResourceId());
233        assertEquals(RESOURCE_ID, operations.get(2).getResourceId());
234
235        verify(tx).lockResource(RESOURCE_ID);
236        verify(tx).lockResource(RESOURCE_DESCRIPTION_ID);
237        verify(tx).lockResource(RESOURCE_ACL_ID);
238    }
239
240}