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.models;
007
008import static org.fcrepo.kernel.api.FedoraTypes.FEDORA_ID_PREFIX;
009import static org.junit.Assert.assertFalse;
010import static org.junit.Assert.assertTrue;
011import static org.mockito.Mockito.when;
012import static org.springframework.test.util.ReflectionTestUtils.setField;
013
014import javax.inject.Inject;
015
016import java.util.UUID;
017
018import org.fcrepo.kernel.api.ContainmentIndex;
019import org.fcrepo.kernel.api.ReadOnlyTransaction;
020import org.fcrepo.kernel.api.Transaction;
021import org.fcrepo.kernel.api.exception.RepositoryRuntimeException;
022import org.fcrepo.kernel.api.identifiers.FedoraId;
023import org.fcrepo.kernel.impl.TestTransactionHelper;
024import org.fcrepo.persistence.api.PersistentStorageSession;
025import org.fcrepo.persistence.api.PersistentStorageSessionManager;
026import org.fcrepo.persistence.api.exceptions.PersistentSessionClosedException;
027import org.junit.Before;
028import org.junit.Test;
029import org.junit.runner.RunWith;
030import org.mockito.InjectMocks;
031import org.mockito.Mock;
032import org.mockito.MockitoAnnotations;
033import org.springframework.test.context.ContextConfiguration;
034import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
035
036/**
037 * Test for ResourceHelper
038 * @author whikloj
039 * @since 6.0.0
040 */
041@RunWith(SpringJUnit4ClassRunner.class)
042@ContextConfiguration("/containmentIndexTest.xml")
043public class ResourceHelperImplTest {
044
045    @Mock
046    private PersistentStorageSessionManager sessionManager;
047
048    @Mock
049    private PersistentStorageSession psSession;
050
051    private Transaction mockTx;
052
053    @Inject
054    private ContainmentIndex containmentIndex;
055
056    @InjectMocks
057    private ResourceHelperImpl resourceHelper;
058
059    private String fedoraIdStr;
060
061    private String sessionId;
062
063    private final FedoraId rootId = FedoraId.getRepositoryRootId();
064
065    private FedoraId fedoraId;
066
067    private String fedoraMementoIdStr;
068
069    private FedoraId fedoraMementoId;
070
071    private Transaction readOnlyTx;
072
073    @Before
074    public void setup() throws Exception {
075        MockitoAnnotations.openMocks(this);
076        fedoraIdStr = FEDORA_ID_PREFIX + "/" + UUID.randomUUID().toString();
077        fedoraId = FedoraId.create(fedoraIdStr);
078        fedoraMementoIdStr = fedoraIdStr + "/fcr:versions/20000102120000";
079        fedoraMementoId = FedoraId.create(fedoraMementoIdStr);
080
081        sessionId = UUID.randomUUID().toString();
082        mockTx = TestTransactionHelper.mockTransaction(sessionId, false);
083
084        resourceHelper = new ResourceHelperImpl();
085
086        setField(resourceHelper, "persistentStorageSessionManager", sessionManager);
087        setField(resourceHelper, "containmentIndex", containmentIndex);
088
089        when(sessionManager.getSession(mockTx)).thenReturn(psSession);
090        when(sessionManager.getReadOnlySession()).thenReturn(psSession);
091
092        readOnlyTx = ReadOnlyTransaction.INSTANCE;
093    }
094
095    @Test
096    public void doesResourceExist_Exists_WithSession() throws Exception {
097        containmentIndex.addContainedBy(mockTx, rootId, fedoraId);
098        final boolean answerIn = resourceHelper.doesResourceExist(mockTx, fedoraId, false);
099        assertTrue(answerIn);
100        final boolean answerOut = resourceHelper.doesResourceExist(readOnlyTx, fedoraId, false);
101        assertFalse(answerOut);
102    }
103
104    @Test
105    public void doesResourceExist_Exists_Description_WithSession() {
106        containmentIndex.addContainedBy(mockTx, rootId, fedoraId);
107        final FedoraId descId = fedoraId.asDescription();
108        final boolean answerIn = resourceHelper.doesResourceExist(mockTx, descId, false);
109        assertTrue(answerIn);
110        final boolean answerOut = resourceHelper.doesResourceExist(readOnlyTx, descId, false);
111        assertFalse(answerOut);
112    }
113
114    @Test
115    public void doesResourceExist_Exists_WithoutSession() throws Exception {
116        containmentIndex.addContainedBy(mockTx, rootId, fedoraId);
117        containmentIndex.commitTransaction(mockTx);
118        final boolean answer = resourceHelper.doesResourceExist(readOnlyTx, fedoraId, false);
119        assertTrue(answer);
120    }
121
122    @Test
123    public void doesResourceExist_Exists_Description_WithoutSession() {
124        containmentIndex.addContainedBy(mockTx, rootId, fedoraId);
125        containmentIndex.commitTransaction(mockTx);
126        final FedoraId descId = fedoraId.asDescription();
127        final boolean answer = resourceHelper.doesResourceExist(readOnlyTx, descId, false);
128        assertTrue(answer);
129    }
130
131    @Test
132    public void doesResourceExist_DoesntExist_WithSession() throws Exception {
133        final boolean answer = resourceHelper.doesResourceExist(mockTx, fedoraId, false);
134        assertFalse(answer);
135    }
136
137    @Test
138    public void doesResourceExist_DoesntExists_Description_WithSession() {
139        final FedoraId descId = fedoraId.asDescription();
140        final boolean answer = resourceHelper.doesResourceExist(mockTx, descId, false);
141        assertFalse(answer);
142    }
143
144    @Test
145    public void doesResourceExist_DoesntExist_WithoutSession() throws Exception {
146        final boolean answer = resourceHelper.doesResourceExist(readOnlyTx, fedoraId, false);
147        assertFalse(answer);
148    }
149
150    @Test
151    public void doesResourceExist_DoesntExists_Description_WithoutSession() {
152        final FedoraId descId = fedoraId.asDescription();
153        final boolean answer = resourceHelper.doesResourceExist(readOnlyTx, descId, false);
154        assertFalse(answer);
155    }
156
157    /**
158     * Only Mementos go to the persistence layer.
159     */
160    @Test(expected = RepositoryRuntimeException.class)
161    public void doesResourceExist_Exception_WithSession() throws Exception {
162        when(psSession.getHeaders(fedoraMementoId, fedoraMementoId.getMementoInstant()))
163                .thenThrow(PersistentSessionClosedException.class);
164        resourceHelper.doesResourceExist(mockTx, fedoraMementoId, false);
165    }
166
167    /**
168     * Only Mementos go to the persistence layer.
169     */
170    @Test(expected = RepositoryRuntimeException.class)
171    public void doesResourceExist_Exception_WithoutSession() throws Exception {
172        when(psSession.getHeaders(fedoraMementoId, fedoraMementoId.getMementoInstant()))
173                .thenThrow(PersistentSessionClosedException.class);
174        resourceHelper.doesResourceExist(readOnlyTx, fedoraMementoId, false);
175    }
176
177    /**
178     * Test an item is not a ghost node because it exists.
179     */
180    @Test
181    public void testGhostNodeFailure() {
182        containmentIndex.addContainedBy(mockTx, rootId, fedoraId);
183        // Inside the transaction the resource exists, so its not a ghost node.
184        assertTrue(resourceHelper.doesResourceExist(mockTx, fedoraId, false));
185        assertFalse(resourceHelper.isGhostNode(mockTx, fedoraId));
186        // Outside the transaction the resource does not exist.
187        assertFalse(resourceHelper.doesResourceExist(readOnlyTx, fedoraId, false));
188        // Because there are no other items it is not a ghost node.
189        assertFalse(resourceHelper.isGhostNode(readOnlyTx, fedoraId));
190
191        containmentIndex.commitTransaction(mockTx);
192
193        // Now it exists outside the transaction.
194        assertTrue(resourceHelper.doesResourceExist(readOnlyTx, fedoraId, false));
195        // So it can't be a ghost node.
196        assertFalse(resourceHelper.isGhostNode(readOnlyTx, fedoraId));
197    }
198
199    /**
200     * Test that when the resource that does exist shares the ID of a resource that does not, then we have a ghost node.
201     */
202    @Test
203    public void testGhostNodeSuccess() {
204        final var resourceId = fedoraId.resolve("the/child/path");
205        containmentIndex.addContainedBy(mockTx, rootId, resourceId);
206        assertTrue(resourceHelper.doesResourceExist(mockTx, resourceId, false));
207        assertFalse(resourceHelper.doesResourceExist(mockTx, fedoraId, false));
208        assertTrue(resourceHelper.isGhostNode(mockTx, fedoraId));
209        assertFalse(resourceHelper.doesResourceExist(readOnlyTx, resourceId, false));
210        assertFalse(resourceHelper.doesResourceExist(readOnlyTx, fedoraId, false));
211        assertFalse(resourceHelper.isGhostNode(readOnlyTx, fedoraId));
212
213        containmentIndex.commitTransaction(mockTx);
214
215        assertTrue(resourceHelper.doesResourceExist(readOnlyTx, resourceId, false));
216        assertFalse(resourceHelper.doesResourceExist(readOnlyTx, fedoraId,false));
217        assertTrue(resourceHelper.isGhostNode(readOnlyTx, fedoraId));
218    }
219}