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.api.services; 019 020import static org.fcrepo.config.ServerManagedPropsMode.STRICT; 021import static org.junit.Assert.assertEquals; 022import static org.junit.Assert.assertTrue; 023import static org.junit.Assert.assertFalse; 024import static org.mockito.Mockito.when; 025import static org.fcrepo.kernel.api.RdfCollectors.toModel; 026import static org.fcrepo.kernel.api.rdf.DefaultRdfStream.fromModel; 027import static org.slf4j.LoggerFactory.getLogger; 028 029import org.apache.jena.graph.Node; 030import org.apache.jena.graph.NodeFactory; 031import org.apache.jena.rdf.model.ModelFactory; 032import org.apache.jena.rdf.model.ResourceFactory; 033import org.apache.jena.vocabulary.DC; 034import org.apache.jena.vocabulary.DCTerms; 035import javax.ws.rs.core.MediaType; 036import org.apache.jena.rdf.model.Model; 037import org.apache.jena.rdf.model.Resource; 038import org.junit.Before; 039import org.junit.Test; 040import java.io.InputStream; 041import java.io.ByteArrayInputStream; 042import org.junit.runner.RunWith; 043 044import org.fcrepo.config.FedoraPropsConfig; 045import org.fcrepo.http.commons.api.rdf.HttpIdentifierConverter; 046import org.fcrepo.kernel.api.identifiers.FedoraId; 047import org.fcrepo.kernel.api.models.FedoraResource; 048import org.fcrepo.kernel.api.RdfStream; 049import org.mockito.InjectMocks; 050import org.mockito.Mock; 051import org.mockito.junit.MockitoJUnitRunner; 052import org.slf4j.Logger; 053 054/** 055 * Unit tests for HttpRdfService 056 * @author bseeger 057 * @since 2019-11-08 058 */ 059@RunWith(MockitoJUnitRunner.Silent.class) 060public class HttpRdfServiceTest { 061 062 private static final Logger log = getLogger(HttpRdfService.class); 063 064 @Mock 065 private HttpIdentifierConverter idTranslator; 066 067 @Mock 068 private FedoraResource resource; 069 070 @Mock 071 private FedoraPropsConfig fedoraPropsConfig; 072 073 @InjectMocks 074 private HttpRdfService httpRdfService; 075 076 private static final String FEDORA_URI_1 = "http://www.example.com/fedora/rest/resource1"; 077 private static final Resource FEDORA_URI_1_RESOURCE = ResourceFactory.createResource(FEDORA_URI_1); 078 private static final String FEDORA_URI_2 = "http://www.example.com/fedora/rest/resource2"; 079 private static final Resource FEDORA_URI_2_RESOURCE = ResourceFactory.createResource(FEDORA_URI_2); 080 private static final FedoraId FEDORA_ID_1 = FedoraId.create("info:fedora/resource1"); 081 private static final Resource FEDORA_ID_1_RESOURCE = ResourceFactory.createResource(FEDORA_ID_1.getFullId()); 082 private static final FedoraId FEDORA_ID_2 = FedoraId.create("info:fedora/resource2"); 083 private static final Resource FEDORA_ID_2_RESOURCE = ResourceFactory.createResource(FEDORA_ID_2.getFullId()); 084 private static final String NON_FEDORA_URI = "http://www.otherdomain.org/resource5"; 085 private static final Resource NON_FEDORA_URI_RESOURCE = ResourceFactory.createResource(NON_FEDORA_URI); 086 private static final String RDF = 087 "@prefix dc: <" + DC.getURI() + "> ." + 088 "@prefix dcterms: <" + DCTerms.getURI() + "> ." + 089 "<" + FEDORA_URI_1 + "> dc:title 'fancy title' ;" + 090 " dcterms:isPartOf <" + NON_FEDORA_URI + "> ;" + 091 " dcterms:isPartOf <" + FEDORA_URI_2 + "> ."; 092 private static final String INTERNAL_RDF = 093 "@prefix dc: <" + DC.getURI() + "> ." + 094 "@prefix dcterms: <" + DCTerms.getURI() + "> ." + 095 "<" + FEDORA_ID_1 + "> dc:title 'fancy title' ;" + 096 " dcterms:isPartOf <" + NON_FEDORA_URI + "> ;" + 097 " dcterms:isPartOf <" + FEDORA_ID_2 + "> ."; 098 private static final MediaType CONTENT_TYPE = new MediaType("text", "turtle"); 099 100 @Before 101 public void setup() { 102 when(fedoraPropsConfig.getServerManagedPropsMode()).thenReturn(STRICT); 103 when(idTranslator.translateUri(FEDORA_URI_1)).thenReturn(FEDORA_ID_1.getFullId()); 104 when(idTranslator.translateUri(FEDORA_URI_2)).thenReturn(FEDORA_ID_2.getFullId()); 105 when(idTranslator.translateUri(NON_FEDORA_URI)).thenReturn(NON_FEDORA_URI); 106 107 when(idTranslator.toExternalId(FEDORA_ID_1.getFullId())).thenReturn(FEDORA_URI_1); 108 when(idTranslator.toExternalId(FEDORA_ID_2.getFullId())).thenReturn(FEDORA_URI_2); 109 when(idTranslator.inInternalDomain(FEDORA_ID_1.getFullId())).thenReturn(true); 110 when(idTranslator.inInternalDomain(FEDORA_ID_2.getFullId())).thenReturn(true); 111 when(idTranslator.inInternalDomain(NON_FEDORA_URI)).thenReturn(false); 112 113 when(resource.getId()).thenReturn(FEDORA_ID_1.getFullId()); 114 115 when(idTranslator.translateUri("http://other.com/resource")).thenReturn("http://other.com/resource"); 116 when(idTranslator.translateUri(FEDORA_ID_1.getFullId())).thenReturn(FEDORA_ID_1.getFullId()); 117 118 log.debug("Rdf is: {}", RDF); 119 } 120 121 @Test 122 public void testGetModelFromInputStream() { 123 final InputStream requestBodyStream = new ByteArrayInputStream(RDF.getBytes()); 124 final Model model = httpRdfService.bodyToInternalModel(FEDORA_ID_1, requestBodyStream, 125 CONTENT_TYPE, idTranslator, false); 126 127 assertFalse(model.isEmpty()); 128 129 verifyTriples(model); 130 } 131 132 @Test 133 public void testConvertInternalToExternalStream() { 134 final InputStream requestBodyStream = new ByteArrayInputStream(INTERNAL_RDF.getBytes()); 135 final Node topic = NodeFactory.createURI(FEDORA_ID_1.getEncodedFullId()); 136 final Model internalGraph = ModelFactory.createDefaultModel(); 137 internalGraph.read(requestBodyStream, FEDORA_ID_1.getEncodedFullId(), "TURTLE"); 138 final RdfStream stream = fromModel(topic, internalGraph); 139 140 final RdfStream converted = httpRdfService.bodyToExternalStream(FEDORA_ID_1.getFullId(), stream, 141 idTranslator); 142 143 final Model model = converted.collect(toModel()); 144 assertFalse(model.contains(FEDORA_ID_1_RESOURCE, DCTerms.isPartOf, NON_FEDORA_URI_RESOURCE)); 145 assertTrue(model.contains(FEDORA_URI_1_RESOURCE, DCTerms.isPartOf, NON_FEDORA_URI_RESOURCE)); 146 147 assertFalse(model.contains(FEDORA_ID_1_RESOURCE, DCTerms.isPartOf, FEDORA_ID_2_RESOURCE)); 148 assertTrue(model.contains(FEDORA_URI_1_RESOURCE, DCTerms.isPartOf, FEDORA_URI_2_RESOURCE)); 149 150 assertFalse(model.containsResource(FEDORA_ID_1_RESOURCE)); 151 assertFalse(model.containsResource(FEDORA_ID_2_RESOURCE)); 152 } 153 154 @Test 155 public void testPatchToInternal_EmptySubjectUpdate() { 156 final String rdf = "prefix test: <http://example.org#> INSERT { <> test:pointer <http://other.com/resource>" + 157 " } WHERE {}"; 158 final String translated = httpRdfService.patchRequestToInternalString(FEDORA_ID_1, rdf, idTranslator); 159 assertStringMatch("<" + FEDORA_ID_1 + "> <http://example.org#pointer> <http://other.com/resource>", 160 translated); 161 } 162 163 @Test 164 public void testPatchToInternal_EmptySubjectInsert() { 165 final String rdf = "prefix test: <http://example.org#> INSERT DATA { <> test:pointer " + 166 "<http://other.com/resource> }"; 167 final String translated = httpRdfService.patchRequestToInternalString(FEDORA_ID_1, rdf, idTranslator); 168 assertStringMatch("<" + FEDORA_ID_1 + "> <http://example.org#pointer> <http://other.com/resource>", 169 translated); 170 } 171 172 @Test 173 public void testPatchToInternal_EmptySubjectDelete() { 174 final String rdf = "prefix test: <http://example.org#> DELETE DATA { <> test:pointer " + 175 "<http://other.com/resource> }"; 176 final String translated = httpRdfService.patchRequestToInternalString(FEDORA_ID_1, rdf, idTranslator); 177 assertStringMatch("<" + FEDORA_ID_1 + "> <http://example.org#pointer> <http://other.com/resource>", 178 translated); 179 } 180 181 @Test 182 public void testPatchToInternal_ExplicitSubjectUpdate() { 183 final String rdf = "prefix test: <http://example.org#> INSERT { <" + FEDORA_URI_1 + "> test:pointer " + 184 "<http://other.com/resource> } WHERE {}"; 185 final String translated = httpRdfService.patchRequestToInternalString(FEDORA_ID_1, rdf, idTranslator); 186 assertStringMatch("<" + FEDORA_ID_1 + "> <http://example.org#pointer> <http://other.com/resource>", 187 translated); 188 } 189 190 @Test 191 public void testPatchToInternal_ExplicitSubjectInsert() { 192 final String rdf = "prefix test: <http://example.org#> INSERT DATA { <" + FEDORA_URI_1 + "> test:pointer " + 193 "<http://other.com/resource> }"; 194 final String translated = httpRdfService.patchRequestToInternalString(FEDORA_ID_1, rdf, idTranslator); 195 assertStringMatch("<" + FEDORA_ID_1 + "> <http://example.org#pointer> <http://other.com/resource>", 196 translated); 197 } 198 199 @Test 200 public void testPatchToInternal_ExplicitSubjectDelete() { 201 final String rdf = "prefix test: <http://example.org#> DELETE DATA { <" + FEDORA_URI_1 + "> test:pointer " + 202 "<http://other.com/resource> }"; 203 final String translated = httpRdfService.patchRequestToInternalString(FEDORA_ID_1, rdf, idTranslator); 204 assertStringMatch("<" + FEDORA_ID_1 + "> <http://example.org#pointer> <http://other.com/resource>", 205 translated); 206 } 207 208 @Test 209 public void testPatchToInternal_EmptySubjectInsertDeleteWhere() { 210 final String rdf = "prefix test: <http://example.org#> DELETE { <> test:pointer ?o } INSERT { <> test:pointer" + 211 " <http://other.com/resource> } WHERE { <> test:pointer ?o }"; 212 final String translated = httpRdfService.patchRequestToInternalString(FEDORA_ID_1, rdf, idTranslator); 213 assertStringMatch("<" + FEDORA_ID_1 + "> <http://example.org#pointer> ?o ", translated); 214 assertStringMatch("<" + FEDORA_ID_1 + "> <http://example.org#pointer> <http://other.com/resource>", 215 translated); 216 } 217 218 @Test 219 public void testPatchToInternal_EmptySubjectObjectInsert() { 220 final String rdf = "prefix test: <http://example.org#> INSERT DATA { <> test:pointer " + 221 "<" + FEDORA_URI_2 + "> }"; 222 final String translated = httpRdfService.patchRequestToInternalString(FEDORA_ID_1, rdf, idTranslator); 223 assertStringMatch("<" + FEDORA_ID_1 + "> <http://example.org#pointer> <" + FEDORA_ID_2 + ">", 224 translated); 225 } 226 227 @Test 228 public void testPatchToInternal_ExplicitSubjectObjectInsert() { 229 final String rdf = "prefix test: <http://example.org#> INSERT DATA { <" + FEDORA_URI_1 + "> " + 230 "test:pointer <" + FEDORA_URI_2 + "> }"; 231 final String translated = httpRdfService.patchRequestToInternalString(FEDORA_ID_1, rdf, idTranslator); 232 assertStringMatch("<" + FEDORA_ID_1 + "> <http://example.org#pointer> <" + FEDORA_ID_2 + ">", 233 translated); 234 } 235 236 @Test 237 public void testPatchToInternal_EmptySubjectExplicitObjectDelete() { 238 final String rdf = "prefix test: <http://example.org#> DELETE DATA { <> test:pointer <" + FEDORA_URI_2 + "> }"; 239 final String translated = httpRdfService.patchRequestToInternalString(FEDORA_ID_1, rdf, idTranslator); 240 assertStringMatch("<" + FEDORA_ID_1 + "> <http://example.org#pointer> <" + FEDORA_ID_2 + ">", 241 translated); 242 } 243 244 @Test 245 public void testPatchToInternal_ExplicitSubjectObjectDelete() { 246 final String rdf = "prefix test: <http://example.org#> DELETE DATA { <" + FEDORA_URI_1 + "> test:pointer <" + 247 FEDORA_URI_2 + "> }"; 248 final String translated = httpRdfService.patchRequestToInternalString(FEDORA_ID_1, rdf, idTranslator); 249 assertStringMatch("<" + FEDORA_ID_1 + "> <http://example.org#pointer> <" + FEDORA_ID_2 + ">", 250 translated); 251 } 252 253 @Test 254 public void testPatchToInternal_EmptySubjectExplicitObjectUpdate() { 255 final String rdf = "prefix test: <http://example.org#> DELETE { <> test:pointer ?o } INSERT { <> test:pointer" + 256 " <" + FEDORA_URI_2 + "> } WHERE { <> test:pointer ?o }"; 257 final String translated = httpRdfService.patchRequestToInternalString(FEDORA_ID_1, rdf, idTranslator); 258 assertStringMatch("<" + FEDORA_ID_1 + "> <http://example.org#pointer> <" + FEDORA_ID_2 + ">", 259 translated); 260 assertStringMatch("<" + FEDORA_ID_1 + "> <http://example.org#pointer> ?o", translated); 261 } 262 263 @Test 264 public void testPatchToInternal_ExplicitSubjectObjectUpdate() { 265 final String rdf = "prefix test: <http://example.org#> DELETE { <" + FEDORA_URI_2 + "> " + 266 "test:pointer ?o } INSERT { <" + FEDORA_URI_2 + "> test:pointer" + 267 " <" + FEDORA_URI_1 + "> } WHERE { <" + FEDORA_URI_2 + "> test:pointer ?o }"; 268 final String translated = httpRdfService.patchRequestToInternalString(FEDORA_ID_2, rdf, idTranslator); 269 assertStringMatch("<" + FEDORA_ID_2 + "> <http://example.org#pointer> <" + FEDORA_ID_1 + ">", 270 translated); 271 assertStringMatch("<" + FEDORA_ID_2 + "> <http://example.org#pointer> ?o", translated); 272 } 273 274 @Test 275 public void testPatchToInternal_ExplicitSubjectObjectUpdateWhereSubject() { 276 final String rdf = "prefix test: <http://example.org#> INSERT { <" + FEDORA_URI_2 + "> test:pointer" + 277 " <" + FEDORA_URI_1 + "> } WHERE { <" + FEDORA_URI_2 + "> test:pointer ?o }"; 278 final String translated = httpRdfService.patchRequestToInternalString(FEDORA_ID_2, rdf, idTranslator); 279 assertStringMatch("<" + FEDORA_ID_2 + "> <http://example.org#pointer> " + 280 "<" + FEDORA_ID_1 + ">", translated); 281 assertStringMatch("<" + FEDORA_ID_2 + "> <http://example.org#pointer> ?o", translated); 282 } 283 284 @Test 285 public void testPatchToInternal_ExplicitSubjectObjectUpdateWhereObject() { 286 final String rdf = "prefix test: <http://example.org#> INSERT { <" + FEDORA_URI_2 + "> test:pointer" + 287 " <" + FEDORA_URI_1 + "> } WHERE { ?o test:pointer <" + FEDORA_URI_2 + "> }"; 288 final String translated = httpRdfService.patchRequestToInternalString(FEDORA_ID_2, rdf, idTranslator); 289 assertStringMatch("<" + FEDORA_ID_2 + "> <http://example.org#pointer> " + 290 "<" + FEDORA_ID_1 + ">", translated); 291 assertStringMatch("?o <http://example.org#pointer> <" + FEDORA_ID_2 + ">", translated); 292 } 293 294 /** 295 * Assert 2 strings match regardless of whitespace. 296 * @param expected the expected string. 297 * @param comparison the string to test. 298 */ 299 private void assertStringMatch(final String expected, final String comparison) { 300 final String cleanedExpected = expected.replaceAll("[\t\n]", "") 301 .replaceAll(" {2,}", " "); 302 final String cleanedTest = comparison.replaceAll("[\t\n]", "") 303 .replaceAll(" {2,}", " "); 304 assertEquals(cleanedExpected, cleanedExpected); 305 } 306 307 private void verifyTriples(final Model model) { 308 309 final Resource fedoraResource = model.createResource(FEDORA_ID_1.getEncodedFullId()); 310 311 // make sure it changed the fedora uri to id, but didn't touch the non fedora domain 312 assertTrue(model.contains(fedoraResource, DCTerms.isPartOf, model.createResource(NON_FEDORA_URI))); 313 314 // make sure it translated the fedora uri to fedora id for this triple in both the subject and object 315 assertTrue(model.contains(fedoraResource, DCTerms.isPartOf, 316 model.createResource(FEDORA_ID_2.getEncodedFullId()))); 317 318 // make sure there are no triples that have the subject of the fedora uri 319 assertFalse(model.containsResource(model.createResource(FEDORA_URI_1))); 320 } 321}