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.client; 019 020import static java.net.URI.create; 021import static org.fcrepo.client.FedoraHeaderConstants.CONTENT_TYPE; 022import static org.fcrepo.client.FedoraHeaderConstants.LOCATION; 023import static org.fcrepo.client.TestUtils.RDF_XML; 024import static org.fcrepo.client.TestUtils.SPARQL_UPDATE; 025import static org.fcrepo.client.TestUtils.TEXT_TURTLE; 026import static org.fcrepo.client.TestUtils.baseUrl; 027import static org.fcrepo.client.TestUtils.rdfXml; 028import static org.fcrepo.client.TestUtils.setField; 029import static org.fcrepo.client.TestUtils.sparqlUpdate; 030import static org.junit.Assert.assertEquals; 031import static org.mockito.ArgumentMatchers.any; 032import static org.mockito.Mockito.when; 033 034import java.io.ByteArrayInputStream; 035import java.io.IOException; 036import java.io.InputStream; 037import java.net.URI; 038 039import org.apache.commons.io.IOUtils; 040import org.apache.http.Header; 041import org.apache.http.HttpEntity; 042import org.apache.http.StatusLine; 043import org.apache.http.client.methods.CloseableHttpResponse; 044import org.apache.http.client.methods.HttpUriRequest; 045import org.apache.http.entity.ByteArrayEntity; 046import org.apache.http.impl.client.CloseableHttpClient; 047import org.apache.http.message.BasicHeader; 048import org.junit.Before; 049import org.junit.Ignore; 050import org.junit.Test; 051import org.junit.runner.RunWith; 052import org.mockito.Mock; 053import org.mockito.junit.MockitoJUnitRunner; 054 055/** 056 * @author acoburn 057 */ 058@RunWith(MockitoJUnitRunner.class) 059public class FcrepoClientTest { 060 061 private FcrepoClient testClient; 062 063 @Mock 064 private CloseableHttpClient mockHttpclient; 065 066 @Mock 067 private CloseableHttpResponse mockResponse; 068 069 @Mock 070 private StatusLine mockStatus; 071 072 @Mock 073 private HttpEntity mockEntity; 074 075 @Before 076 public void setUp() throws IOException { 077 testClient = FcrepoClient.client().throwExceptionOnFailure().build(); 078 setField(testClient, "httpclient", mockHttpclient); 079 } 080 081 @Test 082 public void testGet() throws IOException, FcrepoOperationFailedException { 083 final int status = 200; 084 final URI uri = create(baseUrl); 085 final ByteArrayEntity entity = new ByteArrayEntity(rdfXml.getBytes()); 086 entity.setContentType(RDF_XML); 087 088 doSetupMockRequest(RDF_XML, entity, status); 089 090 final FcrepoResponse response = testClient.get(uri) 091 .accept(RDF_XML) 092 .preferMinimal() 093 .perform(); 094 095 assertEquals(response.getUrl(), uri); 096 assertEquals(response.getStatusCode(), status); 097 assertEquals(response.getContentType(), RDF_XML); 098 assertEquals(response.getLocation(), null); 099 assertEquals(IOUtils.toString(response.getBody(), "UTF-8"), rdfXml); 100 } 101 102 @Test(expected = FcrepoOperationFailedException.class) 103 public void testGetError() throws Exception { 104 final int status = 400; 105 final URI uri = create(baseUrl); 106 final ByteArrayEntity entity = new ByteArrayEntity(rdfXml.getBytes()); 107 entity.setContentType(RDF_XML); 108 109 doSetupMockRequest(RDF_XML, entity, status); 110 testClient.get(uri) 111 .accept(RDF_XML) 112 .preferRepresentation() 113 .perform(); 114 } 115 116 @Test(expected = FcrepoOperationFailedException.class) 117 public void testGet100() throws Exception { 118 final int status = 100; 119 final URI uri = create(baseUrl); 120 final ByteArrayEntity entity = new ByteArrayEntity(rdfXml.getBytes()); 121 entity.setContentType(RDF_XML); 122 123 doSetupMockRequest(RDF_XML, entity, status); 124 testClient.get(uri) 125 .accept(RDF_XML) 126 .perform(); 127 } 128 129 @Test 130 public void testGet300() throws Exception { 131 final int status = 300; 132 final URI uri = create(baseUrl); 133 final String redirect = baseUrl + "/bar"; 134 final Header linkHeader = new BasicHeader("Link", "<" + redirect + ">; rel=\"describedby\""); 135 final Header contentType = new BasicHeader(CONTENT_TYPE, RDF_XML); 136 final Header[] headers = new Header[] { contentType, linkHeader }; 137 final CloseableHttpResponse mockResponse = doSetupMockRequest(RDF_XML, null, status); 138 139 when(mockResponse.getAllHeaders()).thenReturn(headers); 140 141 final FcrepoResponse response = testClient.get(uri) 142 .accept(RDF_XML) 143 .perform(); 144 145 assertEquals(response.getUrl(), uri); 146 assertEquals(response.getStatusCode(), status); 147 assertEquals(response.getContentType(), RDF_XML); 148 assertEquals(response.getLocation(), create(redirect)); 149 assertEquals(response.getBody(), null); 150 } 151 152 @Test 153 public void testGetNoAccept() throws Exception { 154 final int status = 200; 155 final URI uri = create(baseUrl); 156 157 doSetupMockRequest(RDF_XML, null, status); 158 159 final FcrepoResponse response = testClient.get(uri).perform(); 160 161 assertEquals(response.getUrl(), uri); 162 assertEquals(response.getStatusCode(), status); 163 assertEquals(response.getContentType(), RDF_XML); 164 assertEquals(response.getLocation(), null); 165 assertEquals(response.getBody(), null); 166 } 167 168 @Test 169 public void testHead() throws IOException, FcrepoOperationFailedException { 170 final int status = 200; 171 final URI uri = create(baseUrl); 172 173 doSetupMockRequest(TEXT_TURTLE, null, status); 174 175 final FcrepoResponse response = testClient.head(uri).perform(); 176 177 assertEquals(response.getUrl(), uri); 178 assertEquals(response.getStatusCode(), status); 179 assertEquals(response.getContentType(), TEXT_TURTLE); 180 assertEquals(response.getLocation(), null); 181 assertEquals(response.getBody(), null); 182 } 183 184 @Test 185 public void testHeadersCaseInsensitive() throws Exception { 186 final int status = 200; 187 final URI uri = create(baseUrl); 188 189 doSetupMockRequest(TEXT_TURTLE, null, status); 190 191 final FcrepoResponse response = testClient.head(uri).perform(); 192 193 // Verify that the case of header names returned by server doesn't impact retrieval 194 assertEquals(response.getHeaderValue("content-type"), TEXT_TURTLE); 195 } 196 197 @Test(expected = FcrepoOperationFailedException.class) 198 public void testHeadError() throws IOException, FcrepoOperationFailedException { 199 doSetupMockRequest(TEXT_TURTLE, null, 404); 200 testClient.head(create(baseUrl)).perform(); 201 } 202 203 @Test 204 public void testPut() throws IOException, FcrepoOperationFailedException { 205 final int status = 204; 206 final URI uri = create(baseUrl); 207 final InputStream body = new ByteArrayInputStream(rdfXml.getBytes()); 208 209 doSetupMockRequest(RDF_XML, null, status); 210 211 final FcrepoResponse response = testClient.put(uri) 212 .body(body, RDF_XML) 213 .perform(); 214 215 assertEquals(response.getUrl(), uri); 216 assertEquals(response.getStatusCode(), status); 217 assertEquals(response.getContentType(), RDF_XML); 218 assertEquals(response.getLocation(), null); 219 assertEquals(response.getBody(), null); 220 } 221 222 @Test 223 public void testPutNoBody() throws IOException, FcrepoOperationFailedException { 224 final int status = 204; 225 final URI uri = create(baseUrl); 226 227 doSetupMockRequest(null, null, status); 228 229 final FcrepoResponse response = testClient.put(uri).perform(); 230 231 assertEquals(response.getUrl(), uri); 232 assertEquals(response.getStatusCode(), status); 233 assertEquals(response.getContentType(), null); 234 assertEquals(response.getLocation(), null); 235 assertEquals(response.getBody(), null); 236 } 237 238 @Test 239 public void testPutWithResponseBody() throws IOException, FcrepoOperationFailedException { 240 final int status = 201; 241 final URI uri = create(baseUrl); 242 243 doSetupMockRequest(null, new ByteArrayEntity(uri.toString().getBytes()), status); 244 245 final FcrepoResponse response = testClient.put(uri).perform(); 246 247 assertEquals(response.getUrl(), uri); 248 assertEquals(response.getStatusCode(), status); 249 assertEquals(response.getContentType(), null); 250 assertEquals(response.getLocation(), null); 251 assertEquals(IOUtils.toString(response.getBody(), "UTF-8"), uri.toString()); 252 } 253 254 @Test(expected = FcrepoOperationFailedException.class) 255 public void testPutError() throws IOException, FcrepoOperationFailedException { 256 final int status = 500; 257 final URI uri = create(baseUrl); 258 final InputStream body = new ByteArrayInputStream(rdfXml.getBytes()); 259 260 doSetupMockRequest(RDF_XML, null, status); 261 testClient.put(uri) 262 .body(body, RDF_XML) 263 .perform(); 264 } 265 266 @Test 267 public void testDelete() throws IOException, FcrepoOperationFailedException { 268 final int status = 204; 269 final URI uri = create(baseUrl); 270 271 doSetupMockRequest(SPARQL_UPDATE, null, status); 272 273 final FcrepoResponse response = testClient.delete(uri).perform(); 274 275 assertEquals(response.getUrl(), uri); 276 assertEquals(response.getStatusCode(), status); 277 assertEquals(response.getContentType(), SPARQL_UPDATE); 278 assertEquals(response.getLocation(), null); 279 assertEquals(response.getBody(), null); 280 } 281 282 @Test 283 public void testDeleteWithResponseBody() throws IOException, FcrepoOperationFailedException { 284 final int status = 204; 285 final URI uri = create(baseUrl); 286 final String responseText = "tombstone found"; 287 288 doSetupMockRequest(null, new ByteArrayEntity(responseText.getBytes()), status); 289 290 final FcrepoResponse response = testClient.delete(uri).perform(); 291 292 assertEquals(response.getUrl(), uri); 293 assertEquals(response.getStatusCode(), status); 294 assertEquals(response.getContentType(), null); 295 assertEquals(response.getLocation(), null); 296 assertEquals(IOUtils.toString(response.getBody(), "UTF-8"), responseText); 297 } 298 299 @Test(expected = FcrepoOperationFailedException.class) 300 public void testDeleteError() throws IOException, FcrepoOperationFailedException { 301 final int status = 401; 302 final URI uri = create(baseUrl); 303 304 doSetupMockRequest(SPARQL_UPDATE, null, status); 305 testClient.delete(uri).perform(); 306 } 307 308 @Test 309 public void testPatch() throws IOException, FcrepoOperationFailedException { 310 final int status = 204; 311 final URI uri = create(baseUrl); 312 final InputStream body = new ByteArrayInputStream(sparqlUpdate.getBytes()); 313 314 doSetupMockRequest(SPARQL_UPDATE, null, status); 315 316 final FcrepoResponse response = testClient.patch(uri) 317 .body(body) 318 .perform(); 319 320 assertEquals(response.getUrl(), uri); 321 assertEquals(response.getStatusCode(), status); 322 assertEquals(response.getContentType(), SPARQL_UPDATE); 323 assertEquals(response.getLocation(), null); 324 assertEquals(response.getBody(), null); 325 } 326 327 @Ignore 328 @Test(expected = IllegalArgumentException.class) 329 public void testPatchNoContent() throws IOException, FcrepoOperationFailedException { 330 final int status = 204; 331 final URI uri = create(baseUrl); 332 333 doSetupMockRequest(SPARQL_UPDATE, null, status); 334 testClient.patch(uri).perform(); 335 } 336 337 @Test 338 public void testPatchResponseBody() throws IOException, FcrepoOperationFailedException { 339 final int status = 204; 340 final URI uri = create(baseUrl); 341 final String responseText = "Sparql-update response"; 342 final InputStream body = new ByteArrayInputStream(sparqlUpdate.getBytes()); 343 344 doSetupMockRequest(SPARQL_UPDATE, new ByteArrayEntity(responseText.getBytes()), status); 345 346 final FcrepoResponse response = testClient.patch(uri) 347 .body(body) 348 .perform(); 349 350 assertEquals(response.getUrl(), uri); 351 assertEquals(response.getStatusCode(), status); 352 assertEquals(response.getContentType(), SPARQL_UPDATE); 353 assertEquals(IOUtils.toString(response.getBody(), "UTF-8"), responseText); 354 } 355 356 @Test(expected = FcrepoOperationFailedException.class) 357 public void testPatchError() throws IOException, FcrepoOperationFailedException { 358 final int status = 415; 359 final URI uri = create(baseUrl); 360 final InputStream body = new ByteArrayInputStream(sparqlUpdate.getBytes()); 361 362 doSetupMockRequest(SPARQL_UPDATE, null, status); 363 testClient.patch(uri) 364 .body(body) 365 .perform(); 366 } 367 368 @Test 369 public void testPost() throws IOException, FcrepoOperationFailedException { 370 final int status = 204; 371 final URI uri = create(baseUrl); 372 final InputStream body = new ByteArrayInputStream(sparqlUpdate.getBytes()); 373 374 doSetupMockRequest(SPARQL_UPDATE, null, status); 375 376 final FcrepoResponse response = testClient.post(uri) 377 .body(body, SPARQL_UPDATE) 378 .perform(); 379 380 assertEquals(response.getUrl(), uri); 381 assertEquals(response.getStatusCode(), status); 382 assertEquals(response.getContentType(), SPARQL_UPDATE); 383 assertEquals(response.getLocation(), null); 384 assertEquals(response.getBody(), null); 385 } 386 387 @Test 388 public void testPostResponseBody() throws IOException, FcrepoOperationFailedException { 389 final int status = 204; 390 final URI uri = create(baseUrl); 391 final String responseText = baseUrl + "/bar"; 392 final InputStream body = new ByteArrayInputStream(sparqlUpdate.getBytes()); 393 394 doSetupMockRequest(SPARQL_UPDATE, new ByteArrayEntity(responseText.getBytes()), status); 395 396 final FcrepoResponse response = testClient.post(uri) 397 .body(body, SPARQL_UPDATE) 398 .perform(); 399 400 assertEquals(response.getUrl(), uri); 401 assertEquals(response.getStatusCode(), status); 402 assertEquals(response.getContentType(), SPARQL_UPDATE); 403 assertEquals(response.getLocation(), null); 404 assertEquals(IOUtils.toString(response.getBody(), "UTF-8"), responseText); 405 } 406 407 @Test 408 public void testPostNoBody() throws IOException, FcrepoOperationFailedException { 409 final int status = 204; 410 final URI uri = create(baseUrl); 411 final String responseText = baseUrl + "/bar"; 412 413 doSetupMockRequest(null, new ByteArrayEntity(responseText.getBytes()), status); 414 415 final FcrepoResponse response = testClient.post(uri).perform(); 416 417 assertEquals(response.getUrl(), uri); 418 assertEquals(response.getStatusCode(), status); 419 assertEquals(response.getContentType(), null); 420 assertEquals(response.getLocation(), null); 421 assertEquals(IOUtils.toString(response.getBody(), "UTF-8"), responseText); 422 } 423 424 @Test(expected = FcrepoOperationFailedException.class) 425 public void testPostError() throws IOException, FcrepoOperationFailedException { 426 final int status = 415; 427 final URI uri = create(baseUrl); 428 final InputStream body = new ByteArrayInputStream(sparqlUpdate.getBytes()); 429 430 doSetupMockRequest(SPARQL_UPDATE, null, status); 431 testClient.post(uri) 432 .body(body, SPARQL_UPDATE) 433 .perform(); 434 } 435 436 @Test(expected = IllegalArgumentException.class) 437 public void testPostErrorNullUrl() throws Exception { 438 final int status = 401; 439 final String statusPhrase = "Unauthorized"; 440 final String response = "Response error"; 441 final InputStream body = new ByteArrayInputStream(sparqlUpdate.getBytes()); 442 final ByteArrayEntity responseBody = new ByteArrayEntity(response.getBytes()); 443 444 doSetupMockRequest(SPARQL_UPDATE, responseBody, status, statusPhrase); 445 446 testClient.post(null) 447 .body(body, SPARQL_UPDATE) 448 .perform(); 449 } 450 451 @Test 452 public void testBadRequest() throws IOException, FcrepoOperationFailedException { 453 final URI uri = create(baseUrl); 454 final InputStream body = new ByteArrayInputStream(sparqlUpdate.getBytes()); 455 456 when(mockHttpclient.execute(any(HttpUriRequest.class))).thenThrow(new IOException("Expected error")); 457 458 try { 459 testClient.post(uri) 460 .body(body, SPARQL_UPDATE) 461 .perform(); 462 } catch (final FcrepoOperationFailedException ex) { 463 assertEquals(ex.getUrl(), uri); 464 assertEquals(ex.getStatusText(), "Expected error"); 465 assertEquals(ex.getStatusCode(), -1); 466 } 467 } 468 469 @Test 470 public void testBadResponseBody() throws IOException, FcrepoOperationFailedException { 471 final int status = 200; 472 final URI uri = create(baseUrl); 473 final ByteArrayEntity entity = new ByteArrayEntity(rdfXml.getBytes()); 474 entity.setContentType(RDF_XML); 475 476 doSetupMockRequest(RDF_XML, entity, status); 477 when(mockResponse.getEntity()).thenReturn(mockEntity); 478 when(mockEntity.getContent()).thenThrow(new IOException("Expected IO error")); 479 480 final FcrepoResponse response = testClient.get(uri) 481 .accept(RDF_XML) 482 .preferMinimal() 483 .perform(); 484 485 assertEquals(response.getUrl(), uri); 486 assertEquals(response.getStatusCode(), status); 487 assertEquals(response.getContentType(), RDF_XML); 488 assertEquals(response.getLocation(), null); 489 assertEquals(response.getBody(), null); 490 } 491 492 private CloseableHttpResponse doSetupMockRequest(final String contentType, final ByteArrayEntity entity, 493 final int status) throws IOException { 494 return doSetupMockRequest(contentType, entity, status, null); 495 } 496 497 private CloseableHttpResponse doSetupMockRequest(final String contentType, final ByteArrayEntity entity, 498 final int status, final String statusPhrase) throws IOException { 499 final Header contentTypeHeader = new BasicHeader("Content-Type", contentType); 500 final Header locationHeader = new BasicHeader(LOCATION, null); 501 final Header[] responseHeaders = new Header[] { locationHeader, contentTypeHeader }; 502 503 when(mockHttpclient.execute(any(HttpUriRequest.class))).thenReturn(mockResponse); 504 when(mockResponse.getAllHeaders()).thenReturn(responseHeaders); 505 when(mockResponse.getEntity()).thenReturn(entity); 506 when(mockResponse.getStatusLine()).thenReturn(mockStatus); 507 when(mockStatus.getStatusCode()).thenReturn(status); 508 when(mockStatus.getReasonPhrase()).thenReturn(statusPhrase); 509 510 return mockResponse; 511 } 512}