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.fcrepo.kernel.api.FedoraTypes.FEDORA_ID_PREFIX;
021import static org.junit.Assert.assertEquals;
022import static org.mockito.ArgumentMatchers.any;
023import static org.mockito.Mockito.times;
024import static org.mockito.Mockito.verify;
025import static org.mockito.Mockito.when;
026import static org.springframework.test.util.ReflectionTestUtils.setField;
027
028import java.util.List;
029import java.util.Optional;
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.impl.TestTransactionHelper;
046import org.fcrepo.kernel.impl.operations.DeleteResourceOperationFactoryImpl;
047import org.fcrepo.kernel.impl.operations.PurgeResourceOperation;
048import org.fcrepo.persistence.api.PersistentStorageSession;
049import org.fcrepo.persistence.api.PersistentStorageSessionManager;
050
051import org.junit.Before;
052import org.junit.Test;
053import org.junit.runner.RunWith;
054import org.mockito.ArgumentCaptor;
055import org.mockito.Captor;
056import org.mockito.InjectMocks;
057import org.mockito.Mock;
058import org.mockito.MockitoAnnotations;
059import org.springframework.test.context.ContextConfiguration;
060import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
061
062import com.github.benmanes.caffeine.cache.Cache;
063
064/**
065 * PurgeResourceServiceTest
066 *
067 * Copy of DeleteResourceServiceTest
068 *
069 * @author dbernstein
070 * @author whikloj
071 */
072@RunWith(SpringJUnit4ClassRunner.class)
073@ContextConfiguration("/containmentIndexTest.xml")
074public class PurgeResourceServiceImplTest {
075
076    private static final String USER = "fedoraAdmin";
077
078    private Transaction tx;
079
080    @Mock
081    private EventAccumulator eventAccumulator;
082
083    @Mock
084    private PersistentStorageSession pSession;
085
086    @Inject
087    private ContainmentIndex containmentIndex;
088
089    @Mock
090    private PersistentStorageSessionManager psManager;
091
092    @Mock
093    private ResourceFactory resourceFactory;
094
095    @Mock
096    private Container container;
097
098    @Mock
099    private Container childContainer;
100
101    @Mock
102    private Binary binary;
103
104    @Mock
105    private WebacAcl acl;
106
107    @Mock
108    private NonRdfSourceDescription binaryDesc;
109
110    @Mock
111    private ResourceHeaders resourceHeaders;
112    @Mock
113    private ResourceHeaders childHeaders;
114    @Mock
115    private ResourceHeaders descHeaders;
116    @Mock
117    private ResourceHeaders aclHeaders;
118    @Mock
119    private Cache<String, Optional<ACLHandle>> authHandleCache;
120
121    @Captor
122    private ArgumentCaptor<PurgeResourceOperation> operationCaptor;
123
124    @InjectMocks
125    private PurgeResourceServiceImpl service;
126
127    private static final FedoraId RESOURCE_ID =  FedoraId.create("test-resource");
128    private static final FedoraId CHILD_RESOURCE_ID = RESOURCE_ID.resolve("test-resource-child");
129    private static final FedoraId RESOURCE_DESCRIPTION_ID =
130            FedoraId.create(FEDORA_ID_PREFIX + "test-resource-description");
131    private static final FedoraId RESOURCE_ACL_ID = FedoraId.create("test-resource-acl");
132    private static final String TX_ID = "tx-1234";
133
134    @Before
135    public void setup() {
136        MockitoAnnotations.openMocks(this);
137        tx = TestTransactionHelper.mockTransaction(TX_ID, false);
138        when(psManager.getSession(any(Transaction.class))).thenReturn(pSession);
139        final DeleteResourceOperationFactoryImpl factoryImpl = new DeleteResourceOperationFactoryImpl();
140        setField(service, "deleteResourceFactory", factoryImpl);
141        setField(service, "containmentIndex", containmentIndex);
142        setField(service, "eventAccumulator", eventAccumulator);
143        when(container.getFedoraId()).thenReturn(RESOURCE_ID);
144
145        when(pSession.getHeaders(RESOURCE_ID, null)).thenReturn(resourceHeaders);
146        when(pSession.getHeaders(CHILD_RESOURCE_ID, null)).thenReturn(childHeaders);
147        when(pSession.getHeaders(RESOURCE_DESCRIPTION_ID, null)).thenReturn(descHeaders);
148        when(pSession.getHeaders(RESOURCE_ACL_ID, null)).thenReturn(aclHeaders);
149    }
150
151    @Test
152    public void testContainerPurge() throws Exception {
153        when(container.isAcl()).thenReturn(false);
154        when(container.getAcl()).thenReturn(null);
155
156        service.perform(tx, container, USER);
157        verifyResourceOperation(RESOURCE_ID, operationCaptor, pSession);
158    }
159
160    @Test
161    public void testRecursivePurge() throws Exception {
162        when(container.isAcl()).thenReturn(false);
163        when(container.getAcl()).thenReturn(null);
164        when(childContainer.getFedoraId()).thenReturn(CHILD_RESOURCE_ID);
165        when(childContainer.isAcl()).thenReturn(false);
166        when(childContainer.getAcl()).thenReturn(null);
167
168        when(resourceFactory.getResource(tx, CHILD_RESOURCE_ID)).thenReturn(childContainer);
169        containmentIndex.addContainedBy(tx, container.getFedoraId(), childContainer.getFedoraId());
170        containmentIndex.commitTransaction(tx);
171        containmentIndex.removeContainedBy(tx, container.getFedoraId(), childContainer.getFedoraId());
172
173        when(container.isAcl()).thenReturn(false);
174        when(container.getAcl()).thenReturn(null);
175
176        service.perform(tx, container, USER);
177
178        verify(pSession, times(2)).persist(operationCaptor.capture());
179        final List<PurgeResourceOperation> operations = operationCaptor.getAllValues();
180        assertEquals(2, operations.size());
181
182        assertEquals(CHILD_RESOURCE_ID, operations.get(0).getResourceId());
183        assertEquals(RESOURCE_ID, operations.get(1).getResourceId());
184
185        assertEquals(0, containmentIndex.getContains(tx, RESOURCE_ID).count());
186
187        verify(tx).lockResource(RESOURCE_ID);
188        verify(tx).lockResource(CHILD_RESOURCE_ID);
189    }
190
191    private void verifyResourceOperation(final FedoraId fedoraID,
192                                         final ArgumentCaptor<PurgeResourceOperation> captor,
193                                         final PersistentStorageSession pSession) throws Exception {
194        verify(pSession).persist(captor.capture());
195        final PurgeResourceOperation containerOperation = captor.getValue();
196        assertEquals(fedoraID, containerOperation.getResourceId());
197    }
198
199    @Test
200    public void testAclPurge() throws Exception {
201        when(acl.getFedoraId()).thenReturn(RESOURCE_ACL_ID);
202        when(acl.isAcl()).thenReturn(true);
203        service.perform(tx, acl, USER);
204        verifyResourceOperation(RESOURCE_ACL_ID, operationCaptor, pSession);
205    }
206
207    @Test(expected = RepositoryRuntimeException.class)
208    public void testBinaryDescriptionPurge() throws Exception {
209        when(binaryDesc.getFedoraId()).thenReturn(RESOURCE_DESCRIPTION_ID);
210        service.perform(tx, binaryDesc, USER);
211    }
212
213    @Test
214    public void testBinaryPurgeWithAcl() throws Exception {
215        when(binary.getFedoraId()).thenReturn(RESOURCE_ID);
216        when(binary.isAcl()).thenReturn(false);
217        when(binary.getDescription()).thenReturn(binaryDesc);
218        when(binaryDesc.getFedoraId()).thenReturn(RESOURCE_DESCRIPTION_ID);
219        when(binary.getAcl()).thenReturn(acl);
220        when(acl.getFedoraId()).thenReturn(RESOURCE_ACL_ID);
221
222        service.perform(tx, binary, USER);
223
224        verify(pSession, times(3)).persist(operationCaptor.capture());
225        final List<PurgeResourceOperation> operations = operationCaptor.getAllValues();
226        assertEquals(3, operations.size());
227
228        assertEquals(RESOURCE_DESCRIPTION_ID, operations.get(0).getResourceId());
229        assertEquals(RESOURCE_ACL_ID, operations.get(1).getResourceId());
230        assertEquals(RESOURCE_ID, operations.get(2).getResourceId());
231
232        verify(tx).lockResource(RESOURCE_ID);
233        verify(tx).lockResource(RESOURCE_DESCRIPTION_ID);
234        verify(tx).lockResource(RESOURCE_ACL_ID);
235    }
236
237}