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.http.commons.test.util;
017
018import static com.hp.hpl.jena.rdf.model.ModelFactory.createDefaultModel;
019import static java.net.URI.create;
020import static javax.ws.rs.core.UriBuilder.fromUri;
021import static org.junit.Assert.assertNotNull;
022import static org.apache.jena.riot.RDFLanguages.contentTypeToLang;
023import static org.fcrepo.kernel.api.utils.ContentDigest.asURI;
024import static org.mockito.Matchers.anyString;
025import static org.mockito.Mockito.mock;
026import static org.mockito.Mockito.when;
027
028import java.io.IOException;
029import java.io.InputStream;
030import java.lang.reflect.Field;
031import java.net.URI;
032import java.security.MessageDigest;
033import java.security.NoSuchAlgorithmException;
034import java.security.Principal;
035import java.util.Arrays;
036import java.util.Collection;
037import java.util.Date;
038
039import javax.jcr.LoginException;
040import javax.jcr.Node;
041import javax.jcr.NodeIterator;
042import javax.jcr.RepositoryException;
043import javax.jcr.Session;
044import javax.jcr.ValueFactory;
045import javax.jcr.Workspace;
046import javax.jcr.nodetype.NodeType;
047import javax.jcr.nodetype.NodeTypeIterator;
048import javax.jcr.nodetype.NodeTypeManager;
049import javax.jcr.query.Query;
050import javax.jcr.query.QueryResult;
051import javax.ws.rs.core.MediaType;
052import javax.ws.rs.core.SecurityContext;
053import javax.ws.rs.core.UriBuilder;
054import javax.ws.rs.core.UriInfo;
055
056import org.apache.commons.io.IOUtils;
057import org.apache.http.HttpEntity;
058import org.apache.http.util.EntityUtils;
059import org.apache.jena.riot.Lang;
060
061import org.fcrepo.http.commons.AbstractResource;
062import org.fcrepo.kernel.api.models.NonRdfSourceDescription;
063import org.fcrepo.kernel.api.models.FedoraBinary;
064import org.fcrepo.kernel.api.services.functions.HierarchicalIdentifierSupplier;
065
066import org.mockito.Mockito;
067import org.mockito.invocation.InvocationOnMock;
068import org.mockito.stubbing.Answer;
069import org.modeshape.jcr.api.NamespaceRegistry;
070import org.modeshape.jcr.api.Repository;
071import org.modeshape.jcr.api.query.QueryManager;
072
073import com.hp.hpl.jena.rdf.model.Model;
074import com.hp.hpl.jena.update.GraphStoreFactory;
075
076/**
077 * <p>Abstract TestHelpers class.</p>
078 *
079 * @author awoods
080 */
081public abstract class TestHelpers {
082
083    public static String MOCK_PREFIX = "mockPrefix";
084
085    public static String MOCK_URI_STRING = "mock.namespace.org";
086
087    public static UriInfo getUriInfoImpl() {
088        // UriInfo ui = mock(UriInfo.class,withSettings().verboseLogging());
089        final UriInfo ui = mock(UriInfo.class);
090
091        final Answer<UriBuilder> answer = new Answer<UriBuilder>() {
092
093            @Override
094            public UriBuilder answer(final InvocationOnMock invocation) {
095                return fromUri("http://localhost/fcrepo");
096            }
097        };
098
099        when(ui.getRequestUri()).thenReturn(
100                URI.create("http://localhost/fcrepo"));
101        when(ui.getBaseUri()).thenReturn(create("http://localhost/fcrepo"));
102        when(ui.getBaseUriBuilder()).thenAnswer(answer);
103        when(ui.getAbsolutePathBuilder()).thenAnswer(answer);
104
105        return ui;
106    }
107
108    public static Session getQuerySessionMock() {
109        final Session mock = mock(Session.class);
110        final Workspace mockWS = mock(Workspace.class);
111        final QueryManager mockQM = mock(QueryManager.class);
112        try {
113            final Query mockQ = getQueryMock();
114            when(mockQM.createQuery(anyString(), anyString()))
115                    .thenReturn((org.modeshape.jcr.api.query.Query) mockQ);
116            when(mockWS.getQueryManager()).thenReturn(mockQM);
117        } catch (final RepositoryException e) {
118            e.printStackTrace();
119        }
120        when(mock.getWorkspace()).thenReturn(mockWS);
121        final ValueFactory mockVF = mock(ValueFactory.class);
122        try {
123            when(mock.getValueFactory()).thenReturn(mockVF);
124        } catch (final RepositoryException e) {
125            e.printStackTrace();
126        }
127        return mock;
128    }
129
130    @SuppressWarnings("unchecked")
131    public static Query getQueryMock() {
132        final Query mockQ = mock(Query.class);
133        final QueryResult mockResults = mock(QueryResult.class);
134        final NodeIterator mockNodes = mock(NodeIterator.class);
135        when(mockNodes.getSize()).thenReturn(2L);
136        when(mockNodes.hasNext()).thenReturn(true, true, false);
137        final Node node1 = mock(Node.class);
138        final Node node2 = mock(Node.class);
139        try {
140            when(node1.getName()).thenReturn("node1");
141            when(node2.getName()).thenReturn("node2");
142        } catch (final RepositoryException e) {
143            e.printStackTrace();
144        }
145        when(mockNodes.nextNode()).thenReturn(node1, node2).thenThrow(
146                IndexOutOfBoundsException.class);
147        try {
148            when(mockResults.getNodes()).thenReturn(mockNodes);
149            when(mockQ.execute()).thenReturn(mockResults);
150        } catch (final RepositoryException e) {
151            e.printStackTrace();
152        }
153        return mockQ;
154    }
155
156    @SuppressWarnings("unchecked")
157    public static Session getSessionMock() throws RepositoryException {
158        final String[] mockPrefixes = {MOCK_PREFIX};
159        final Session mockSession = mock(Session.class);
160        final Workspace mockWorkspace = mock(Workspace.class);
161        final NamespaceRegistry mockNameReg = mock(NamespaceRegistry.class);
162        final NodeTypeManager mockNTM = mock(NodeTypeManager.class);
163        final NodeTypeIterator mockNTI = mock(NodeTypeIterator.class);
164        final NodeType mockNodeType = mock(NodeType.class);
165        when(mockSession.getWorkspace()).thenReturn(mockWorkspace);
166        when(mockWorkspace.getNamespaceRegistry()).thenReturn(mockNameReg);
167        when(mockNameReg.getPrefixes()).thenReturn(mockPrefixes);
168        when(mockNameReg.getURI(MOCK_PREFIX)).thenReturn(MOCK_URI_STRING);
169        when(mockWorkspace.getNodeTypeManager()).thenReturn(mockNTM);
170        when(mockNodeType.getName()).thenReturn("mockName");
171        when(mockNodeType.toString()).thenReturn("mockString");
172        when(mockNTM.getAllNodeTypes()).thenReturn(mockNTI);
173        when(mockNTI.hasNext()).thenReturn(true, false);
174        when(mockNTI.nextNodeType()).thenReturn(mockNodeType).thenThrow(
175                ArrayIndexOutOfBoundsException.class);
176        return mockSession;
177    }
178
179    public static Collection<String>
180            parseChildren(final HttpEntity entity) throws IOException {
181        final String body = EntityUtils.toString(entity);
182        System.err.println(body);
183        final String[] names =
184            body.replace("[", "").replace("]", "").trim().split(",\\s?");
185        return Arrays.asList(names);
186    }
187
188    public static Session mockSession(final AbstractResource testObj) {
189
190        final SecurityContext mockSecurityContext = mock(SecurityContext.class);
191        final Principal mockPrincipal = mock(Principal.class);
192
193        final String mockUser = "testuser";
194
195        final Session mockSession = mock(Session.class);
196        when(mockSession.getUserID()).thenReturn(mockUser);
197        when(mockSecurityContext.getUserPrincipal()).thenReturn(mockPrincipal);
198        when(mockPrincipal.getName()).thenReturn(mockUser);
199
200        final Workspace mockWorkspace = mock(Workspace.class);
201        when(mockSession.getWorkspace()).thenReturn(mockWorkspace);
202        when(mockWorkspace.getName()).thenReturn("default");
203
204        setField(testObj, "uriInfo", getUriInfoImpl());
205        setField(testObj, "pidMinter", new PathMinterImpl());
206        return mockSession;
207
208    }
209
210    public static Repository mockRepository() throws LoginException,
211                                             RepositoryException {
212        final Repository mockRepo = mock(Repository.class);
213        when(mockRepo.login()).thenReturn(
214                mock(org.modeshape.jcr.api.Session.class, Mockito.withSettings().extraInterfaces(Session.class)));
215        return mockRepo;
216    }
217
218    public static NonRdfSourceDescription mockDatastream(final String pid,
219                                            final String dsId, final String content) {
220        final FedoraBinary mockBinary = mockBinary(pid, dsId, content);
221        final NonRdfSourceDescription mockDs = mock(NonRdfSourceDescription.class);
222        when(mockDs.getDescribedResource()).thenReturn(mockBinary);
223        when(mockDs.getDescribedResource().getDescription()).thenReturn(mockDs);
224        when(mockDs.getPath()).thenReturn("/" + pid + "/" + dsId);
225        when(mockDs.getCreatedDate()).thenReturn(new Date());
226        when(mockDs.getLastModifiedDate()).thenReturn(new Date());
227        if (content != null) {
228            final MessageDigest md;
229            try {
230                md = MessageDigest.getInstance("SHA-1");
231            } catch (final NoSuchAlgorithmException e) {
232                throw new RuntimeException(e);
233            }
234            final byte[] digest = md.digest(content.getBytes());
235            final URI cd = asURI("SHA-1", digest);
236            when(mockDs.getEtagValue()).thenReturn(cd.toString());
237        }
238        return mockDs;
239    }
240
241    public static FedoraBinary mockBinary(final String pid,
242                                          final String dsId, final String content) {
243        final FedoraBinary mockBinary = mock(FedoraBinary.class);
244        when(mockBinary.getPath()).thenReturn("/" + pid + "/" + dsId + "/jcr:content");
245        when(mockBinary.getMimeType()).thenReturn("application/octet-stream");
246        when(mockBinary.getCreatedDate()).thenReturn(new Date());
247        when(mockBinary.getLastModifiedDate()).thenReturn(new Date());
248        if (content != null) {
249            final MessageDigest md;
250            try {
251                md = MessageDigest.getInstance("SHA-1");
252            } catch (final NoSuchAlgorithmException e) {
253                throw new RuntimeException(e);
254            }
255            final byte[] digest = md.digest(content.getBytes());
256            final URI cd = asURI("SHA-1", digest);
257            when(mockBinary.getContent()).thenReturn(
258                    IOUtils.toInputStream(content));
259            when(mockBinary.getContentDigest()).thenReturn(cd);
260            when(mockBinary.getEtagValue()).thenReturn(cd.toString());
261        }
262        return mockBinary;
263    }
264
265    private static String getRdfSerialization(final HttpEntity entity) {
266        final MediaType mediaType = MediaType.valueOf(entity.getContentType().getValue());
267        final Lang lang = contentTypeToLang(mediaType.toString());
268        assertNotNull("Entity is not an RDF serialization", lang);
269        return lang.getName();
270    }
271
272    public static CloseableGraphStore parseTriples(final HttpEntity entity) throws IOException {
273        return parseTriples(entity.getContent(), getRdfSerialization(entity));
274    }
275
276    public static CloseableGraphStore parseTriples(final InputStream content) {
277        return parseTriples(content, "N3");
278    }
279
280    public static CloseableGraphStore parseTriples(final InputStream content, final String contentType) {
281        final Model model = createDefaultModel();
282        model.read(content, "", contentType);
283        return new CloseableGraphStore(GraphStoreFactory.create(model));
284    }
285
286    /**
287     * Set a field via reflection
288     *
289     * @param parent the owner object of the field
290     * @param name the name of the field
291     * @param obj the value to set
292     */
293    public static void setField(final Object parent, final String name, final Object obj) {
294        /* check the parent class too if the field could not be found */
295        try {
296            final Field f = findField(parent.getClass(), name);
297            f.setAccessible(true);
298            f.set(parent, obj);
299        } catch (IllegalArgumentException | IllegalAccessException | NoSuchFieldException e) {
300            throw new RuntimeException(e);
301        }
302    }
303
304    private static Field findField(final Class<?> clazz, final String name)
305        throws NoSuchFieldException {
306        for (final Field f : clazz.getDeclaredFields()) {
307            if (f.getName().equals(name)) {
308                return f;
309            }
310        }
311        if (clazz.getSuperclass() == null) {
312            throw new NoSuchFieldException("Field " + name
313                    + " could not be found");
314        }
315        return findField(clazz.getSuperclass(), name);
316    }
317
318    private static class PathMinterImpl implements HierarchicalIdentifierSupplier { }
319}