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