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