001/**
002 * Copyright 2015 DuraSpace, Inc.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *     http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016package org.fcrepo.connector.file;
017
018import static java.nio.file.Files.createTempDirectory;
019import static java.nio.file.Files.createTempFile;
020import static org.fcrepo.kernel.api.FedoraJcrTypes.CONTENT_DIGEST;
021import static org.fcrepo.kernel.api.RdfLexicon.REPOSITORY_NAMESPACE;
022import static org.junit.Assert.assertEquals;
023import static org.junit.Assert.assertNotNull;
024import static org.junit.Assert.assertNull;
025import static org.junit.Assert.assertTrue;
026import static org.mockito.Matchers.any;
027import static org.mockito.Matchers.anyString;
028import static org.mockito.Matchers.eq;
029import static org.mockito.Mockito.doReturn;
030import static org.mockito.Mockito.mock;
031import static org.mockito.Mockito.spy;
032import static org.mockito.Mockito.verify;
033import static org.mockito.Mockito.when;
034import static org.modeshape.jcr.api.JcrConstants.JCR_DATA;
035import static org.modeshape.jcr.api.JcrConstants.NT_FILE;
036import static org.modeshape.jcr.api.JcrConstants.NT_FOLDER;
037import static org.modeshape.jcr.api.JcrConstants.NT_RESOURCE;
038import static org.slf4j.LoggerFactory.getLogger;
039import static org.springframework.test.util.ReflectionTestUtils.setField;
040
041import java.io.File;
042import java.io.FileOutputStream;
043import java.io.IOException;
044import java.nio.file.Path;
045
046import javax.jcr.NamespaceRegistry;
047
048import org.infinispan.schematic.document.Document;
049import org.junit.Before;
050import org.junit.BeforeClass;
051import org.junit.Test;
052import org.junit.runner.RunWith;
053import org.mockito.Mock;
054import org.mockito.runners.MockitoJUnitRunner;
055import org.modeshape.jcr.ExecutionContext;
056import org.modeshape.jcr.api.nodetype.NodeTypeManager;
057import org.modeshape.jcr.cache.document.DocumentTranslator;
058import org.modeshape.jcr.spi.federation.DocumentReader;
059import org.modeshape.jcr.spi.federation.ExtraPropertiesStore;
060import org.modeshape.jcr.value.BinaryValue;
061import org.modeshape.jcr.value.NameFactory;
062import org.modeshape.jcr.value.Property;
063import org.modeshape.jcr.value.ValueFactories;
064import org.modeshape.jcr.value.basic.BasicName;
065import org.modeshape.jcr.value.basic.BasicSingleValueProperty;
066import org.slf4j.Logger;
067
068/**
069 * @author Andrew Woods
070 *         Date: 2/3/14
071 */
072@RunWith(MockitoJUnitRunner.class)
073public class FedoraFileSystemConnectorTest {
074
075    private FedoraFileSystemConnector connector;
076
077    private static Path directoryPath;
078
079    private static File tmpFile;
080    private static File tmpFile2;
081    private static final String ROOT_PATH = "/";
082    private static final String PARENT_FIELD = "parent";
083    private static final String CHILD_FIELD = "children";
084
085    @Mock
086    private NamespaceRegistry mockRegistry;
087
088    @Mock
089    private NodeTypeManager mockNodeTypeManager;
090
091    @Mock
092    private DocumentTranslator mockTranslator;
093
094    @Mock
095    private NameFactory mockNameFactory;
096
097    @Mock
098    private ValueFactories mockValueFactories;
099
100    @Mock
101    private ExtraPropertiesStore mockExtraPropertiesStore;
102
103    @Mock
104    private Property binaryProperty;
105
106    @Mock
107    private BinaryValue binaryValue;
108
109    private final ExecutionContext mockContext = new ExecutionContext();
110
111    private static final Logger logger =
112        getLogger(FedoraFileSystemConnectorTest.class);
113
114    @BeforeClass
115    public static void beforeClass() throws IOException {
116        directoryPath = createTempDirectory("fedora-filesystemtest");
117        tmpFile =
118            createTempFile(directoryPath, "fedora-filesystemtestfile",
119                    "txt").toFile();
120        tmpFile.deleteOnExit();
121        try (FileOutputStream outputStream = new FileOutputStream(tmpFile)) {
122            outputStream.write("hello".getBytes());
123        } catch (final IOException e) {
124            logger.error("Error creating: {} - {}", tmpFile.getAbsolutePath(),
125                    e.getMessage());
126        }
127
128        tmpFile2 =
129            createTempFile(directoryPath, "fedora-filesystemtestfile",
130                    "txt").toFile();
131        tmpFile2.deleteOnExit();
132        try (FileOutputStream outputStream = new FileOutputStream(tmpFile2)) {
133            outputStream.write("goodbye".getBytes());
134        } catch (final IOException e) {
135            logger.error("Error creating: {} - {}", tmpFile2.getAbsolutePath(),
136                    e.getMessage());
137        }
138    }
139
140    @Before
141    public void setUp() throws IOException {
142
143        connector = new FedoraFileSystemConnector();
144        setField(connector, "directoryPath", directoryPath.toString());
145        setField(connector, "translator", mockTranslator);
146        setField(connector, "context", mockContext);
147        setField(connector, "extraPropertiesStore", mockExtraPropertiesStore);
148        setField(mockTranslator, "names", mockNameFactory);
149        setField(connector, "readonly", true);
150        connector.initialize(mockRegistry, mockNodeTypeManager);
151        mockContext.getNamespaceRegistry().register("fedora", REPOSITORY_NAMESPACE);
152        mockContext.getNamespaceRegistry().register("premis", "http://www.loc.gov/premis/rdf/v1#");
153    }
154
155    @Test
156    public void testGetDocumentByIdNull() {
157        final Document doc = connector.getDocumentById(null);
158        assertNull(doc);
159    }
160
161    @Test
162    public void testGetDocumentByIdDatastream() {
163        when(mockTranslator.getPrimaryTypeName(any(Document.class)))
164                .thenReturn(NT_FILE);
165        when(mockNameFactory.create(anyString())).thenReturn(
166                new BasicName("", tmpFile.getName()));
167
168        final Document doc = connector.getDocumentById(ROOT_PATH + tmpFile.getName());
169        assertNotNull(doc);
170        assertTrue(doc.containsField(PARENT_FIELD));
171        assertTrue(doc.containsField(CHILD_FIELD));
172        assertEquals(ROOT_PATH, doc.getString(PARENT_FIELD));
173        assertNull(doc.getString(CHILD_FIELD));
174    }
175
176    @Test
177    public void testGetDocumentByIdObject() {
178        when(mockTranslator.getPrimaryTypeName(any(Document.class)))
179                .thenReturn(NT_FOLDER);
180        when(mockNameFactory.create(anyString())).thenReturn(
181                new BasicName("", tmpFile.getName()));
182
183        final Document doc = connector.getDocumentById(ROOT_PATH + tmpFile.getName());
184        assertNotNull(doc);
185        assertTrue(doc.containsField(PARENT_FIELD));
186        assertTrue(doc.containsField(CHILD_FIELD));
187        assertEquals(ROOT_PATH, doc.getString(PARENT_FIELD));
188        assertNull(doc.getString(CHILD_FIELD));
189    }
190
191    @Test
192    public void testGetDocumentByIdNone() {
193        when(mockTranslator.getPrimaryTypeName(any(Document.class)))
194                .thenReturn("");
195        when(mockNameFactory.create(anyString())).thenReturn(
196                new BasicName("", tmpFile.getName()));
197
198        final Document doc = connector.getDocumentById(ROOT_PATH + tmpFile.getName());
199        assertNotNull(doc);
200        assertTrue(doc.containsField(PARENT_FIELD));
201        assertTrue(doc.containsField(CHILD_FIELD));
202        assertEquals(ROOT_PATH, doc.getString(PARENT_FIELD));
203        assertNull(doc.getString(CHILD_FIELD));
204    }
205
206    @Test
207    public void testGetDocumentByIdContent() {
208        when(mockTranslator.getPrimaryTypeName(any(Document.class)))
209                .thenReturn(NT_RESOURCE);
210        when(mockNameFactory.create(anyString())).thenReturn(
211                new BasicName("", tmpFile.getName()));
212
213        when(binaryProperty.getFirstValue()).thenReturn(binaryValue);
214        when(mockTranslator.getProperty(any(Document.class), eq(JCR_DATA)))
215                .thenReturn(binaryProperty);
216
217        final Document doc = connector.getDocumentById(ROOT_PATH + tmpFile.getName());
218
219        assertNotNull(doc);
220        assertTrue(doc.containsField(PARENT_FIELD));
221        assertTrue(doc.containsField(CHILD_FIELD));
222        assertEquals(ROOT_PATH, doc.getString(PARENT_FIELD));
223        assertNull(doc.getString(CHILD_FIELD));
224    }
225
226    @Test
227    public void testSha1WhenContentDigestIsCached() {
228        when(mockTranslator.getPrimaryTypeName(any(Document.class)))
229                .thenReturn(NT_RESOURCE);
230        when(mockNameFactory.create(anyString())).thenReturn(new BasicName("", tmpFile.getName()));
231        when(binaryProperty.getFirstValue()).thenReturn(binaryValue);
232        when(mockTranslator.getProperty(any(Document.class), eq(JCR_DATA)))
233                .thenReturn(binaryProperty);
234
235        final String chksum = "aaf4c61ddcc5e8a2dabede0f3b482cd9aea9434d";
236
237        when(mockTranslator.getProperty(any(Document.class), eq(CONTENT_DIGEST)))
238                .thenReturn(new BasicSingleValueProperty(new BasicName("", CONTENT_DIGEST.toString()),
239                                     chksum));
240
241        final String sha1 = connector.sha1(tmpFile);
242
243        assertNotNull(sha1);
244        assert(sha1.contains(chksum));
245    }
246
247    @Test
248    public void testSha1ContentDigestIsNotCached() {
249        when(mockTranslator.getPrimaryTypeName(any(Document.class)))
250                .thenReturn(NT_RESOURCE);
251        when(mockNameFactory.create(anyString())).thenReturn(new BasicName("", tmpFile.getName()));
252        when(binaryProperty.getFirstValue()).thenReturn(binaryValue);
253        when(mockTranslator.getProperty(any(Document.class), eq(JCR_DATA)))
254                .thenReturn(binaryProperty);
255
256        final String chksum = "aaf4c61ddcc5e8a2dabede0f3b482cd9aea9434d";
257
258        when(mockTranslator.getProperty(any(Document.class), eq(CONTENT_DIGEST)))
259                .thenReturn(null);
260
261        final String sha1 = connector.sha1(tmpFile);
262
263        assertNotNull(sha1);
264        assert(sha1.contains(chksum));
265    }
266
267    @Test
268    public void testRemoveDocument() {
269        final String id = "/" + tmpFile2.getName();
270        final FedoraFileSystemConnector spy = spy(connector);
271        assertTrue("Removing document should return true!", spy.removeDocument(id));
272        verify(spy).touchParent(id);
273    }
274
275    @Test
276    public void testStoreDocument() {
277        final String id = "/" + tmpFile.getName();
278        final DocumentReader reader = mock(DocumentReader.class);
279        final FedoraFileSystemConnector spy = spy(connector);
280        doReturn(tmpFile).when(spy).fileFor(anyString());
281        doReturn(reader).when(spy).readDocument(any(Document.class));
282        doReturn(id).when(reader).getDocumentId();
283        doReturn(NT_FILE).when(reader).getPrimaryTypeName();
284        spy.storeDocument(spy.getDocumentById(id));
285        verify(spy).touchParent(id);
286    }
287
288    @Test
289    public void testFileSystemConnectorReadOnly() {
290        assertTrue("FedoraFileSystemConnector is not read-only!", connector.isReadonly());
291    }
292}