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}